pyfig 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Reggie Bowser
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,77 @@
1
- # Temporary Holding Version
1
+ # pyfig
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `pyfig` finds and configures a compatible Python interpreter and Python package manager from Bun or Node.js. It validates versions with npm's `semver`, checks the usual `python3`/`python` and `pip3`/`pip` names, and uses uv when a compatible local tool is unavailable.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ bun add pyfig
9
+ ```
10
+
11
+ ## Library
12
+
13
+ ```ts
14
+ import { ensureEnvironment } from 'pyfig';
15
+
16
+ const environment = await ensureEnvironment({
17
+ pythonRange: '>=3.11.0 <4.0.0',
18
+ pipRange: '>=23.0.0',
19
+ });
20
+
21
+ console.log(environment.python);
22
+ console.log(environment.packageManager);
23
+ ```
24
+
25
+ Defaults are Python `>=3.10.0 <4.0.0`, pip `>=20.3.0`, managed Python `3.12`, and uv `0.13.6`. Pass any npm-compatible SemVer range.
26
+
27
+ If pip is unsuitable, `packageManager.kind` is `uv`; its `prefixArgs` is `['pip']`. If Python is unsuitable, pyfig installs the configured managed Python with uv and returns that interpreter.
28
+
29
+ ## CLI
30
+
31
+ ```sh
32
+ bunx pyfig
33
+ bunx pyfig --check
34
+ bunx pyfig --python-range '>=3.12.0 <3.14.0' --pip-range '>=24.0.0'
35
+ bunx pyfig --json
36
+ ```
37
+
38
+ `--check` prohibits installation and exits nonzero when no compatible environment exists.
39
+
40
+ ## Configuration
41
+
42
+ Precedence is API/CLI option, environment variable, then default.
43
+
44
+ | API option | Environment variable | Default |
45
+ | --- | --- | --- |
46
+ | `python` | `PYFIG_PYTHON` | auto-detect |
47
+ | `pip` | `PYFIG_PIP` | auto-detect |
48
+ | `pythonRange` | `PYFIG_PYTHON_RANGE` | `>=3.10.0 <4.0.0` |
49
+ | `pipRange` | `PYFIG_PIP_RANGE` | `>=20.3.0` |
50
+ | `managedPython` | `PYFIG_MANAGED_PYTHON` | `3.12` |
51
+ | `uvVersion` | `PYFIG_UV_VERSION` | `0.13.6` |
52
+ | `cacheDir` | `PYFIG_HOME` | OS cache directory |
53
+ | `autoInstall` | `PYFIG_AUTO_INSTALL` | `true` |
54
+
55
+ Set `PYFIG_UV` to select an existing uv executable.
56
+
57
+ ## Installation safety
58
+
59
+ pyfig uses the official, version-pinned Astral installer: PowerShell on Windows and POSIX `sh` elsewhere. The installer chooses the supported platform and architecture. `UV_UNMANAGED_INSTALL` keeps uv inside pyfig's cache, and `UV_NO_MODIFY_PATH=1` prevents shell-profile changes.
60
+
61
+ uv and managed Python installation use `proper-lockfile` with a check-lock-check sequence:
62
+
63
+ 1. Check for a usable tool.
64
+ 2. Acquire a cross-process filesystem lock.
65
+ 3. Check again after acquiring the lock.
66
+ 4. Install only when the tool is still absent.
67
+
68
+ ## Development
69
+
70
+ ```sh
71
+ bun install --ignore-scripts
72
+ bun run verify
73
+ bun run build
74
+ bun pm pack --dry-run
75
+ ```
76
+
77
+ MIT licensed.
package/SECURITY.md ADDED
@@ -0,0 +1,7 @@
1
+ # Security
2
+
3
+ pyfig can download and execute Astral's official uv installer and can use uv to download Python distributions. Automatic installation is enabled by default; set `autoInstall: false`, pass `--check`, or set `PYFIG_AUTO_INSTALL=0` when downloads are not allowed.
4
+
5
+ The uv installer URL is pinned to the configured uv version and fetched over HTTPS. uv is installed into pyfig's cache without modifying shell profiles or `PATH`. Cross-process locks prevent concurrent installation into the same managed directories.
6
+
7
+ Executable paths and version ranges are configuration, not untrusted request data. Do not let an untrusted user choose `python`, `pip`, `PYFIG_UV`, `cacheDir`, or related environment variables in a privileged process.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { ensureEnvironment } from './provision.js';
4
+ const program = new Command()
5
+ .name('pyfig')
6
+ .description('Find compatible Python and pip executables, using uv as a locked fallback.')
7
+ .version('0.1.0')
8
+ .option('--python <path>', 'Python executable to check first')
9
+ .option('--pip <path>', 'pip executable to check first')
10
+ .option('--python-range <range>', 'accepted Python SemVer range')
11
+ .option('--pip-range <range>', 'accepted pip SemVer range')
12
+ .option('--managed-python <request>', 'Python version request passed to uv')
13
+ .option('--uv-version <version>', 'pinned uv installer version')
14
+ .option('--home <path>', 'cache and managed-tool directory')
15
+ .option('--check', 'check only; do not install Python or uv', false)
16
+ .option('--json', 'print machine-readable JSON', false)
17
+ .action(async (cli) => {
18
+ const environment = await ensureEnvironment({
19
+ python: cli.python,
20
+ pip: cli.pip,
21
+ pythonRange: cli.pythonRange,
22
+ pipRange: cli.pipRange,
23
+ managedPython: cli.managedPython,
24
+ uvVersion: cli.uvVersion,
25
+ cacheDir: cli.home,
26
+ autoInstall: cli.check ? false : undefined,
27
+ });
28
+ if (cli.json) {
29
+ console.log(JSON.stringify(environment, null, 2));
30
+ return;
31
+ }
32
+ console.log(`Python ${environment.python.version}: ${environment.python.executable}`);
33
+ const manager = environment.packageManager;
34
+ const suffix = manager.kind === 'uv' ? ' (pip fallback)' : '';
35
+ console.log(`${manager.kind} ${manager.version}: ${manager.executable}${suffix}`);
36
+ });
37
+ program.parseAsync().catch((error) => {
38
+ const message = error instanceof Error ? error.message : String(error);
39
+ console.error(`pyfig: ${message}`);
40
+ process.exitCode = 1;
41
+ });
42
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAcnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,4EAA4E,CAAC;KACzF,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,iBAAiB,EAAE,kCAAkC,CAAC;KAC7D,MAAM,CAAC,cAAc,EAAE,+BAA+B,CAAC;KACvD,MAAM,CAAC,wBAAwB,EAAE,8BAA8B,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,2BAA2B,CAAC;KAC1D,MAAM,CAAC,4BAA4B,EAAE,qCAAqC,CAAC;KAC3E,MAAM,CAAC,wBAAwB,EAAE,6BAA6B,CAAC;KAC/D,MAAM,CAAC,eAAe,EAAE,kCAAkC,CAAC;KAC3D,MAAM,CAAC,SAAS,EAAE,yCAAyC,EAAE,KAAK,CAAC;KACnE,MAAM,CAAC,QAAQ,EAAE,6BAA6B,EAAE,KAAK,CAAC;KACtD,MAAM,CAAC,KAAK,EAAE,GAAe,EAAE,EAAE;IAChC,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC;QAC1C,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,QAAQ,EAAE,GAAG,CAAC,IAAI;QAClB,WAAW,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;KAC3C,CAAC,CAAC;IAEH,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,UAAU,WAAW,CAAC,MAAM,CAAC,OAAO,KAAK,WAAW,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,WAAW,CAAC,cAAc,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,UAAU,GAAG,MAAM,EAAE,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC5C,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;IACnC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,32 @@
1
+ export declare const DEFAULT_PYTHON_RANGE = ">=3.10.0 <4.0.0";
2
+ export declare const DEFAULT_PIP_RANGE = ">=20.3.0";
3
+ export declare const DEFAULT_MANAGED_PYTHON = "3.12";
4
+ export declare const DEFAULT_UV_VERSION = "0.13.6";
5
+ export interface PyfigOptions {
6
+ python?: string;
7
+ pip?: string;
8
+ pythonRange?: string;
9
+ pipRange?: string;
10
+ managedPython?: string;
11
+ uvVersion?: string;
12
+ autoInstall?: boolean;
13
+ cacheDir?: string;
14
+ probeTimeoutMs?: number;
15
+ installTimeoutMs?: number;
16
+ env?: NodeJS.ProcessEnv;
17
+ }
18
+ export interface ResolvedPyfigOptions {
19
+ python: string;
20
+ pip: string;
21
+ pythonRange: string;
22
+ pipRange: string;
23
+ managedPython: string;
24
+ uvVersion: string;
25
+ autoInstall: boolean;
26
+ cacheDir: string;
27
+ probeTimeoutMs: number;
28
+ installTimeoutMs: number;
29
+ env: NodeJS.ProcessEnv;
30
+ }
31
+ export declare function resolveOptions(options?: PyfigOptions): ResolvedPyfigOptions;
32
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,sBAAsB,SAAS,CAAC;AAC7C,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,YAAiB,GAAG,oBAAoB,CAyB/E"}
package/dist/config.js ADDED
@@ -0,0 +1,46 @@
1
+ import path from 'node:path';
2
+ import envPaths from 'env-paths';
3
+ import semver from 'semver';
4
+ export const DEFAULT_PYTHON_RANGE = '>=3.10.0 <4.0.0';
5
+ export const DEFAULT_PIP_RANGE = '>=20.3.0';
6
+ export const DEFAULT_MANAGED_PYTHON = '3.12';
7
+ export const DEFAULT_UV_VERSION = '0.13.6';
8
+ export function resolveOptions(options = {}) {
9
+ const env = options.env ?? process.env;
10
+ const pythonRange = options.pythonRange ?? env['PYFIG_PYTHON_RANGE'] ?? DEFAULT_PYTHON_RANGE;
11
+ const pipRange = options.pipRange ?? env['PYFIG_PIP_RANGE'] ?? DEFAULT_PIP_RANGE;
12
+ assertRange('Python', pythonRange);
13
+ assertRange('pip', pipRange);
14
+ const configuredHome = options.cacheDir ?? env['PYFIG_HOME'];
15
+ const cacheDir = configuredHome
16
+ ? path.resolve(configuredHome)
17
+ : envPaths('pyfig', { suffix: '' }).cache;
18
+ return {
19
+ python: options.python ?? env['PYFIG_PYTHON'] ?? '',
20
+ pip: options.pip ?? env['PYFIG_PIP'] ?? '',
21
+ pythonRange,
22
+ pipRange,
23
+ managedPython: options.managedPython ?? env['PYFIG_MANAGED_PYTHON'] ?? DEFAULT_MANAGED_PYTHON,
24
+ uvVersion: options.uvVersion ?? env['PYFIG_UV_VERSION'] ?? DEFAULT_UV_VERSION,
25
+ autoInstall: options.autoInstall ?? parseBoolean(env['PYFIG_AUTO_INSTALL'], true),
26
+ cacheDir,
27
+ probeTimeoutMs: options.probeTimeoutMs ?? 10_000,
28
+ installTimeoutMs: options.installTimeoutMs ?? 10 * 60_000,
29
+ env,
30
+ };
31
+ }
32
+ function assertRange(tool, range) {
33
+ if (!semver.validRange(range)) {
34
+ throw new TypeError(`Invalid ${tool} SemVer range: ${range}`);
35
+ }
36
+ }
37
+ function parseBoolean(value, fallback) {
38
+ if (value === undefined)
39
+ return fallback;
40
+ if (/^(1|true|yes|on)$/iu.test(value))
41
+ return true;
42
+ if (/^(0|false|no|off)$/iu.test(value))
43
+ return false;
44
+ throw new TypeError(`Invalid boolean value: ${value}`);
45
+ }
46
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AACtD,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAC5C,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AA8B3C,MAAM,UAAU,cAAc,CAAC,OAAO,GAAiB,EAAE;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC,oBAAoB,CAAC,IAAI,oBAAoB,CAAC;IAC7F,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC,iBAAiB,CAAC,IAAI,iBAAiB,CAAC;IACjF,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACnC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE7B,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,cAAc;QAC7B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC;QAC9B,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC;IAE5C,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE;QACnD,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE;QAC1C,WAAW;QACX,QAAQ;QACR,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,GAAG,CAAC,sBAAsB,CAAC,IAAI,sBAAsB;QAC7F,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC,kBAAkB,CAAC,IAAI,kBAAkB;QAC7E,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,YAAY,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,IAAI,CAAC;QACjF,QAAQ;QACR,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,MAAM;QAChD,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,EAAE,GAAG,MAAM;QACzD,GAAG;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,KAAa;IAC9C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,WAAW,IAAI,kBAAkB,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB,EAAE,QAAiB;IAChE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACzC,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,MAAM,IAAI,SAAS,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { DEFAULT_MANAGED_PYTHON, DEFAULT_PIP_RANGE, DEFAULT_PYTHON_RANGE, DEFAULT_UV_VERSION, resolveOptions, } from './config.js';
2
+ export type { PyfigOptions, ResolvedPyfigOptions } from './config.js';
3
+ export { findPip, findPython, findUv, probePython, probeUv } from './probes.js';
4
+ export type { PackageManagerInfo, PythonInfo, UvInfo } from './probes.js';
5
+ export { ensureEnvironment, ensurePip, ensurePython } from './provision.js';
6
+ export type { PythonEnvironment } from './provision.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAChF,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC5E,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { DEFAULT_MANAGED_PYTHON, DEFAULT_PIP_RANGE, DEFAULT_PYTHON_RANGE, DEFAULT_UV_VERSION, resolveOptions, } from './config.js';
2
+ export { findPip, findPython, findUv, probePython, probeUv } from './probes.js';
3
+ export { ensureEnvironment, ensurePip, ensurePython } from './provision.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { ResolvedPyfigOptions } from './config.js';
2
+ import { type CommandSpec } from './process.js';
3
+ export interface PythonInfo {
4
+ executable: string;
5
+ version: string;
6
+ major: number;
7
+ minor: number;
8
+ patch: number;
9
+ prefix: string;
10
+ }
11
+ export interface PackageManagerInfo {
12
+ kind: 'pip' | 'uv';
13
+ executable: string;
14
+ prefixArgs: string[];
15
+ version: string;
16
+ pythonExecutable: string;
17
+ }
18
+ export interface UvInfo {
19
+ executable: string;
20
+ version: string;
21
+ }
22
+ export declare function findPython(options: ResolvedPyfigOptions): Promise<PythonInfo | null>;
23
+ export declare function probePython(candidate: CommandSpec, options: ResolvedPyfigOptions): Promise<PythonInfo | null>;
24
+ export declare function findPip(python: PythonInfo, options: ResolvedPyfigOptions): Promise<PackageManagerInfo | null>;
25
+ export declare function findUv(options: ResolvedPyfigOptions): Promise<UvInfo | null>;
26
+ export declare function probeUv(executable: string, options: ResolvedPyfigOptions): UvInfo | null;
27
+ //# sourceMappingURL=probes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probes.d.ts","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD,wBAAsB,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAuB1F;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAe5B;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA+BpC;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIlF;AAED,wBAAgB,OAAO,CACrB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,oBAAoB,GAC5B,MAAM,GAAG,IAAI,CAQf"}
package/dist/probes.js ADDED
@@ -0,0 +1,88 @@
1
+ import semver from 'semver';
2
+ import { resolveExecutable, run } from './process.js';
3
+ const PYTHON_PROBE = [
4
+ 'import json,sys',
5
+ 'print(json.dumps({"executable":sys.executable,"version":".".join(map(str,sys.version_info[:3])),"major":sys.version_info.major,"minor":sys.version_info.minor,"patch":sys.version_info.micro,"prefix":sys.prefix}))',
6
+ ].join(';');
7
+ export async function findPython(options) {
8
+ const candidates = [];
9
+ if (options.python)
10
+ candidates.push({ executable: options.python, args: [] });
11
+ if (process.platform === 'win32') {
12
+ candidates.push({ executable: 'python', args: [] }, { executable: 'python3', args: [] }, { executable: 'py', args: ['-3'] });
13
+ }
14
+ else {
15
+ candidates.push(...['python3.15', 'python3.14', 'python3.13', 'python3.12', 'python3.11', 'python3.10']
16
+ .map((executable) => ({ executable, args: [] })), { executable: 'python3', args: [] }, { executable: 'python', args: [] });
17
+ }
18
+ for (const candidate of candidates) {
19
+ const info = await probePython(candidate, options);
20
+ if (info && semver.satisfies(info.version, options.pythonRange))
21
+ return info;
22
+ }
23
+ return null;
24
+ }
25
+ export async function probePython(candidate, options) {
26
+ const executable = await resolveExecutable(candidate.executable, options.env);
27
+ if (!executable)
28
+ return null;
29
+ try {
30
+ const { stdout } = run(executable, [...candidate.args, '-c', PYTHON_PROBE], options.env, options.probeTimeoutMs);
31
+ const info = JSON.parse(stdout.trim());
32
+ return semver.valid(info.version) ? info : null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ export async function findPip(python, options) {
39
+ const candidates = [
40
+ { executable: python.executable, args: ['-m', 'pip'] },
41
+ ];
42
+ if (options.pip)
43
+ candidates.push({ executable: options.pip, args: [] });
44
+ candidates.push({ executable: 'pip3', args: [] }, { executable: 'pip', args: [] });
45
+ for (const candidate of candidates) {
46
+ const executable = await resolveExecutable(candidate.executable, options.env);
47
+ if (!executable)
48
+ continue;
49
+ try {
50
+ const { stdout } = run(executable, [...candidate.args, '--version'], options.env, options.probeTimeoutMs);
51
+ const version = normalizeVersion(stdout.match(/^pip\s+(\S+)/iu)?.[1]);
52
+ if (!version || !semver.satisfies(version, options.pipRange))
53
+ continue;
54
+ return {
55
+ kind: 'pip',
56
+ executable,
57
+ prefixArgs: candidate.args,
58
+ version,
59
+ pythonExecutable: python.executable,
60
+ };
61
+ }
62
+ catch {
63
+ // Try the next conventional pip spelling.
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+ export async function findUv(options) {
69
+ const configured = options.env['PYFIG_UV'] ?? 'uv';
70
+ const executable = await resolveExecutable(configured, options.env);
71
+ return executable ? probeUv(executable, options) : null;
72
+ }
73
+ export function probeUv(executable, options) {
74
+ try {
75
+ const { stdout } = run(executable, ['--version'], options.env, options.probeTimeoutMs);
76
+ const version = normalizeVersion(stdout.match(/^uv\s+(\S+)/iu)?.[1]);
77
+ return version ? { executable, version } : null;
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ function normalizeVersion(value) {
84
+ if (!value)
85
+ return null;
86
+ return semver.valid(value) ?? semver.coerce(value)?.version ?? null;
87
+ }
88
+ //# sourceMappingURL=probes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probes.js","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface CommandSpec {
2
+ executable: string;
3
+ args: string[];
4
+ }
5
+ export interface RunResult {
6
+ stdout: string;
7
+ stderr: string;
8
+ }
9
+ export declare function resolveExecutable(value: string, env: NodeJS.ProcessEnv): Promise<string | null>;
10
+ export declare function run(executable: string, args: string[], env: NodeJS.ProcessEnv, timeoutMs: number): RunResult;
11
+ //# sourceMappingURL=process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED,wBAAgB,GAAG,CACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,SAAS,EAAE,MAAM,GAChB,SAAS,CAaX"}
@@ -0,0 +1,28 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import path from 'node:path';
3
+ import which from 'which';
4
+ export async function resolveExecutable(value, env) {
5
+ if (!value)
6
+ return null;
7
+ if (path.isAbsolute(value)) {
8
+ return await which(value, { nothrow: true, path: env['PATH'], pathExt: env['PATHEXT'] });
9
+ }
10
+ if (!/^[A-Za-z0-9_.-]+$/u.test(value))
11
+ return null;
12
+ return await which(value, { nothrow: true, path: env['PATH'], pathExt: env['PATHEXT'] });
13
+ }
14
+ export function run(executable, args, env, timeoutMs) {
15
+ const result = spawnSync(executable, args, {
16
+ encoding: 'utf8',
17
+ env,
18
+ timeout: timeoutMs,
19
+ maxBuffer: 4 * 1024 * 1024,
20
+ windowsHide: true,
21
+ });
22
+ if (result.status !== 0 || result.error) {
23
+ const detail = result.stderr?.trim() || result.error?.message || `exit ${result.status}`;
24
+ throw new Error(`${path.basename(executable)} ${args.slice(0, 3).join(' ')} failed: ${detail}`);
25
+ }
26
+ return { stdout: result.stdout, stderr: result.stderr };
27
+ }
28
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAa,EACb,GAAsB;IAEtB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,GAAG,CACjB,UAAkB,EAClB,IAAc,EACd,GAAsB,EACtB,SAAiB;IAEjB,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE;QACzC,QAAQ,EAAE,MAAM;QAChB,GAAG;QACH,OAAO,EAAE,SAAS;QAClB,SAAS,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;QAC1B,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;QACzF,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type PyfigOptions } from './config.js';
2
+ import { type PackageManagerInfo, type PythonInfo } from './probes.js';
3
+ export interface PythonEnvironment {
4
+ python: PythonInfo;
5
+ packageManager: PackageManagerInfo;
6
+ }
7
+ export declare function ensurePython(options?: PyfigOptions): Promise<PythonInfo>;
8
+ export declare function ensurePip(options?: PyfigOptions, python?: PythonInfo): Promise<PackageManagerInfo>;
9
+ export declare function ensureEnvironment(options?: PyfigOptions): Promise<PythonEnvironment>;
10
+ //# sourceMappingURL=provision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,KAAK,YAAY,EAA6B,MAAM,aAAa,CAAC;AAC3F,OAAO,EAML,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,kBAAkB,CAAC;CACpC;AAQD,wBAAsB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAElF;AAED,wBAAsB,SAAS,CAC7B,OAAO,GAAE,YAAiB,EAC1B,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,kBAAkB,CAAC,CAI7B;AAED,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAK9F"}
@@ -0,0 +1,156 @@
1
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import lockfile from 'proper-lockfile';
5
+ import semver from 'semver';
6
+ import { resolveOptions } from './config.js';
7
+ import { findPip, findPython, findUv, probePython, probeUv, } from './probes.js';
8
+ import { resolveExecutable, run } from './process.js';
9
+ const LOCK_OPTIONS = {
10
+ realpath: true,
11
+ stale: 10 * 60_000,
12
+ retries: { retries: 40, factor: 1.2, minTimeout: 100, maxTimeout: 1000 },
13
+ };
14
+ export async function ensurePython(options = {}) {
15
+ return ensurePythonResolved(resolveOptions(options));
16
+ }
17
+ export async function ensurePip(options = {}, python) {
18
+ const resolved = resolveOptions(options);
19
+ const selectedPython = python ?? await ensurePythonResolved(resolved);
20
+ return ensurePipResolved(selectedPython, resolved);
21
+ }
22
+ export async function ensureEnvironment(options = {}) {
23
+ const resolved = resolveOptions(options);
24
+ const python = await ensurePythonResolved(resolved);
25
+ const packageManager = await ensurePipResolved(python, resolved);
26
+ return { python, packageManager };
27
+ }
28
+ async function ensurePythonResolved(options) {
29
+ const existing = await findPython(options);
30
+ if (existing)
31
+ return existing;
32
+ if (!options.autoInstall) {
33
+ throw new Error(`No Python interpreter satisfies ${options.pythonRange}; automatic installation is disabled.`);
34
+ }
35
+ const lockTarget = path.join(options.cacheDir, 'locks', 'python');
36
+ await mkdir(lockTarget, { recursive: true });
37
+ const release = await lockfile.lock(lockTarget, LOCK_OPTIONS);
38
+ try {
39
+ const rechecked = await findPython(options);
40
+ if (rechecked)
41
+ return rechecked;
42
+ const uv = await ensureUv(options);
43
+ const uvEnv = uvEnvironment(options);
44
+ run(uv.executable, ['python', 'install', options.managedPython], uvEnv, options.installTimeoutMs);
45
+ const { stdout } = run(uv.executable, ['python', 'find', '--python-preference', 'only-managed', options.managedPython], uvEnv, options.probeTimeoutMs);
46
+ const installed = await probePython({ executable: stdout.trim(), args: [] }, { ...options, env: uvEnv });
47
+ if (!installed || !semver.satisfies(installed.version, options.pythonRange)) {
48
+ throw new Error(`uv installed Python ${options.managedPython}, but it does not satisfy ${options.pythonRange}.`);
49
+ }
50
+ return installed;
51
+ }
52
+ finally {
53
+ await release();
54
+ }
55
+ }
56
+ async function ensurePipResolved(python, options) {
57
+ const existing = await findPip(python, options);
58
+ if (existing)
59
+ return existing;
60
+ const existingUv = await findAvailableUv(options);
61
+ if (existingUv)
62
+ return uvPackageManager(existingUv, python);
63
+ if (!options.autoInstall) {
64
+ throw new Error(`No pip executable satisfies ${options.pipRange}; automatic uv installation is disabled.`);
65
+ }
66
+ const uv = await ensureUv(options);
67
+ return uvPackageManager(uv, python);
68
+ }
69
+ async function ensureUv(options) {
70
+ const existing = await findAvailableUv(options);
71
+ if (existing)
72
+ return existing;
73
+ if (!options.autoInstall)
74
+ throw new Error('uv is unavailable and automatic installation is disabled.');
75
+ const installDir = uvInstallDirectory(options);
76
+ const executable = uvExecutable(options);
77
+ const lockTarget = path.join(options.cacheDir, 'locks', 'uv');
78
+ await mkdir(lockTarget, { recursive: true });
79
+ const release = await lockfile.lock(lockTarget, LOCK_OPTIONS);
80
+ try {
81
+ const rechecked = probeUv(executable, options);
82
+ if (rechecked)
83
+ return rechecked;
84
+ await installUv(installDir, options);
85
+ const result = probeUv(executable, options);
86
+ if (!result)
87
+ throw new Error('The uv installer completed, but the installed executable could not run.');
88
+ return result;
89
+ }
90
+ finally {
91
+ await release();
92
+ }
93
+ }
94
+ async function findAvailableUv(options) {
95
+ return await findUv(options) ?? probeUv(uvExecutable(options), options);
96
+ }
97
+ function uvInstallDirectory(options) {
98
+ return path.join(options.cacheDir, 'tools', 'uv', options.uvVersion);
99
+ }
100
+ function uvExecutable(options) {
101
+ return path.join(uvInstallDirectory(options), process.platform === 'win32' ? 'uv.exe' : 'uv');
102
+ }
103
+ function uvPackageManager(uv, python) {
104
+ return {
105
+ kind: 'uv',
106
+ executable: uv.executable,
107
+ prefixArgs: ['pip'],
108
+ version: uv.version,
109
+ pythonExecutable: python.executable,
110
+ };
111
+ }
112
+ async function installUv(installDir, options) {
113
+ const scriptName = process.platform === 'win32' ? 'install.ps1' : 'install.sh';
114
+ const installerUrl = `https://astral.sh/uv/${options.uvVersion}/${scriptName}`;
115
+ const temporary = await mkdtemp(path.join(os.tmpdir(), 'pyfig-uv-'));
116
+ try {
117
+ const installer = path.join(temporary, scriptName);
118
+ await download(installerUrl, installer);
119
+ await mkdir(installDir, { recursive: true });
120
+ const env = {
121
+ ...options.env,
122
+ UV_UNMANAGED_INSTALL: installDir,
123
+ UV_NO_MODIFY_PATH: '1',
124
+ };
125
+ if (process.platform === 'win32') {
126
+ const powershell = await resolveExecutable('powershell.exe', env) ??
127
+ await resolveExecutable('pwsh', env);
128
+ if (!powershell)
129
+ throw new Error('The uv Windows installer requires PowerShell or pwsh.');
130
+ run(powershell, ['-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', installer], env, options.installTimeoutMs);
131
+ }
132
+ else {
133
+ const shell = await resolveExecutable('sh', env);
134
+ if (!shell)
135
+ throw new Error('The uv installer requires a POSIX sh executable.');
136
+ run(shell, [installer], env, options.installTimeoutMs);
137
+ }
138
+ }
139
+ finally {
140
+ await rm(temporary, { recursive: true, force: true });
141
+ }
142
+ }
143
+ async function download(url, destination) {
144
+ const response = await fetch(url, { redirect: 'follow' });
145
+ if (!response.ok)
146
+ throw new Error(`Could not download ${url}: HTTP ${response.status}`);
147
+ await writeFile(destination, new Uint8Array(await response.arrayBuffer()));
148
+ }
149
+ function uvEnvironment(options) {
150
+ return {
151
+ ...options.env,
152
+ UV_PYTHON_INSTALL_DIR: path.join(options.cacheDir, 'python'),
153
+ UV_CACHE_DIR: path.join(options.cacheDir, 'uv-cache'),
154
+ };
155
+ }
156
+ //# sourceMappingURL=provision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provision.js","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "pyfig",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Find compatible Python and pip executables, with a locked uv fallback.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "pyfig": "dist/cli.js"
16
+ },
17
+ "files": [
18
+ "dist/",
19
+ "LICENSE",
20
+ "README.md",
21
+ "SECURITY.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.json",
25
+ "test": "bun test",
26
+ "typecheck": "tsc --noEmit -p tsconfig.json",
27
+ "verify": "bun run typecheck && bun test",
28
+ "prepack": "bun run verify && bun run build"
29
+ },
30
+ "keywords": [
31
+ "python",
32
+ "pip",
33
+ "uv",
34
+ "bootstrap",
35
+ "bun",
36
+ "cli"
37
+ ],
38
+ "author": "Reggie Bowser",
39
+ "license": "MIT",
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "sideEffects": false,
44
+ "engines": {
45
+ "node": ">=20.17.0",
46
+ "bun": ">=1.2.0"
47
+ },
48
+ "dependencies": {
49
+ "commander": "^14.0.3",
50
+ "env-paths": "^4.0.0",
51
+ "proper-lockfile": "^4.1.2",
52
+ "semver": "^7.8.5",
53
+ "which": "^6.0.1"
54
+ },
55
+ "devDependencies": {
56
+ "@types/bun": "^1.4.4",
57
+ "@types/node": "^26.6.6",
58
+ "@types/proper-lockfile": "^4.1.4",
59
+ "@types/semver": "^7.8.0",
60
+ "@types/which": "^3.0.4",
61
+ "typescript": "^7.0.2"
62
+ }
63
+ }