blitzwing 0.2.2 → 0.2.4
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 +2 -2
- package/runtime/app.py +1 -0
- package/src/config.js +1 -1
- package/src/install.js +94 -9
- package/src/wizard.js +8 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzwing",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"config": {
|
|
26
|
-
"discoveryUrl": "http://
|
|
26
|
+
"discoveryUrl": "http://34.60.5.221:9000"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@clack/prompts": "^0.9.1",
|
package/runtime/app.py
CHANGED
package/src/config.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export const DEFAULT_DISCOVERY_URL =
|
|
3
3
|
process.env.BLITZWING_DISCOVERY_URL ||
|
|
4
4
|
process.env.npm_package_config_discoveryUrl ||
|
|
5
|
-
"http://
|
|
5
|
+
"http://34.60.5.221:9000";
|
|
6
6
|
|
|
7
7
|
export const HOME_DIR = process.env.BLITZWING_HOME || `${process.env.HOME || process.env.USERPROFILE}/.blitzwing`;
|
|
8
8
|
export const STATE_PATH = `${HOME_DIR}/contributor.json`;
|
package/src/install.js
CHANGED
|
@@ -10,6 +10,72 @@ const PACKAGE_ROOT = path.join(__dirname, "..");
|
|
|
10
10
|
const RUNTIME_SRC = path.join(PACKAGE_ROOT, "runtime");
|
|
11
11
|
const RUNTIME_DEST = path.join(HOME_DIR, "runtime");
|
|
12
12
|
|
|
13
|
+
function sleep(ms) {
|
|
14
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Kill leftover contributor shard/Petals processes so a new join does not talk to a
|
|
19
|
+
* stale uvicorn that still reports last_exit_code=-15 (SIGTERM from a prior leave).
|
|
20
|
+
*/
|
|
21
|
+
export function stopLocalContributorStack(extraPids = []) {
|
|
22
|
+
for (const pid of extraPids) {
|
|
23
|
+
if (!pid || !Number.isFinite(Number(pid))) continue;
|
|
24
|
+
try {
|
|
25
|
+
process.kill(Number(pid), "SIGTERM");
|
|
26
|
+
} catch {
|
|
27
|
+
/* already gone */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (process.platform === "win32") {
|
|
32
|
+
for (const port of [SHARD_PORT, PETALS_PORT]) {
|
|
33
|
+
try {
|
|
34
|
+
const out = execFileSync("netstat", ["-ano"], { encoding: "utf8" });
|
|
35
|
+
const re = new RegExp(`:${port}\\s+.*LISTENING\\s+(\\d+)`, "i");
|
|
36
|
+
const m = out.match(re);
|
|
37
|
+
if (m) {
|
|
38
|
+
execFileSync("taskkill", ["/PID", m[1], "/T", "/F"], { stdio: "ignore" });
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
/* ignore */
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
execFileSync(
|
|
49
|
+
"bash",
|
|
50
|
+
[
|
|
51
|
+
"-lc",
|
|
52
|
+
[
|
|
53
|
+
`fuser -k ${SHARD_PORT}/tcp ${PETALS_PORT}/tcp 2>/dev/null || true`,
|
|
54
|
+
`pkill -f 'uvicorn app:app --host 0.0.0.0 --port ${SHARD_PORT}' 2>/dev/null || true`,
|
|
55
|
+
`pkill -f 'petals.cli.run_server' 2>/dev/null || true`,
|
|
56
|
+
"sleep 1",
|
|
57
|
+
].join("; "),
|
|
58
|
+
],
|
|
59
|
+
{ stdio: "ignore" }
|
|
60
|
+
);
|
|
61
|
+
} catch {
|
|
62
|
+
/* ignore */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function petalsLogShowsSessionStart(logPath) {
|
|
67
|
+
try {
|
|
68
|
+
if (!fs.existsSync(logPath)) return false;
|
|
69
|
+
const text = fs.readFileSync(logPath, "utf8");
|
|
70
|
+
return (
|
|
71
|
+
/Starting Petals:/.test(text) ||
|
|
72
|
+
/\[INFO\] Started\b/.test(text) ||
|
|
73
|
+
/Running a server on/.test(text)
|
|
74
|
+
);
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
13
79
|
function venvPython() {
|
|
14
80
|
if (process.platform === "win32") {
|
|
15
81
|
return path.join(VENV_PATH, "Scripts", "python.exe");
|
|
@@ -226,7 +292,16 @@ export function writeLocalShardManager() {
|
|
|
226
292
|
}
|
|
227
293
|
|
|
228
294
|
export function startShardManagerProcess({ python, env, logPath }) {
|
|
295
|
+
// Always clear stale listeners before binding — leave() used to stop Petals only.
|
|
296
|
+
stopLocalContributorStack();
|
|
229
297
|
const appPath = syncRuntimeFiles();
|
|
298
|
+
fs.mkdirSync(HOME_DIR, { recursive: true });
|
|
299
|
+
// Fresh logs so wait/error tails are from this session.
|
|
300
|
+
try {
|
|
301
|
+
fs.writeFileSync(path.join(HOME_DIR, "petals.log"), "");
|
|
302
|
+
} catch {
|
|
303
|
+
/* ignore */
|
|
304
|
+
}
|
|
230
305
|
const out = fs.openSync(logPath, "a");
|
|
231
306
|
const petalsPy = petalsImportable(venvPython()) ? venvPython() : python;
|
|
232
307
|
const child = spawn(
|
|
@@ -253,28 +328,38 @@ export async function waitForShardRunning({ timeoutMs = 300000, statusHost } = {
|
|
|
253
328
|
const host = statusHost || process.env.BLITZWING_STATUS_HOST || "127.0.0.1";
|
|
254
329
|
const petalsLog = path.join(HOME_DIR, "petals.log");
|
|
255
330
|
const start = Date.now();
|
|
331
|
+
let sawRunning = false;
|
|
332
|
+
|
|
256
333
|
while (Date.now() - start < timeoutMs) {
|
|
257
334
|
try {
|
|
258
335
|
const res = await fetch(`http://${host}:${SHARD_PORT}/status`);
|
|
259
336
|
if (res.ok) {
|
|
260
337
|
const body = await res.json();
|
|
261
|
-
if (body.running && body.pid)
|
|
338
|
+
if (body.running && body.pid) {
|
|
339
|
+
sawRunning = true;
|
|
340
|
+
return body;
|
|
341
|
+
}
|
|
262
342
|
if (body.last_exit_code != null) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
343
|
+
// A prior leave/stop leaves uvicorn up with last_exit_code=-15. Ignore that
|
|
344
|
+
// until this session's Petals has actually started (or we already saw running).
|
|
345
|
+
const sessionStarted = petalsLogShowsSessionStart(petalsLog);
|
|
346
|
+
if (sawRunning || sessionStarted) {
|
|
347
|
+
const tail = fs.existsSync(petalsLog)
|
|
348
|
+
? fs.readFileSync(petalsLog, "utf8").trim().split("\n").slice(-8).join("\n")
|
|
349
|
+
: "";
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Petals exited (code ${body.last_exit_code}). ${tail || "See " + petalsLog}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
269
354
|
}
|
|
270
355
|
}
|
|
271
356
|
} catch (err) {
|
|
272
357
|
if (err instanceof Error && err.message.startsWith("Petals exited")) throw err;
|
|
273
358
|
}
|
|
274
|
-
await
|
|
359
|
+
await sleep(2000);
|
|
275
360
|
}
|
|
276
361
|
const tail = fs.existsSync(petalsLog)
|
|
277
|
-
? fs.readFileSync(petalsLog, "utf8").trim().split("\n").slice(-
|
|
362
|
+
? fs.readFileSync(petalsLog, "utf8").trim().split("\n").slice(-8).join("\n")
|
|
278
363
|
: "";
|
|
279
364
|
throw new Error(
|
|
280
365
|
`Timed out waiting for Petals on http://${host}:${SHARD_PORT}/status. ${tail || "See " + petalsLog}`
|
package/src/wizard.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ensureVenvAndPetals,
|
|
9
9
|
startShardManagerProcess,
|
|
10
10
|
waitForShardRunning,
|
|
11
|
+
stopLocalContributorStack,
|
|
11
12
|
syncRuntimeFiles,
|
|
12
13
|
venvPython,
|
|
13
14
|
} from "./install.js";
|
|
@@ -283,8 +284,9 @@ async function wizard(args) {
|
|
|
283
284
|
spin.stop("Petals is serving your layers");
|
|
284
285
|
|
|
285
286
|
spin.start("Finalizing handoff with mother…");
|
|
287
|
+
let readyResult;
|
|
286
288
|
try {
|
|
287
|
-
await readyHost(selected.mother_url, {
|
|
289
|
+
readyResult = await readyHost(selected.mother_url, {
|
|
288
290
|
host_id: assignment.host_id,
|
|
289
291
|
});
|
|
290
292
|
} catch (err) {
|
|
@@ -309,6 +311,7 @@ async function wizard(args) {
|
|
|
309
311
|
shard_pid: pid,
|
|
310
312
|
discovery_url: args.discoveryUrl,
|
|
311
313
|
hedera_account_id: hederaAccountId,
|
|
314
|
+
ens_name: readyResult?.ens_name || null,
|
|
312
315
|
joined_at: new Date().toISOString(),
|
|
313
316
|
};
|
|
314
317
|
saveState(state);
|
|
@@ -317,8 +320,10 @@ async function wizard(args) {
|
|
|
317
320
|
hostId: state.host_id,
|
|
318
321
|
});
|
|
319
322
|
|
|
323
|
+
const ensLine = state.ens_name ? `ENS name: ${color.cyan(state.ens_name)}\n` : "";
|
|
320
324
|
p.outro(
|
|
321
325
|
`${color.green("You are online.")} Hosting ${color.cyan(String(state.layers_hosted))} layers of ${color.cyan(state.model)} at ${state.block_indices}\n` +
|
|
326
|
+
ensLine +
|
|
322
327
|
`Tunnel: ${color.cyan(tunnel.url)}\n` +
|
|
323
328
|
`Run ${color.bold("blitzwing status")} anytime, or ${color.bold("blitzwing leave")} to exit.`
|
|
324
329
|
);
|
|
@@ -338,6 +343,7 @@ async function showStatus() {
|
|
|
338
343
|
console.log(` mother: ${state.mother_url}`);
|
|
339
344
|
console.log(` tunnel: ${state.tunnel_url || state.shard_manager_url || "—"}`);
|
|
340
345
|
console.log(` hedera: ${state.hedera_account_id || "—"}`);
|
|
346
|
+
console.log(` ens_name: ${state.ens_name || "—"}`);
|
|
341
347
|
try {
|
|
342
348
|
const res = await fetch(`http://127.0.0.1:${SHARD_PORT}/status`);
|
|
343
349
|
if (res.ok) {
|
|
@@ -370,6 +376,7 @@ async function doLeave() {
|
|
|
370
376
|
} catch {
|
|
371
377
|
/* ignore */
|
|
372
378
|
}
|
|
379
|
+
stopLocalContributorStack([state.shard_pid, state.tunnel_pid].filter(Boolean));
|
|
373
380
|
if (state.tunnel_pid) {
|
|
374
381
|
stopTunnelProcess(state.tunnel_pid);
|
|
375
382
|
}
|