blitzwing 0.1.9 → 0.1.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzwing",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Interactive setup wizard to join a Blitzwing Petals mother swarm as a compute contributor",
5
5
  "bin": {
6
6
  "blitzwing": "bin/blitzwing.js"
@@ -0,0 +1,64 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { heartbeatHost } from "./api.js";
5
+ import { HOME_DIR } from "./config.js";
6
+
7
+ /** Must be well below mother HEARTBEAT_TTL_SECONDS (default 60). */
8
+ export const DEFAULT_HEARTBEAT_INTERVAL_MS = 20_000;
9
+
10
+ /**
11
+ * Start a detached background process that heartbeats the mother orchestrator.
12
+ * @param {{ motherUrl: string, hostId: string, intervalMs?: number }} opts
13
+ */
14
+ export function startContributorHeartbeatDaemon({
15
+ motherUrl,
16
+ hostId,
17
+ intervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS,
18
+ }) {
19
+ const hbPath = path.join(HOME_DIR, "heartbeat.mjs");
20
+ fs.mkdirSync(HOME_DIR, { recursive: true });
21
+ fs.writeFileSync(
22
+ hbPath,
23
+ `const mother = ${JSON.stringify(motherUrl)};
24
+ const hostId = ${JSON.stringify(hostId)};
25
+ const intervalMs = ${intervalMs};
26
+ async function beat() {
27
+ try {
28
+ await fetch(mother.replace(/\\/$/, "") + "/v1/hosts/heartbeat", {
29
+ method: "POST",
30
+ headers: {
31
+ "Content-Type": "application/json",
32
+ "ngrok-skip-browser-warning": "true",
33
+ },
34
+ body: JSON.stringify({ host_id: hostId }),
35
+ });
36
+ } catch {}
37
+ }
38
+ setInterval(beat, intervalMs);
39
+ beat();
40
+ `
41
+ );
42
+ const child = spawn(process.execPath, [hbPath], {
43
+ detached: true,
44
+ stdio: "ignore",
45
+ });
46
+ child.unref();
47
+ }
48
+
49
+ /** In-process heartbeat loop (for tests or long-running CLI). */
50
+ export async function startContributorHeartbeatLoop({
51
+ motherUrl,
52
+ hostId,
53
+ intervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS,
54
+ }) {
55
+ async function beat() {
56
+ try {
57
+ await heartbeatHost(motherUrl, { host_id: hostId });
58
+ } catch {
59
+ /* mother may be briefly unavailable */
60
+ }
61
+ }
62
+ setInterval(beat, intervalMs);
63
+ await beat();
64
+ }
package/src/wizard.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  venvPython,
19
19
  } from "./install.js";
20
20
  import { saveState, loadState, clearState, ensureHome } from "./state.js";
21
+ import { startContributorHeartbeatDaemon } from "./heartbeat.js";
21
22
 
22
23
  function parseArgs(argv) {
23
24
  const out = { cmd: null, discoveryUrl: process.env.BLITZWING_DISCOVERY_URL || DEFAULT_DISCOVERY_URL };
@@ -322,7 +323,10 @@ async function wizard(args) {
322
323
  joined_at: new Date().toISOString(),
323
324
  };
324
325
  saveState(state);
325
- startHeartbeatDaemon(state);
326
+ startContributorHeartbeatDaemon({
327
+ motherUrl: state.mother_url,
328
+ hostId: state.host_id,
329
+ });
326
330
 
327
331
  p.outro(
328
332
  `${color.green("You are online.")} Hosting ${color.cyan(String(state.layers_hosted))} layers of ${color.cyan(state.model)} at ${state.block_indices}\n` +
@@ -330,32 +334,6 @@ async function wizard(args) {
330
334
  );
331
335
  }
332
336
 
333
- function startHeartbeatDaemon(state) {
334
- const hbPath = path.join(HOME_DIR, "heartbeat.mjs");
335
- fs.writeFileSync(
336
- hbPath,
337
- `const mother = ${JSON.stringify(state.mother_url)};
338
- const hostId = ${JSON.stringify(state.host_id)};
339
- async function beat() {
340
- try {
341
- await fetch(mother.replace(/\\/$/, "") + "/v1/hosts/heartbeat", {
342
- method: "POST",
343
- headers: { "Content-Type": "application/json" },
344
- body: JSON.stringify({ host_id: hostId }),
345
- });
346
- } catch {}
347
- }
348
- setInterval(beat, 60000);
349
- beat();
350
- `
351
- );
352
- const child = spawn(process.execPath, [hbPath], {
353
- detached: true,
354
- stdio: "ignore",
355
- });
356
- child.unref();
357
- }
358
-
359
337
  async function showStatus() {
360
338
  const state = loadState();
361
339
  if (!state) {