@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.cjs
CHANGED
|
@@ -29,6 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
juliaEngine: () => juliaEngine,
|
|
30
30
|
juliaupInstallCommand: () => juliaupInstallCommand,
|
|
31
31
|
listVersions: () => listVersions,
|
|
32
|
+
listWorkers: () => listWorkers,
|
|
32
33
|
managedProjectDir: () => managedProjectDir,
|
|
33
34
|
managedProjectReady: () => managedProjectReady,
|
|
34
35
|
planSetup: () => planSetup,
|
|
@@ -37,10 +38,13 @@ __export(index_exports, {
|
|
|
37
38
|
resolveVersion: () => resolveVersion,
|
|
38
39
|
runDoctor: () => runDoctor,
|
|
39
40
|
runFit: () => runFit,
|
|
41
|
+
runFitAuto: () => runFitAuto,
|
|
40
42
|
runMatrix: () => runMatrix,
|
|
41
43
|
runPredict: () => runPredict,
|
|
42
44
|
runSetup: () => runSetup,
|
|
43
45
|
setDefaultVersion: () => setDefaultVersion,
|
|
46
|
+
sharedTmpParent: () => sharedTmpParent,
|
|
47
|
+
stopWorker: () => stopWorker,
|
|
44
48
|
updateVersion: () => updateVersion
|
|
45
49
|
});
|
|
46
50
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -104,17 +108,24 @@ var juliaEngine = {
|
|
|
104
108
|
|
|
105
109
|
// src/fit.ts
|
|
106
110
|
var import_node_fs = require("fs");
|
|
107
|
-
var import_node_os2 = require("os");
|
|
108
111
|
var import_node_path3 = require("path");
|
|
109
112
|
var import_core = require("@mcmcjs/core");
|
|
110
113
|
|
|
111
114
|
// src/runner-common.ts
|
|
112
115
|
var import_node_crypto = require("crypto");
|
|
116
|
+
var import_node_os2 = require("os");
|
|
113
117
|
var import_node_path2 = require("path");
|
|
114
118
|
var import_node_url = require("url");
|
|
119
|
+
function sharedTmpParent() {
|
|
120
|
+
const uid = typeof process.getuid === "function" ? `-${process.getuid()}` : "";
|
|
121
|
+
return (0, import_node_path2.join)((0, import_node_os2.tmpdir)(), `mcmcjs${uid}`);
|
|
122
|
+
}
|
|
115
123
|
function driverPath() {
|
|
116
124
|
return (0, import_node_path2.join)((0, import_node_path2.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl)), "driver.jl");
|
|
117
125
|
}
|
|
126
|
+
function workerPath() {
|
|
127
|
+
return (0, import_node_path2.join)((0, import_node_path2.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl)), "worker.jl");
|
|
128
|
+
}
|
|
118
129
|
function sha256(text) {
|
|
119
130
|
return (0, import_node_crypto.createHash)("sha256").update(text).digest("hex");
|
|
120
131
|
}
|
|
@@ -136,45 +147,40 @@ var STAGES = /* @__PURE__ */ new Set([
|
|
|
136
147
|
"load_samples",
|
|
137
148
|
"predict",
|
|
138
149
|
"write",
|
|
139
|
-
"spawn"
|
|
150
|
+
"spawn",
|
|
151
|
+
"worker"
|
|
140
152
|
]);
|
|
141
153
|
function toStage(value) {
|
|
142
154
|
return typeof value === "string" && STAGES.has(value) ? value : void 0;
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
// src/fit.ts
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
158
|
+
function makeRequestDir(kind) {
|
|
159
|
+
const parent = sharedTmpParent();
|
|
160
|
+
(0, import_node_fs.mkdirSync)(parent, { recursive: true });
|
|
161
|
+
return (0, import_node_fs.mkdtempSync)((0, import_node_path3.join)(parent, `${kind}-`));
|
|
162
|
+
}
|
|
163
|
+
function fitRequest(spec, outPath) {
|
|
164
|
+
return {
|
|
165
|
+
schema_version: spec.schema_version,
|
|
166
|
+
backend: { id: spec.backend.id },
|
|
167
|
+
model: { file: spec.modelPath, entry: spec.model.entry },
|
|
168
|
+
data: spec.data,
|
|
169
|
+
sampler: spec.sampler,
|
|
170
|
+
seed: spec.seed,
|
|
171
|
+
out: outPath
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function finalizeOkFit(spec, resolved, io, provenance, startedAt, elapsedMs) {
|
|
149
175
|
const runtimeRequested = spec.backend.version;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
data: spec.data,
|
|
159
|
-
sampler: spec.sampler,
|
|
160
|
-
seed: spec.seed,
|
|
161
|
-
out: io.outPath
|
|
162
|
-
})
|
|
163
|
-
);
|
|
164
|
-
const args = [
|
|
165
|
-
...resolved.args,
|
|
166
|
-
"--startup-file=no",
|
|
167
|
-
`--project=${io.projectDir}`,
|
|
168
|
-
driverPath(),
|
|
169
|
-
requestPath
|
|
170
|
-
];
|
|
171
|
-
const { stdout, stderr, code } = await io.spawn(resolved.command, args);
|
|
172
|
-
const elapsedMs = Math.round(performance.now() - start);
|
|
173
|
-
if (code !== 0 || !(0, import_node_fs.existsSync)(io.outPath)) {
|
|
174
|
-
const failure = lastJsonLine(stderr);
|
|
175
|
-
const stage = toStage(failure?.stage);
|
|
176
|
-
const error = failure?.error ?? stderr.trim().split("\n").pop() ?? `julia exited with code ${code}`;
|
|
177
|
-
return { status: "error", runtimeRequested, elapsedMs, stage, error };
|
|
176
|
+
if (!(0, import_node_fs.existsSync)(io.outPath)) {
|
|
177
|
+
return {
|
|
178
|
+
status: "error",
|
|
179
|
+
runtimeRequested,
|
|
180
|
+
elapsedMs,
|
|
181
|
+
stage: "write",
|
|
182
|
+
error: "the runtime reported success but wrote no samples file"
|
|
183
|
+
};
|
|
178
184
|
}
|
|
179
185
|
try {
|
|
180
186
|
(0, import_core.parseSamples)((0, import_node_fs.readFileSync)(io.outPath, "utf8"));
|
|
@@ -187,7 +193,6 @@ async function runFit(spec, resolved, io) {
|
|
|
187
193
|
error: `samples file did not parse: ${error.message}`
|
|
188
194
|
};
|
|
189
195
|
}
|
|
190
|
-
const provenance = lastJsonLine(stdout);
|
|
191
196
|
const julia = provenance?.julia_version;
|
|
192
197
|
const record = {
|
|
193
198
|
schema_version: import_core.RUN_RECORD_SCHEMA_VERSION,
|
|
@@ -203,7 +208,7 @@ async function runFit(spec, resolved, io) {
|
|
|
203
208
|
started_at: startedAt,
|
|
204
209
|
elapsed_ms: elapsedMs
|
|
205
210
|
};
|
|
206
|
-
(0, import_node_fs.writeFileSync)(`${io.outPath}.run.json`, `${JSON.stringify(record, null, 2)}
|
|
211
|
+
(0, import_node_fs.writeFileSync)(io.recordPath ?? `${io.outPath}.run.json`, `${JSON.stringify(record, null, 2)}
|
|
207
212
|
`);
|
|
208
213
|
return {
|
|
209
214
|
status: "ok",
|
|
@@ -213,10 +218,43 @@ async function runFit(spec, resolved, io) {
|
|
|
213
218
|
elapsedMs
|
|
214
219
|
};
|
|
215
220
|
}
|
|
221
|
+
async function runFit(spec, resolved, io) {
|
|
222
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
223
|
+
const start = performance.now();
|
|
224
|
+
const runtimeRequested = spec.backend.version;
|
|
225
|
+
const ownTmp = io.tmpDir === void 0;
|
|
226
|
+
const tmp = io.tmpDir ?? makeRequestDir("fit");
|
|
227
|
+
const requestPath = (0, import_node_path3.join)(tmp, "request.json");
|
|
228
|
+
(0, import_node_fs.writeFileSync)(requestPath, JSON.stringify(fitRequest(spec, io.outPath)));
|
|
229
|
+
const args = [
|
|
230
|
+
...resolved.args,
|
|
231
|
+
"--startup-file=no",
|
|
232
|
+
`--project=${io.projectDir}`,
|
|
233
|
+
driverPath(),
|
|
234
|
+
requestPath
|
|
235
|
+
];
|
|
236
|
+
let stdout;
|
|
237
|
+
let stderr;
|
|
238
|
+
let code;
|
|
239
|
+
try {
|
|
240
|
+
({ stdout, stderr, code } = await io.spawn(resolved.command, args, {
|
|
241
|
+
onProgress: io.onProgress
|
|
242
|
+
}));
|
|
243
|
+
} finally {
|
|
244
|
+
if (ownTmp) (0, import_node_fs.rmSync)(tmp, { recursive: true, force: true });
|
|
245
|
+
}
|
|
246
|
+
const elapsedMs = Math.round(performance.now() - start);
|
|
247
|
+
if (code !== 0 || !(0, import_node_fs.existsSync)(io.outPath)) {
|
|
248
|
+
const failure = lastJsonLine(stderr);
|
|
249
|
+
const stage = toStage(failure?.stage);
|
|
250
|
+
const error = failure?.error ?? stderr.trim().split("\n").pop() ?? `julia exited with code ${code}`;
|
|
251
|
+
return { status: "error", runtimeRequested, elapsedMs, stage, error };
|
|
252
|
+
}
|
|
253
|
+
return finalizeOkFit(spec, resolved, io, lastJsonLine(stdout), startedAt, elapsedMs);
|
|
254
|
+
}
|
|
216
255
|
|
|
217
256
|
// src/predict.ts
|
|
218
257
|
var import_node_fs2 = require("fs");
|
|
219
|
-
var import_node_os3 = require("os");
|
|
220
258
|
var import_node_path4 = require("path");
|
|
221
259
|
var import_core2 = require("@mcmcjs/core");
|
|
222
260
|
function predictData(spec) {
|
|
@@ -234,7 +272,8 @@ async function runPredict(spec, resolved, io) {
|
|
|
234
272
|
const start = performance.now();
|
|
235
273
|
const runtimeRequested = spec.backend.version;
|
|
236
274
|
const data = predictData(spec);
|
|
237
|
-
const
|
|
275
|
+
const ownTmp = io.tmpDir === void 0;
|
|
276
|
+
const tmp = io.tmpDir ?? makeRequestDir("predict");
|
|
238
277
|
const requestPath = (0, import_node_path4.join)(tmp, "request.json");
|
|
239
278
|
(0, import_node_fs2.writeFileSync)(
|
|
240
279
|
requestPath,
|
|
@@ -257,7 +296,14 @@ async function runPredict(spec, resolved, io) {
|
|
|
257
296
|
driverPath(),
|
|
258
297
|
requestPath
|
|
259
298
|
];
|
|
260
|
-
|
|
299
|
+
let stdout;
|
|
300
|
+
let stderr;
|
|
301
|
+
let code;
|
|
302
|
+
try {
|
|
303
|
+
({ stdout, stderr, code } = await io.spawn(resolved.command, args));
|
|
304
|
+
} finally {
|
|
305
|
+
if (ownTmp) (0, import_node_fs2.rmSync)(tmp, { recursive: true, force: true });
|
|
306
|
+
}
|
|
261
307
|
const elapsedMs = Math.round(performance.now() - start);
|
|
262
308
|
if (code !== 0 || !(0, import_node_fs2.existsSync)(io.outPath)) {
|
|
263
309
|
const failure = lastJsonLine(stderr);
|
|
@@ -306,11 +352,13 @@ async function runPredict(spec, resolved, io) {
|
|
|
306
352
|
|
|
307
353
|
// src/project.ts
|
|
308
354
|
var import_node_fs3 = require("fs");
|
|
309
|
-
var
|
|
355
|
+
var import_node_os3 = require("os");
|
|
310
356
|
var import_node_path5 = require("path");
|
|
311
357
|
var import_engine3 = require("@mcmcjs/engine");
|
|
312
358
|
var PACKAGES = [
|
|
313
359
|
"Turing",
|
|
360
|
+
"FlexiChains",
|
|
361
|
+
"DimensionalData",
|
|
314
362
|
"MCMCChains",
|
|
315
363
|
"JuliaBUGS",
|
|
316
364
|
"AdvancedHMC",
|
|
@@ -319,7 +367,7 @@ var PACKAGES = [
|
|
|
319
367
|
"StableRNGs"
|
|
320
368
|
];
|
|
321
369
|
function managedProjectDir() {
|
|
322
|
-
const base = process.env.XDG_DATA_HOME || (0, import_node_path5.join)((0,
|
|
370
|
+
const base = process.env.XDG_DATA_HOME || (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".local", "share");
|
|
323
371
|
return (0, import_node_path5.join)(base, "mcmcjs", "julia", "env");
|
|
324
372
|
}
|
|
325
373
|
function sentinelPath(dir) {
|
|
@@ -505,7 +553,7 @@ async function resolveVersion(bin, channel, run) {
|
|
|
505
553
|
`Julia version "${channel}" is not installed. Add it with: mcmc julia version add ${channel}`
|
|
506
554
|
);
|
|
507
555
|
}
|
|
508
|
-
return { command: match.File, args: match.Args ?? [] };
|
|
556
|
+
return { command: match.File, args: match.Args ?? [], version: match.Version };
|
|
509
557
|
}
|
|
510
558
|
function assertVersionsInstalled(channels, installed) {
|
|
511
559
|
const have = new Set(installed.map((v) => v.id));
|
|
@@ -517,6 +565,256 @@ function assertVersionsInstalled(channels, installed) {
|
|
|
517
565
|
);
|
|
518
566
|
}
|
|
519
567
|
}
|
|
568
|
+
|
|
569
|
+
// src/worker.ts
|
|
570
|
+
var import_node_child_process = require("child_process");
|
|
571
|
+
var import_node_fs4 = require("fs");
|
|
572
|
+
var import_node_net = require("net");
|
|
573
|
+
var import_node_path7 = require("path");
|
|
574
|
+
var import_engine4 = require("@mcmcjs/engine");
|
|
575
|
+
var START_WAIT_MS = 18e4;
|
|
576
|
+
var CONNECT_TIMEOUT_MS = 1e3;
|
|
577
|
+
var STOP_ACK_TIMEOUT_MS = 3e3;
|
|
578
|
+
function fitInactivityTimeoutMs() {
|
|
579
|
+
const fromEnv = Number(process.env.MCMC_WORKER_TIMEOUT_MS);
|
|
580
|
+
return Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : 30 * 6e4;
|
|
581
|
+
}
|
|
582
|
+
var WorkerTimeoutError = class extends Error {
|
|
583
|
+
};
|
|
584
|
+
function workersDir() {
|
|
585
|
+
const runtime = process.env.XDG_RUNTIME_DIR;
|
|
586
|
+
const dir = runtime ? (0, import_node_path7.join)(runtime, "mcmcjs") : (0, import_node_path7.join)(sharedTmpParent(), "workers");
|
|
587
|
+
(0, import_node_fs4.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
588
|
+
return dir;
|
|
589
|
+
}
|
|
590
|
+
function workerSocketPath(command, projectDir) {
|
|
591
|
+
return (0, import_node_path7.join)(workersDir(), `worker-${sha256(`${command}
|
|
592
|
+
${projectDir}`).slice(0, 12)}.sock`);
|
|
593
|
+
}
|
|
594
|
+
function connectOnce(path) {
|
|
595
|
+
return new Promise((resolve, reject) => {
|
|
596
|
+
const sock = (0, import_node_net.createConnection)(path);
|
|
597
|
+
const timer = setTimeout(() => {
|
|
598
|
+
sock.destroy();
|
|
599
|
+
reject(new Error("connect timeout"));
|
|
600
|
+
}, CONNECT_TIMEOUT_MS);
|
|
601
|
+
sock.once("connect", () => {
|
|
602
|
+
clearTimeout(timer);
|
|
603
|
+
resolve(sock);
|
|
604
|
+
});
|
|
605
|
+
sock.once("error", (error) => {
|
|
606
|
+
clearTimeout(timer);
|
|
607
|
+
reject(error);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
612
|
+
function acquireStartLock(socketPath) {
|
|
613
|
+
const lock = `${socketPath}.start`;
|
|
614
|
+
try {
|
|
615
|
+
(0, import_node_fs4.mkdirSync)(lock);
|
|
616
|
+
return true;
|
|
617
|
+
} catch {
|
|
618
|
+
try {
|
|
619
|
+
if (Date.now() - (0, import_node_fs4.statSync)(lock).mtimeMs > START_WAIT_MS) {
|
|
620
|
+
(0, import_node_fs4.rmdirSync)(lock);
|
|
621
|
+
(0, import_node_fs4.mkdirSync)(lock);
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
function releaseStartLock(socketPath) {
|
|
630
|
+
try {
|
|
631
|
+
(0, import_node_fs4.rmdirSync)(`${socketPath}.start`);
|
|
632
|
+
} catch {
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async function ensureWorker(resolved, projectDir, notify) {
|
|
636
|
+
const path = workerSocketPath(resolved.command, projectDir);
|
|
637
|
+
try {
|
|
638
|
+
return await connectOnce(path);
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
let spawnFailure;
|
|
642
|
+
const startedHere = acquireStartLock(path);
|
|
643
|
+
if (startedHere) {
|
|
644
|
+
try {
|
|
645
|
+
(0, import_node_fs4.rmSync)(path, { force: true });
|
|
646
|
+
notify?.("starting the Julia worker (first use loads Turing, can take a minute)...");
|
|
647
|
+
const child = (0, import_node_child_process.spawn)(
|
|
648
|
+
resolved.command,
|
|
649
|
+
[...resolved.args, "--startup-file=no", `--project=${projectDir}`, workerPath(), path],
|
|
650
|
+
{ detached: true, stdio: "ignore" }
|
|
651
|
+
);
|
|
652
|
+
child.on("error", (error) => {
|
|
653
|
+
spawnFailure = error;
|
|
654
|
+
});
|
|
655
|
+
child.on("exit", (code) => {
|
|
656
|
+
if (code !== 0 && code !== null) {
|
|
657
|
+
spawnFailure = new Error(`the worker exited with code ${code} before serving`);
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
child.unref();
|
|
661
|
+
} catch (error) {
|
|
662
|
+
releaseStartLock(path);
|
|
663
|
+
throw error;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
const deadline = Date.now() + START_WAIT_MS;
|
|
668
|
+
for (; ; ) {
|
|
669
|
+
if (spawnFailure) throw spawnFailure;
|
|
670
|
+
try {
|
|
671
|
+
return await connectOnce(path);
|
|
672
|
+
} catch {
|
|
673
|
+
}
|
|
674
|
+
if (Date.now() > deadline) throw new Error("the Julia worker did not start in time");
|
|
675
|
+
await delay(250);
|
|
676
|
+
}
|
|
677
|
+
} finally {
|
|
678
|
+
if (startedHere) releaseStartLock(path);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function readWorkerResponse(sock, onProgress, inactivityMs = fitInactivityTimeoutMs()) {
|
|
682
|
+
return new Promise((resolve, reject) => {
|
|
683
|
+
let pending = "";
|
|
684
|
+
let settled = false;
|
|
685
|
+
const settle = (fn) => {
|
|
686
|
+
if (settled) return;
|
|
687
|
+
settled = true;
|
|
688
|
+
sock.setTimeout(0);
|
|
689
|
+
fn();
|
|
690
|
+
};
|
|
691
|
+
sock.setTimeout(inactivityMs, () => {
|
|
692
|
+
const error = new WorkerTimeoutError(
|
|
693
|
+
`the worker sent nothing for ${Math.round(inactivityMs / 6e4)} minutes`
|
|
694
|
+
);
|
|
695
|
+
settle(() => {
|
|
696
|
+
sock.destroy();
|
|
697
|
+
reject(error);
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
sock.on("data", (chunk) => {
|
|
701
|
+
pending += chunk.toString("utf8");
|
|
702
|
+
for (; ; ) {
|
|
703
|
+
const at = pending.indexOf("\n");
|
|
704
|
+
if (at === -1) break;
|
|
705
|
+
const line = pending.slice(0, at).trim();
|
|
706
|
+
pending = pending.slice(at + 1);
|
|
707
|
+
if (!line) continue;
|
|
708
|
+
const progress = (0, import_engine4.parseProgressLine)(line);
|
|
709
|
+
if (progress) {
|
|
710
|
+
onProgress?.(progress);
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
try {
|
|
714
|
+
const doc = JSON.parse(line);
|
|
715
|
+
settle(() => {
|
|
716
|
+
sock.end();
|
|
717
|
+
resolve(doc);
|
|
718
|
+
});
|
|
719
|
+
return;
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
sock.on("error", (error) => settle(() => reject(error)));
|
|
725
|
+
sock.on("close", () => settle(() => reject(new Error("worker closed the connection"))));
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
async function runFitViaWorker(spec, resolved, io) {
|
|
729
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
730
|
+
const start = performance.now();
|
|
731
|
+
const sock = await ensureWorker(resolved, io.projectDir, io.notify);
|
|
732
|
+
sock.write(`${JSON.stringify(fitRequest(spec, io.outPath))}
|
|
733
|
+
`);
|
|
734
|
+
const final = await readWorkerResponse(sock, io.onProgress);
|
|
735
|
+
const elapsedMs = Math.round(performance.now() - start);
|
|
736
|
+
if (final.ok !== true) {
|
|
737
|
+
if (final.stage === "worker") throw new Error(String(final.error ?? "worker error"));
|
|
738
|
+
return {
|
|
739
|
+
status: "error",
|
|
740
|
+
runtimeRequested: spec.backend.version,
|
|
741
|
+
elapsedMs,
|
|
742
|
+
stage: toStage(final.stage),
|
|
743
|
+
error: String(final.error ?? "worker error")
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
return finalizeOkFit(
|
|
747
|
+
spec,
|
|
748
|
+
resolved,
|
|
749
|
+
io,
|
|
750
|
+
final.provenance,
|
|
751
|
+
startedAt,
|
|
752
|
+
elapsedMs
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
async function runFitAuto(spec, resolved, io) {
|
|
756
|
+
if (io.daemon && process.platform !== "win32") {
|
|
757
|
+
const start = performance.now();
|
|
758
|
+
try {
|
|
759
|
+
return await runFitViaWorker(spec, resolved, io);
|
|
760
|
+
} catch (error) {
|
|
761
|
+
if (error instanceof WorkerTimeoutError) {
|
|
762
|
+
return {
|
|
763
|
+
status: "error",
|
|
764
|
+
runtimeRequested: spec.backend.version,
|
|
765
|
+
elapsedMs: Math.round(performance.now() - start),
|
|
766
|
+
stage: "worker",
|
|
767
|
+
error: `${error.message}; the worker may be wedged, try mcmc daemon stop`
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
io.notify?.(
|
|
771
|
+
`worker unavailable (${error.message}); falling back to a one-shot run`
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return runFit(spec, resolved, io);
|
|
776
|
+
}
|
|
777
|
+
async function listWorkers() {
|
|
778
|
+
const dir = workersDir();
|
|
779
|
+
const sockets = (0, import_node_fs4.readdirSync)(dir).filter((f) => f.startsWith("worker-") && f.endsWith(".sock"));
|
|
780
|
+
const statuses = [];
|
|
781
|
+
for (const name of sockets) {
|
|
782
|
+
const socket = (0, import_node_path7.join)(dir, name);
|
|
783
|
+
let alive = false;
|
|
784
|
+
try {
|
|
785
|
+
const sock = await connectOnce(socket);
|
|
786
|
+
sock.destroy();
|
|
787
|
+
alive = true;
|
|
788
|
+
} catch {
|
|
789
|
+
}
|
|
790
|
+
statuses.push({ socket, alive });
|
|
791
|
+
}
|
|
792
|
+
return statuses;
|
|
793
|
+
}
|
|
794
|
+
async function stopWorker(socket) {
|
|
795
|
+
let sock;
|
|
796
|
+
try {
|
|
797
|
+
sock = await connectOnce(socket);
|
|
798
|
+
} catch {
|
|
799
|
+
(0, import_node_fs4.rmSync)(socket, { force: true });
|
|
800
|
+
return "stale";
|
|
801
|
+
}
|
|
802
|
+
const acked = await new Promise((resolve) => {
|
|
803
|
+
const timer = setTimeout(() => resolve(false), STOP_ACK_TIMEOUT_MS);
|
|
804
|
+
const finish = (ok) => {
|
|
805
|
+
clearTimeout(timer);
|
|
806
|
+
resolve(ok);
|
|
807
|
+
};
|
|
808
|
+
sock.resume();
|
|
809
|
+
sock.on("close", () => finish(true));
|
|
810
|
+
sock.on("error", () => finish(false));
|
|
811
|
+
sock.write('{"mcmcjs":"stop"}\n');
|
|
812
|
+
});
|
|
813
|
+
sock.destroy();
|
|
814
|
+
if (!acked) return "pending";
|
|
815
|
+
(0, import_node_fs4.rmSync)(socket, { force: true });
|
|
816
|
+
return "stopped";
|
|
817
|
+
}
|
|
520
818
|
// Annotate the CommonJS export names for ESM import in node:
|
|
521
819
|
0 && (module.exports = {
|
|
522
820
|
addVersion,
|
|
@@ -528,6 +826,7 @@ function assertVersionsInstalled(channels, installed) {
|
|
|
528
826
|
juliaEngine,
|
|
529
827
|
juliaupInstallCommand,
|
|
530
828
|
listVersions,
|
|
829
|
+
listWorkers,
|
|
531
830
|
managedProjectDir,
|
|
532
831
|
managedProjectReady,
|
|
533
832
|
planSetup,
|
|
@@ -536,10 +835,13 @@ function assertVersionsInstalled(channels, installed) {
|
|
|
536
835
|
resolveVersion,
|
|
537
836
|
runDoctor,
|
|
538
837
|
runFit,
|
|
838
|
+
runFitAuto,
|
|
539
839
|
runMatrix,
|
|
540
840
|
runPredict,
|
|
541
841
|
runSetup,
|
|
542
842
|
setDefaultVersion,
|
|
843
|
+
sharedTmpParent,
|
|
844
|
+
stopWorker,
|
|
543
845
|
updateVersion
|
|
544
846
|
});
|
|
545
847
|
//# sourceMappingURL=index.cjs.map
|