@moldesk/runtime 0.0.3 → 0.0.5

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.
@@ -24,6 +24,16 @@ export interface StageCompanionInputsParams {
24
24
  * reference arbitrary filesystem locations outside where it itself lives. */
25
25
  allowedRoot?: string;
26
26
  }
27
+ /**
28
+ * True when `target` (already `path.resolve`d) is `root` itself or strictly
29
+ * inside it. Uses `path.relative`, not `string.startsWith(root)` — a prefix
30
+ * check would incorrectly accept a sibling directory like `/allowed-root-evil`
31
+ * when `root` is `/allowed-root`, since the string literally starts with that
32
+ * prefix. `path.relative` returns a `..`-leading (or absolute, on Windows
33
+ * across drives) path whenever `target` escapes `root`, regardless of any
34
+ * shared string prefix.
35
+ */
36
+ export declare function isWithinRoot(root: string, target: string): boolean;
27
37
  /**
28
38
  * Safely copies one or more adapter-declared companion files (e.g. DiffDock-L's
29
39
  * protein PDB and ligand file) into a run's `input/` directory, confined to
@@ -14,7 +14,7 @@ function companionError(code, message, remediation, details) {
14
14
  * across drives) path whenever `target` escapes `root`, regardless of any
15
15
  * shared string prefix.
16
16
  */
17
- function isWithinRoot(root, target) {
17
+ export function isWithinRoot(root, target) {
18
18
  if (target === root)
19
19
  return true;
20
20
  const rel = path.relative(root, target);
@@ -1,4 +1,4 @@
1
- export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir } from "./paths.js";
1
+ export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir, visualizationDir } from "./paths.js";
2
2
  export type { MoldeskPaths } from "./paths.js";
3
- export { stageCompanionInputs } from "./companion-inputs.js";
3
+ export { isWithinRoot, stageCompanionInputs } from "./companion-inputs.js";
4
4
  export type { CompanionInputRef, StageCompanionInputsParams, StagedCompanionInputPath } from "./companion-inputs.js";
@@ -1,2 +1,2 @@
1
- export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir } from "./paths.js";
2
- export { stageCompanionInputs } from "./companion-inputs.js";
1
+ export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir, visualizationDir } from "./paths.js";
2
+ export { isWithinRoot, stageCompanionInputs } from "./companion-inputs.js";
@@ -16,5 +16,7 @@ export declare function getMoldeskPaths(env?: NodeJS.ProcessEnv): MoldeskPaths;
16
16
  export declare function modelVersionDir(model: string, modelVersion: string, env?: NodeJS.ProcessEnv): string;
17
17
  export declare function modelInstallDir(model: string, modelVersion: string, runtimeFingerprint: string, env?: NodeJS.ProcessEnv): string;
18
18
  export declare function runDir(runId: string, env?: NodeJS.ProcessEnv): string;
19
+ /** Owned location for standalone (non-run) visualizations: `<cache>/visualizations/<id>`. */
20
+ export declare function visualizationDir(id: string, env?: NodeJS.ProcessEnv): string;
19
21
  export declare function uvToolDir(version: string, env?: NodeJS.ProcessEnv): string;
20
22
  export declare function pythonToolDir(version: string, env?: NodeJS.ProcessEnv): string;
@@ -71,6 +71,10 @@ export function modelInstallDir(model, modelVersion, runtimeFingerprint, env) {
71
71
  export function runDir(runId, env) {
72
72
  return path.join(getMoldeskPaths(env).runs, safeRunId(runId));
73
73
  }
74
+ /** Owned location for standalone (non-run) visualizations: `<cache>/visualizations/<id>`. */
75
+ export function visualizationDir(id, env) {
76
+ return path.join(getMoldeskPaths(env).cache, "visualizations", safeSegment(id, "visualization id", /^[A-Za-z0-9][A-Za-z0-9-]{7,63}$/));
77
+ }
74
78
  export function uvToolDir(version, env) {
75
79
  return path.join(getMoldeskPaths(env).tools, "uv", safeVersion(version, "uv version"));
76
80
  }
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export interface LegacySystemReport {
13
13
  * so `install.ts` can inject a verified Tier-2 Docker GPU probe result.
14
14
  */
15
15
  export declare function getSystemReport(deps?: SystemReportDeps): Promise<SystemReport>;
16
- export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir, stageCompanionInputs, } from "./filesystem/index.js";
16
+ export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir, visualizationDir, isWithinRoot, stageCompanionInputs, } from "./filesystem/index.js";
17
17
  export type { MoldeskPaths, CompanionInputRef, StageCompanionInputsParams, StagedCompanionInputPath } from "./filesystem/index.js";
18
18
  export type { ExecuteRequest, ExecutionResult, MoldeskErrorData, PrepareRuntimeRequest, PreparedRuntime, RemoveRuntimeRequest, RuntimeCapability, RuntimeProvider, } from "./providers.js";
19
19
  export { MoldeskError } from "./providers.js";
