@openagentsinc/pylon 0.1.0 → 0.1.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.
Files changed (4) hide show
  1. package/README.md +20 -4
  2. package/package.json +1 -1
  3. package/src/cli.js +108 -8
  4. package/src/index.js +1050 -82
package/README.md CHANGED
@@ -1,14 +1,19 @@
1
1
  # `@openagentsinc/pylon`
2
2
 
3
3
  Bootstrap the latest tagged standalone `Pylon` release asset from GitHub
4
- Releases and run the first-run smoke path without Cargo.
4
+ Releases, fall back to a deterministic source build when no matching asset
5
+ exists for the local platform, stream first-run status updates in the terminal,
6
+ and open the Pylon terminal UI without Cargo when prebuilt binaries are
7
+ available.
5
8
 
6
9
  ## Usage
7
10
 
8
11
  ```bash
9
12
  npx @openagentsinc/pylon
10
- npx @openagentsinc/pylon --version 0.1.0
11
- npx @openagentsinc/pylon --model gemma-4-e2b --diagnostic-repeats 2
13
+ npx @openagentsinc/pylon --version 0.0.1-rc4
14
+ npx @openagentsinc/pylon --no-launch
15
+ npx @openagentsinc/pylon --download-curated-cache --model gemma-4-e2b --diagnostic-repeats 2
16
+ npx @openagentsinc/pylon --verbose
12
17
  ```
13
18
 
14
19
  The launcher:
@@ -17,12 +22,23 @@ The launcher:
17
22
  tagged `Pylon` version when `--version` is provided
18
23
  - resolves the correct `pylon-v<version>-<os>-<arch>.tar.gz` asset for the
19
24
  current machine
25
+ - falls back to the exact tagged source checkout and builds `pylon` plus
26
+ `pylon-tui` locally when no matching release asset exists for the machine
27
+ - prompts before installing the Rust toolchain via `rustup` if a source build
28
+ is needed and `cargo` / `rustc` are missing
20
29
  - downloads the archive and published SHA-256 checksum
21
30
  - verifies the checksum before extracting
22
31
  - caches the unpacked binaries under `~/.openagents/pylon/bootstrap/`
32
+ - prints status lines such as release resolution, runtime checks, and local
33
+ model scanning while it runs
23
34
  - runs `pylon --help`, `init`, `status --json`, and `inventory --json`
24
- - runs `pylon gemma download <model>`
25
35
  - runs `pylon gemma diagnose <model> --json`
36
+ - only runs `pylon gemma download <model>` when `--download-curated-cache` is
37
+ set, because the optional GGUF cache does not satisfy the sellable runtime by
38
+ itself
39
+ - falls back to `curl` for release metadata and asset downloads when the Node
40
+ fetch path fails in constrained network contexts
41
+ - opens `pylon-tui` by default after the smoke path unless `--no-launch` is set
26
42
 
27
43
  ## Publish
28
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagentsinc/pylon",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Bootstrap the standalone OpenAgents Pylon release asset and run first-run smoke checks.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  DEFAULT_RELEASE_REPO,
7
7
  bootstrapInstalledPylon,
8
8
  ensureReleaseInstall,
9
+ launchInstalledPylonTui,
9
10
  renderBootstrapSummary,
10
11
  } from "./index.js";
11
12
 
@@ -17,28 +18,83 @@ function parseIntegerFlag(value, label) {
17
18
  return parsed;
18
19
  }
19
20
 
