pinokiod 7.2.10 → 7.2.11

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.
Files changed (2) hide show
  1. package/kernel/environment.js +119 -90
  2. package/package.json +1 -1
@@ -5,136 +5,165 @@ const fs = require('fs')
5
5
  const Util = require('./util')
6
6
  const TEMP_ENV_KEYS = ["TMP", "TEMP", "TMPDIR", "PIP_TMPDIR"]
7
7
  const CACHE_ENV_KEYS = ["UV_CACHE_DIR", "PIP_CACHE_DIR"]
8
+ const CACHE_PREFLIGHT_KEYS = TEMP_ENV_KEYS.concat(CACHE_ENV_KEYS)
8
9
 
9
- const envKey = (env, key) => {
10
- return Object.keys(env).find((candidate) => candidate.toLowerCase() === key.toLowerCase())
11
- }
12
-
13
- const setEnv = (env, key, value) => {
14
- for (const candidate of Object.keys(env)) {
15
- if (candidate.toLowerCase() === key.toLowerCase() && candidate !== key) {
16
- delete env[candidate]
17
- }
10
+ const formatCachePreflightError = (error) => {
11
+ if (!error) {
12
+ return ""
18
13
  }
19
- env[key] = value
20
- }
21
-
22
- const normalizeEnvPath = (value, root) => {
23
- if (typeof value !== "string") {
24
- return null
14
+ const parts = []
15
+ if (error.code) {
16
+ parts.push(`code=${error.code}`)
17
+ }
18
+ if (typeof error.errno !== "undefined") {
19
+ parts.push(`errno=${error.errno}`)
25
20
  }
26
- const trimmed = value.trim()
27
- if (!trimmed) {
28
- return null
21
+ if (error.syscall) {
22
+ parts.push(`syscall=${error.syscall}`)
29
23
  }
30
- if (path.isAbsolute(trimmed)) {
31
- return path.resolve(trimmed)
24
+ if (error.path) {
25
+ parts.push(`path=${error.path}`)
32
26
  }
33
- if (trimmed.startsWith("./") || trimmed.startsWith(".\\")) {
34
- return path.resolve(root, trimmed)
27
+ if (error.dest) {
28
+ parts.push(`dest=${error.dest}`)
35
29
  }
36
- return path.resolve(root, trimmed)
30
+ if (error.message) {
31
+ parts.push(`message=${error.message}`)
32
+ }
33
+ return parts.join(" ")
37
34
  }
38
35
 
39
- const isInsidePath = (candidate, root) => {
40
- const rel = path.relative(path.resolve(root), path.resolve(candidate))
41
- return rel === "" || (!!rel && !rel.startsWith("..") && !path.isAbsolute(rel))
36
+ const logCachePreflight = (message) => {
37
+ console.log(`[Pinokio cache preflight] ${message}`)
42
38
  }
43
39
 
44
- const canWriteDirectory = async (dirPath) => {
45
- if (!dirPath) {
46
- return false
47
- }
48
- const testDir = path.resolve(
40
+ const probeCacheDir = async (dirPath) => {
41
+ const probeDir = path.resolve(
49
42
  dirPath,
50
- `.pinokio-write-test-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
43
+ `.pinokio-cache-probe-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
51
44
  )
52
- const testFile = path.resolve(testDir, "probe.tmp")
53
- const renamedFile = path.resolve(testDir, "probe-renamed.tmp")
54
- try {
55
- await fs.promises.mkdir(testDir, { recursive: true })
56
- await fs.promises.writeFile(testFile, "pinokio")
57
- await fs.promises.appendFile(testFile, "-probe")
58
- await fs.promises.rename(testFile, renamedFile)
59
- await fs.promises.unlink(renamedFile)
60
- await fs.promises.rmdir(testDir)
61
- return true
62
- } catch (error) {
63
- await fs.promises.rm(testDir, { recursive: true, force: true }).catch(() => {})
64
- return false
45
+ const probeFile = path.resolve(probeDir, "probe.tmp")
46
+ const renamedFile = path.resolve(probeDir, "probe-renamed.tmp")
47
+ const steps = [
48
+ ["create probe directory", () => fs.promises.mkdir(probeDir, { recursive: false })],
49
+ ["write probe file", () => fs.promises.writeFile(probeFile, "pinokio")],
50
+ ["append probe file", () => fs.promises.appendFile(probeFile, "-cache-probe")],
51
+ ["rename probe file", () => fs.promises.rename(probeFile, renamedFile)],
52
+ ["delete probe file", () => fs.promises.unlink(renamedFile)],
53
+ ["remove probe directory", () => fs.promises.rmdir(probeDir)]
54
+ ]
55
+
56
+ for (const [step, run] of steps) {
57
+ try {
58
+ await run()
59
+ } catch (error) {
60
+ await fs.promises.rm(probeDir, { recursive: true, force: true }).catch(() => {})
61
+ return { ok: false, step, error }
62
+ }
65
63
  }
64
+ return { ok: true }
66
65
  }
67
66
 
68
- const ensureWritableDirectory = async (dirPath, options = {}) => {
69
- if (!dirPath) {
70
- return false
67
+ const managedCacheEnvDefaults = () => {
68
+ const defaults = {}
69
+ for (const key of CACHE_PREFLIGHT_KEYS) {
70
+ defaults[key] = `./cache/${key}`
71
71
  }
72
- const repair = !!options.repair
72
+ return defaults
73
+ }
74
+
75
+ const ensureCachePreflightDir = async (key, targetPath) => {
76
+ logCachePreflight(`${key}: target=${targetPath}`)
73
77
  try {
74
- await fs.promises.mkdir(dirPath, { recursive: true })
78
+ await fs.promises.mkdir(targetPath, { recursive: true })
79
+ logCachePreflight(`${key}: mkdir ok`)
75
80
  } catch (error) {
76
- if (!repair) {
77
- return false
78
- }
79
- await fs.promises.rm(dirPath, { recursive: true, force: true }).catch(() => {})
80
- try {
81
- await fs.promises.mkdir(dirPath, { recursive: true })
82
- } catch (mkdirError) {
83
- return false
84
- }
85
- }
86
- if (await canWriteDirectory(dirPath)) {
87
- return true
81
+ logCachePreflight(`${key}: mkdir failed ${formatCachePreflightError(error)}`)
88
82
  }
89
- if (!repair) {
90
- return false
83
+
84
+ const firstProbe = await probeCacheDir(targetPath)
85
+ if (firstProbe.ok) {
86
+ logCachePreflight(`${key}: probe ok`)
87
+ return { key, path: targetPath, repaired: false, ok: true }
91
88
  }
92
- await fs.promises.rm(dirPath, { recursive: true, force: true }).catch(() => {})
89
+
90
+ logCachePreflight(`${key}: probe failed step="${firstProbe.step}" ${formatCachePreflightError(firstProbe.error)}`)
91
+ logCachePreflight(`${key}: repair delete start path=${targetPath}`)
92
+
93
93
  try {
94
- await fs.promises.mkdir(dirPath, { recursive: true })
94
+ await fs.promises.rm(targetPath, { recursive: true, force: true })
95
+ logCachePreflight(`${key}: repair delete ok`)
95
96
  } catch (error) {
96
- return false
97
+ logCachePreflight(`${key}: repair delete failed ${formatCachePreflightError(error)}`)
98
+ return { key, path: targetPath, repaired: false, ok: false, step: "repair delete", error }
97
99
  }
98
- return canWriteDirectory(dirPath)
99
- }
100
100
 
101
- const canRepairManagedCachePath = (candidate, cacheRoot) => {
102
- const rel = path.relative(path.resolve(cacheRoot), path.resolve(candidate))
103
- return !!rel && !rel.startsWith("..") && !path.isAbsolute(rel)
104
- }
101
+ try {
102
+ await fs.promises.mkdir(targetPath, { recursive: true })
103
+ logCachePreflight(`${key}: repair mkdir ok`)
104
+ } catch (error) {
105
+ logCachePreflight(`${key}: repair mkdir failed ${formatCachePreflightError(error)}`)
106
+ return { key, path: targetPath, repaired: true, ok: false, step: "repair mkdir", error }
107
+ }
105
108
 
106
- const managedCacheEnvDefaults = () => {
107
- const defaults = {}
108
- for (const key of TEMP_ENV_KEYS.concat(CACHE_ENV_KEYS)) {
109
- defaults[key] = `./cache/${key}`
109
+ const secondProbe = await probeCacheDir(targetPath)
110
+ if (secondProbe.ok) {
111
+ logCachePreflight(`${key}: repair probe ok`)
112
+ return { key, path: targetPath, repaired: true, ok: true }
110
113
  }
111
- return defaults
114
+
115
+ logCachePreflight(`${key}: repair probe failed step="${secondProbe.step}" ${formatCachePreflightError(secondProbe.error)}`)
116
+ return { key, path: targetPath, repaired: true, ok: false, step: secondProbe.step, error: secondProbe.error }
112
117
  }
113
118
 
114
- const ensurePinokioCacheDirs = async (kernel) => {
119
+ const ensurePinokioCacheDirs = async (kernel, options = {}) => {
115
120
  if (!kernel || !kernel.homedir) {
116
121
  return {}
117
122
  }
123
+ const throwOnFailure = !!options.throwOnFailure
118
124
  const root = path.resolve(kernel.homedir)
119
125
  const cacheRoot = path.resolve(root, "cache")
120
126
  const envPath = path.resolve(root, "ENVIRONMENT")
121
127
  const defaults = managedCacheEnvDefaults()
128
+ logCachePreflight(`start root=${root}`)
122
129
  await Util.update_env(envPath, defaults)
123
- const env = await get(root, kernel)
130
+ logCachePreflight(`ENVIRONMENT updated keys=${CACHE_PREFLIGHT_KEYS.join(",")}`)
131
+ try {
132
+ await fs.promises.mkdir(cacheRoot, { recursive: true })
133
+ logCachePreflight(`cache root mkdir ok path=${cacheRoot}`)
134
+ } catch (error) {
135
+ logCachePreflight(`cache root mkdir failed path=${cacheRoot} ${formatCachePreflightError(error)}`)
136
+ }
137
+
138
+ const errors = []
139
+ const results = []
124
140
 
125
- for (const key of TEMP_ENV_KEYS.concat(CACHE_ENV_KEYS)) {
126
- const existingKey = envKey(env, key)
127
- const dirPath = normalizeEnvPath(existingKey ? env[existingKey] : defaults[key], root)
128
- const managedPath = path.resolve(cacheRoot, key)
129
- const targetPath = dirPath && isInsidePath(dirPath, root) ? dirPath : managedPath
130
- const repair = canRepairManagedCachePath(targetPath, cacheRoot)
131
- if (!repair || !(await ensureWritableDirectory(targetPath, { repair: true }))) {
132
- throw new Error(`Pinokio could not create a writable ${key} directory: ${targetPath}`)
141
+ for (const key of CACHE_PREFLIGHT_KEYS) {
142
+ const targetPath = path.resolve(cacheRoot, key)
143
+ const result = await ensureCachePreflightDir(key, targetPath)
144
+ results.push(result)
145
+ if (!result.ok) {
146
+ errors.push(result)
133
147
  }
134
- setEnv(env, key, targetPath)
135
148
  }
136
149
 
137
- return env
150
+ if (errors.length > 0) {
151
+ kernel.cacheDirErrors = errors
152
+ const message = errors
153
+ .map((error) => `${error.key}: ${error.path} (${error.step || "unknown"} ${formatCachePreflightError(error.error)})`)
154
+ .join(", ")
155
+ logCachePreflight(`failed ${message}`)
156
+ if (throwOnFailure) {
157
+ throw new Error(`Pinokio could not create writable cache directories: ${message}`)
158
+ }
159
+ } else {
160
+ kernel.cacheDirErrors = []
161
+ logCachePreflight(`complete ok checked=${results.length} repaired=${results.filter((result) => result.repaired).length}`)
162
+ }
163
+
164
+ kernel.cacheDirPreflight = results
165
+ const env = await get(root, kernel)
166
+ return { env, errors, results }
138
167
  }
139
168
  const ENVS = async () => {
140
169
  // const primary_port = 80
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.2.10",
3
+ "version": "7.2.11",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {