@mcmcjs/julia 0.1.0 → 0.2.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/index.cjs CHANGED
@@ -22,7 +22,10 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  detectJulia: () => detectJulia,
24
24
  detectJuliaup: () => detectJuliaup,
25
- runDoctor: () => runDoctor
25
+ juliaupInstallCommand: () => juliaupInstallCommand,
26
+ planSetup: () => planSetup,
27
+ runDoctor: () => runDoctor,
28
+ runSetup: () => runSetup
26
29
  });
27
30
  module.exports = __toCommonJS(index_exports);
28
31
 
@@ -32,10 +35,16 @@ var import_node_os = require("os");
32
35
  var import_node_path = require("path");
33
36
  var import_node_util = require("util");
34
37
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
35
- var defaultRunner = async (command, args) => {
36
- const { stdout } = await execFileAsync(command, args, { timeout: 1e4 });
37
- return stdout;
38
- };
38
+ function createRunner(timeoutMs = 1e4) {
39
+ return async (command, args) => {
40
+ const { stdout } = await execFileAsync(command, args, {
41
+ timeout: timeoutMs,
42
+ maxBuffer: 16 * 1024 * 1024
43
+ });
44
+ return stdout;
45
+ };
46
+ }
47
+ var defaultRunner = createRunner();
39
48
  function candidates(binary) {
40
49
  return [(0, import_node_path.join)((0, import_node_os.homedir)(), ".juliaup", "bin", binary), binary];
41
50
  }
@@ -62,10 +71,74 @@ async function runDoctor(runner) {
62
71
  const [juliaup, julia] = await Promise.all([detectJuliaup(runner), detectJulia(runner)]);
63
72
  return { juliaup, julia, ready: julia.found };
64
73
  }
74
+
75
+ // src/setup.ts
76
+ var JULIAUP_INSTALL_URL = "https://install.julialang.org";
77
+ function juliaupInstallCommand(platform) {
78
+ if (platform === "win32") {
79
+ return null;
80
+ }
81
+ return { command: "sh", args: ["-c", `curl -fsSL ${JULIAUP_INSTALL_URL} | sh -s -- --yes`] };
82
+ }
83
+ function planSetup(report, platform) {
84
+ if (report.julia.found) return [];
85
+ if (report.juliaup.found) {
86
+ return [
87
+ {
88
+ tool: "julia",
89
+ label: "install the latest stable Julia via juliaup",
90
+ command: { command: report.juliaup.path ?? "juliaup", args: ["add", "release"] }
91
+ }
92
+ ];
93
+ }
94
+ return [
95
+ {
96
+ tool: "juliaup",
97
+ label: "install juliaup (this also installs Julia)",
98
+ command: juliaupInstallCommand(platform)
99
+ }
100
+ ];
101
+ }
102
+ async function runSetup(options = {}) {
103
+ const {
104
+ runner,
105
+ installer = createRunner(15 * 6e4),
106
+ platform = process.platform,
107
+ dryRun = false
108
+ } = options;
109
+ const before = await runDoctor(runner);
110
+ if (before.ready) return { ...before, steps: [] };
111
+ const plan = planSetup(before, platform);
112
+ if (dryRun) {
113
+ return {
114
+ ...before,
115
+ steps: plan.map((step) => ({ ...step, status: step.command ? "skipped" : "unsupported" }))
116
+ };
117
+ }
118
+ const steps = [];
119
+ for (const step of plan) {
120
+ if (!step.command) {
121
+ steps.push({ ...step, status: "unsupported" });
122
+ continue;
123
+ }
124
+ try {
125
+ await installer(step.command.command, step.command.args);
126
+ steps.push({ ...step, status: "ran" });
127
+ } catch (error) {
128
+ const detail = error instanceof Error ? error.message : String(error);
129
+ steps.push({ ...step, status: "failed", detail });
130
+ }
131
+ }
132
+ const after = await runDoctor(runner);
133
+ return { juliaup: after.juliaup, julia: after.julia, ready: after.ready, steps };
134
+ }
65
135
  // Annotate the CommonJS export names for ESM import in node:
66
136
  0 && (module.exports = {
67
137
  detectJulia,
68
138
  detectJuliaup,
69
- runDoctor
139
+ juliaupInstallCommand,
140
+ planSetup,
141
+ runDoctor,
142
+ runSetup
70
143
  });
