@mcmcjs/julia 0.3.0 → 0.5.0
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/dist/driver.jl +10 -178
- package/dist/fitlib.jl +289 -0
- package/dist/index.cjs +343 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +341 -43
- package/dist/index.js.map +1 -1
- package/dist/worker.jl +95 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -52,8 +52,7 @@ var juliaEngine = {
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
// src/fit.ts
|
|
55
|
-
import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "fs";
|
|
56
|
-
import { tmpdir } from "os";
|
|
55
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
57
56
|
import { join as join3 } from "path";
|
|
58
57
|
import {
|
|
59
58
|
canonicalJson,
|
|
@@ -63,11 +62,19 @@ import {
|
|
|
63
62
|
|
|
64
63
|
// src/runner-common.ts
|
|
65
64
|
import { createHash } from "crypto";
|
|
65
|
+
import { tmpdir } from "os";
|
|
66
66
|
import { dirname, join as join2 } from "path";
|
|
67
67
|
import { fileURLToPath } from "url";
|
|
68
|
+
function sharedTmpParent() {
|
|
69
|
+
const uid = typeof process.getuid === "function" ? `-${process.getuid()}` : "";
|
|
70
|
+
return join2(tmpdir(), `mcmcjs${uid}`);
|
|
71
|
+
}
|
|
68
72
|
function driverPath() {
|
|
69
73
|
return join2(dirname(fileURLToPath(import.meta.url)), "driver.jl");
|
|
70
74
|
}
|
|
75
|
+
function workerPath() {
|
|
76
|
+
return join2(dirname(fileURLToPath(import.meta.url)), "worker.jl");
|
|
77
|
+
}
|
|
71
78
|
function sha256(text) {
|
|
72
79
|
return createHash("sha256").update(text).digest("hex");
|
|
73
80
|
}
|
|
@@ -89,45 +96,40 @@ var STAGES = /* @__PURE__ */ new Set([
|
|
|
89
96
|
"load_samples",
|
|
90
97
|
"predict",
|
|
91
98
|
"write",
|
|
92
|
-
"spawn"
|
|
99
|
+
"spawn",
|
|
100
|
+
"worker"
|
|
93
101
|
]);
|
|
94
102
|
function toStage(value) {
|
|
95
103
|
return typeof value === "string" && STAGES.has(value) ? value : void 0;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
// src/fit.ts
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
107
|
+
function makeRequestDir(kind) {
|
|
108
|
+
const parent = sharedTmpParent();
|
|
109
|
+
mkdirSync(parent, { recursive: true });
|
|
110
|
+
return mkdtempSync(join3(parent, `${kind}-`));
|
|
111
|
+
}
|
|
112
|
+
function fitRequest(spec, outPath) {
|
|
113
|
+
return {
|
|
114
|
+
schema_version: spec.schema_version,
|
|
115
|
+
backend: { id: spec.backend.id },
|
|
116
|
+
model: { file: spec.modelPath, entry: spec.model.entry },
|
|
117
|
+
data: spec.data,
|
|
118
|
+
sampler: spec.sampler,
|
|
119
|
+
seed: spec.seed,
|
|
120
|
+
out: outPath
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function finalizeOkFit(spec, resolved, io, provenance, startedAt, elapsedMs) {
|
|
102
124
|
const runtimeRequested = spec.backend.version;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
data: spec.data,
|
|
112
|
-
sampler: spec.sampler,
|
|
113
|
-
seed: spec.seed,
|
|
114
|
-
out: io.outPath
|
|
115
|
-
})
|
|
116
|
-
);
|
|
117
|
-
const args = [
|
|
118
|
-
...resolved.args,
|
|
119
|
-
"--startup-file=no",
|
|
120
|
-
`--project=${io.projectDir}`,
|
|
121
|
-
driverPath(),
|
|
122
|
-
requestPath
|
|
123
|
-
];
|
|
124
|
-
const { stdout, stderr, code } = await io.spawn(resolved.command, args);
|
|
125
|
-
const elapsedMs = Math.round(performance.now() - start);
|
|
126
|
-
if (code !== 0 || !existsSync(io.outPath)) {
|
|
127
|
-
const failure = lastJsonLine(stderr);
|
|
128
|
-
const stage = toStage(failure?.stage);
|
|
129
|
-
const error = failure?.error ?? stderr.trim().split("\n").pop() ?? `julia exited with code ${code}`;
|
|
130
|
-
return { status: "error", runtimeRequested, elapsedMs, stage, error };
|
|
125
|
+
if (!existsSync(io.outPath)) {
|
|
126
|
+
return {
|
|
127
|
+
status: "error",
|
|
128
|
+
runtimeRequested,
|
|
129
|
+
elapsedMs,
|
|
130
|
+
stage: "write",
|
|
131
|
+
error: "the runtime reported success but wrote no samples file"
|
|
132
|
+
};
|
|
131
133
|
}
|
|
132
134
|
try {
|
|
133
135
|
parseSamples(readFileSync(io.outPath, "utf8"));
|
|
@@ -140,7 +142,6 @@ async function runFit(spec, resolved, io) {
|
|
|
140
142
|
error: `samples file did not parse: ${error.message}`
|
|
141
143
|
};
|
|
142
144
|
}
|
|
143
|
-
const provenance = lastJsonLine(stdout);
|
|
144
145
|
const julia = provenance?.julia_version;
|
|
145
146
|
const record = {
|
|
146
147
|
schema_version: RUN_RECORD_SCHEMA_VERSION,
|
|
@@ -156,7 +157,7 @@ async function runFit(spec, resolved, io) {
|
|
|
156
157
|
started_at: startedAt,
|
|
157
158
|
elapsed_ms: elapsedMs
|
|
158
159
|
};
|
|
159
|
-
writeFileSync(`${io.outPath}.run.json`, `${JSON.stringify(record, null, 2)}
|
|
160
|
+
writeFileSync(io.recordPath ?? `${io.outPath}.run.json`, `${JSON.stringify(record, null, 2)}
|
|
160
161
|
`);
|
|
161
162
|
return {
|
|
162
163
|
status: "ok",
|
|
@@ -166,10 +167,43 @@ async function runFit(spec, resolved, io) {
|
|
|
166
167
|
elapsedMs
|
|
167
168
|
};
|
|
168
169
|
}
|
|
170
|
+
async function runFit(spec, resolved, io) {
|
|
171
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
172
|
+
const start = performance.now();
|
|
173
|
+
const runtimeRequested = spec.backend.version;
|
|
174
|
+
const ownTmp = io.tmpDir === void 0;
|
|
175
|
+
const tmp = io.tmpDir ?? makeRequestDir("fit");
|
|
176
|
+
const requestPath = join3(tmp, "request.json");
|
|
177
|
+
writeFileSync(requestPath, JSON.stringify(fitRequest(spec, io.outPath)));
|
|
178
|
+
const args = [
|
|
179
|
+
...resolved.args,
|
|
180
|
+
"--startup-file=no",
|
|
181
|
+
`--project=${io.projectDir}`,
|
|
182
|
+
driverPath(),
|
|
183
|
+
requestPath
|
|
184
|
+
];
|
|
185
|
+
let stdout;
|
|
186
|
+
let stderr;
|
|
187
|
+
let code;
|
|
188
|
+
try {
|
|
189
|
+
({ stdout, stderr, code } = await io.spawn(resolved.command, args, {
|
|
190
|
+
onProgress: io.onProgress
|
|
191
|
+
}));
|
|
192
|
+
} finally {
|
|
193
|
+
if (ownTmp) rmSync(tmp, { recursive: true, force: true });
|
|
194
|
+
}
|
|
195
|
+
const elapsedMs = Math.round(performance.now() - start);
|
|
196
|
+
if (code !== 0 || !existsSync(io.outPath)) {
|
|
197
|
+
const failure = lastJsonLine(stderr);
|
|
198
|
+
const stage = toStage(failure?.stage);
|
|
199
|
+
const error = failure?.error ?? stderr.trim().split("\n").pop() ?? `julia exited with code ${code}`;
|
|
200
|
+
return { status: "error", runtimeRequested, elapsedMs, stage, error };
|
|
201
|
+
}
|
|
202
|
+
return finalizeOkFit(spec, resolved, io, lastJsonLine(stdout), startedAt, elapsedMs);
|
|
203
|
+
}
|
|
169
204
|
|
|
170
205
|
// src/predict.ts
|
|
171
|
-
import { existsSync as existsSync2,
|
|
172
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
206
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
173
207
|
import { join as join4 } from "path";
|
|
174
208
|
import {
|
|
175
209
|
canonicalJson as canonicalJson2,
|
|
@@ -191,7 +225,8 @@ async function runPredict(spec, resolved, io) {
|
|
|
191
225
|
const start = performance.now();
|
|
192
226
|
const runtimeRequested = spec.backend.version;
|
|
193
227
|
const data = predictData(spec);
|
|
194
|
-
const
|
|
228
|
+
const ownTmp = io.tmpDir === void 0;
|
|
229
|
+
const tmp = io.tmpDir ?? makeRequestDir("predict");
|
|
195
230
|
const requestPath = join4(tmp, "request.json");
|
|
196
231
|
writeFileSync2(
|
|
197
232
|
requestPath,
|
|
@@ -214,7 +249,14 @@ async function runPredict(spec, resolved, io) {
|
|
|
214
249
|
driverPath(),
|
|
215
250
|
requestPath
|
|
216
251
|
];
|
|
217
|
-
|
|
252
|
+
let stdout;
|
|
253
|
+
let stderr;
|
|
254
|
+
let code;
|
|
255
|
+
try {
|
|
256
|
+
({ stdout, stderr, code } = await io.spawn(resolved.command, args));
|
|
257
|
+
} finally {
|
|
258
|
+
if (ownTmp) rmSync2(tmp, { recursive: true, force: true });
|
|
259
|
+
}
|
|
218
260
|
const elapsedMs = Math.round(performance.now() - start);
|
|
219
261
|
if (code !== 0 || !existsSync2(io.outPath)) {
|
|
220
262
|
const failure = lastJsonLine(stderr);
|
|
@@ -262,12 +304,14 @@ async function runPredict(spec, resolved, io) {
|
|
|
262
304
|
}
|
|
263
305
|
|
|
264
306
|
// src/project.ts
|
|
265
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
307
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
266
308
|
import { homedir as homedir2 } from "os";
|
|
267
309
|
import { join as join5 } from "path";
|
|
268
310
|
import { createRunner as createRunner3 } from "@mcmcjs/engine";
|
|
269
311
|
var PACKAGES = [
|
|
270
312
|
"Turing",
|
|
313
|
+
"FlexiChains",
|
|
314
|
+
"DimensionalData",
|
|
271
315
|
"MCMCChains",
|
|
272
316
|
"JuliaBUGS",
|
|
273
317
|
"AdvancedHMC",
|
|
@@ -294,7 +338,7 @@ function managedProjectReady(dir = managedProjectDir()) {
|
|
|
294
338
|
}
|
|
295
339
|
async function ensureProject(juliaBin, run = createRunner3(30 * 6e4), dir = managedProjectDir()) {
|
|
296
340
|
if (managedProjectReady(dir)) return dir;
|
|
297
|
-
|
|
341
|
+
mkdirSync2(dir, { recursive: true });
|
|
298
342
|
const code = `using Pkg; Pkg.add(${JSON.stringify(PACKAGES)}); Pkg.precompile()`;
|
|
299
343
|
await run(juliaBin, ["--startup-file=no", `--project=${dir}`, "-e", code]);
|
|
300
344
|
writeFileSync3(sentinelPath(dir), JSON.stringify([...PACKAGES].sort()));
|
|
@@ -462,7 +506,7 @@ async function resolveVersion(bin, channel, run) {
|
|
|
462
506
|
`Julia version "${channel}" is not installed. Add it with: mcmc julia version add ${channel}`
|
|
463
507
|
);
|
|
464
508
|
}
|
|
465
|
-
return { command: match.File, args: match.Args ?? [] };
|
|
509
|
+
return { command: match.File, args: match.Args ?? [], version: match.Version };
|
|
466
510
|
}
|
|
467
511
|
function assertVersionsInstalled(channels, installed) {
|
|
468
512
|
const have = new Set(installed.map((v) => v.id));
|
|
@@ -474,6 +518,256 @@ function assertVersionsInstalled(channels, installed) {
|
|
|
474
518
|
);
|
|
475
519
|
}
|
|
476
520
|
}
|
|
521
|
+
|
|
522
|
+
// src/worker.ts
|
|
523
|
+
import { spawn } from "child_process";
|
|
524
|
+
import { mkdirSync as mkdirSync3, readdirSync, rmdirSync, rmSync as rmSync3, statSync } from "fs";
|
|
525
|
+
import { createConnection } from "net";
|
|
526
|
+
import { join as join7 } from "path";
|
|
527
|
+
import { parseProgressLine } from "@mcmcjs/engine";
|
|
528
|
+
var START_WAIT_MS = 18e4;
|
|
529
|
+
var CONNECT_TIMEOUT_MS = 1e3;
|
|
530
|
+
var STOP_ACK_TIMEOUT_MS = 3e3;
|
|
531
|
+
function fitInactivityTimeoutMs() {
|
|
532
|
+
const fromEnv = Number(process.env.MCMC_WORKER_TIMEOUT_MS);
|
|
533
|
+
return Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : 30 * 6e4;
|
|
534
|
+
}
|
|
535
|
+
var WorkerTimeoutError = class extends Error {
|
|
536
|
+
};
|
|
537
|
+
function workersDir() {
|
|
538
|
+
const runtime = process.env.XDG_RUNTIME_DIR;
|
|
539
|
+
const dir = runtime ? join7(runtime, "mcmcjs") : join7(sharedTmpParent(), "workers");
|
|
540
|
+
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
541
|
+
return dir;
|
|
542
|
+
}
|
|
543
|
+
function workerSocketPath(command, projectDir) {
|
|
544
|
+
return join7(workersDir(), `worker-${sha256(`${command}
|
|
545
|
+
${projectDir}`).slice(0, 12)}.sock`);
|
|
546
|
+
}
|
|
547
|
+
function connectOnce(path) {
|
|
548
|
+
return new Promise((resolve, reject) => {
|
|
549
|
+
const sock = createConnection(path);
|
|
550
|
+
const timer = setTimeout(() => {
|
|
551
|
+
sock.destroy();
|
|
552
|
+
reject(new Error("connect timeout"));
|
|
553
|
+
}, CONNECT_TIMEOUT_MS);
|
|
554
|
+
sock.once("connect", () => {
|
|
555
|
+
clearTimeout(timer);
|
|
556
|
+
resolve(sock);
|
|
557
|
+
});
|
|
558
|
+
sock.once("error", (error) => {
|
|
559
|
+
clearTimeout(timer);
|
|
560
|
+
reject(error);
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
565
|
+
function acquireStartLock(socketPath) {
|
|
566
|
+
const lock = `${socketPath}.start`;
|
|
567
|
+
try {
|
|
568
|
+
mkdirSync3(lock);
|
|
569
|
+
return true;
|
|
570
|
+
} catch {
|
|
571
|
+
try {
|
|
572
|
+
if (Date.now() - statSync(lock).mtimeMs > START_WAIT_MS) {
|
|
573
|
+
rmdirSync(lock);
|
|
574
|
+
mkdirSync3(lock);
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
} catch {
|
|
578
|
+
}
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function releaseStartLock(socketPath) {
|
|
583
|
+
try {
|
|
584
|
+
rmdirSync(`${socketPath}.start`);
|
|
585
|
+
} catch {
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
async function ensureWorker(resolved, projectDir, notify) {
|
|
589
|
+
const path = workerSocketPath(resolved.command, projectDir);
|
|
590
|
+
try {
|
|
591
|
+
return await connectOnce(path);
|
|
592
|
+
} catch {
|
|
593
|
+
}
|
|
594
|
+
let spawnFailure;
|
|
595
|
+
const startedHere = acquireStartLock(path);
|
|
596
|
+
if (startedHere) {
|
|
597
|
+
try {
|
|
598
|
+
rmSync3(path, { force: true });
|
|
599
|
+
notify?.("starting the Julia worker (first use loads Turing, can take a minute)...");
|
|
600
|
+
const child = spawn(
|
|
601
|
+
resolved.command,
|
|
602
|
+
[...resolved.args, "--startup-file=no", `--project=${projectDir}`, workerPath(), path],
|
|
603
|
+
{ detached: true, stdio: "ignore" }
|
|
604
|
+
);
|
|
605
|
+
child.on("error", (error) => {
|
|
606
|
+
spawnFailure = error;
|
|
607
|
+
});
|
|
608
|
+
child.on("exit", (code) => {
|
|
609
|
+
if (code !== 0 && code !== null) {
|
|
610
|
+
spawnFailure = new Error(`the worker exited with code ${code} before serving`);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
child.unref();
|
|
614
|
+
} catch (error) {
|
|
615
|
+
releaseStartLock(path);
|
|
616
|
+
throw error;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
const deadline = Date.now() + START_WAIT_MS;
|
|
621
|
+
for (; ; ) {
|
|
622
|
+
if (spawnFailure) throw spawnFailure;
|
|
623
|
+
try {
|
|
624
|
+
return await connectOnce(path);
|
|
625
|
+
} catch {
|
|
626
|
+
}
|
|
627
|
+
if (Date.now() > deadline) throw new Error("the Julia worker did not start in time");
|
|
628
|
+
await delay(250);
|
|
629
|
+
}
|
|
630
|
+
} finally {
|
|
631
|
+
if (startedHere) releaseStartLock(path);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function readWorkerResponse(sock, onProgress, inactivityMs = fitInactivityTimeoutMs()) {
|
|
635
|
+
return new Promise((resolve, reject) => {
|
|
636
|
+
let pending = "";
|
|
637
|
+
let settled = false;
|
|
638
|
+
const settle = (fn) => {
|
|
639
|
+
if (settled) return;
|
|
640
|
+
settled = true;
|
|
641
|
+
sock.setTimeout(0);
|
|
642
|
+
fn();
|
|
643
|
+
};
|
|
644
|
+
sock.setTimeout(inactivityMs, () => {
|
|
645
|
+
const error = new WorkerTimeoutError(
|
|
646
|
+
`the worker sent nothing for ${Math.round(inactivityMs / 6e4)} minutes`
|
|
647
|
+
);
|
|
648
|
+
settle(() => {
|
|
649
|
+
sock.destroy();
|
|
650
|
+
reject(error);
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
sock.on("data", (chunk) => {
|
|
654
|
+
pending += chunk.toString("utf8");
|
|
655
|
+
for (; ; ) {
|
|
656
|
+
const at = pending.indexOf("\n");
|
|
657
|
+
if (at === -1) break;
|
|
658
|
+
const line = pending.slice(0, at).trim();
|
|
659
|
+
pending = pending.slice(at + 1);
|
|
660
|
+
if (!line) continue;
|
|
661
|
+
const progress = parseProgressLine(line);
|
|
662
|
+
if (progress) {
|
|
663
|
+
onProgress?.(progress);
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
const doc = JSON.parse(line);
|
|
668
|
+
settle(() => {
|
|
669
|
+
sock.end();
|
|
670
|
+
resolve(doc);
|
|
671
|
+
});
|
|
672
|
+
return;
|
|
673
|
+
} catch {
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
sock.on("error", (error) => settle(() => reject(error)));
|
|
678
|
+
sock.on("close", () => settle(() => reject(new Error("worker closed the connection"))));
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
async function runFitViaWorker(spec, resolved, io) {
|
|
682
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
683
|
+
const start = performance.now();
|
|
684
|
+
const sock = await ensureWorker(resolved, io.projectDir, io.notify);
|
|
685
|
+
sock.write(`${JSON.stringify(fitRequest(spec, io.outPath))}
|
|
686
|
+
`);
|
|
687
|
+
const final = await readWorkerResponse(sock, io.onProgress);
|
|
688
|
+
const elapsedMs = Math.round(performance.now() - start);
|
|
689
|
+
if (final.ok !== true) {
|
|
690
|
+
if (final.stage === "worker") throw new Error(String(final.error ?? "worker error"));
|
|
691
|
+
return {
|
|
692
|
+
status: "error",
|
|
693
|
+
runtimeRequested: spec.backend.version,
|
|
694
|
+
elapsedMs,
|
|
695
|
+
stage: toStage(final.stage),
|
|
696
|
+
error: String(final.error ?? "worker error")
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
return finalizeOkFit(
|
|
700
|
+
spec,
|
|
701
|
+
resolved,
|
|
702
|
+
io,
|
|
703
|
+
final.provenance,
|
|
704
|
+
startedAt,
|
|
705
|
+
elapsedMs
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
async function runFitAuto(spec, resolved, io) {
|
|
709
|
+
if (io.daemon && process.platform !== "win32") {
|
|
710
|
+
const start = performance.now();
|
|
711
|
+
try {
|
|
712
|
+
return await runFitViaWorker(spec, resolved, io);
|
|
713
|
+
} catch (error) {
|
|
714
|
+
if (error instanceof WorkerTimeoutError) {
|
|
715
|
+
return {
|
|
716
|
+
status: "error",
|
|
717
|
+
runtimeRequested: spec.backend.version,
|
|
718
|
+
elapsedMs: Math.round(performance.now() - start),
|
|
719
|
+
stage: "worker",
|
|
720
|
+
error: `${error.message}; the worker may be wedged, try mcmc daemon stop`
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
io.notify?.(
|
|
724
|
+
`worker unavailable (${error.message}); falling back to a one-shot run`
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return runFit(spec, resolved, io);
|
|
729
|
+
}
|
|
730
|
+
async function listWorkers() {
|
|
731
|
+
const dir = workersDir();
|
|
732
|
+
const sockets = readdirSync(dir).filter((f) => f.startsWith("worker-") && f.endsWith(".sock"));
|
|
733
|
+
const statuses = [];
|
|
734
|
+
for (const name of sockets) {
|
|
735
|
+
const socket = join7(dir, name);
|
|
736
|
+
let alive = false;
|
|
737
|
+
try {
|
|
738
|
+
const sock = await connectOnce(socket);
|
|
739
|
+
sock.destroy();
|
|
740
|
+
alive = true;
|
|
741
|
+
} catch {
|
|
742
|
+
}
|
|
743
|
+
statuses.push({ socket, alive });
|
|
744
|
+
}
|
|
745
|
+
return statuses;
|
|
746
|
+
}
|
|
747
|
+
async function stopWorker(socket) {
|
|
748
|
+
let sock;
|
|
749
|
+
try {
|
|
750
|
+
sock = await connectOnce(socket);
|
|
751
|
+
} catch {
|
|
752
|
+
rmSync3(socket, { force: true });
|
|
753
|
+
return "stale";
|
|
754
|
+
}
|
|
755
|
+
const acked = await new Promise((resolve) => {
|
|
756
|
+
const timer = setTimeout(() => resolve(false), STOP_ACK_TIMEOUT_MS);
|
|
757
|
+
const finish = (ok) => {
|
|
758
|
+
clearTimeout(timer);
|
|
759
|
+
resolve(ok);
|
|
760
|
+
};
|
|
761
|
+
sock.resume();
|
|
762
|
+
sock.on("close", () => finish(true));
|
|
763
|
+
sock.on("error", () => finish(false));
|
|
764
|
+
sock.write('{"mcmcjs":"stop"}\n');
|
|
765
|
+
});
|
|
766
|
+
sock.destroy();
|
|
767
|
+
if (!acked) return "pending";
|
|
768
|
+
rmSync3(socket, { force: true });
|
|
769
|
+
return "stopped";
|
|
770
|
+
}
|
|
477
771
|
export {
|
|
478
772
|
addVersion,
|
|
479
773
|
assertVersionsInstalled,
|
|
@@ -484,6 +778,7 @@ export {
|
|
|
484
778
|
juliaEngine,
|
|
485
779
|
juliaupInstallCommand,
|
|
486
780
|
listVersions,
|
|
781
|
+
listWorkers,
|
|
487
782
|
managedProjectDir,
|
|
488
783
|
managedProjectReady,
|
|
489
784
|
planSetup,
|
|
@@ -492,10 +787,13 @@ export {
|
|
|
492
787
|
resolveVersion,
|
|
493
788
|
runDoctor,
|
|
494
789
|
runFit,
|
|
790
|
+
runFitAuto,
|
|
495
791
|
runMatrix,
|
|
496
792
|
runPredict,
|
|
497
793
|
runSetup,
|
|
498
794
|
setDefaultVersion,
|
|
795
|
+
sharedTmpParent,
|
|
796
|
+
stopWorker,
|
|
499
797
|
updateVersion
|
|
500
798
|
};
|
|
501
799
|
//# sourceMappingURL=index.js.map
|