@@ -25,6 +25,8 @@ export { detectDocker, isImageCachedLocally, pullDockerImage, probeDockerGpuAcce
25
25
  export { detectNvidia } from "./nvidia/index.js";
26
26
  export { collectSystemReport } from "./system.js";
27
27
  export { runCommand } from "./process/index.js";
28
+ export { openInBrowser } from "./process/open.js";
29
+ export type { OpenResult } from "./process/open.js";
28
30
  export type { RunOptions, RunResult } from "./process/index.js";
29
31
  export { cacheAsset, ensureManagedUv, managedUvDownloadBytes, materializeAsset, prepareDockerImage, preparePythonEnvironment, temporaryInstallDir, MANAGED_UV_VERSION, } from "./install/index.js";
30
32
  export type { AssetFetch, InstallCommandRunner, InstallProgress, PostInstallHookResult, PreparedPythonEnvironment, PythonInstallRequest, } from "./install/index.js";
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { collectSystemReport, } from "./system.js";
7
7
  export async function getSystemReport(deps = {}) {
8
8
  return collectSystemReport(deps);
9
9
  }
10
- export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir, stageCompanionInputs, } from "./filesystem/index.js";
10
+ export { getMoldeskHome, getMoldeskPaths, modelInstallDir, modelVersionDir, pythonToolDir, runDir, uvToolDir, visualizationDir, isWithinRoot, stageCompanionInputs, } from "./filesystem/index.js";
11
11
  export { MoldeskError } from "./providers.js";
12
12
  export { PythonRuntimeProvider } from "./providers/python.js";
13
13
  export { redactEnv } from "./redact.js";
@@ -17,4 +17,5 @@ export { detectDocker, isImageCachedLocally, pullDockerImage, probeDockerGpuAcce
17
17
  export { detectNvidia } from "./nvidia/index.js";
18
18
  export { collectSystemReport } from "./system.js";
19
19
  export { runCommand } from "./process/index.js";
20
+ export { openInBrowser } from "./process/open.js";
20
21
  export { cacheAsset, ensureManagedUv, managedUvDownloadBytes, materializeAsset, prepareDockerImage, preparePythonEnvironment, temporaryInstallDir, MANAGED_UV_VERSION, } from "./install/index.js";
@@ -0,0 +1,14 @@
1
+ import { type RunResult } from "./index.js";
2
+ export interface OpenResult {
3
+ opened: boolean;
4
+ error?: string;
5
+ }
6
+ /**
7
+ * Opens a local file in the default browser. Structured executable + args, no shell,
8
+ * so paths with spaces, quotes, `$()`, backticks, Unicode, or a leading dash are inert:
9
+ * the path is made absolute first, so it can never be parsed as an option.
10
+ */
11
+ export declare function openInBrowser(filePath: string, deps?: {
12
+ platform?: NodeJS.Platform;
13
+ run?: (command: string, args: string[]) => Promise<Pick<RunResult, "code" | "stderr">>;
14
+ }): Promise<OpenResult>;
@@ -0,0 +1,24 @@
1
+ import path from "node:path";
2
+ import { runCommand } from "./index.js";
3
+ /**
4
+ * Opens a local file in the default browser. Structured executable + args, no shell,
5
+ * so paths with spaces, quotes, `$()`, backticks, Unicode, or a leading dash are inert:
6
+ * the path is made absolute first, so it can never be parsed as an option.
7
+ */
8
+ export async function openInBrowser(filePath, deps = {}) {
9
+ const platform = deps.platform ?? process.platform;
10
+ const executable = platform === "darwin" ? "open" : platform === "linux" ? "xdg-open" : undefined;
11
+ if (!executable)
12
+ return { opened: false, error: `Opening a browser is not supported on ${platform}.` };
13
+ const target = path.resolve(filePath);
14
+ const run = deps.run ?? ((command, args) => runCommand(command, args, { timeoutMs: 10_000 }));
15
+ try {
16
+ const result = await run(executable, [target]);
17
+ if (result.code === 0)
18
+ return { opened: true };
19
+ return { opened: false, error: result.stderr.trim() || `${executable} exited with code ${result.code}` };
20
+ }
21
+ catch (error) {
22
+ return { opened: false, error: error instanceof Error ? error.message : String(error) };
23
+ }
24
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { openInBrowser } from "./open.js";
3
+ describe("openInBrowser", () => {
4
+ it.each([
5
+ ["darwin", "open"],
6
+ ["linux", "xdg-open"],
7
+ ])("uses %s -> %s with the path as one argument", async (platform, exe) => {
8
+ const calls = [];
9
+ const hostile = "-evil $(touch /tmp/pwned) `x` 'q' \"d\" ü.html";
10
+ const result = await openInBrowser(hostile, {
11
+ platform,
12
+ run: async (c, a) => {
13
+ calls.push([c, a]);
14
+ return { code: 0, stderr: "" };
15
+ },
16
+ });
17
+ expect(result).toEqual({ opened: true });
18
+ expect(calls).toHaveLength(1);
19
+ expect(calls[0][0]).toBe(exe);
20
+ expect(calls[0][1]).toHaveLength(1);
21
+ expect(calls[0][1][0].startsWith("/")).toBe(true);
22
+ expect(calls[0][1][0].endsWith(hostile)).toBe(true);
23
+ });
24
+ it("reports failures instead of throwing", async () => {
25
+ expect((await openInBrowser("/a.html", { platform: "linux", run: async () => ({ code: 3, stderr: "no display\n" }) })).error).toBe("no display");
26
+ expect((await openInBrowser("/a.html", { platform: "linux", run: async () => { throw new Error("ENOENT"); } })).error).toBe("ENOENT");
27
+ expect((await openInBrowser("/a.html", { platform: "win32" })).opened).toBe(false);
28
+ });
29
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moldesk/runtime",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Hardware detection and Python/Docker/process execution for MoleculeDesk",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  "dist"
20
20
  ],
21
21
  "dependencies": {
22
- "@moldesk/registry": "0.0.3"
22
+ "@moldesk/registry": "0.0.5"
23
23
  },
24
24
  "devDependencies": {
25
25
  "typescript": "^5.6.3",