pinokiod 8.0.5 → 8.0.6

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.
@@ -168,6 +168,13 @@ class HF {
168
168
  }
169
169
  await this.htmlModal.close(this.modalRequest(req, this.modalId(req)), ondata, kernel)
170
170
  }
171
+ authContext(req = {}, params = {}) {
172
+ return {
173
+ parentPath: req.parent && req.parent.path,
174
+ cwd: req.cwd,
175
+ env: params.env,
176
+ }
177
+ }
171
178
  async cancelLogin(connect, params) {
172
179
  if (connect && typeof connect.cancelLogin === "function") {
173
180
  await connect.cancelLogin("huggingface", params || {})
@@ -201,9 +208,10 @@ class HF {
201
208
  const shouldWait = params.wait !== false
202
209
  const timeout = positiveNumber(params.timeout, 120000)
203
210
  const interval = positiveNumber(params.interval, 2000)
211
+ const authContext = this.authContext(req, params)
204
212
 
205
213
  if (!force) {
206
- const existing = await connect.keys("huggingface")
214
+ const existing = await connect.keys("huggingface", authContext)
207
215
  if (existing && existing.access_token) {
208
216
  return {
209
217
  status: "success",
@@ -216,7 +224,7 @@ class HF {
216
224
  if (typeof connect.login !== "function") {
217
225
  throw new Error("Hugging Face connect login is not available")
218
226
  }
219
- const response = await connect.login("huggingface", params)
227
+ const response = await connect.login("huggingface", params, authContext)
220
228
  if (!response || response.status === "error") {
221
229
  return response
222
230
  }
@@ -262,7 +270,7 @@ class HF {
262
270
 
263
271
  const startedAt = Date.now()
264
272
  while (Date.now() - startedAt < timeout) {
265
- const keys = await connect.keys("huggingface")
273
+ const keys = await connect.keys("huggingface", authContext)
266
274
  if (keys && keys.access_token) {
267
275
  if (useModal) {
268
276
  await this.closeLoginModal(req, ondata, kernel)
@@ -293,7 +301,8 @@ class HF {
293
301
  */
294
302
  async logout(req, ondata, kernel) {
295
303
  const connect = this.connect(kernel, "logout")
296
- await connect.logout("huggingface", req.params || {})
304
+ const params = req.params || {}
305
+ await connect.logout("huggingface", params, this.authContext(req, params))
297
306
  return { status: "success" }
298
307
  }
299
308
  /*
@@ -24,12 +24,12 @@ class Connect {
24
24
  return null
25
25
  }
26
26
  }
27
- async login(provider, req) {
28
- let res = await this.clients[provider].login(req)
27
+ async login(provider, params, context) {
28
+ let res = await this.clients[provider].login(params, context)
29
29
  return res
30
30
  }
31
- async logout(provider, req) {
32
- let res = await this.clients[provider].logout(req)
31
+ async logout(provider, params, context) {
32
+ let res = await this.clients[provider].logout(params, context)
33
33
  return res
34
34
  }
35
35
  async cancelLogin(provider, req) {
@@ -38,8 +38,8 @@ class Connect {
38
38
  }
39
39
  return null
40
40
  }
41
- async keys(provider) {
42
- let res = await this.clients[provider].keys()
41
+ async keys(provider, context) {
42
+ let res = await this.clients[provider].keys(context)
43
43
  return res
44
44
  }
45
45
  async connected(provider, options) {
@@ -18,30 +18,55 @@ class Huggingface {
18
18
  defaultTokenPath() {
19
19
  return this.kernel.path("cache", "HF_AUTH", "token")
20
20
  }
21
- async authEnv() {
22
- let systemEnv = {}
21
+ requestCwd(context = {}) {
22
+ if (context.cwd) {
23
+ return path.resolve(context.cwd)
24
+ }
25
+ if (context.parentPath) {
26
+ return path.dirname(path.resolve(context.parentPath))
27
+ }
28
+ return this.kernel.homedir
29
+ }
30
+ async repairTokenPath(tokenPath) {
23
31
  try {
24
- systemEnv = await Environment.get(this.kernel.homedir, this.kernel)
25
- } catch (_) {
26
- systemEnv = {}
32
+ const stat = await fs.promises.lstat(tokenPath)
33
+ if (stat.isDirectory()) {
34
+ await fs.promises.rm(tokenPath, { recursive: true, force: true })
35
+ }
36
+ } catch (error) {
37
+ if (!error || error.code !== "ENOENT") {
38
+ throw error
39
+ }
40
+ }
41
+ await fs.promises.mkdir(path.dirname(tokenPath), { recursive: true })
42
+ }
43
+ async authEnv(context = {}) {
44
+ let env
45
+ if (context.parentPath) {
46
+ env = await Environment.get2(context.parentPath, this.kernel)
47
+ } else {
48
+ let systemEnv = {}
49
+ try {
50
+ systemEnv = await Environment.get(this.kernel.homedir, this.kernel)
51
+ } catch (_) {
52
+ systemEnv = {}
53
+ }
54
+ env = Object.assign({}, process.env, systemEnv)
55
+ }
56
+ if (context.env && typeof context.env === "object") {
57
+ env = Object.assign(env, context.env)
27
58
  }
28
- const env = Object.assign({}, process.env, systemEnv)
29
59
  if (!env.HF_TOKEN_PATH) {
30
60
  env.HF_TOKEN_PATH = this.defaultTokenPath()
61
+ } else if (!path.isAbsolute(env.HF_TOKEN_PATH)) {
62
+ env.HF_TOKEN_PATH = path.resolve(this.requestCwd(context), env.HF_TOKEN_PATH)
31
63
  }
32
64
  if (!env.HF_HUB_DISABLE_UPDATE_CHECK) {
33
65
  env.HF_HUB_DISABLE_UPDATE_CHECK = "1"
34
66
  }
35
67
  delete env.HF_TOKEN
36
68
  delete env.HUGGING_FACE_HUB_TOKEN
37
- try {
38
- await fs.promises.rmdir(env.HF_TOKEN_PATH)
39
- } catch (error) {
40
- if (!error || !["ENOENT", "ENOTDIR"].includes(error.code)) {
41
- throw new Error(`Hugging Face token path must be a file: ${env.HF_TOKEN_PATH}`)
42
- }
43
- }
44
- await fs.promises.mkdir(path.dirname(env.HF_TOKEN_PATH), { recursive: true }).catch(() => {})
69
+ await this.repairTokenPath(env.HF_TOKEN_PATH)
45
70
  return env
46
71
  }
47
72
  hfPath() {
@@ -59,11 +84,11 @@ class Huggingface {
59
84
  }
60
85
  return candidates[0]
61
86
  }
62
- async runHf(args, options = {}) {
63
- const env = await this.authEnv()
87
+ async runHf(args, options = {}, context = {}) {
88
+ const env = await this.authEnv(context)
64
89
  return new Promise((resolve, reject) => {
65
90
  execFile(this.hfPath(), args, {
66
- cwd: this.kernel.homedir,
91
+ cwd: this.requestCwd(context),
67
92
  env,
68
93
  timeout: Number.isFinite(options.timeout) ? options.timeout : undefined,
69
94
  maxBuffer: 1024 * 1024,
@@ -122,11 +147,18 @@ class Huggingface {
122
147
  expires_in: expiresMatch ? Number(expiresMatch[1]) : null,
123
148
  }
124
149
  }
125
- async login() {
150
+ async login(_params = {}, context = {}) {
151
+ const env = await this.authEnv(context)
126
152
  if (this.loginSession && this.loginSession.status === "pending") {
153
+ if (this.loginSession.tokenPath !== env.HF_TOKEN_PATH) {
154
+ return {
155
+ status: "error",
156
+ error: `A Hugging Face login is already pending for ${this.loginSession.tokenPath}`,
157
+ token_path: env.HF_TOKEN_PATH,
158
+ }
159
+ }
127
160
  return this.serializeLoginSession(this.loginSession)
128
161
  }
129
- const env = await this.authEnv()
130
162
  const session = {
131
163
  status: "pending",
132
164
  login: null,
@@ -156,7 +188,7 @@ class Huggingface {
156
188
  }
157
189
  }
158
190
  const child = spawn(this.hfPath(), ["auth", "login", "--format", "agent", "--force"], {
159
- cwd: this.kernel.homedir,
191
+ cwd: this.requestCwd(context),
160
192
  env,
161
193
  windowsHide: false,
162
194
  })
@@ -192,12 +224,15 @@ class Huggingface {
192
224
  }
193
225
  return this.serializeLoginSession(session)
194
226
  }
195
- async keys() {
227
+ async keys(context = {}) {
196
228
  if (this.loginSession && this.loginSession.status === "pending") {
197
- return this.serializeLoginSession(this.loginSession)
229
+ const env = await this.authEnv(context)
230
+ if (this.loginSession.tokenPath === env.HF_TOKEN_PATH) {
231
+ return this.serializeLoginSession(this.loginSession)
232
+ }
198
233
  }
199
234
  try {
200
- const { stdout, env } = await this.runHf(["auth", "token", "--format", "quiet"])
235
+ const { stdout, env } = await this.runHf(["auth", "token", "--format", "quiet"], {}, context)
201
236
  const access_token = stripAnsi(stdout).trim().split(/\r?\n/).find(Boolean)
202
237
  if (!access_token) {
203
238
  return null
@@ -235,12 +270,12 @@ class Huggingface {
235
270
  await this.config.profile.cache(response, connectPath).catch(() => {})
236
271
  return this.config.profile.render(response)
237
272
  }
238
- async destroy() {
273
+ async destroy(context = {}) {
239
274
  if (this.loginSession && this.loginSession.child && this.loginSession.status === "pending") {
240
275
  this.loginSession.child.kill()
241
276
  }
242
277
  this.loginSession = null
243
- await this.runHf(["auth", "logout"]).catch(() => {})
278
+ await this.runHf(["auth", "logout"], {}, context).catch(() => {})
244
279
  await fs.promises.rm(this.kernel.path("connect", "huggingface"), { recursive: true, force: true }).catch(() => {})
245
280
  }
246
281
  async cancelLogin() {
@@ -257,8 +292,8 @@ class Huggingface {
257
292
  this.loginSession = null
258
293
  }
259
294
  }
260
- async logout() {
261
- await this.destroy()
295
+ async logout(_params = {}, context = {}) {
296
+ await this.destroy(context)
262
297
  }
263
298
  }
264
299
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "8.0.5",
3
+ "version": "8.0.6",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,12 +17,14 @@ test('hf.login shows a modal, waits for the user to open Hugging Face, then clos
17
17
  const originalOpenURI = Util.openURI
18
18
  const originalClipboard = Util.clipboard
19
19
  let keyReads = 0
20
+ const keyContexts = []
20
21
  const loginCalls = []
21
22
  const waitCalls = []
22
23
  const kernel = {
23
24
  connect: {
24
- async keys(provider) {
25
+ async keys(provider, context) {
25
26
  assert.equal(provider, 'huggingface')
27
+ keyContexts.push(context)
26
28
  keyReads += 1
27
29
  if (keyReads < 3) {
28
30
  return null
@@ -32,8 +34,8 @@ test('hf.login shows a modal, waits for the user to open Hugging Face, then clos
32
34
  token_path: '/tmp/pinokio/hf-token'
33
35
  }
34
36
  },
35
- async login(provider, params) {
36
- loginCalls.push({ provider, params })
37
+ async login(provider, params, context) {
38
+ loginCalls.push({ provider, params, context })
37
39
  return {
38
40
  status: 'pending',
39
41
  login,
@@ -58,15 +60,28 @@ test('hf.login shows a modal, waits for the user to open Hugging Face, then clos
58
60
  }
59
61
 
60
62
  try {
61
- const result = await hf.login({
63
+ const request = {
62
64
  parent: { id: 'test-script', path: '/pinokio/api/test/hf-login.js' },
63
65
  params: {
64
66
  timeout: 100,
65
67
  interval: 1
66
68
  }
67
- }, (stream, type) => packets.push({ type, stream }), kernel)
69
+ }
70
+ const result = await hf.login(request, (stream, type) => packets.push({ type, stream }), kernel)
68
71
 
69
- assert.deepEqual(loginCalls, [{ provider: 'huggingface', params: { timeout: 100, interval: 1 } }])
72
+ const authContext = {
73
+ parentPath: '/pinokio/api/test/hf-login.js',
74
+ cwd: undefined,
75
+ env: undefined
76
+ }
77
+ assert.deepEqual(loginCalls, [{
78
+ provider: 'huggingface',
79
+ params: request.params,
80
+ context: authContext
81
+ }])
82
+ assert.equal(loginCalls[0].params, request.params)
83
+ assert.equal(keyContexts.length, 3)
84
+ assert.equal(keyContexts.every((context) => context === loginCalls[0].context), true)
70
85
  assert.deepEqual(waitCalls, ['/pinokio/api/test/hf-login.js'])
71
86
  assert.deepEqual(actions, [{
72
87
  type: 'clipboard',
@@ -185,9 +200,14 @@ test('hf.login can use the non-modal browser fallback when modal is disabled', a
185
200
  assert.equal(provider, 'huggingface')
186
201
  return null
187
202
  },
188
- async login(provider, params) {
203
+ async login(provider, params, context) {
189
204
  assert.equal(provider, 'huggingface')
190
205
  assert.deepEqual(params, { modal: false, wait: false })
206
+ assert.deepEqual(context, {
207
+ parentPath: undefined,
208
+ cwd: undefined,
209
+ env: undefined
210
+ })
191
211
  return {
192
212
  status: 'pending',
193
213
  login,
@@ -396,8 +416,8 @@ test('hf.logout delegates to the Hugging Face connect provider and returns succe
396
416
  const calls = []
397
417
  const kernel = {
398
418
  connect: {
399
- async logout(provider, params) {
400
- calls.push({ method: 'logout', provider, params })
419
+ async logout(provider, params, context) {
420
+ calls.push({ method: 'logout', provider, params, context })
401
421
  }
402
422
  }
403
423
  }
@@ -405,7 +425,16 @@ test('hf.logout delegates to the Hugging Face connect provider and returns succe
405
425
  const result = await hf.logout(req, () => {}, kernel)
406
426
 
407
427
  assert.deepEqual(result, { status: 'success' })
408
- assert.deepEqual(calls, [{ method: 'logout', provider: 'huggingface', params: req.params }])
428
+ assert.deepEqual(calls, [{
429
+ method: 'logout',
430
+ provider: 'huggingface',
431
+ params: req.params,
432
+ context: {
433
+ parentPath: undefined,
434
+ cwd: undefined,
435
+ env: undefined
436
+ }
437
+ }])
409
438
  })
410
439
 
411
440
  test('hf.upload runs hf upload through shell.run with Hugging Face CLI args', async () => {
@@ -56,15 +56,16 @@ test('Hugging Face connect parses managed hf agent login instructions', () => {
56
56
  })
57
57
  })
58
58
 
59
- test('Hugging Face connect uses shared HF_TOKEN_PATH and ignores ambient HF_TOKEN', async () => {
59
+ test('Hugging Face connect preserves configured HF paths and ignores ambient HF_TOKEN', async () => {
60
60
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-'))
61
- await fs.writeFile(path.join(root, 'ENVIRONMENT'), 'HF_TOKEN_PATH=./custom/hf-token\n')
61
+ await fs.writeFile(path.join(root, 'ENVIRONMENT'), 'HF_HOME=./custom/hf-home\nHF_TOKEN_PATH=./custom/hf-token\n')
62
62
  const oldToken = process.env.HF_TOKEN
63
63
  process.env.HF_TOKEN = 'hf_should_not_win'
64
64
  try {
65
65
  const provider = new HuggingfaceConnect(createKernel(root), {})
66
66
  const env = await provider.authEnv()
67
67
 
68
+ assert.equal(env.HF_HOME, path.join(root, 'custom', 'hf-home'))
68
69
  assert.equal(env.HF_TOKEN_PATH, path.join(root, 'custom', 'hf-token'))
69
70
  assert.equal(env.HF_TOKEN, undefined)
70
71
  assert.equal(env.HF_HUB_DISABLE_UPDATE_CHECK, '1')
@@ -78,6 +79,63 @@ test('Hugging Face connect uses shared HF_TOKEN_PATH and ignores ambient HF_TOKE
78
79
  }
79
80
  })
80
81
 
82
+ test('Hugging Face connect uses the app token path for its managed CLI', async () => {
83
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-app-env-'))
84
+ const appDir = path.join(root, 'api', 'demo')
85
+ const scriptPath = path.join(appDir, 'start.js')
86
+ const tokenPath = path.join(appDir, 'auth', 'token')
87
+ await fs.mkdir(appDir, { recursive: true })
88
+ await fs.writeFile(scriptPath, '')
89
+ await fs.writeFile(
90
+ path.join(appDir, 'ENVIRONMENT'),
91
+ 'HF_HOME=./cache/huggingface\n'
92
+ )
93
+ try {
94
+ const provider = new HuggingfaceConnect(createKernel(root), {})
95
+ const context = {
96
+ parentPath: scriptPath,
97
+ cwd: appDir,
98
+ }
99
+ const sharedEnv = await provider.authEnv(context)
100
+ assert.equal(sharedEnv.HF_HOME, path.join(appDir, 'cache', 'huggingface'))
101
+ assert.equal(sharedEnv.HF_TOKEN_PATH, path.join(root, 'cache', 'HF_AUTH', 'token'))
102
+
103
+ await fs.writeFile(
104
+ path.join(appDir, 'ENVIRONMENT'),
105
+ 'HF_HOME=./cache/huggingface\nHF_TOKEN_PATH=./auth/token\n'
106
+ )
107
+ provider.hfPath = () => process.execPath
108
+ const { env } = await provider.runHf([
109
+ '-e',
110
+ 'require("node:fs").writeFileSync(process.env.HF_TOKEN_PATH, "hf_app_token")'
111
+ ], {}, context)
112
+
113
+ assert.equal(env.HF_HOME, path.join(appDir, 'cache', 'huggingface'))
114
+ assert.equal(env.HF_TOKEN_PATH, tokenPath)
115
+ assert.equal(await fs.readFile(tokenPath, 'utf8'), 'hf_app_token')
116
+ await assert.rejects(
117
+ fs.access(path.join(root, 'cache', 'HF_AUTH', 'token')),
118
+ { code: 'ENOENT' }
119
+ )
120
+
121
+ const commandTokenPath = path.join(appDir, 'command-auth', 'token')
122
+ const commandContext = {
123
+ parentPath: scriptPath,
124
+ cwd: appDir,
125
+ env: { HF_TOKEN_PATH: './command-auth/token' }
126
+ }
127
+ const commandResult = await provider.runHf([
128
+ '-e',
129
+ 'require("node:fs").writeFileSync(process.env.HF_TOKEN_PATH, "hf_command_token")'
130
+ ], {}, commandContext)
131
+
132
+ assert.equal(commandResult.env.HF_TOKEN_PATH, commandTokenPath)
133
+ assert.equal(await fs.readFile(commandTokenPath, 'utf8'), 'hf_command_token')
134
+ } finally {
135
+ await fs.rm(root, { recursive: true, force: true })
136
+ }
137
+ })
138
+
81
139
  test('Hugging Face connect verifies status with bounded whoami', async () => {
82
140
  const provider = new HuggingfaceConnect(createKernel('/tmp/pinokio'), {})
83
141
  let receivedArgs
@@ -99,7 +157,7 @@ test('Hugging Face connect verifies status with bounded whoami', async () => {
99
157
  assert.equal(await provider.connected({ timeout: 2500 }), false)
100
158
  })
101
159
 
102
- test('Hugging Face connect supplies the shared token path at runtime when it is not persisted', async () => {
160
+ test('Hugging Face connect supplies the shared token path when none is configured', async () => {
103
161
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-default-'))
104
162
  await fs.writeFile(path.join(root, 'ENVIRONMENT'), 'OTHER=value\n')
105
163
  try {
@@ -129,16 +187,43 @@ test('Hugging Face connect repairs an empty directory at the token file path', a
129
187
  }
130
188
  })
131
189
 
132
- test('Hugging Face connect preserves and rejects a non-empty token directory', async () => {
133
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-invalid-'))
134
- const tokenPath = path.join(root, 'cache', 'HF_AUTH', 'token')
190
+ test('Hugging Face connect leaves existing token and refresh files unchanged', async () => {
191
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-existing-login-'))
192
+ const authDir = path.join(root, 'cache', 'HF_AUTH')
193
+ await fs.mkdir(authDir, { recursive: true })
194
+ await fs.writeFile(path.join(authDir, 'token'), 'hf_existing_token')
195
+ await fs.writeFile(path.join(authDir, 'stored_tokens'), 'existing stored tokens')
196
+ try {
197
+ const provider = new HuggingfaceConnect(createKernel(root), {})
198
+ const env = await provider.authEnv()
199
+
200
+ assert.equal(env.HF_TOKEN_PATH, path.join(authDir, 'token'))
201
+ assert.equal(await fs.readFile(path.join(authDir, 'token'), 'utf8'), 'hf_existing_token')
202
+ assert.equal(await fs.readFile(path.join(authDir, 'stored_tokens'), 'utf8'), 'existing stored tokens')
203
+ } finally {
204
+ await fs.rm(root, { recursive: true, force: true })
205
+ }
206
+ })
207
+
208
+ test('Hugging Face connect removes a non-empty token directory', async () => {
209
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-invalid-dir-'))
210
+ const authDir = path.join(root, 'cache', 'HF_AUTH')
211
+ const tokenPath = path.join(authDir, 'token')
135
212
  await fs.mkdir(tokenPath, { recursive: true })
136
- await fs.writeFile(path.join(tokenPath, 'keep.txt'), 'keep')
213
+ await fs.writeFile(path.join(tokenPath, 'leftover.txt'), 'leftover')
214
+ await fs.writeFile(path.join(authDir, 'stored_tokens'), 'refresh state')
137
215
  try {
138
216
  const provider = new HuggingfaceConnect(createKernel(root), {})
217
+ await Promise.all([provider.authEnv(), provider.authEnv()])
218
+ provider.hfPath = () => process.execPath
219
+ const { env } = await provider.runHf([
220
+ '-e',
221
+ 'require("node:fs").writeFileSync(process.env.HF_TOKEN_PATH, "hf_replacement_token")'
222
+ ])
139
223
 
140
- await assert.rejects(provider.authEnv(), /Hugging Face token path must be a file/)
141
- assert.equal(await fs.readFile(path.join(tokenPath, 'keep.txt'), 'utf8'), 'keep')
224
+ assert.equal(env.HF_TOKEN_PATH, tokenPath)
225
+ assert.equal(await fs.readFile(tokenPath, 'utf8'), 'hf_replacement_token')
226
+ assert.equal(await fs.readFile(path.join(authDir, 'stored_tokens'), 'utf8'), 'refresh state')
142
227
  } finally {
143
228
  await fs.rm(root, { recursive: true, force: true })
144
229
  }
@@ -172,6 +172,39 @@ test('Shell.init_env disables Hugging Face hub update checks by default without
172
172
  assert.equal(overrideShell.env.HF_TOKEN_PATH, path.join(root, 'custom', 'hf-token'))
173
173
  })
174
174
 
175
+ test('Shell.init_env preserves an app HF_HOME without changing the shared token default', async () => {
176
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-shell-hf-app-env-'))
177
+ const appDir = path.join(root, 'api', 'demo')
178
+ const scriptPath = path.join(appDir, 'start.js')
179
+ await fs.mkdir(appDir, { recursive: true })
180
+ await fs.writeFile(scriptPath, '')
181
+ await fs.writeFile(path.join(appDir, 'ENVIRONMENT'), 'HF_HOME=./cache/huggingface\n')
182
+
183
+ const shell = createShell(createKernel(root))
184
+ await shell.init_env({
185
+ path: appDir,
186
+ $parent: { path: scriptPath },
187
+ env: {}
188
+ })
189
+
190
+ assert.equal(shell.env.HF_HOME, path.join(appDir, 'cache', 'huggingface'))
191
+ assert.equal(shell.env.HF_TOKEN_PATH, path.join(root, 'cache', 'HF_AUTH', 'token'))
192
+
193
+ await fs.writeFile(
194
+ path.join(appDir, 'ENVIRONMENT'),
195
+ 'HF_HOME=./cache/huggingface\nHF_TOKEN_PATH=./auth/token\n'
196
+ )
197
+ const overrideShell = createShell(createKernel(root))
198
+ await overrideShell.init_env({
199
+ path: appDir,
200
+ $parent: { path: scriptPath },
201
+ env: {}
202
+ })
203
+
204
+ assert.equal(overrideShell.env.HF_HOME, path.join(appDir, 'cache', 'huggingface'))
205
+ assert.equal(overrideShell.env.HF_TOKEN_PATH, path.join(appDir, 'auth', 'token'))
206
+ })
207
+
175
208
  test('Shell.init_env keeps Windows Hugging Face symlink defaults scoped to win32', async () => {
176
209
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-shell-hf-win-'))
177
210
  await fs.mkdir(path.join(root, 'api'), { recursive: true })