21
+ function createTerminalStyles(enableColor) {
22
+ if (!enableColor) {
23
+ return {
24
+ bold: (value) => value,
25
+ cyan: (value) => value,
26
+ dim: (value) => value,
27
+ green: (value) => value,
28
+ red: (value) => value,
29
+ yellow: (value) => value,
30
+ };
31
+ }
32
+
33
+ const wrap = (open, close) => (value) => `${open}${value}${close}`;
34
+ return {
35
+ bold: wrap("\u001B[1m", "\u001B[22m"),
36
+ cyan: wrap("\u001B[36m", "\u001B[39m"),
37
+ dim: wrap("\u001B[2m", "\u001B[22m"),
38
+ green: wrap("\u001B[32m", "\u001B[39m"),
39
+ red: wrap("\u001B[31m", "\u001B[39m"),
40
+ yellow: wrap("\u001B[33m", "\u001B[39m"),
41
+ };
42
+ }
43
+
44
+ function createReporter({ enableColor = process.stdout.isTTY && !process.env.NO_COLOR } = {}) {
45
+ const styles = createTerminalStyles(enableColor);
46
+ return {
47
+ status({ message, detail = null }) {
48
+ const prefix = styles.cyan("›");
49
+ const suffix = detail ? ` ${styles.dim(detail)}` : "";
50
+ console.log(`${prefix} ${styles.bold(message)}${suffix}`);
51
+ },
52
+ success(message, detail = null) {
53
+ const suffix = detail ? ` ${styles.dim(detail)}` : "";
54
+ console.log(`${styles.green("✓")} ${styles.bold(message)}${suffix}`);
55
+ },
56
+ warning(message, detail = null) {
57
+ const suffix = detail ? ` ${styles.dim(detail)}` : "";
58
+ console.log(`${styles.yellow("!")} ${styles.bold(message)}${suffix}`);
59
+ },
60
+ failure(message, detail = null) {
61
+ const suffix = detail ? ` ${styles.dim(detail)}` : "";
62
+ console.error(`${styles.red("x")} ${styles.bold(message)}${suffix}`);
63
+ },
64
+ };
65
+ }
66
+
20
67
  export function usage() {
21
68
  return `Usage:
22
69
  npx @openagentsinc/pylon [options]
23
70
 
24
71
  Description:
25
72
  Download the latest tagged standalone Pylon release asset for this machine,
26
- or a specific tagged Pylon version when --version is set. Verify its
27
- checksum, cache the binaries locally, and run the first-run smoke path.
73
+ or a specific tagged Pylon version when --version is set. If no matching
74
+ asset exists for the local platform, fetch the exact tagged source checkout
75
+ and build it locally instead. Cache the binaries, run the first-run smoke
76
+ path, and then open the Pylon terminal UI by default with live status
77
+ updates.
28
78
 
29
79
  Options:
30
80
  --version <x.y.z> Resolve a specific Pylon release.
31
81
  --install-root <path> Override the launcher cache/install root.
32
82
  --config-path <path> Override OPENAGENTS_PYLON_CONFIG_PATH.
33
83
  --pylon-home <path> Override OPENAGENTS_PYLON_HOME.
34
- --model <model-id> Model to download and diagnose.
84
+ --model <model-id> Model to diagnose, and optionally
85
+ prefetch into the local GGUF cache.
35
86
  Default: ${DEFAULT_MODEL_ID}
87
+ --download-curated-cache Prefetch the optional Hugging Face GGUF
88
+ cache before opening the TUI.
36
89
  --diagnostic-repeats <n> Repeat count for pylon gemma diagnose.
37
90
  Default: ${DEFAULT_DIAGNOSTIC_REPEATS}
38
91
  --diagnostic-max-output-tokens <n> Max output tokens for diagnostics.
39
92
  Default: ${DEFAULT_DIAGNOSTIC_MAX_OUTPUT_TOKENS}
40
- --skip-model-download Skip pylon gemma download.
93
+ --skip-model-download Keep the curated GGUF cache skipped.
41
94
  --skip-diagnostics Skip pylon gemma diagnose.
95
+ --no-launch Do not open pylon-tui after bootstrap.
96
+ --verbose Print extra network and recovery detail.
97
+ --debug-network Alias for --verbose.
42
98
  --json Emit a machine-readable JSON summary.
43
99
 
44
100
  Test and maintainer options:
@@ -59,8 +115,10 @@ export function parseArgs(argv) {
59
115
  model: DEFAULT_MODEL_ID,
60
116
  diagnosticRepeats: DEFAULT_DIAGNOSTIC_REPEATS,
61
117
  diagnosticMaxOutputTokens: DEFAULT_DIAGNOSTIC_MAX_OUTPUT_TOKENS,
62
- skipModelDownload: false,
118
+ skipModelDownload: true,
63
119
  skipDiagnostics: false,
120
+ noLaunch: false,
121
+ verbose: false,
64
122
  json: false,
65
123
  help: false,
66
124
  };
@@ -98,6 +156,9 @@ export function parseArgs(argv) {
98
156
  throw new Error("--model requires a value.");
99
157
  }
100
158
  break;
159
+ case "--download-curated-cache":
160
+ options.skipModelDownload = false;
161
+ break;
101
162
  case "--diagnostic-repeats":
102
163
  options.diagnosticRepeats = parseIntegerFlag(
103
164
  argv[++index],
@@ -116,6 +177,13 @@ export function parseArgs(argv) {
116
177
  case "--skip-diagnostics":
117
178
  options.skipDiagnostics = true;
118
179
  break;
180
+ case "--no-launch":
181
+ options.noLaunch = true;
182
+ break;
183
+ case "--verbose":
184
+ case "--debug-network":
185
+ options.verbose = true;
186
+ break;
119
187
  case "--json":
120
188
  options.json = true;
121
189
  break;
@@ -144,26 +212,58 @@ export function parseArgs(argv) {
144
212
  }
145
213
 
146
214
  export async function main(argv = process.argv.slice(2), dependencies = {}) {
215
+ const {
216
+ ensureReleaseInstallImpl = ensureReleaseInstall,
217
+ bootstrapInstalledPylonImpl = bootstrapInstalledPylon,
218
+ launchInstalledPylonTuiImpl = launchInstalledPylonTui,
219
+ } = dependencies;
147
220
  const options = parseArgs(argv);
148
221
  if (options.help) {
149
222
  console.log(usage());
150
223
  return null;
151
224
  }
152
225
 
153
- const install = await ensureReleaseInstall(options, dependencies);
154
- const summary = await bootstrapInstalledPylon(
226
+ const reporter = options.json ? null : createReporter();
227
+
228
+ const install = await ensureReleaseInstallImpl(options, {
229
+ ...dependencies,
230
+ onStatus: reporter?.status,
231
+ });
232
+ const summary = await bootstrapInstalledPylonImpl(
155
233
  {
156
234
  ...options,
157
235
  ...install,
158
236
  version: install.version,
159
237
  },
160
- dependencies,
238
+ {
239
+ ...dependencies,
240
+ onStatus: reporter?.status,
241
+ },
161
242
  );
162
243
 
163
244
  if (options.json) {
164
245
  console.log(JSON.stringify(summary, null, 2));
165
246
  } else {
247
+ reporter?.success("Pylon bootstrap complete");
166
248
  console.log(renderBootstrapSummary(summary));
249
+ if (!options.noLaunch) {
250
+ await launchInstalledPylonTuiImpl(
251
+ {
252
+ ...options,
253
+ ...install,
254
+ version: install.version,
255
+ },
256
+ {
257
+ ...dependencies,
258
+ onStatus: reporter?.status,
259
+ },
260
+ );
261
+ } else {
262
+ reporter?.warning(
263
+ "Skipped Pylon terminal UI launch",
264
+ "pass no flag to open pylon-tui by default",
265
+ );
266
+ }
167
267
  }
168
268
  return summary;
169
269
  }