pyfig 0.1.0 → 0.3.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/README.md +154 -17
- package/SECURITY.md +5 -3
- package/dist/cjs/index.js +1504 -0
- package/dist/cjs/index.js.map +21 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/sync-resolver.js +1242 -0
- package/dist/cjs/sync-resolver.js.map +18 -0
- package/dist/cli.js +17 -6
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +20 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +23 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +45 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +130 -9
- package/dist/config.js.map +1 -1
- package/dist/coordination.d.ts +25 -0
- package/dist/coordination.d.ts.map +1 -0
- package/dist/coordination.js +187 -0
- package/dist/coordination.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/package-target.d.ts +12 -0
- package/dist/package-target.d.ts.map +1 -0
- package/dist/package-target.js +97 -0
- package/dist/package-target.js.map +1 -0
- package/dist/probes.d.ts +27 -0
- package/dist/probes.d.ts.map +1 -1
- package/dist/probes.js +140 -33
- package/dist/probes.js.map +1 -1
- package/dist/process.d.ts +2 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +63 -1
- package/dist/process.js.map +1 -1
- package/dist/project-root.d.ts +3 -0
- package/dist/project-root.d.ts.map +1 -0
- package/dist/project-root.js +83 -0
- package/dist/project-root.js.map +1 -0
- package/dist/provision.d.ts +3 -1
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +375 -97
- package/dist/provision.js.map +1 -1
- package/dist/runners.d.ts +18 -0
- package/dist/runners.d.ts.map +1 -0
- package/dist/runners.js +151 -0
- package/dist/runners.js.map +1 -0
- package/dist/runtime-files.d.ts +3 -0
- package/dist/runtime-files.d.ts.map +1 -0
- package/dist/runtime-files.js +12 -0
- package/dist/runtime-files.js.map +1 -0
- package/dist/state.d.ts +10 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +62 -0
- package/dist/state.js.map +1 -0
- package/dist/sync-resolver.d.ts +2 -0
- package/dist/sync-resolver.d.ts.map +1 -0
- package/dist/sync-resolver.js +24 -0
- package/dist/sync-resolver.js.map +1 -0
- package/dist/virtual-environment.d.ts +9 -0
- package/dist/virtual-environment.d.ts.map +1 -0
- package/dist/virtual-environment.js +42 -0
- package/dist/virtual-environment.js.map +1 -0
- package/package.json +26 -7
package/dist/config.js
CHANGED
|
@@ -1,39 +1,146 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import path from 'node:path';
|
|
2
|
-
import envPaths from 'env-paths';
|
|
3
|
+
import * as envPaths from 'env-paths';
|
|
3
4
|
import semver from 'semver';
|
|
5
|
+
import { resolveProjectRoot } from './project-root.js';
|
|
4
6
|
export const DEFAULT_PYTHON_RANGE = '>=3.10.0 <4.0.0';
|
|
5
7
|
export const DEFAULT_PIP_RANGE = '>=20.3.0';
|
|
6
8
|
export const DEFAULT_MANAGED_PYTHON = '3.12';
|
|
7
9
|
export const DEFAULT_UV_VERSION = '0.13.6';
|
|
10
|
+
export const DEFAULT_UV_RANGE = `>=${DEFAULT_UV_VERSION} <1.0.0`;
|
|
11
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 10_000;
|
|
12
|
+
const DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60_000;
|
|
13
|
+
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 5 * 60_000;
|
|
14
|
+
const DEFAULT_CACHE_TTL_MS = 30_000;
|
|
15
|
+
// Bun's CommonJS bundler wraps an external ESM default one level deeper than
|
|
16
|
+
// Node.js. Normalize that boundary once instead of leaking runtime checks into
|
|
17
|
+
// configuration resolution.
|
|
18
|
+
const envPathsFunction = (() => {
|
|
19
|
+
const imported = envPaths.default;
|
|
20
|
+
const implementation = typeof imported === 'function' ? imported : imported.default;
|
|
21
|
+
if (!implementation)
|
|
22
|
+
throw new TypeError('env-paths did not expose its default function.');
|
|
23
|
+
return implementation;
|
|
24
|
+
})();
|
|
8
25
|
export function resolveOptions(options = {}) {
|
|
9
|
-
|
|
26
|
+
// Snapshot the environment so callers cannot change an in-flight policy by
|
|
27
|
+
// mutating process.env or a reused options object after resolution.
|
|
28
|
+
const env = { ...(options.env ?? process.env) };
|
|
10
29
|
const pythonRange = options.pythonRange ?? env['PYFIG_PYTHON_RANGE'] ?? DEFAULT_PYTHON_RANGE;
|
|
11
30
|
const pipRange = options.pipRange ?? env['PYFIG_PIP_RANGE'] ?? DEFAULT_PIP_RANGE;
|
|
31
|
+
const uvRange = options.uvRange ?? env['PYFIG_UV_RANGE'] ?? DEFAULT_UV_RANGE;
|
|
12
32
|
assertRange('Python', pythonRange);
|
|
13
33
|
assertRange('pip', pipRange);
|
|
34
|
+
assertRange('uv', uvRange);
|
|
35
|
+
const managedPython = options.managedPython ??
|
|
36
|
+
env['PYFIG_MANAGED_PYTHON'] ?? DEFAULT_MANAGED_PYTHON;
|
|
37
|
+
const uvVersion = options.uvVersion ?? env['PYFIG_UV_VERSION'] ?? DEFAULT_UV_VERSION;
|
|
38
|
+
assertManagedPythonRequest(managedPython, pythonRange);
|
|
39
|
+
assertUvVersion(uvVersion, uvRange);
|
|
40
|
+
const probeTimeoutMs = resolveDuration('probeTimeoutMs', options.probeTimeoutMs, env['PYFIG_PROBE_TIMEOUT_MS'], DEFAULT_PROBE_TIMEOUT_MS);
|
|
41
|
+
const installTimeoutMs = resolveDuration('installTimeoutMs', options.installTimeoutMs, env['PYFIG_INSTALL_TIMEOUT_MS'], DEFAULT_INSTALL_TIMEOUT_MS);
|
|
42
|
+
const downloadTimeoutMs = resolveDuration('downloadTimeoutMs', options.downloadTimeoutMs, env['PYFIG_DOWNLOAD_TIMEOUT_MS'], DEFAULT_DOWNLOAD_TIMEOUT_MS);
|
|
43
|
+
const coordinationTimeoutMs = resolveDuration('coordinationTimeoutMs', options.coordinationTimeoutMs, env['PYFIG_COORDINATION_TIMEOUT_MS'], installTimeoutMs + downloadTimeoutMs + 60_000);
|
|
44
|
+
const cacheTtlMs = resolveDuration('cacheTtlMs', options.cacheTtlMs, env['PYFIG_CACHE_TTL_MS'], DEFAULT_CACHE_TTL_MS, true);
|
|
14
45
|
const configuredHome = options.cacheDir ?? env['PYFIG_HOME'];
|
|
15
46
|
const cacheDir = configuredHome
|
|
16
47
|
? path.resolve(configuredHome)
|
|
17
|
-
:
|
|
48
|
+
: envPathsFunction('pyfig', { suffix: '' }).cache;
|
|
49
|
+
const configuredProjectRoot = options.projectRoot ?? env['PYFIG_PROJECT_ROOT'];
|
|
50
|
+
const projectRoot = configuredProjectRoot
|
|
51
|
+
? path.resolve(configuredProjectRoot)
|
|
52
|
+
: resolveProjectRoot();
|
|
53
|
+
const autoInstall = options.autoInstall ?? parseBoolean(env['PYFIG_AUTO_INSTALL'], true);
|
|
18
54
|
return {
|
|
19
55
|
python: options.python ?? env['PYFIG_PYTHON'] ?? '',
|
|
20
|
-
pip: options.pip ?? env['PYFIG_PIP'] ?? '',
|
|
21
56
|
pythonRange,
|
|
22
57
|
pipRange,
|
|
23
|
-
managedPython
|
|
24
|
-
uvVersion
|
|
25
|
-
|
|
58
|
+
managedPython,
|
|
59
|
+
uvVersion,
|
|
60
|
+
uvRange,
|
|
61
|
+
requireEmbedding: options.requireEmbedding ?? parseBoolean(env['PYFIG_REQUIRE_EMBEDDING'], false),
|
|
62
|
+
requireGil: options.requireGil ?? parseBoolean(env['PYFIG_REQUIRE_GIL'], false),
|
|
63
|
+
autoInstall,
|
|
64
|
+
installPython: options.installPython ?? parseBoolean(env['PYFIG_INSTALL_PYTHON'], autoInstall),
|
|
65
|
+
installUv: options.installUv ?? parseBoolean(env['PYFIG_INSTALL_UV'], autoInstall),
|
|
66
|
+
createVirtualEnvironment: options.createVirtualEnvironment ??
|
|
67
|
+
parseBoolean(env['PYFIG_CREATE_VIRTUAL_ENVIRONMENT'], autoInstall),
|
|
26
68
|
cacheDir,
|
|
27
|
-
|
|
28
|
-
|
|
69
|
+
projectRoot,
|
|
70
|
+
probeTimeoutMs,
|
|
71
|
+
installTimeoutMs,
|
|
72
|
+
downloadTimeoutMs,
|
|
73
|
+
coordinationTimeoutMs,
|
|
74
|
+
cacheTtlMs,
|
|
29
75
|
env,
|
|
30
76
|
};
|
|
31
77
|
}
|
|
78
|
+
/** Opaque identity shared by every async and sync resolution cache. */
|
|
79
|
+
export function configurationFingerprint(options) {
|
|
80
|
+
const policy = [
|
|
81
|
+
normalizedPath(options.projectRoot),
|
|
82
|
+
normalizedPath(options.cacheDir),
|
|
83
|
+
options.python,
|
|
84
|
+
options.pythonRange,
|
|
85
|
+
options.pipRange,
|
|
86
|
+
options.managedPython,
|
|
87
|
+
options.uvVersion,
|
|
88
|
+
options.uvRange,
|
|
89
|
+
options.requireEmbedding,
|
|
90
|
+
options.requireGil,
|
|
91
|
+
options.autoInstall,
|
|
92
|
+
options.installPython,
|
|
93
|
+
options.installUv,
|
|
94
|
+
options.createVirtualEnvironment,
|
|
95
|
+
options.probeTimeoutMs,
|
|
96
|
+
options.installTimeoutMs,
|
|
97
|
+
options.downloadTimeoutMs,
|
|
98
|
+
options.coordinationTimeoutMs,
|
|
99
|
+
options.cacheTtlMs,
|
|
100
|
+
environmentFingerprint(options.env),
|
|
101
|
+
];
|
|
102
|
+
return createHash('sha256').update(JSON.stringify(policy)).digest('hex');
|
|
103
|
+
}
|
|
104
|
+
/** Keep uv-managed Python and cache data inside pyfig's configured cache root. */
|
|
105
|
+
export function uvEnvironment(options) {
|
|
106
|
+
return {
|
|
107
|
+
...options.env,
|
|
108
|
+
UV_PYTHON_INSTALL_DIR: path.join(options.cacheDir, 'python'),
|
|
109
|
+
UV_CACHE_DIR: path.join(options.cacheDir, 'uv-cache'),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
32
112
|
function assertRange(tool, range) {
|
|
33
113
|
if (!semver.validRange(range)) {
|
|
34
114
|
throw new TypeError(`Invalid ${tool} SemVer range: ${range}`);
|
|
35
115
|
}
|
|
36
116
|
}
|
|
117
|
+
function assertUvVersion(version, range) {
|
|
118
|
+
if (!semver.valid(version)) {
|
|
119
|
+
throw new TypeError(`Invalid exact uv version: ${version}`);
|
|
120
|
+
}
|
|
121
|
+
if (!semver.satisfies(version, range)) {
|
|
122
|
+
throw new TypeError(`Pinned uv ${version} does not satisfy supported range ${range}.`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function assertManagedPythonRequest(request, pythonRange) {
|
|
126
|
+
if (!request || /[\\/\0\r\n]/u.test(request) || request.includes('..')) {
|
|
127
|
+
throw new TypeError(`Invalid managed Python request: ${request}`);
|
|
128
|
+
}
|
|
129
|
+
if (/^\d+\.\d+(?:\.\d+)?$/u.test(request)) {
|
|
130
|
+
const normalized = request.split('.').length === 2 ? `${request}.0` : request;
|
|
131
|
+
if (!semver.satisfies(normalized, pythonRange)) {
|
|
132
|
+
throw new TypeError(`Managed Python ${request} cannot satisfy configured range ${pythonRange}.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function resolveDuration(name, explicit, environment, fallback, allowZero = false) {
|
|
137
|
+
const value = explicit ?? (environment === undefined ? fallback : Number(environment));
|
|
138
|
+
const minimum = allowZero ? 0 : 1;
|
|
139
|
+
if (!Number.isSafeInteger(value) || value < minimum) {
|
|
140
|
+
throw new TypeError(`${name} must be ${allowZero ? 'a non-negative' : 'a positive'} integer.`);
|
|
141
|
+
}
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
37
144
|
function parseBoolean(value, fallback) {
|
|
38
145
|
if (value === undefined)
|
|
39
146
|
return fallback;
|
|
@@ -43,4 +150,18 @@ function parseBoolean(value, fallback) {
|
|
|
43
150
|
return false;
|
|
44
151
|
throw new TypeError(`Invalid boolean value: ${value}`);
|
|
45
152
|
}
|
|
153
|
+
function environmentFingerprint(env) {
|
|
154
|
+
const entries = Object.entries(env)
|
|
155
|
+
.filter((entry) => entry[1] !== undefined)
|
|
156
|
+
.map(([key, value]) => [
|
|
157
|
+
process.platform === 'win32' ? key.toUpperCase() : key,
|
|
158
|
+
value,
|
|
159
|
+
])
|
|
160
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
161
|
+
return createHash('sha256').update(JSON.stringify(entries)).digest('hex');
|
|
162
|
+
}
|
|
163
|
+
function normalizedPath(value) {
|
|
164
|
+
const resolved = path.resolve(value);
|
|
165
|
+
return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
|
|
166
|
+
}
|
|
46
167
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ResourceLockOptions {
|
|
2
|
+
/** Stable logical identity shared by every caller of the protected resource. */
|
|
3
|
+
key: string;
|
|
4
|
+
/** Existing directory used by the portable cross-process lease. */
|
|
5
|
+
target: string;
|
|
6
|
+
timeoutMs: number;
|
|
7
|
+
env: NodeJS.ProcessEnv;
|
|
8
|
+
}
|
|
9
|
+
export interface ProvisioningLockOptions<T> extends ResourceLockOptions {
|
|
10
|
+
check: () => Promise<T | null>;
|
|
11
|
+
mutate: (signal: AbortSignal) => Promise<T>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Apply check-lock-check around an idempotent provisioning mutation. The lock
|
|
15
|
+
* stack is ordered identically everywhere to prevent lock-order inversions.
|
|
16
|
+
*/
|
|
17
|
+
export declare function withProvisioningLock<T>(options: ProvisioningLockOptions<T>): Promise<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Serialize a resource in the current realm, across supported Node workers,
|
|
20
|
+
* through native flock when present, and finally through a portable file lease.
|
|
21
|
+
*/
|
|
22
|
+
export declare function withResourceLock<T>(options: ResourceLockOptions, operation: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
23
|
+
/** Create a canonical lock target below a configured cache directory. */
|
|
24
|
+
export declare function cacheLockTarget(cacheDirectory: string, name: string): string;
|
|
25
|
+
//# sourceMappingURL=coordination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination.d.ts","sourceRoot":"","sources":["../src/coordination.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC,CAAE,SAAQ,mBAAmB;IACrE,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7C;AAWD;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CAQZ;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,CAAC,CAMZ;AAuKD,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5E"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { mkdir } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import * as workerThreads from 'node:worker_threads';
|
|
6
|
+
import lockfile from 'proper-lockfile';
|
|
7
|
+
import { resolveExecutable } from './process.js';
|
|
8
|
+
const realmMutexTails = new Map();
|
|
9
|
+
const flockLookups = new Map();
|
|
10
|
+
const FLOCK_READY = 'PYFIG_FLOCK_READY';
|
|
11
|
+
const FLOCK_HOLDER = [
|
|
12
|
+
`process.stdout.write('${FLOCK_READY}\\n')`,
|
|
13
|
+
'process.stdin.resume()',
|
|
14
|
+
'process.stdin.once("end",()=>process.exit(0))',
|
|
15
|
+
].join(';');
|
|
16
|
+
/**
|
|
17
|
+
* Apply check-lock-check around an idempotent provisioning mutation. The lock
|
|
18
|
+
* stack is ordered identically everywhere to prevent lock-order inversions.
|
|
19
|
+
*/
|
|
20
|
+
export async function withProvisioningLock(options) {
|
|
21
|
+
const existing = await options.check();
|
|
22
|
+
if (existing !== null)
|
|
23
|
+
return existing;
|
|
24
|
+
return withResourceLock(options, async (signal) => {
|
|
25
|
+
const rechecked = await options.check();
|
|
26
|
+
return rechecked ?? options.mutate(signal);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Serialize a resource in the current realm, across supported Node workers,
|
|
31
|
+
* through native flock when present, and finally through a portable file lease.
|
|
32
|
+
*/
|
|
33
|
+
export async function withResourceLock(options, operation) {
|
|
34
|
+
await mkdir(options.target, { recursive: true });
|
|
35
|
+
return withRealmMutex(options.key, () => withWorkerLock(options, () => withNativeFlock(options, () => withPortableLease(options, operation))));
|
|
36
|
+
}
|
|
37
|
+
async function withRealmMutex(key, operation) {
|
|
38
|
+
const previous = realmMutexTails.get(key) ?? Promise.resolve();
|
|
39
|
+
let releaseGate;
|
|
40
|
+
const gate = new Promise((resolve) => {
|
|
41
|
+
releaseGate = resolve;
|
|
42
|
+
});
|
|
43
|
+
const tail = previous.catch(() => undefined).then(() => gate);
|
|
44
|
+
realmMutexTails.set(key, tail);
|
|
45
|
+
await previous.catch(() => undefined);
|
|
46
|
+
try {
|
|
47
|
+
return await operation();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
releaseGate();
|
|
51
|
+
if (realmMutexTails.get(key) === tail)
|
|
52
|
+
realmMutexTails.delete(key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function withWorkerLock(options, operation) {
|
|
56
|
+
const manager = workerThreads.locks;
|
|
57
|
+
if (!manager?.request)
|
|
58
|
+
return operation();
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const timeout = setTimeout(() => controller.abort(new Error(`Timed out waiting for worker lock ${options.key}.`)), options.timeoutMs);
|
|
61
|
+
let entered = false;
|
|
62
|
+
try {
|
|
63
|
+
return await manager.request(workerLockName(options.key), { mode: 'exclusive', signal: controller.signal }, async () => {
|
|
64
|
+
entered = true;
|
|
65
|
+
return operation();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Worker locks are an optimization on runtimes that expose the experimental
|
|
70
|
+
// API. The portable lease remains authoritative when acquisition is absent.
|
|
71
|
+
if (entered)
|
|
72
|
+
throw error;
|
|
73
|
+
return operation();
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
clearTimeout(timeout);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function withNativeFlock(options, operation) {
|
|
80
|
+
if (process.platform === 'win32')
|
|
81
|
+
return operation();
|
|
82
|
+
// Coordination helpers belong to the host runtime, not the child discovery
|
|
83
|
+
// environment, which callers may intentionally strip of Python and uv paths.
|
|
84
|
+
const executable = await findFlock(process.env);
|
|
85
|
+
if (!executable)
|
|
86
|
+
return operation();
|
|
87
|
+
const child = await acquireFlock(executable, `${options.target}.flock`, options.timeoutMs);
|
|
88
|
+
if (!child)
|
|
89
|
+
return operation();
|
|
90
|
+
try {
|
|
91
|
+
return await operation();
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
await releaseFlock(child);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function findFlock(env) {
|
|
98
|
+
const lookupKey = `${env['PATH'] ?? ''}\0${env['PATHEXT'] ?? ''}`;
|
|
99
|
+
let lookup = flockLookups.get(lookupKey);
|
|
100
|
+
if (!lookup) {
|
|
101
|
+
lookup = resolveExecutable('flock', env);
|
|
102
|
+
flockLookups.set(lookupKey, lookup);
|
|
103
|
+
}
|
|
104
|
+
return lookup;
|
|
105
|
+
}
|
|
106
|
+
async function acquireFlock(executable, lockPath, timeoutMs) {
|
|
107
|
+
const waitSeconds = Math.max(1, Math.ceil(Math.min(timeoutMs, 30_000) / 1000));
|
|
108
|
+
const child = spawn(executable, ['-x', '-w', String(waitSeconds), lockPath, process.execPath, '-e', FLOCK_HOLDER], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
109
|
+
child.stderr.resume();
|
|
110
|
+
let output = '';
|
|
111
|
+
const acquired = await new Promise((resolve) => {
|
|
112
|
+
let settled = false;
|
|
113
|
+
const finish = (value) => {
|
|
114
|
+
if (settled)
|
|
115
|
+
return;
|
|
116
|
+
settled = true;
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
resolve(value);
|
|
119
|
+
};
|
|
120
|
+
const timer = setTimeout(() => finish(false), Math.min(timeoutMs, 30_000) + 5_000);
|
|
121
|
+
child.once('error', () => finish(false));
|
|
122
|
+
child.once('exit', () => finish(false));
|
|
123
|
+
child.stdout.setEncoding('utf8');
|
|
124
|
+
child.stdout.on('data', (chunk) => {
|
|
125
|
+
output += chunk;
|
|
126
|
+
if (output.includes(FLOCK_READY))
|
|
127
|
+
finish(true);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
if (acquired)
|
|
131
|
+
return child;
|
|
132
|
+
child.kill();
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
async function releaseFlock(child) {
|
|
136
|
+
if (child.exitCode !== null)
|
|
137
|
+
return;
|
|
138
|
+
child.stdin.end();
|
|
139
|
+
await new Promise((resolve) => {
|
|
140
|
+
const timer = setTimeout(() => {
|
|
141
|
+
child.kill();
|
|
142
|
+
resolve();
|
|
143
|
+
}, 5_000);
|
|
144
|
+
child.once('exit', () => {
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
resolve();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async function withPortableLease(options, operation) {
|
|
151
|
+
const controller = new AbortController();
|
|
152
|
+
let compromised = null;
|
|
153
|
+
const retryIntervalMs = 500;
|
|
154
|
+
const release = await lockfile.lock(options.target, {
|
|
155
|
+
realpath: true,
|
|
156
|
+
stale: 120_000,
|
|
157
|
+
update: 15_000,
|
|
158
|
+
retries: {
|
|
159
|
+
retries: Math.ceil(options.timeoutMs / retryIntervalMs),
|
|
160
|
+
factor: 1,
|
|
161
|
+
minTimeout: retryIntervalMs,
|
|
162
|
+
maxTimeout: retryIntervalMs,
|
|
163
|
+
},
|
|
164
|
+
onCompromised: (error) => {
|
|
165
|
+
compromised = error;
|
|
166
|
+
controller.abort(error);
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
try {
|
|
170
|
+
const result = await operation(controller.signal);
|
|
171
|
+
if (compromised)
|
|
172
|
+
throw compromised;
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
controller.abort();
|
|
177
|
+
await release();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function workerLockName(key) {
|
|
181
|
+
return `pyfig:${createHash('sha256').update(key).digest('hex')}`;
|
|
182
|
+
}
|
|
183
|
+
/** Create a canonical lock target below a configured cache directory. */
|
|
184
|
+
export function cacheLockTarget(cacheDirectory, name) {
|
|
185
|
+
return path.join(cacheDirectory, 'locks', name);
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=coordination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination.js","sourceRoot":"","sources":["../src/coordination.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
export { DEFAULT_MANAGED_PYTHON, DEFAULT_PIP_RANGE, DEFAULT_PYTHON_RANGE,
|
|
2
|
-
export type { PyfigOptions
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
5
|
-
export {
|
|
1
|
+
export { DEFAULT_MANAGED_PYTHON, DEFAULT_PIP_RANGE, DEFAULT_PYTHON_RANGE, DEFAULT_UV_RANGE, DEFAULT_UV_VERSION, } from './config.js';
|
|
2
|
+
export type { PyfigOptions } from './config.js';
|
|
3
|
+
export { createPyfig } from './client.js';
|
|
4
|
+
export type { PyfigClient } from './client.js';
|
|
5
|
+
export { resolveProjectRoot } from './project-root.js';
|
|
6
|
+
export type { PackageManagerInfo, PythonEmbeddingInfo, PythonInfo, UvInfo, } from './probes.js';
|
|
7
|
+
export { ensureEnvironment, ensureUv, ensureVirtualEnvironment, } from './provision.js';
|
|
6
8
|
export type { PythonEnvironment } from './provision.js';
|
|
9
|
+
export { pipSpawn, pipSpawnSync, pythonSpawn, pythonSpawnSync, uvSpawn, uvSpawnSync, } from './runners.js';
|
|
10
|
+
export { clearPyfigCaches } from './state.js';
|
|
7
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,MAAM,GACP,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAe,EACf,OAAO,EACP,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export { DEFAULT_MANAGED_PYTHON, DEFAULT_PIP_RANGE, DEFAULT_PYTHON_RANGE,
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
1
|
+
export { DEFAULT_MANAGED_PYTHON, DEFAULT_PIP_RANGE, DEFAULT_PYTHON_RANGE, DEFAULT_UV_RANGE, DEFAULT_UV_VERSION, } from './config.js';
|
|
2
|
+
export { createPyfig } from './client.js';
|
|
3
|
+
export { resolveProjectRoot } from './project-root.js';
|
|
4
|
+
export { ensureEnvironment, ensureUv, ensureVirtualEnvironment, } from './provision.js';
|
|
5
|
+
export { pipSpawn, pipSpawnSync, pythonSpawn, pythonSpawnSync, uvSpawn, uvSpawnSync, } from './runners.js';
|
|
6
|
+
export { clearPyfigCaches } from './state.js';
|
|
4
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAOvD,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAe,EACf,OAAO,EACP,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PackageManagerInfo, PythonInfo } from './probes.js';
|
|
2
|
+
/** Reject package-manager arguments that can escape the selected repository venv. */
|
|
3
|
+
export declare function assertSafePackageArguments(args: readonly string[]): void;
|
|
4
|
+
/** Build a package-manager environment whose target cannot be inherited or configured elsewhere. */
|
|
5
|
+
export declare function packageManagerEnvironment(env: NodeJS.ProcessEnv, packageManager: PackageManagerInfo): NodeJS.ProcessEnv;
|
|
6
|
+
/** Apply the same strict target policy to a direct `uv pip` invocation. */
|
|
7
|
+
export declare function uvPipEnvironment(env: NodeJS.ProcessEnv, python: PythonInfo): NodeJS.ProcessEnv;
|
|
8
|
+
/** Package installation and removal must use uv so its target-environment lock is authoritative. */
|
|
9
|
+
export declare function assertMutationUsesUv(packageManager: PackageManagerInfo, args: readonly string[]): void;
|
|
10
|
+
export declare function isPackageMutation(args: readonly string[]): boolean;
|
|
11
|
+
export declare function isUvPipCommand(args: readonly string[]): boolean;
|
|
12
|
+
//# sourceMappingURL=package-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-target.d.ts","sourceRoot":"","sources":["../src/package-target.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAkClE,qFAAqF;AACrF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAgBxE;AAED,oGAAoG;AACpG,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,cAAc,EAAE,kBAAkB,GACjC,MAAM,CAAC,UAAU,CAmBnB;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,MAAM,EAAE,UAAU,GACjB,MAAM,CAAC,UAAU,CAanB;AAED,oGAAoG;AACpG,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,kBAAkB,EAClC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,IAAI,CAON;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAGlE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAI/D"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { activateVirtualEnvironment } from './virtual-environment.js';
|
|
3
|
+
const TARGET_CHANGING_OPTIONS = new Set([
|
|
4
|
+
'--config-file',
|
|
5
|
+
'--prefix',
|
|
6
|
+
'--python',
|
|
7
|
+
'--root',
|
|
8
|
+
'--system',
|
|
9
|
+
'--target',
|
|
10
|
+
'--user',
|
|
11
|
+
]);
|
|
12
|
+
const TARGET_CHANGING_SHORT_OPTIONS = new Set(['-p', '-t']);
|
|
13
|
+
const TARGET_ENVIRONMENT_VARIABLES = new Set([
|
|
14
|
+
'CONDA_PREFIX',
|
|
15
|
+
'PIP_CONFIG_FILE',
|
|
16
|
+
'PIP_PREFIX',
|
|
17
|
+
'PIP_PYTHON',
|
|
18
|
+
'PIP_ROOT',
|
|
19
|
+
'PIP_TARGET',
|
|
20
|
+
'PIP_USER',
|
|
21
|
+
'PYTHONHOME',
|
|
22
|
+
'PYTHONUSERBASE',
|
|
23
|
+
'UV_CONFIG_FILE',
|
|
24
|
+
'UV_PROJECT_ENVIRONMENT',
|
|
25
|
+
'UV_PREFIX',
|
|
26
|
+
'UV_PYTHON',
|
|
27
|
+
'UV_ROOT',
|
|
28
|
+
'UV_SYSTEM_PYTHON',
|
|
29
|
+
'UV_TARGET',
|
|
30
|
+
]);
|
|
31
|
+
/** Reject package-manager arguments that can escape the selected repository venv. */
|
|
32
|
+
export function assertSafePackageArguments(args) {
|
|
33
|
+
for (const argument of args) {
|
|
34
|
+
if (argument === '--')
|
|
35
|
+
break;
|
|
36
|
+
const [name] = argument.split('=', 1);
|
|
37
|
+
if (name && TARGET_CHANGING_OPTIONS.has(name)) {
|
|
38
|
+
throw new Error(`${name} is not allowed because Pyfig package commands are pinned to the repository .venv.`);
|
|
39
|
+
}
|
|
40
|
+
const shortName = argument.slice(0, 2);
|
|
41
|
+
if (TARGET_CHANGING_SHORT_OPTIONS.has(shortName)) {
|
|
42
|
+
throw new Error(`${shortName} is not allowed because Pyfig package commands are pinned to the repository .venv.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Build a package-manager environment whose target cannot be inherited or configured elsewhere. */
|
|
47
|
+
export function packageManagerEnvironment(env, packageManager) {
|
|
48
|
+
const active = sanitizedTargetEnvironment(env);
|
|
49
|
+
const activated = activateVirtualEnvironment(active, packageManager.virtualEnvironment, packageManager.pythonExecutable);
|
|
50
|
+
return packageManager.kind === 'uv'
|
|
51
|
+
? {
|
|
52
|
+
...activated,
|
|
53
|
+
UV_NO_CONFIG: '1',
|
|
54
|
+
UV_PYTHON: packageManager.pythonExecutable,
|
|
55
|
+
}
|
|
56
|
+
: {
|
|
57
|
+
...activated,
|
|
58
|
+
PIP_CONFIG_FILE: os.devNull,
|
|
59
|
+
PIP_REQUIRE_VIRTUALENV: '1',
|
|
60
|
+
PYTHONNOUSERSITE: '1',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Apply the same strict target policy to a direct `uv pip` invocation. */
|
|
64
|
+
export function uvPipEnvironment(env, python) {
|
|
65
|
+
if (!python.virtualEnvironment) {
|
|
66
|
+
throw new Error('uv pip commands require the repository virtual environment.');
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
...activateVirtualEnvironment(sanitizedTargetEnvironment(env), python.virtualEnvironment, python.executable),
|
|
70
|
+
UV_NO_CONFIG: '1',
|
|
71
|
+
UV_PYTHON: python.executable,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Package installation and removal must use uv so its target-environment lock is authoritative. */
|
|
75
|
+
export function assertMutationUsesUv(packageManager, args) {
|
|
76
|
+
if (packageManager.kind === 'pip' && isPackageMutation(args)) {
|
|
77
|
+
throw new Error('Package mutation requires uv so the repository environment is locked across processes. ' +
|
|
78
|
+
'Enable uv installation or use a read-only pip command.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function isPackageMutation(args) {
|
|
82
|
+
return args.some((argument) => argument === 'install' || argument === 'sync' || argument === 'uninstall');
|
|
83
|
+
}
|
|
84
|
+
export function isUvPipCommand(args) {
|
|
85
|
+
const separator = args.indexOf('--');
|
|
86
|
+
const commandArguments = separator === -1 ? args : args.slice(0, separator);
|
|
87
|
+
return commandArguments.includes('pip');
|
|
88
|
+
}
|
|
89
|
+
function sanitizedTargetEnvironment(env) {
|
|
90
|
+
const sanitized = { ...env };
|
|
91
|
+
for (const key of Object.keys(sanitized)) {
|
|
92
|
+
if (TARGET_ENVIRONMENT_VARIABLES.has(key.toUpperCase()))
|
|
93
|
+
delete sanitized[key];
|
|
94
|
+
}
|
|
95
|
+
return sanitized;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=package-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-target.js","sourceRoot":"","sources":["../src/package-target.ts"],"names":[],"mappings":"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"}
|
package/dist/probes.d.ts
CHANGED
|
@@ -2,11 +2,28 @@ import type { ResolvedPyfigOptions } from './config.js';
|
|
|
2
2
|
import { type CommandSpec } from './process.js';
|
|
3
3
|
export interface PythonInfo {
|
|
4
4
|
executable: string;
|
|
5
|
+
baseExecutable: string;
|
|
5
6
|
version: string;
|
|
6
7
|
major: number;
|
|
7
8
|
minor: number;
|
|
8
9
|
patch: number;
|
|
10
|
+
releaseLevel: string;
|
|
11
|
+
implementation: string;
|
|
12
|
+
architecture: string;
|
|
13
|
+
pointerBits: number;
|
|
9
14
|
prefix: string;
|
|
15
|
+
basePrefix: string;
|
|
16
|
+
virtualEnvironment: string | null;
|
|
17
|
+
gilDisabled: boolean;
|
|
18
|
+
embedding: PythonEmbeddingInfo | null;
|
|
19
|
+
}
|
|
20
|
+
export interface PythonEmbeddingInfo {
|
|
21
|
+
includeDirectory: string;
|
|
22
|
+
headerPath: string;
|
|
23
|
+
libraryPath: string;
|
|
24
|
+
libraryDirectory: string;
|
|
25
|
+
libraryName: string;
|
|
26
|
+
libraryKind: 'import' | 'shared' | 'static';
|
|
10
27
|
}
|
|
11
28
|
export interface PackageManagerInfo {
|
|
12
29
|
kind: 'pip' | 'uv';
|
|
@@ -14,14 +31,24 @@ export interface PackageManagerInfo {
|
|
|
14
31
|
prefixArgs: string[];
|
|
15
32
|
version: string;
|
|
16
33
|
pythonExecutable: string;
|
|
34
|
+
virtualEnvironment: string;
|
|
17
35
|
}
|
|
18
36
|
export interface UvInfo {
|
|
19
37
|
executable: string;
|
|
20
38
|
version: string;
|
|
21
39
|
}
|
|
22
40
|
export declare function findPython(options: ResolvedPyfigOptions): Promise<PythonInfo | null>;
|
|
41
|
+
export declare function pythonSatisfiesRequirements(python: PythonInfo, options: Pick<ResolvedPyfigOptions, 'pythonRange' | 'requireEmbedding' | 'requireGil'>): boolean;
|
|
23
42
|
export declare function probePython(candidate: CommandSpec, options: ResolvedPyfigOptions): Promise<PythonInfo | null>;
|
|
24
43
|
export declare function findPip(python: PythonInfo, options: ResolvedPyfigOptions): Promise<PackageManagerInfo | null>;
|
|
44
|
+
export interface PipVersionDetails {
|
|
45
|
+
version: string;
|
|
46
|
+
location: string;
|
|
47
|
+
pythonMajor: number;
|
|
48
|
+
pythonMinor: number;
|
|
49
|
+
}
|
|
50
|
+
export declare function parsePipVersionOutput(output: string): PipVersionDetails | null;
|
|
51
|
+
export declare function pipBelongsToPython(pip: PipVersionDetails, python: PythonInfo): boolean;
|
|
25
52
|
export declare function findUv(options: ResolvedPyfigOptions): Promise<UvInfo | null>;
|
|
26
53
|
export declare function probeUv(executable: string, options: ResolvedPyfigOptions): UvInfo | null;
|
|
27
54
|
//# sourceMappingURL=probes.d.ts.map
|