@openagentsinc/pylon 0.1.3 → 0.1.5

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  Bootstrap the latest tagged standalone `Pylon` release asset from GitHub
4
4
  Releases, fall back to a deterministic source build when no matching asset
5
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
6
+ and start the Pylon default earning loop without Cargo when prebuilt binaries are
7
7
  available.
8
8
 
9
9
  ## Usage
@@ -13,7 +13,7 @@ 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.0.1-rc4
16
+ npx @openagentsinc/pylon --version 0.0.1-rc10
17
17
  npx @openagentsinc/pylon --no-launch
18
18
  npx @openagentsinc/pylon --download-curated-cache --model gemma-4-e2b --diagnostic-repeats 2
19
19
  npx @openagentsinc/pylon --verbose
@@ -31,6 +31,9 @@ The launcher:
31
31
  `pylon-tui` locally when no matching release asset exists for the machine
32
32
  - prompts before installing the Rust toolchain via `rustup` if a source build
33
33
  is needed and `cargo` / `rustc` are missing
34
+ - emits best-effort anonymous installer telemetry to `openagents.com` so the
35
+ public stats page can show install starts, completions, source-build fallbacks,
36
+ Rust prompts, and smoke-test outcomes
34
37
  - downloads the archive and published SHA-256 checksum
35
38
  - verifies the checksum before extracting
36
39
  - caches the unpacked binaries under `~/.openagents/pylon/bootstrap/`
@@ -48,11 +51,16 @@ The launcher:
48
51
  itself
49
52
  - falls back to `curl` for release metadata and asset downloads when the Node
50
53
  fetch path fails in constrained network contexts
51
- - opens `pylon-tui` by default after the smoke path unless `--no-launch` is set
54
+ - starts the installed `pylon` earning loop by default after the smoke path
55
+ unless `--no-launch` is set
52
56
  - does not try to install or register a local runtime automatically; the
53
57
  bootstrap stays honest about the separate Ollama-compatible runtime
54
58
  prerequisite instead of mutating the host behind the user's back
55
59
 
60
+ Set `OPENAGENTS_DISABLE_TELEMETRY=1` to disable installer telemetry, or
61
+ `OPENAGENTS_TELEMETRY_URL=http://127.0.0.1:8000/api/telemetry/events` to point
62
+ the launcher at a non-production telemetry endpoint.
63
+
56
64
  ## Publish
57
65
 
58
66
  Publish directly from this package directory:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagentsinc/pylon",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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,10 +6,17 @@ import {
6
6
  DEFAULT_RELEASE_REPO,
7
7
  bootstrapInstalledPylon,
8
8
  ensureReleaseInstall,
9
- launchInstalledPylonTui,
9
+ launchInstalledPylon,
10
10
  resolveBootstrapOutcome,
11
+ resolvePlatformTarget,
11
12
  renderBootstrapSummary,
12
13
  } from "./index.js";
14
+ import {
15
+ createTelemetryClient,
16
+ detectPackageInvoker,
17
+ installSourceForTelemetry,
18
+ telemetryFailureContext,
19
+ } from "./telemetry.js";
13
20
 
