@openagentsinc/pylon 0.1.16 → 0.2.2
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 +16 -6
- package/package.json +3 -3
- package/src/cli.js +34 -4
- package/src/index.js +89 -14
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ bun install -g @openagentsinc/pylon && pylon
|
|
|
16
16
|
npx @openagentsinc/pylon --version 0.1.16
|
|
17
17
|
npx @openagentsinc/pylon --no-launch
|
|
18
18
|
npx @openagentsinc/pylon --no-updates
|
|
19
|
+
npx @openagentsinc/pylon status --json
|
|
19
20
|
npx @openagentsinc/pylon --download-curated-cache --model gemma-4-e2b --run-diagnostics
|
|
20
21
|
npx @openagentsinc/pylon --verbose
|
|
21
22
|
```
|
|
@@ -27,8 +28,9 @@ The launcher:
|
|
|
27
28
|
- checks GitHub for the latest tagged `pylon-v...` release on each default run,
|
|
28
29
|
or resolves a specific tagged `Pylon` version when `--version` is provided
|
|
29
30
|
- only installs releases initiated by `AtlantisPleb` in GitHub Releases
|
|
30
|
-
- resolves the correct
|
|
31
|
-
|
|
31
|
+
- resolves the correct release asset for the current machine:
|
|
32
|
+
`pylon-v<version>-<os>-<arch>.tar.gz` on darwin/linux and
|
|
33
|
+
`pylon-v<version>-windows-x86_64.zip` on native Windows x86_64
|
|
32
34
|
- falls back to the exact tagged source checkout and builds `pylon` plus
|
|
33
35
|
`pylon-tui` locally when no matching release asset exists for the machine
|
|
34
36
|
- prompts before installing the Rust toolchain via `rustup` if a source build
|
|
@@ -60,23 +62,31 @@ The launcher:
|
|
|
60
62
|
- starts the installed `pylon-tui` by default after the smoke path; that TUI
|
|
61
63
|
starts and supervises the earning worker
|
|
62
64
|
unless `--no-launch` is set
|
|
65
|
+
- forwards CLI subcommands such as `pylon status --json` to the installed
|
|
66
|
+
`pylon` binary after bootstrap instead of opening `pylon-tui`
|
|
67
|
+
- on native Windows x86_64, installs `pylon.exe` and `pylon-tui.exe` from the
|
|
68
|
+
Windows `.zip` release asset and forwards CLI subcommands to `pylon.exe`
|
|
63
69
|
- while the TUI is running on the default release track, checks GitHub Releases
|
|
64
|
-
|
|
70
|
+
on a six-hour cadence and restarts the TUI from a newer trusted cached release
|
|
65
71
|
without replacing the global npm/bun command
|
|
66
72
|
- use `--no-updates` to keep the current installed release running without
|
|
67
|
-
background GitHub release checks; `--version` remains a pinned release run
|
|
73
|
+
background GitHub release checks; `--version` remains a pinned release run.
|
|
74
|
+
Set `GITHUB_TOKEN` or `GH_TOKEN` when you want authenticated GitHub release
|
|
75
|
+
lookups.
|
|
68
76
|
- owns the current auto-update contract. Directly extracted GitHub release
|
|
69
77
|
assets do not contain a native updater today; if an operator runs
|
|
70
78
|
`./pylon` from an unpacked archive, that process stays on its compiled
|
|
71
79
|
version until the operator manually replaces the archive or switches back to
|
|
72
80
|
the npm/bun launcher.
|
|
73
|
-
- for hosted homework/training work, use launcher `0.1.
|
|
81
|
+
- for hosted homework/training work, use launcher `0.1.17` or newer so the
|
|
74
82
|
cached standalone binary auto-updates while the dashboard is open. The
|
|
75
83
|
`pylon-v0.1.16` standalone binary keeps the long hosted homework ID hashing
|
|
76
84
|
from `0.1.14`, refuses to seal terminal training windows until the worker
|
|
77
85
|
contribution artifact bundle has uploaded and verified for validator replay,
|
|
78
86
|
and packages the minimal Psionic training runtime so standalone installs can
|
|
79
|
-
advertise homework-worker capability.
|
|
87
|
+
advertise homework-worker capability. Launcher `0.1.17` adds CLI subcommand
|
|
88
|
+
forwarding and bounds background GitHub release checks to avoid
|
|
89
|
+
unauthenticated rate-limit churn.
|
|
80
90
|
- does not try to install or register a local runtime automatically; the
|
|
81
91
|
bootstrap stays honest about the separate local Gemma runtime
|
|
82
92
|
prerequisite instead of mutating the host behind the user's back
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openagentsinc/pylon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Bootstrap the standalone OpenAgents Pylon release asset and run first-run smoke checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"pylon": "
|
|
7
|
+
"pylon": "bin/pylon"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/OpenAgentsInc/openagents.git",
|
|
19
|
+
"url": "git+https://github.com/OpenAgentsInc/openagents.git",
|
|
20
20
|
"directory": "packages/pylon-bootstrap"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
package/src/cli.js
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
DEFAULT_RELEASE_REPO,
|
|
7
7
|
bootstrapInstalledPylon,
|
|
8
8
|
ensureReleaseInstall,
|
|
9
|
-
launchInstalledPylon,
|
|
10
9
|
launchInstalledPylonWithUpdates,
|
|
11
10
|
resolveBootstrapOutcome,
|
|
12
11
|
resolvePlatformTarget,
|
|
13
12
|
renderBootstrapSummary,
|
|
13
|
+
runInstalledPylonCli,
|
|
14
14
|
} from "./index.js";
|
|
15
15
|
import {
|
|
16
16
|
createTelemetryClient,
|
|
@@ -78,6 +78,8 @@ export function usage() {
|
|
|
78
78
|
npx @openagentsinc/pylon [options]
|
|
79
79
|
bunx @openagentsinc/pylon [options]
|
|
80
80
|
pylon [options]
|
|
81
|
+
pylon [options] <pylon-command> [pylon-options]
|
|
82
|
+
pylon [options] -- <pylon-command> [pylon-options]
|
|
81
83
|
|
|
82
84
|
Description:
|
|
83
85
|
Download the latest tagged standalone Pylon release asset for this machine,
|
|
@@ -86,11 +88,15 @@ Description:
|
|
|
86
88
|
and build it locally instead. Cache the binaries, run the first-run smoke
|
|
87
89
|
path, and then start the Pylon terminal UI by default. The terminal UI manages
|
|
88
90
|
the earning worker and keeps live status visible. The launcher checks GitHub
|
|
89
|
-
for newer tagged pylon-v... releases on each default run and
|
|
91
|
+
for newer tagged pylon-v... releases on each default run and periodically
|
|
90
92
|
while the dashboard is open. Only releases initiated by AtlantisPleb are
|
|
91
93
|
accepted. New standalone binaries are cached under the local bootstrap root;
|
|
92
94
|
the global npm or bun pylon command is not replaced.
|
|
93
95
|
|
|
96
|
+
When a Pylon command is provided, the launcher bootstraps the managed release
|
|
97
|
+
and forwards that command to the installed pylon binary instead of opening
|
|
98
|
+
pylon-tui. For example: pylon status --json.
|
|
99
|
+
|
|
94
100
|
Options:
|
|
95
101
|
--version <x.y.z> Resolve a specific Pylon release.
|
|
96
102
|
--install-root <path> Override the launcher cache/install root.
|
|
@@ -109,7 +115,7 @@ Options:
|
|
|
109
115
|
--skip-model-download Keep the curated GGUF cache skipped.
|
|
110
116
|
--skip-diagnostics Keep optional pylon gemma diagnose skipped.
|
|
111
117
|
--no-launch Do not start pylon-tui after bootstrap.
|
|
112
|
-
--no-updates Disable
|
|
118
|
+
--no-updates Disable background GitHub release polling
|
|
113
119
|
and dashboard restart while pylon runs.
|
|
114
120
|
--verbose Print extra network and recovery detail.
|
|
115
121
|
--debug-network Alias for --verbose.
|
|
@@ -140,10 +146,19 @@ export function parseArgs(argv) {
|
|
|
140
146
|
verbose: false,
|
|
141
147
|
json: false,
|
|
142
148
|
help: false,
|
|
149
|
+
pylonArgs: [],
|
|
143
150
|
};
|
|
144
151
|
|
|
145
152
|
for (let index = 0; index < argv.length; index += 1) {
|
|
146
153
|
const arg = argv[index];
|
|
154
|
+
if (arg === "--") {
|
|
155
|
+
options.pylonArgs = argv.slice(index + 1);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
if (!arg.startsWith("-")) {
|
|
159
|
+
options.pylonArgs = argv.slice(index);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
147
162
|
switch (arg) {
|
|
148
163
|
case "--version":
|
|
149
164
|
options.version = argv[++index];
|
|
@@ -241,6 +256,7 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
241
256
|
ensureReleaseInstallImpl = ensureReleaseInstall,
|
|
242
257
|
bootstrapInstalledPylonImpl = bootstrapInstalledPylon,
|
|
243
258
|
launchInstalledPylonImpl = launchInstalledPylonWithUpdates,
|
|
259
|
+
runInstalledPylonCliImpl = runInstalledPylonCli,
|
|
244
260
|
createTelemetryClientImpl = createTelemetryClient,
|
|
245
261
|
} = dependencies;
|
|
246
262
|
const options = parseArgs(argv);
|
|
@@ -320,7 +336,21 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
320
336
|
reporter?.warning(`Pylon ${outcome.verdict}`, outcome.detail);
|
|
321
337
|
}
|
|
322
338
|
console.log(renderBootstrapSummary(summary));
|
|
323
|
-
if (
|
|
339
|
+
if (options.pylonArgs.length > 0) {
|
|
340
|
+
await runInstalledPylonCliImpl(
|
|
341
|
+
{
|
|
342
|
+
...options,
|
|
343
|
+
...install,
|
|
344
|
+
version: install.version,
|
|
345
|
+
},
|
|
346
|
+
options.pylonArgs,
|
|
347
|
+
{
|
|
348
|
+
...dependencies,
|
|
349
|
+
onStatus: reporter?.status,
|
|
350
|
+
telemetryClient,
|
|
351
|
+
},
|
|
352
|
+
);
|
|
353
|
+
} else if (!options.noLaunch) {
|
|
324
354
|
await launchInstalledPylonImpl(
|
|
325
355
|
{
|
|
326
356
|
...options,
|
package/src/index.js
CHANGED
|
@@ -20,7 +20,7 @@ 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 =
|
|
23
|
+
export const DEFAULT_UPDATE_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
24
24
|
export const DEFAULT_TRUSTED_RELEASE_AUTHOR = "AtlantisPleb";
|
|
25
25
|
const PYLON_RELEASE_TAG_PREFIX = "pylon-v";
|
|
26
26
|
const RELEASE_ASSET_INSTALL_METHOD = "release_asset";
|
|
@@ -30,6 +30,18 @@ const LEGACY_SOURCE_BUILD_SIBLING_REPOSITORIES = {
|
|
|
30
30
|
"spark-sdk": "https://github.com/AtlantisPleb/spark-sdk.git",
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
function archiveExtensionForTarget(target) {
|
|
34
|
+
return target?.os === "windows" ? ".zip" : ".tar.gz";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function binaryExtensionForTarget(target) {
|
|
38
|
+
return target?.os === "windows" ? ".exe" : "";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function binaryNameForTarget(binaryName, target) {
|
|
42
|
+
return `${binaryName}${binaryExtensionForTarget(target)}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
function emitStatus(onStatus, message, detail = null) {
|
|
34
46
|
if (typeof onStatus === "function") {
|
|
35
47
|
onStatus({ message, detail });
|
|
@@ -215,8 +227,9 @@ function requestHeaders() {
|
|
|
215
227
|
accept: "application/vnd.github+json",
|
|
216
228
|
"user-agent": "@openagentsinc/pylon bootstrap",
|
|
217
229
|
};
|
|
218
|
-
|
|
219
|
-
|
|
230
|
+
const githubToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
231
|
+
if (githubToken) {
|
|
232
|
+
headers.authorization = `Bearer ${githubToken}`;
|
|
220
233
|
}
|
|
221
234
|
return headers;
|
|
222
235
|
}
|
|
@@ -389,10 +402,11 @@ export function resolvePlatformTarget(
|
|
|
389
402
|
{
|
|
390
403
|
darwin: "darwin",
|
|
391
404
|
linux: "linux",
|
|
405
|
+
win32: "windows",
|
|
392
406
|
}[platform] ?? null;
|
|
393
407
|
if (!osLabel) {
|
|
394
408
|
throw new Error(
|
|
395
|
-
`Unsupported platform \`${platform}\`. The npm launcher only supports darwin and
|
|
409
|
+
`Unsupported platform \`${platform}\`. The npm launcher only supports darwin, linux, and native Windows in v1.`,
|
|
396
410
|
);
|
|
397
411
|
}
|
|
398
412
|
|
|
@@ -419,10 +433,12 @@ export function buildArchiveBasename(version, target) {
|
|
|
419
433
|
|
|
420
434
|
export function buildAssetNames(version, target) {
|
|
421
435
|
const archiveBasename = buildArchiveBasename(version, target);
|
|
436
|
+
const archiveExtension = archiveExtensionForTarget(target);
|
|
437
|
+
const archiveName = `${archiveBasename}${archiveExtension}`;
|
|
422
438
|
return {
|
|
423
439
|
archiveBasename,
|
|
424
|
-
archiveName
|
|
425
|
-
checksumName: `${
|
|
440
|
+
archiveName,
|
|
441
|
+
checksumName: `${archiveName}.sha256`,
|
|
426
442
|
};
|
|
427
443
|
}
|
|
428
444
|
|
|
@@ -761,8 +777,8 @@ export function buildInstallPaths(installRoot, version, target) {
|
|
|
761
777
|
archivePath: path.join(downloadsDir, archiveName),
|
|
762
778
|
checksumPath: path.join(downloadsDir, checksumName),
|
|
763
779
|
manifestPath: path.join(installDir, "install.json"),
|
|
764
|
-
pylonPath: path.join(installDir, "pylon"),
|
|
765
|
-
pylonTuiPath: path.join(installDir, "pylon-tui"),
|
|
780
|
+
pylonPath: path.join(installDir, binaryNameForTarget("pylon", target)),
|
|
781
|
+
pylonTuiPath: path.join(installDir, binaryNameForTarget("pylon-tui", target)),
|
|
766
782
|
};
|
|
767
783
|
}
|
|
768
784
|
|
|
@@ -1218,8 +1234,18 @@ async function installSourceBuild(
|
|
|
1218
1234
|
stdio: "inherit",
|
|
1219
1235
|
});
|
|
1220
1236
|
|
|
1221
|
-
const builtPylonPath = path.join(
|
|
1222
|
-
|
|
1237
|
+
const builtPylonPath = path.join(
|
|
1238
|
+
repoDir,
|
|
1239
|
+
"target",
|
|
1240
|
+
"release",
|
|
1241
|
+
binaryNameForTarget("pylon", target),
|
|
1242
|
+
);
|
|
1243
|
+
const builtPylonTuiPath = path.join(
|
|
1244
|
+
repoDir,
|
|
1245
|
+
"target",
|
|
1246
|
+
"release",
|
|
1247
|
+
binaryNameForTarget("pylon-tui", target),
|
|
1248
|
+
);
|
|
1223
1249
|
if (!(await pathExists(builtPylonPath)) || !(await pathExists(builtPylonTuiPath))) {
|
|
1224
1250
|
throw new Error(
|
|
1225
1251
|
`Source build completed without the expected binaries at ${path.join(repoDir, "target", "release")}.`,
|
|
@@ -1314,8 +1340,11 @@ async function findLatestCachedInstall(installRoot, target) {
|
|
|
1314
1340
|
|
|
1315
1341
|
const installDir = path.join(versionsDir, entry.name);
|
|
1316
1342
|
const manifestPath = path.join(installDir, "install.json");
|
|
1317
|
-
const pylonPath = path.join(installDir, "pylon");
|
|
1318
|
-
const pylonTuiPath = path.join(
|
|
1343
|
+
const pylonPath = path.join(installDir, binaryNameForTarget("pylon", target));
|
|
1344
|
+
const pylonTuiPath = path.join(
|
|
1345
|
+
installDir,
|
|
1346
|
+
binaryNameForTarget("pylon-tui", target),
|
|
1347
|
+
);
|
|
1319
1348
|
if (
|
|
1320
1349
|
!(await pathExists(manifestPath)) ||
|
|
1321
1350
|
!(await pathExists(pylonPath)) ||
|
|
@@ -1446,8 +1475,34 @@ function spawnPylonTui(pylonTuiPath, options, spawnProcessImpl) {
|
|
|
1446
1475
|
});
|
|
1447
1476
|
}
|
|
1448
1477
|
|
|
1449
|
-
async function extractArchive(archivePath, destinationDir, runProcessImpl) {
|
|
1478
|
+
async function extractArchive(archivePath, destinationDir, target, runProcessImpl) {
|
|
1450
1479
|
await fs.mkdir(destinationDir, { recursive: true });
|
|
1480
|
+
if (target?.os === "windows") {
|
|
1481
|
+
const args = [
|
|
1482
|
+
"-NoProfile",
|
|
1483
|
+
"-NonInteractive",
|
|
1484
|
+
"-ExecutionPolicy",
|
|
1485
|
+
"Bypass",
|
|
1486
|
+
"-Command",
|
|
1487
|
+
"& { param([string]$ArchivePath, [string]$DestinationPath) Expand-Archive -LiteralPath $ArchivePath -DestinationPath $DestinationPath -Force }",
|
|
1488
|
+
archivePath,
|
|
1489
|
+
destinationDir,
|
|
1490
|
+
];
|
|
1491
|
+
for (const command of ["powershell.exe", "pwsh", "powershell"]) {
|
|
1492
|
+
try {
|
|
1493
|
+
await runProcessImpl(command, args);
|
|
1494
|
+
return;
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1497
|
+
if (!message.startsWith(`Failed to start ${command}`)) {
|
|
1498
|
+
throw error;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
throw new Error(
|
|
1503
|
+
"Windows archive extraction requires PowerShell (tried powershell.exe, pwsh, and powershell).",
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1451
1506
|
await runProcessImpl("tar", ["-xzf", archivePath, "-C", destinationDir]);
|
|
1452
1507
|
}
|
|
1453
1508
|
|
|
@@ -1781,7 +1836,7 @@ export async function ensureReleaseInstall(
|
|
|
1781
1836
|
paths.installDir,
|
|
1782
1837
|
);
|
|
1783
1838
|
await fs.rm(paths.installDir, { recursive: true, force: true });
|
|
1784
|
-
await extractArchive(paths.archivePath, paths.versionsDir, runProcessImpl);
|
|
1839
|
+
await extractArchive(paths.archivePath, paths.versionsDir, target, runProcessImpl);
|
|
1785
1840
|
|
|
1786
1841
|
if (!(await pathExists(paths.pylonPath)) || !(await pathExists(paths.pylonTuiPath))) {
|
|
1787
1842
|
throw new Error(
|
|
@@ -2016,6 +2071,26 @@ export async function launchInstalledPylon(
|
|
|
2016
2071
|
|
|
2017
2072
|
export const launchInstalledPylonTui = launchInstalledPylon;
|
|
2018
2073
|
|
|
2074
|
+
export async function runInstalledPylonCli(
|
|
2075
|
+
options,
|
|
2076
|
+
args = [],
|
|
2077
|
+
{
|
|
2078
|
+
runProcessImpl = runProcess,
|
|
2079
|
+
onStatus = null,
|
|
2080
|
+
} = {},
|
|
2081
|
+
) {
|
|
2082
|
+
const pylonPath = path.resolve(options.pylonPath);
|
|
2083
|
+
emitStatus(
|
|
2084
|
+
onStatus,
|
|
2085
|
+
"Running Pylon CLI command",
|
|
2086
|
+
[path.basename(pylonPath), ...args].join(" "),
|
|
2087
|
+
);
|
|
2088
|
+
return runProcessImpl(pylonPath, args, {
|
|
2089
|
+
env: buildPylonEnv(options),
|
|
2090
|
+
stdio: "inherit",
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2019
2094
|
export async function launchInstalledPylonWithUpdates(
|
|
2020
2095
|
options,
|
|
2021
2096
|
{
|