71
144
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/doctor.ts"],"sourcesContent":["export type { DoctorReport } from \"./doctor\";\nexport { runDoctor } from \"./doctor\";\nexport type { CommandRunner, ToolInfo } from \"./environment\";\nexport { detectJulia, detectJuliaup } from \"./environment\";\n","import { execFile } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Information about a detected command-line tool. */\nexport interface ToolInfo {\n found: boolean;\n version?: string;\n path?: string;\n}\n\n/** Runs a command and resolves its stdout. Injectable so detection is testable. */\nexport type CommandRunner = (command: string, args: string[]) => Promise<string>;\n\nconst defaultRunner: CommandRunner = async (command, args) => {\n const { stdout } = await execFileAsync(command, args, { timeout: 10_000 });\n return stdout;\n};\n\n// juliaup installs its shims here; we also fall back to whatever is on PATH.\nfunction candidates(binary: string): string[] {\n return [join(homedir(), \".juliaup\", \"bin\", binary), binary];\n}\n\nasync function detect(\n binary: string,\n parseVersion: (stdout: string) => string | undefined,\n runner: CommandRunner,\n): Promise<ToolInfo> {\n for (const path of candidates(binary)) {\n try {\n const version = parseVersion(await runner(path, [\"--version\"]));\n if (version) return { found: true, version, path };\n } catch {\n // not available at this path; try the next candidate\n }\n }\n return { found: false };\n}\n\nconst versionNumber = (stdout: string): string | undefined => stdout.match(/(\\d+\\.\\d+\\.\\d+)/)?.[1];\n\n/** Detects the Julia runtime via `julia --version`. */\nexport function detectJulia(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"julia\", versionNumber, runner);\n}\n\n/** Detects the juliaup version manager via `juliaup --version`. */\nexport function detectJuliaup(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"juliaup\", versionNumber, runner);\n}\n","import { type CommandRunner, detectJulia, detectJuliaup, type ToolInfo } from \"./environment\";\n\n/** A summary of the Julia toolchain available for inference. */\nexport interface DoctorReport {\n juliaup: ToolInfo;\n julia: ToolInfo;\n /** True when Julia itself is available (the minimum needed to run inference). */\n ready: boolean;\n}\n\n/** Detects the installed Julia toolchain and reports whether inference can run. */\nexport async function runDoctor(runner?: CommandRunner): Promise<DoctorReport> {\n const [juliaup, julia] = await Promise.all([detectJuliaup(runner), detectJulia(runner)]);\n return { juliaup, julia, ready: julia.found };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,gCAAyB;AACzB,qBAAwB;AACxB,uBAAqB;AACrB,uBAA0B;AAE1B,IAAM,oBAAgB,4BAAU,kCAAQ;AAYxC,IAAM,gBAA+B,OAAO,SAAS,SAAS;AAC5D,QAAM,EAAE,OAAO,IAAI,MAAM,cAAc,SAAS,MAAM,EAAE,SAAS,IAAO,CAAC;AACzE,SAAO;AACT;AAGA,SAAS,WAAW,QAA0B;AAC5C,SAAO,KAAC,2BAAK,wBAAQ,GAAG,YAAY,OAAO,MAAM,GAAG,MAAM;AAC5D;AAEA,eAAe,OACb,QACA,cACA,QACmB;AACnB,aAAW,QAAQ,WAAW,MAAM,GAAG;AACrC,QAAI;AACF,YAAM,UAAU,aAAa,MAAM,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;AAC9D,UAAI,QAAS,QAAO,EAAE,OAAO,MAAM,SAAS,KAAK;AAAA,IACnD,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,IAAM,gBAAgB,CAAC,WAAuC,OAAO,MAAM,iBAAiB,IAAI,CAAC;AAG1F,SAAS,YAAY,SAAwB,eAAkC;AACpF,SAAO,OAAO,SAAS,eAAe,MAAM;AAC9C;AAGO,SAAS,cAAc,SAAwB,eAAkC;AACtF,SAAO,OAAO,WAAW,eAAe,MAAM;AAChD;;;AC1CA,eAAsB,UAAU,QAA+C;AAC7E,QAAM,CAAC,SAAS,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC,cAAc,MAAM,GAAG,YAAY,MAAM,CAAC,CAAC;AACvF,SAAO,EAAE,SAAS,OAAO,OAAO,MAAM,MAAM;AAC9C;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/doctor.ts","../src/setup.ts"],"sourcesContent":["export type { DoctorReport } from \"./doctor\";\nexport { runDoctor } from \"./doctor\";\nexport type { CommandRunner, ToolInfo } from \"./environment\";\nexport { detectJulia, detectJuliaup } from \"./environment\";\nexport type {\n InstallCommand,\n SetupOptions,\n SetupResult,\n SetupStep,\n SetupStepResult,\n StepStatus,\n} from \"./setup\";\nexport { juliaupInstallCommand, planSetup, runSetup } from \"./setup\";\n","import { execFile } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Information about a detected command-line tool. */\nexport interface ToolInfo {\n found: boolean;\n version?: string;\n path?: string;\n}\n\n/** Runs a command and resolves its stdout. Injectable so detection is testable. */\nexport type CommandRunner = (command: string, args: string[]) => Promise<string>;\n\n/** Creates a runner backed by execFile with the given timeout in milliseconds. */\nexport function createRunner(timeoutMs = 10_000): CommandRunner {\n return async (command, args) => {\n const { stdout } = await execFileAsync(command, args, {\n timeout: timeoutMs,\n maxBuffer: 16 * 1024 * 1024,\n });\n return stdout;\n };\n}\n\nconst defaultRunner = createRunner();\n\n// juliaup installs its shims here; we also fall back to whatever is on PATH.\nfunction candidates(binary: string): string[] {\n return [join(homedir(), \".juliaup\", \"bin\", binary), binary];\n}\n\nasync function detect(\n binary: string,\n parseVersion: (stdout: string) => string | undefined,\n runner: CommandRunner,\n): Promise<ToolInfo> {\n for (const path of candidates(binary)) {\n try {\n const version = parseVersion(await runner(path, [\"--version\"]));\n if (version) return { found: true, version, path };\n } catch {\n // not available at this path; try the next candidate\n }\n }\n return { found: false };\n}\n\nconst versionNumber = (stdout: string): string | undefined => stdout.match(/(\\d+\\.\\d+\\.\\d+)/)?.[1];\n\n/** Detects the Julia runtime via `julia --version`. */\nexport function detectJulia(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"julia\", versionNumber, runner);\n}\n\n/** Detects the juliaup version manager via `juliaup --version`. */\nexport function detectJuliaup(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"juliaup\", versionNumber, runner);\n}\n","import { type CommandRunner, detectJulia, detectJuliaup, type ToolInfo } from \"./environment\";\n\n/** A summary of the Julia toolchain available for inference. */\nexport interface DoctorReport {\n juliaup: ToolInfo;\n julia: ToolInfo;\n /** True when Julia itself is available (the minimum needed to run inference). */\n ready: boolean;\n}\n\n/** Detects the installed Julia toolchain and reports whether inference can run. */\nexport async function runDoctor(runner?: CommandRunner): Promise<DoctorReport> {\n const [juliaup, julia] = await Promise.all([detectJuliaup(runner), detectJulia(runner)]);\n return { juliaup, julia, ready: julia.found };\n}\n","import { type DoctorReport, runDoctor } from \"./doctor\";\nimport { type CommandRunner, createRunner, type ToolInfo } from \"./environment\";\n\n/** The official juliaup install script, piped to a shell by the install step. */\nconst JULIAUP_INSTALL_URL = \"https://install.julialang.org\";\n\n/** An executable and its arguments, run directly (no shell word-splitting). */\nexport interface InstallCommand {\n command: string;\n args: string[];\n}\n\n/**\n * The command that installs juliaup on the given platform, or null when\n * automatic install is not yet supported there.\n */\nexport function juliaupInstallCommand(platform: NodeJS.Platform): InstallCommand | null {\n if (platform === \"win32\") {\n // TODO: install juliaup on Windows via winget or the Microsoft Store.\n return null;\n }\n return { command: \"sh\", args: [\"-c\", `curl -fsSL ${JULIAUP_INSTALL_URL} | sh -s -- --yes`] };\n}\n\n/** A single provisioning step needed to make the toolchain ready. */\nexport interface SetupStep {\n tool: \"juliaup\" | \"julia\";\n /** A short description of what the step does. */\n label: string;\n /** The command to run, or null when the step cannot be performed automatically. */\n command: InstallCommand | null;\n}\n\n/** The ordered steps needed to reach a ready toolchain, given the current state. */\nexport function planSetup(report: DoctorReport, platform: NodeJS.Platform): SetupStep[] {\n if (report.julia.found) return [];\n if (report.juliaup.found) {\n return [\n {\n tool: \"julia\",\n label: \"install the latest stable Julia via juliaup\",\n command: { command: report.juliaup.path ?? \"juliaup\", args: [\"add\", \"release\"] },\n },\n ];\n }\n return [\n {\n tool: \"juliaup\",\n label: \"install juliaup (this also installs Julia)\",\n command: juliaupInstallCommand(platform),\n },\n ];\n}\n\nexport type StepStatus = \"ran\" | \"skipped\" | \"failed\" | \"unsupported\";\n\nexport interface SetupStepResult extends SetupStep {\n status: StepStatus;\n /** Error detail when the step failed. */\n detail?: string;\n}\n\nexport interface SetupResult {\n juliaup: ToolInfo;\n julia: ToolInfo;\n /** True when Julia is available after setup. */\n ready: boolean;\n steps: SetupStepResult[];\n}\n\nexport interface SetupOptions {\n /** Runs detection commands. Injectable for tests. */\n runner?: CommandRunner;\n /** Runs install steps, which may take several minutes. Injectable for tests. */\n installer?: CommandRunner;\n /** Target platform; defaults to the current process platform. */\n platform?: NodeJS.Platform;\n /** Plan the steps but do not run them. */\n dryRun?: boolean;\n}\n\n/** Installs the Julia toolchain (juliaup and Julia) needed for inference. */\nexport async function runSetup(options: SetupOptions = {}): Promise<SetupResult> {\n const {\n runner,\n installer = createRunner(15 * 60_000),\n platform = process.platform,\n dryRun = false,\n } = options;\n\n const before = await runDoctor(runner);\n if (before.ready) return { ...before, steps: [] };\n\n const plan = planSetup(before, platform);\n\n if (dryRun) {\n return {\n ...before,\n steps: plan.map((step) => ({ ...step, status: step.command ? \"skipped\" : \"unsupported\" })),\n };\n }\n\n const steps: SetupStepResult[] = [];\n for (const step of plan) {\n if (!step.command) {\n steps.push({ ...step, status: \"unsupported\" });\n continue;\n }\n try {\n await installer(step.command.command, step.command.args);\n steps.push({ ...step, status: \"ran\" });\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n steps.push({ ...step, status: \"failed\", detail });\n }\n }\n\n const after = await runDoctor(runner);\n return { juliaup: after.juliaup, julia: after.julia, ready: after.ready, steps };\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -21,4 +21,50 @@ interface DoctorReport {
21
21
  /** Detects the installed Julia toolchain and reports whether inference can run. */
22
22
  declare function runDoctor(runner?: CommandRunner): Promise<DoctorReport>;
23
23
 
24
- export { type CommandRunner, type DoctorReport, type ToolInfo, detectJulia, detectJuliaup, runDoctor };
24
+ /** An executable and its arguments, run directly (no shell word-splitting). */
25
+ interface InstallCommand {
26
+ command: string;
27
+ args: string[];
28
+ }
29
+ /**
30
+ * The command that installs juliaup on the given platform, or null when
31
+ * automatic install is not yet supported there.
32
+ */
33
+ declare function juliaupInstallCommand(platform: NodeJS.Platform): InstallCommand | null;
34
+ /** A single provisioning step needed to make the toolchain ready. */
35
+ interface SetupStep {
36
+ tool: "juliaup" | "julia";
37
+ /** A short description of what the step does. */
38
+ label: string;
39
+ /** The command to run, or null when the step cannot be performed automatically. */
40
+ command: InstallCommand | null;
41
+ }
42
+ /** The ordered steps needed to reach a ready toolchain, given the current state. */
43
+ declare function planSetup(report: DoctorReport, platform: NodeJS.Platform): SetupStep[];
44
+ type StepStatus = "ran" | "skipped" | "failed" | "unsupported";
45
+ interface SetupStepResult extends SetupStep {
46
+ status: StepStatus;
47
+ /** Error detail when the step failed. */
48
+ detail?: string;
49
+ }
50
+ interface SetupResult {
51
+ juliaup: ToolInfo;
52
+ julia: ToolInfo;
53
+ /** True when Julia is available after setup. */
54
+ ready: boolean;
55
+ steps: SetupStepResult[];
56
+ }
57
+ interface SetupOptions {
58
+ /** Runs detection commands. Injectable for tests. */
59
+ runner?: CommandRunner;
60
+ /** Runs install steps, which may take several minutes. Injectable for tests. */
61
+ installer?: CommandRunner;
62
+ /** Target platform; defaults to the current process platform. */
63
+ platform?: NodeJS.Platform;
64
+ /** Plan the steps but do not run them. */
65
+ dryRun?: boolean;
66
+ }
67
+ /** Installs the Julia toolchain (juliaup and Julia) needed for inference. */
68
+ declare function runSetup(options?: SetupOptions): Promise<SetupResult>;
69
+
70
+ export { type CommandRunner, type DoctorReport, type InstallCommand, type SetupOptions, type SetupResult, type SetupStep, type SetupStepResult, type StepStatus, type ToolInfo, detectJulia, detectJuliaup, juliaupInstallCommand, planSetup, runDoctor, runSetup };
package/dist/index.d.ts CHANGED
@@ -21,4 +21,50 @@ interface DoctorReport {
21
21
  /** Detects the installed Julia toolchain and reports whether inference can run. */
22
22
  declare function runDoctor(runner?: CommandRunner): Promise<DoctorReport>;
23
23
 
24
- export { type CommandRunner, type DoctorReport, type ToolInfo, detectJulia, detectJuliaup, runDoctor };
24
+ /** An executable and its arguments, run directly (no shell word-splitting). */
25
+ interface InstallCommand {
26
+ command: string;
27
+ args: string[];
28
+ }
29
+ /**
30
+ * The command that installs juliaup on the given platform, or null when
31
+ * automatic install is not yet supported there.
32
+ */
33
+ declare function juliaupInstallCommand(platform: NodeJS.Platform): InstallCommand | null;
34
+ /** A single provisioning step needed to make the toolchain ready. */
35
+ interface SetupStep {
36
+ tool: "juliaup" | "julia";
37
+ /** A short description of what the step does. */
38
+ label: string;
39
+ /** The command to run, or null when the step cannot be performed automatically. */
40
+ command: InstallCommand | null;
41
+ }
42
+ /** The ordered steps needed to reach a ready toolchain, given the current state. */
43
+ declare function planSetup(report: DoctorReport, platform: NodeJS.Platform): SetupStep[];
44
+ type StepStatus = "ran" | "skipped" | "failed" | "unsupported";
45
+ interface SetupStepResult extends SetupStep {
46
+ status: StepStatus;
47
+ /** Error detail when the step failed. */
48
+ detail?: string;
49
+ }
50
+ interface SetupResult {
51
+ juliaup: ToolInfo;
52
+ julia: ToolInfo;
53
+ /** True when Julia is available after setup. */
54
+ ready: boolean;
55
+ steps: SetupStepResult[];
56
+ }
57
+ interface SetupOptions {
58
+ /** Runs detection commands. Injectable for tests. */
59
+ runner?: CommandRunner;
60
+ /** Runs install steps, which may take several minutes. Injectable for tests. */
61
+ installer?: CommandRunner;
62
+ /** Target platform; defaults to the current process platform. */
63
+ platform?: NodeJS.Platform;
64
+ /** Plan the steps but do not run them. */
65
+ dryRun?: boolean;
66
+ }
67
+ /** Installs the Julia toolchain (juliaup and Julia) needed for inference. */
68
+ declare function runSetup(options?: SetupOptions): Promise<SetupResult>;
69
+
70
+ export { type CommandRunner, type DoctorReport, type InstallCommand, type SetupOptions, type SetupResult, type SetupStep, type SetupStepResult, type StepStatus, type ToolInfo, detectJulia, detectJuliaup, juliaupInstallCommand, planSetup, runDoctor, runSetup };
package/dist/index.js CHANGED
@@ -4,10 +4,16 @@ import { homedir } from "os";
4
4
  import { join } from "path";
5
5
  import { promisify } from "util";
6
6
  var execFileAsync = promisify(execFile);
7
- var defaultRunner = async (command, args) => {
8
- const { stdout } = await execFileAsync(command, args, { timeout: 1e4 });
9
- return stdout;
10
- };
7
+ function createRunner(timeoutMs = 1e4) {
8
+ return async (command, args) => {
9
+ const { stdout } = await execFileAsync(command, args, {
10
+ timeout: timeoutMs,
11
+ maxBuffer: 16 * 1024 * 1024
12
+ });
13
+ return stdout;
14
+ };
15
+ }
16
+ var defaultRunner = createRunner();
11
17
  function candidates(binary) {
12
18
  return [join(homedir(), ".juliaup", "bin", binary), binary];
13
19
  }
@@ -34,9 +40,73 @@ async function runDoctor(runner) {
34
40
  const [juliaup, julia] = await Promise.all([detectJuliaup(runner), detectJulia(runner)]);
35
41
  return { juliaup, julia, ready: julia.found };
36
42
  }
43
+
44
+ // src/setup.ts
45
+ var JULIAUP_INSTALL_URL = "https://install.julialang.org";
46
+ function juliaupInstallCommand(platform) {
47
+ if (platform === "win32") {
48
+ return null;
49
+ }
50
+ return { command: "sh", args: ["-c", `curl -fsSL ${JULIAUP_INSTALL_URL} | sh -s -- --yes`] };
51
+ }
52
+ function planSetup(report, platform) {
53
+ if (report.julia.found) return [];
54
+ if (report.juliaup.found) {
55
+ return [
56
+ {
57
+ tool: "julia",
58
+ label: "install the latest stable Julia via juliaup",
59
+ command: { command: report.juliaup.path ?? "juliaup", args: ["add", "release"] }
60
+ }
61
+ ];
62
+ }
63
+ return [
64
+ {
65
+ tool: "juliaup",
66
+ label: "install juliaup (this also installs Julia)",
67
+ command: juliaupInstallCommand(platform)
68
+ }
69
+ ];
70
+ }
71
+ async function runSetup(options = {}) {
72
+ const {
73
+ runner,
74
+ installer = createRunner(15 * 6e4),
75
+ platform = process.platform,
76
+ dryRun = false
77
+ } = options;
78
+ const before = await runDoctor(runner);
79
+ if (before.ready) return { ...before, steps: [] };
80
+ const plan = planSetup(before, platform);
81
+ if (dryRun) {
82
+ return {
83
+ ...before,
84
+ steps: plan.map((step) => ({ ...step, status: step.command ? "skipped" : "unsupported" }))
85
+ };
86
+ }
87
+ const steps = [];
88
+ for (const step of plan) {
89
+ if (!step.command) {
90
+ steps.push({ ...step, status: "unsupported" });
91
+ continue;
92
+ }
93
+ try {
94
+ await installer(step.command.command, step.command.args);
95
+ steps.push({ ...step, status: "ran" });
96
+ } catch (error) {
97
+ const detail = error instanceof Error ? error.message : String(error);
98
+ steps.push({ ...step, status: "failed", detail });
99
+ }
100
+ }
101
+ const after = await runDoctor(runner);
102
+ return { juliaup: after.juliaup, julia: after.julia, ready: after.ready, steps };
103
+ }
37
104
  export {
38
105
  detectJulia,
39
106
  detectJuliaup,
40
- runDoctor
107
+ juliaupInstallCommand,
108
+ planSetup,
109
+ runDoctor,
110
+ runSetup
41
111
  };
42
112
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/environment.ts","../src/doctor.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Information about a detected command-line tool. */\nexport interface ToolInfo {\n found: boolean;\n version?: string;\n path?: string;\n}\n\n/** Runs a command and resolves its stdout. Injectable so detection is testable. */\nexport type CommandRunner = (command: string, args: string[]) => Promise<string>;\n\nconst defaultRunner: CommandRunner = async (command, args) => {\n const { stdout } = await execFileAsync(command, args, { timeout: 10_000 });\n return stdout;\n};\n\n// juliaup installs its shims here; we also fall back to whatever is on PATH.\nfunction candidates(binary: string): string[] {\n return [join(homedir(), \".juliaup\", \"bin\", binary), binary];\n}\n\nasync function detect(\n binary: string,\n parseVersion: (stdout: string) => string | undefined,\n runner: CommandRunner,\n): Promise<ToolInfo> {\n for (const path of candidates(binary)) {\n try {\n const version = parseVersion(await runner(path, [\"--version\"]));\n if (version) return { found: true, version, path };\n } catch {\n // not available at this path; try the next candidate\n }\n }\n return { found: false };\n}\n\nconst versionNumber = (stdout: string): string | undefined => stdout.match(/(\\d+\\.\\d+\\.\\d+)/)?.[1];\n\n/** Detects the Julia runtime via `julia --version`. */\nexport function detectJulia(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"julia\", versionNumber, runner);\n}\n\n/** Detects the juliaup version manager via `juliaup --version`. */\nexport function detectJuliaup(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"juliaup\", versionNumber, runner);\n}\n","import { type CommandRunner, detectJulia, detectJuliaup, type ToolInfo } from \"./environment\";\n\n/** A summary of the Julia toolchain available for inference. */\nexport interface DoctorReport {\n juliaup: ToolInfo;\n julia: ToolInfo;\n /** True when Julia itself is available (the minimum needed to run inference). */\n ready: boolean;\n}\n\n/** Detects the installed Julia toolchain and reports whether inference can run. */\nexport async function runDoctor(runner?: CommandRunner): Promise<DoctorReport> {\n const [juliaup, julia] = await Promise.all([detectJuliaup(runner), detectJulia(runner)]);\n return { juliaup, julia, ready: julia.found };\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B,IAAM,gBAAgB,UAAU,QAAQ;AAYxC,IAAM,gBAA+B,OAAO,SAAS,SAAS;AAC5D,QAAM,EAAE,OAAO,IAAI,MAAM,cAAc,SAAS,MAAM,EAAE,SAAS,IAAO,CAAC;AACzE,SAAO;AACT;AAGA,SAAS,WAAW,QAA0B;AAC5C,SAAO,CAAC,KAAK,QAAQ,GAAG,YAAY,OAAO,MAAM,GAAG,MAAM;AAC5D;AAEA,eAAe,OACb,QACA,cACA,QACmB;AACnB,aAAW,QAAQ,WAAW,MAAM,GAAG;AACrC,QAAI;AACF,YAAM,UAAU,aAAa,MAAM,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;AAC9D,UAAI,QAAS,QAAO,EAAE,OAAO,MAAM,SAAS,KAAK;AAAA,IACnD,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,IAAM,gBAAgB,CAAC,WAAuC,OAAO,MAAM,iBAAiB,IAAI,CAAC;AAG1F,SAAS,YAAY,SAAwB,eAAkC;AACpF,SAAO,OAAO,SAAS,eAAe,MAAM;AAC9C;AAGO,SAAS,cAAc,SAAwB,eAAkC;AACtF,SAAO,OAAO,WAAW,eAAe,MAAM;AAChD;;;AC1CA,eAAsB,UAAU,QAA+C;AAC7E,QAAM,CAAC,SAAS,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC,cAAc,MAAM,GAAG,YAAY,MAAM,CAAC,CAAC;AACvF,SAAO,EAAE,SAAS,OAAO,OAAO,MAAM,MAAM;AAC9C;","names":[]}
1
+ {"version":3,"sources":["../src/environment.ts","../src/doctor.ts","../src/setup.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Information about a detected command-line tool. */\nexport interface ToolInfo {\n found: boolean;\n version?: string;\n path?: string;\n}\n\n/** Runs a command and resolves its stdout. Injectable so detection is testable. */\nexport type CommandRunner = (command: string, args: string[]) => Promise<string>;\n\n/** Creates a runner backed by execFile with the given timeout in milliseconds. */\nexport function createRunner(timeoutMs = 10_000): CommandRunner {\n return async (command, args) => {\n const { stdout } = await execFileAsync(command, args, {\n timeout: timeoutMs,\n maxBuffer: 16 * 1024 * 1024,\n });\n return stdout;\n };\n}\n\nconst defaultRunner = createRunner();\n\n// juliaup installs its shims here; we also fall back to whatever is on PATH.\nfunction candidates(binary: string): string[] {\n return [join(homedir(), \".juliaup\", \"bin\", binary), binary];\n}\n\nasync function detect(\n binary: string,\n parseVersion: (stdout: string) => string | undefined,\n runner: CommandRunner,\n): Promise<ToolInfo> {\n for (const path of candidates(binary)) {\n try {\n const version = parseVersion(await runner(path, [\"--version\"]));\n if (version) return { found: true, version, path };\n } catch {\n // not available at this path; try the next candidate\n }\n }\n return { found: false };\n}\n\nconst versionNumber = (stdout: string): string | undefined => stdout.match(/(\\d+\\.\\d+\\.\\d+)/)?.[1];\n\n/** Detects the Julia runtime via `julia --version`. */\nexport function detectJulia(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"julia\", versionNumber, runner);\n}\n\n/** Detects the juliaup version manager via `juliaup --version`. */\nexport function detectJuliaup(runner: CommandRunner = defaultRunner): Promise<ToolInfo> {\n return detect(\"juliaup\", versionNumber, runner);\n}\n","import { type CommandRunner, detectJulia, detectJuliaup, type ToolInfo } from \"./environment\";\n\n/** A summary of the Julia toolchain available for inference. */\nexport interface DoctorReport {\n juliaup: ToolInfo;\n julia: ToolInfo;\n /** True when Julia itself is available (the minimum needed to run inference). */\n ready: boolean;\n}\n\n/** Detects the installed Julia toolchain and reports whether inference can run. */\nexport async function runDoctor(runner?: CommandRunner): Promise<DoctorReport> {\n const [juliaup, julia] = await Promise.all([detectJuliaup(runner), detectJulia(runner)]);\n return { juliaup, julia, ready: julia.found };\n}\n","import { type DoctorReport, runDoctor } from \"./doctor\";\nimport { type CommandRunner, createRunner, type ToolInfo } from \"./environment\";\n\n/** The official juliaup install script, piped to a shell by the install step. */\nconst JULIAUP_INSTALL_URL = \"https://install.julialang.org\";\n\n/** An executable and its arguments, run directly (no shell word-splitting). */\nexport interface InstallCommand {\n command: string;\n args: string[];\n}\n\n/**\n * The command that installs juliaup on the given platform, or null when\n * automatic install is not yet supported there.\n */\nexport function juliaupInstallCommand(platform: NodeJS.Platform): InstallCommand | null {\n if (platform === \"win32\") {\n // TODO: install juliaup on Windows via winget or the Microsoft Store.\n return null;\n }\n return { command: \"sh\", args: [\"-c\", `curl -fsSL ${JULIAUP_INSTALL_URL} | sh -s -- --yes`] };\n}\n\n/** A single provisioning step needed to make the toolchain ready. */\nexport interface SetupStep {\n tool: \"juliaup\" | \"julia\";\n /** A short description of what the step does. */\n label: string;\n /** The command to run, or null when the step cannot be performed automatically. */\n command: InstallCommand | null;\n}\n\n/** The ordered steps needed to reach a ready toolchain, given the current state. */\nexport function planSetup(report: DoctorReport, platform: NodeJS.Platform): SetupStep[] {\n if (report.julia.found) return [];\n if (report.juliaup.found) {\n return [\n {\n tool: \"julia\",\n label: \"install the latest stable Julia via juliaup\",\n command: { command: report.juliaup.path ?? \"juliaup\", args: [\"add\", \"release\"] },\n },\n ];\n }\n return [\n {\n tool: \"juliaup\",\n label: \"install juliaup (this also installs Julia)\",\n command: juliaupInstallCommand(platform),\n },\n ];\n}\n\nexport type StepStatus = \"ran\" | \"skipped\" | \"failed\" | \"unsupported\";\n\nexport interface SetupStepResult extends SetupStep {\n status: StepStatus;\n /** Error detail when the step failed. */\n detail?: string;\n}\n\nexport interface SetupResult {\n juliaup: ToolInfo;\n julia: ToolInfo;\n /** True when Julia is available after setup. */\n ready: boolean;\n steps: SetupStepResult[];\n}\n\nexport interface SetupOptions {\n /** Runs detection commands. Injectable for tests. */\n runner?: CommandRunner;\n /** Runs install steps, which may take several minutes. Injectable for tests. */\n installer?: CommandRunner;\n /** Target platform; defaults to the current process platform. */\n platform?: NodeJS.Platform;\n /** Plan the steps but do not run them. */\n dryRun?: boolean;\n}\n\n/** Installs the Julia toolchain (juliaup and Julia) needed for inference. */\nexport async function runSetup(options: SetupOptions = {}): Promise<SetupResult> {\n const {\n runner,\n installer = createRunner(15 * 60_000),\n platform = process.platform,\n dryRun = false,\n } = options;\n\n const before = await runDoctor(runner);\n if (before.ready) return { ...before, steps: [] };\n\n const plan = planSetup(before, platform);\n\n if (dryRun) {\n return {\n ...before,\n steps: plan.map((step) => ({ ...step, status: step.command ? \"skipped\" : \"unsupported\" })),\n };\n }\n\n const steps: SetupStepResult[] = [];\n for (const step of plan) {\n if (!step.command) {\n steps.push({ ...step, status: \"unsupported\" });\n continue;\n }\n try {\n await installer(step.command.command, step.command.args);\n steps.push({ ...step, status: \"ran\" });\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n steps.push({ ...step, status: \"failed\", detail });\n }\n }\n\n const after = await runDoctor(runner);\n return { juliaup: after.juliaup, julia: after.julia, ready: after.ready, steps };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcmcjs/julia",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Julia toolchain detection and the inference runner for MCMC.js.",
5
5
  "keywords": [
6
6
  "mcmc",