14
21
  function parseIntegerFlag(value, label) {
15
22
  const parsed = Number.parseInt(value, 10);
@@ -76,7 +83,7 @@ Description:
76
83
  or a specific tagged Pylon version when --version is set. If no matching
77
84
  asset exists for the local platform, fetch the exact tagged source checkout
78
85
  and build it locally instead. Cache the binaries, run the first-run smoke
79
- path, and then open the Pylon terminal UI by default with live status
86
+ path, and then start the Pylon default earning loop by default with live status
80
87
  updates. The launcher checks GitHub for newer tagged pylon-v... releases on
81
88
  each default run, but only caches the standalone binaries under the local
82
89
  bootstrap root; it does not replace your global npm or bun pylon command.
@@ -90,14 +97,14 @@ Options:
90
97
  prefetch into the local GGUF cache.
91
98
  Default: ${DEFAULT_MODEL_ID}
92
99
  --download-curated-cache Prefetch the optional Hugging Face GGUF
93
- cache before opening the TUI.
100
+ cache before launching pylon.
94
101
  --diagnostic-repeats <n> Repeat count for pylon gemma diagnose.
95
102
  Default: ${DEFAULT_DIAGNOSTIC_REPEATS}
96
103
  --diagnostic-max-output-tokens <n> Max output tokens for diagnostics.
97
104
  Default: ${DEFAULT_DIAGNOSTIC_MAX_OUTPUT_TOKENS}
98
105
  --skip-model-download Keep the curated GGUF cache skipped.
99
106
  --skip-diagnostics Skip pylon gemma diagnose.
100
- --no-launch Do not open pylon-tui after bootstrap.
107
+ --no-launch Do not start pylon after bootstrap.
101
108
  --verbose Print extra network and recovery detail.
102
109
  --debug-network Alias for --verbose.
103
110
  --json Emit a machine-readable JSON summary.
@@ -220,7 +227,8 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
220
227
  const {
221
228
  ensureReleaseInstallImpl = ensureReleaseInstall,
222
229
  bootstrapInstalledPylonImpl = bootstrapInstalledPylon,
223
- launchInstalledPylonTuiImpl = launchInstalledPylonTui,
230
+ launchInstalledPylonImpl = launchInstalledPylon,
231
+ createTelemetryClientImpl = createTelemetryClient,
224
232
  } = dependencies;
225
233
  const options = parseArgs(argv);
226
234
  if (options.help) {
@@ -229,51 +237,109 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
229
237
  }
230
238
 
231
239
  const reporter = options.json ? null : createReporter();
240
+ const startedAt = Date.now();
241
+ const target = (() => {
242
+ try {
243
+ return resolvePlatformTarget(options.platform, options.arch);
244
+ } catch {
245
+ return {
246
+ os: process.platform,
247
+ arch: process.arch,
248
+ };
249
+ }
250
+ })();
251
+ const telemetryClient =
252
+ dependencies.telemetryClient ??
253
+ createTelemetryClientImpl({
254
+ fetchImpl: dependencies.fetchImpl ?? globalThis.fetch,
255
+ });
256
+ const sharedTelemetry = {
257
+ requested_version: options.version ?? "latest",
258
+ os: target.os,
259
+ arch: target.arch,
260
+ platform_key: `${target.os}-${target.arch}`,
261
+ npm_or_bun_invoker: detectPackageInvoker(),
262
+ };
232
263
 
233
- const install = await ensureReleaseInstallImpl(options, {
234
- ...dependencies,
235
- onStatus: reporter?.status,
236
- });
237
- const summary = await bootstrapInstalledPylonImpl(
238
- {
239
- ...options,
240
- ...install,
241
- version: install.version,
242
- },
243
- {
264
+ telemetryClient?.emit?.("installer_started", sharedTelemetry);
265
+
266
+ let install = null;
267
+
268
+ try {
269
+ install = await ensureReleaseInstallImpl(options, {
244
270
  ...dependencies,
245
271
  onStatus: reporter?.status,
246
- },
247
- );
272
+ telemetryClient,
273
+ });
274
+ const summary = await bootstrapInstalledPylonImpl(
275
+ {
276
+ ...options,
277
+ ...install,
278
+ version: install.version,
279
+ },
280
+ {
281
+ ...dependencies,
282
+ onStatus: reporter?.status,
283
+ telemetryClient,
284
+ },
285
+ );
248
286
 
249
- if (options.json) {
250
- console.log(JSON.stringify(summary, null, 2));
251
- } else {
252
- const outcome = resolveBootstrapOutcome(summary);
253
- if (outcome.level === "success") {
254
- reporter?.success(`Pylon ${outcome.verdict}`, outcome.detail);
255
- } else {
256
- reporter?.warning(`Pylon ${outcome.verdict}`, outcome.detail);
257
- }
258
- console.log(renderBootstrapSummary(summary));
259
- if (!options.noLaunch) {
260
- await launchInstalledPylonTuiImpl(
261
- {
262
- ...options,
263
- ...install,
264
- version: install.version,
265
- },
266
- {
267
- ...dependencies,
268
- onStatus: reporter?.status,
269
- },
270
- );
287
+ telemetryClient?.emit?.("installer_finished", {
288
+ ...sharedTelemetry,
289
+ release_tag: summary.tagName,
290
+ release_commit: install.sourceCommit ?? null,
291
+ duration_ms: Date.now() - startedAt,
292
+ result: "success",
293
+ install_source: installSourceForTelemetry(
294
+ summary.installMethod ?? install.installMethod,
295
+ Boolean(summary.cached),
296
+ ),
297
+ });
298
+ await telemetryClient?.flush?.();
299
+
300
+ if (options.json) {
301
+ console.log(JSON.stringify(summary, null, 2));
271
302
  } else {
272
- reporter?.warning(
273
- "Skipped Pylon terminal UI launch",
274
- "pass no flag to open pylon-tui by default",
275
- );
303
+ const outcome = resolveBootstrapOutcome(summary);
304
+ if (outcome.level === "success") {
305
+ reporter?.success(`Pylon ${outcome.verdict}`, outcome.detail);
306
+ } else {
307
+ reporter?.warning(`Pylon ${outcome.verdict}`, outcome.detail);
308
+ }
309
+ console.log(renderBootstrapSummary(summary));
310
+ if (!options.noLaunch) {
311
+ await launchInstalledPylonImpl(
312
+ {
313
+ ...options,
314
+ ...install,
315
+ version: install.version,
316
+ },
317
+ {
318
+ ...dependencies,
319
+ onStatus: reporter?.status,
320
+ },
321
+ );
322
+ } else {
323
+ reporter?.warning(
324
+ "Skipped Pylon launch",
325
+ "pass no flag to start the default earning loop",
326
+ );
327
+ }
276
328
  }
329
+ return summary;
330
+ } catch (error) {
331
+ telemetryClient?.emit?.("installer_finished", {
332
+ ...sharedTelemetry,
333
+ release_tag: install?.tagName ?? null,
334
+ release_commit: install?.sourceCommit ?? null,
335
+ duration_ms: Date.now() - startedAt,
336
+ result: "failed",
337
+ install_source: install
338
+ ? installSourceForTelemetry(install.installMethod, Boolean(install.cached))
339
+ : null,
340
+ ...telemetryFailureContext(error, "launcher"),
341
+ });
342
+ await telemetryClient?.flush?.();
343
+ throw error;
277
344
  }
278
- return summary;
279
345
  }