pinokiod 7.5.35 → 7.5.37

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.
@@ -69,79 +69,6 @@ const LEGACY_SSL_CERT_DIR_HOOK_FILES = [
69
69
  "zz_pinokio_unset_ssl_cert_dir-win.ps1",
70
70
  "zz_pinokio_unset_ssl_cert_dir-win.sh",
71
71
  ]
72
- const WINDOWS_CONDA_ACTIVATE_RELATIVE_PATH = path.join(
73
- "Lib",
74
- "site-packages",
75
- "conda",
76
- "shell",
77
- "condabin",
78
- "_conda_activate.bat"
79
- )
80
- const WINDOWS_CONDA_ACTIVATE_PINOKIO_MARKER = ":PINOKIO_APPLY_CONDA_ENV"
81
-
82
- function windowsCondaActivateOriginalBlock(eol) {
83
- return [
84
- " ) ELSE ENDLOCAL & (",
85
- ' FOR /F "tokens=1,* delims==" %%A IN (%%T) DO (',
86
- ' :: A, B = "key", "value"',
87
- ' IF "%CONDA_DEBUG%" == "1" ECHO DEBUG: "%%A=%%B">&2',
88
- ' IF "%%A"=="_CONDA_SCRIPT" (',
89
- " :: Script execution, fast exit if activation scripts fail",
90
- ' CALL "%%B" || (',
91
- ' ECHO ERROR: Activation script "%%B" failed with code %ERRORLEVEL%.>&2',
92
- ' CALL :DELETE "%%T"',
93
- ' CALL :DELETE "%__conda_tmp%"',
94
- " EXIT /B 4",
95
- " )",
96
- ' ) ELSE IF "%%B"=="" (',
97
- " :: Unset variable",
98
- ' SET "%%A="',
99
- " ) ELSE (",
100
- " :: Set variable",
101
- ' SET "%%A=%%B"',
102
- " )",
103
- " )",
104
- " :: Clean up",
105
- ' CALL :DELETE "%%T"',
106
- ' CALL :DELETE "%__conda_tmp%"',
107
- " EXIT /B 0",
108
- " )",
109
- ].join(eol)
110
- }
111
-
112
- function windowsCondaActivatePatchedBlock(eol) {
113
- return [
114
- ' ) ELSE ENDLOCAL & @CALL :PINOKIO_APPLY_CONDA_ENV "%%T" "%__conda_tmp%" & @GOTO :EOF',
115
- ].join(eol)
116
- }
117
-
118
- function windowsCondaActivatePatchRoutines(eol) {
119
- return [
120
- ":PINOKIO_APPLY_CONDA_ENV",
121
- "@ECHO OFF",
122
- '@FOR /F "tokens=1,* delims==" %%A IN (%~1) DO (',
123
- '@IF "%CONDA_DEBUG%" == "1" ECHO DEBUG: "%%A=%%B">&2',
124
- '@IF "%%A"=="_CONDA_SCRIPT" (',
125
- '@CALL "%%B" || (',
126
- '@ECHO ERROR: Activation script "%%B" failed with code %ERRORLEVEL%.>&2',
127
- '@CALL :DELETE "%~1"',
128
- '@CALL :DELETE "%~2"',
129
- "@ECHO ON",
130
- "@EXIT /B 4",
131
- ")",
132
- ') ELSE IF "%%B"=="" (',
133
- '@SET "%%A="',
134
- ") ELSE (",
135
- '@SET "%%A=%%B"',
136
- ")",
137
- ")",
138
- '@CALL :DELETE "%~1"',
139
- '@CALL :DELETE "%~2"',
140
- "@ECHO ON",
141
- "@EXIT /B 0",
142
- "",
143
- ].join(eol)
144
- }
145
72
 
146
73
  class Conda {
147
74
  description = "Pinokio uses Conda to install various useful programs in an isolated manner."
@@ -234,57 +161,6 @@ class Conda {
234
161
  }
235
162
  }
236
163
  }
237
- async ensureWindowsCondaActivateEchoPatch() {
238
- if (this.kernel.platform !== "win32" || !(await this.hasCondaMeta())) {
239
- return
240
- }
241
- const activatePath = this.kernel.bin.path(CONDA_ROOT_DIR, WINDOWS_CONDA_ACTIVATE_RELATIVE_PATH)
242
- let content
243
- try {
244
- content = await fs.promises.readFile(activatePath, "utf8")
245
- } catch (error) {
246
- if (error && error.code === "ENOENT") {
247
- return
248
- }
249
- throw error
250
- }
251
- const eol = content.includes("\r\n") ? "\r\n" : "\n"
252
- const routineAnchor = `${eol}:: Routine to delete a temp file if CONDA_DEBUG is not set`
253
- if (!content.includes(routineAnchor)) {
254
- console.warn("Could not patch Conda CMD activation echo leak; missing _conda_activate.bat routine anchor")
255
- return
256
- }
257
- if (content.includes(WINDOWS_CONDA_ACTIVATE_PINOKIO_MARKER)) {
258
- if (content.includes(`${WINDOWS_CONDA_ACTIVATE_PINOKIO_MARKER}${eol}@ECHO OFF`)) {
259
- return
260
- }
261
- const routineStart = content.indexOf(WINDOWS_CONDA_ACTIVATE_PINOKIO_MARKER)
262
- const routineAnchorIndex = content.indexOf(routineAnchor, routineStart)
263
- if (routineAnchorIndex < 0) {
264
- console.warn("Could not update Conda CMD activation echo patch; missing _conda_activate.bat routine anchor")
265
- return
266
- }
267
- const patched = [
268
- content.slice(0, routineStart),
269
- windowsCondaActivatePatchRoutines(eol),
270
- content.slice(routineAnchorIndex + eol.length),
271
- ].join("")
272
- await fs.promises.writeFile(activatePath, patched)
273
- return
274
- }
275
- const originalBlock = windowsCondaActivateOriginalBlock(eol)
276
- if (!content.includes(originalBlock)) {
277
- console.warn("Could not patch Conda CMD activation echo leak; unexpected _conda_activate.bat")
278
- return
279
- }
280
- const patched = content
281
- .replace(originalBlock, windowsCondaActivatePatchedBlock(eol))
282
- .replace(
283
- routineAnchor,
284
- `${eol}${windowsCondaActivatePatchRoutines(eol)}:: Routine to delete a temp file if CONDA_DEBUG is not set`
285
- )
286
- await fs.promises.writeFile(activatePath, patched)
287
- }
288
164
  async init() {
289
165
  if (this.kernel.homedir) {
290
166
  console.log("condarc init")
@@ -307,7 +183,6 @@ report_errors: false`)
307
183
  await this.ensureCompatibilityAlias()
308
184
  }
309
185
  await this.ensureWindowsOpenSslHooks()
310
- await this.ensureWindowsCondaActivateEchoPatch()
311
186
  }
312
187
  }
313
188
  async check() {
@@ -456,7 +331,6 @@ report_errors: false`)
456
331
  )
457
332
  }
458
333
  await this.ensureWindowsOpenSslHooks()
459
- await this.ensureWindowsCondaActivateEchoPatch()
460
334
  await this.ensureCompatibilityAlias()
461
335
  ondata({ raw: `Install finished\r\n` })
462
336
  await this.kernel.bin.rm(installer, ondata)
package/kernel/shell.js CHANGED
@@ -23,6 +23,7 @@ const ShellRunTemplate = require('./api/shell_run_template')
23
23
  const { PYTHON_INSTALL_SPEC } = require('./bin/conda-pins')
24
24
  const CondaRuntimeGuard = require('./shell_conda_runtime_guard')
25
25
  const home = os.homedir()
26
+ const WINDOWS_CMD_CONDA_COMMAND_PATTERN = /(?:^|[&|()]\s*)@?\s*(?:call\s+)?(?:"[^"]*[\\/]conda(?:\.bat|\.exe)?"|(?:[a-z]:)?[^&|()\s"]*[\\/]conda(?:\.bat|\.exe)?|conda(?:\.bat|\.exe)?)\s+/i
26
27
 
27
28
  function normalizeComparablePath(filePath, platform) {
28
29
  const normalized = path.normalize(filePath)
@@ -335,6 +336,33 @@ class Shell {
335
336
  const name = (shellName || '').toLowerCase()
336
337
  return name.includes('powershell') || name.includes('pwsh')
337
338
  }
339
+ hasCondaCommand(message) {
340
+ if (Array.isArray(message)) {
341
+ return message.some((item) => this.hasCondaCommand(item))
342
+ }
343
+ if (message && message.constructor === Object) {
344
+ return this.hasCondaCommand(this.buildStructuredMessage(message))
345
+ }
346
+ return typeof message === "string" && WINDOWS_CMD_CONDA_COMMAND_PATTERN.test(message)
347
+ }
348
+ shouldSuppressCmdEchoForConda(params) {
349
+ return this.platform === "win32" &&
350
+ this.isCmdShell() &&
351
+ !(params && params.input) &&
352
+ this.hasCondaCommand(params && params.message)
353
+ }
354
+ buildCmdEchoSuppressedCommand(command) {
355
+ return `@echo off\r\n${command}\r\n@echo on`
356
+ }
357
+ prepareCommandExecution(params, command) {
358
+ if (!this.shouldSuppressCmdEchoForConda(params)) {
359
+ return { command }
360
+ }
361
+ return {
362
+ command: this.buildCmdEchoSuppressedCommand(command),
363
+ preview: command,
364
+ }
365
+ }
338
366
  isUnresolvedTemplate(value) {
339
367
  return typeof value === "string" && /^\{\{[\s\S]*\}\}$/.test(value)
340
368
  }
@@ -413,6 +441,9 @@ class Shell {
413
441
  this.decsyncBuffer = ''
414
442
  this.stateSync.reset()
415
443
  this.envArgsPreviewed = false
444
+ this.commandEchoPreview = null
445
+ this.commandEchoPreviewed = false
446
+ this.exec_cmd = null
416
447
 
417
448
  /*
418
449
  params := {
@@ -1399,8 +1430,16 @@ class Shell {
1399
1430
  async exec(params) {
1400
1431
  this.parser = new ShellParser()
1401
1432
  this.emitEnvArgsPreview(params)
1433
+ const originalParams = {
1434
+ input: params && params.input,
1435
+ message: params && params.message,
1436
+ }
1402
1437
  params = await this.activate(params)
1403
1438
  this.cmd = this.build(params)
1439
+ const preparedCommand = this.prepareCommandExecution(originalParams, this.cmd)
1440
+ this.exec_cmd = preparedCommand.command
1441
+ this.commandEchoPreview = preparedCommand.preview || null
1442
+ this.commandEchoPreviewed = false
1404
1443
  let res = await new Promise((resolve, reject) => {
1405
1444
  this.resolve = resolve
1406
1445
  this.reject = reject
@@ -1853,7 +1892,11 @@ ${cleaned}
1853
1892
  }
1854
1893
  if (this.ptyProcess) {
1855
1894
  this.stateSync.noteInput()
1856
- this.ptyProcess.write(`${this.cmd}${this.EOL}`)
1895
+ if (this.commandEchoPreview && !this.commandEchoPreviewed) {
1896
+ this.commandEchoPreviewed = true
1897
+ this.queue.push(`${this.commandEchoPreview}${this.EOL}`)
1898
+ }
1899
+ this.ptyProcess.write(`${this.exec_cmd || this.cmd}${this.EOL}`)
1857
1900
  // setTimeout(() => {
1858
1901
  // this.ptyProcess.write('\x1B[?2004h');
1859
1902
  // }, 500)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.5.35",
3
+ "version": "7.5.37",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -125,58 +125,6 @@ const EXPECTED_WINDOWS_OPENSSL_HOOKS = {
125
125
  ].join('\n'),
126
126
  }
127
127
 
128
- const WINDOWS_CONDA_ACTIVATE_PATH = path.join(
129
- 'bin',
130
- 'miniforge',
131
- 'Lib',
132
- 'site-packages',
133
- 'conda',
134
- 'shell',
135
- 'condabin',
136
- '_conda_activate.bat'
137
- )
138
- const WINDOWS_CONDA_ACTIVATE_ORIGINAL_BLOCK = [
139
- ' ) ELSE ENDLOCAL & (',
140
- ' FOR /F "tokens=1,* delims==" %%A IN (%%T) DO (',
141
- ' :: A, B = "key", "value"',
142
- ' IF "%CONDA_DEBUG%" == "1" ECHO DEBUG: "%%A=%%B">&2',
143
- ' IF "%%A"=="_CONDA_SCRIPT" (',
144
- ' :: Script execution, fast exit if activation scripts fail',
145
- ' CALL "%%B" || (',
146
- ' ECHO ERROR: Activation script "%%B" failed with code %ERRORLEVEL%.>&2',
147
- ' CALL :DELETE "%%T"',
148
- ' CALL :DELETE "%__conda_tmp%"',
149
- ' EXIT /B 4',
150
- ' )',
151
- ' ) ELSE IF "%%B"=="" (',
152
- ' :: Unset variable',
153
- ' SET "%%A="',
154
- ' ) ELSE (',
155
- ' :: Set variable',
156
- ' SET "%%A=%%B"',
157
- ' )',
158
- ' )',
159
- ' :: Clean up',
160
- ' CALL :DELETE "%%T"',
161
- ' CALL :DELETE "%__conda_tmp%"',
162
- ' EXIT /B 0',
163
- ' )',
164
- ].join('\r\n')
165
- const WINDOWS_CONDA_ACTIVATE_SCRIPT = [
166
- '@ECHO OFF',
167
- 'SETLOCAL',
168
- 'FOR /F "delims=" %%T IN (%__conda_tmp%) DO (',
169
- ' IF NOT EXIST "%%T" (',
170
- ' ENDLOCAL & EXIT /B 3',
171
- WINDOWS_CONDA_ACTIVATE_ORIGINAL_BLOCK,
172
- ')',
173
- '',
174
- ':: Routine to delete a temp file if CONDA_DEBUG is not set',
175
- ':DELETE',
176
- 'GOTO :EOF',
177
- '',
178
- ].join('\r\n')
179
-
180
128
  async function seedWindowsOpenSslHooks(root, condaRootName) {
181
129
  const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
182
130
  for (const hookRelativePath of Object.keys(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
@@ -195,13 +143,6 @@ async function seedWindowsOpenSslHooks(root, condaRootName) {
195
143
  }
196
144
  }
197
145
 
198
- async function seedWindowsCondaActivateScript(root, content = WINDOWS_CONDA_ACTIVATE_SCRIPT) {
199
- const activatePath = path.join(root, WINDOWS_CONDA_ACTIVATE_PATH)
200
- await fs.mkdir(path.dirname(activatePath), { recursive: true })
201
- await fs.writeFile(activatePath, content)
202
- return activatePath
203
- }
204
-
205
146
  async function assertWindowsOpenSslHooksPatched(root, condaRootName) {
206
147
  const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
207
148
  for (const [hookRelativePath, expectedContent] of Object.entries(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
@@ -262,83 +203,6 @@ test('Conda init patches Windows OpenSSL hooks for legacy Miniconda roots', asyn
262
203
  await assertWindowsOpenSslHooksPatched(root, 'miniconda')
263
204
  })
264
205
 
265
- test('Conda init patches Windows CMD activation echo leak idempotently', async () => {
266
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-patch-'))
267
- await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
268
- const activatePath = await seedWindowsCondaActivateScript(root)
269
- const conda = createConda(createKernel(root))
270
-
271
- await conda.init()
272
- const patched = await fs.readFile(activatePath, 'utf8')
273
- await conda.init()
274
-
275
- assert.equal(await fs.readFile(activatePath, 'utf8'), patched)
276
- assert.equal(patched.includes(WINDOWS_CONDA_ACTIVATE_ORIGINAL_BLOCK), false)
277
- assert.match(patched, /ENDLOCAL & @CALL :PINOKIO_APPLY_CONDA_ENV "%%T" "%__conda_tmp%" & @GOTO :EOF/)
278
- assert.match(patched, /:PINOKIO_APPLY_CONDA_ENV\r\n@ECHO OFF/)
279
- assert.match(patched, /@CALL "%%B" \|\| \(/)
280
- assert.match(patched, /@ECHO ON\r\n@EXIT \/B 0/)
281
- assert.match(patched, /:: Routine to delete a temp file if CONDA_DEBUG is not set/)
282
- })
283
-
284
- test('Conda init updates the earlier Windows CMD activation echo patch', async () => {
285
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-update-patch-'))
286
- await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
287
- const oldPatch = WINDOWS_CONDA_ACTIVATE_SCRIPT
288
- .replace(
289
- WINDOWS_CONDA_ACTIVATE_ORIGINAL_BLOCK,
290
- ' ) ELSE ENDLOCAL & @CALL :PINOKIO_APPLY_CONDA_ENV "%%T" "%__conda_tmp%" & @GOTO :EOF'
291
- )
292
- .replace(
293
- '\r\n:: Routine to delete a temp file if CONDA_DEBUG is not set',
294
- [
295
- '\r\n:PINOKIO_APPLY_CONDA_ENV',
296
- '@FOR /F "tokens=1,* delims==" %%A IN (%~1) DO (',
297
- '@IF "%CONDA_DEBUG%" == "1" ECHO DEBUG: "%%A=%%B">&2',
298
- ')',
299
- '@CALL :DELETE "%~1"',
300
- '@CALL :DELETE "%~2"',
301
- '@EXIT /B 0',
302
- ':: Routine to delete a temp file if CONDA_DEBUG is not set',
303
- ].join('\r\n')
304
- )
305
- const activatePath = await seedWindowsCondaActivateScript(root, oldPatch)
306
- const conda = createConda(createKernel(root))
307
-
308
- await conda.init()
309
-
310
- const patched = await fs.readFile(activatePath, 'utf8')
311
- assert.match(patched, /:PINOKIO_APPLY_CONDA_ENV\r\n@ECHO OFF/)
312
- assert.match(patched, /@ECHO ON\r\n@EXIT \/B 0/)
313
- })
314
-
315
- test('Conda init leaves unexpected Windows CMD activation script unchanged', async () => {
316
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-unexpected-'))
317
- await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
318
- const unexpected = '@ECHO OFF\r\nREM unexpected upstream script\r\n'
319
- const activatePath = await seedWindowsCondaActivateScript(root, unexpected)
320
- const conda = createConda(createKernel(root))
321
-
322
- await conda.init()
323
-
324
- assert.equal(await fs.readFile(activatePath, 'utf8'), unexpected)
325
- })
326
-
327
- test('Conda init leaves Windows CMD activation script unchanged when patch anchor is missing', async () => {
328
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-missing-anchor-'))
329
- await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
330
- const missingAnchor = WINDOWS_CONDA_ACTIVATE_SCRIPT.replace(
331
- '\r\n:: Routine to delete a temp file if CONDA_DEBUG is not set',
332
- '\r\n:: unexpected routine header'
333
- )
334
- const activatePath = await seedWindowsCondaActivateScript(root, missingAnchor)
335
- const conda = createConda(createKernel(root))
336
-
337
- await conda.init()
338
-
339
- assert.equal(await fs.readFile(activatePath, 'utf8'), missingAnchor)
340
- })
341
-
342
206
  test('Conda pins Python 3.10.20 consistently across platforms', async () => {
343
207
  assert.equal(PYTHON_INSTALL_SPEC, 'python=3.10.20')
344
208
  assert.equal(WINDOWS_PYTHON_SSL_FIX_SPEC, 'python=3.10.20=*_1_cpython')
@@ -381,7 +245,6 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
381
245
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-python-pin-win32-'))
382
246
  const kernel = createKernel(root, 'win32')
383
247
  const calls = []
384
- let activatePath
385
248
 
386
249
  kernel.bin.mods = []
387
250
  kernel.bin.download = async () => {}
@@ -392,7 +255,6 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
392
255
  const miniforge = path.join(root, 'bin', 'miniforge')
393
256
  await fs.mkdir(path.join(miniforge, 'conda-meta'), { recursive: true })
394
257
  await fs.writeFile(path.join(miniforge, 'python.exe'), 'fake python\n')
395
- activatePath = await seedWindowsCondaActivateScript(root)
396
258
  }
397
259
  }
398
260
 
@@ -405,7 +267,6 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
405
267
  condaInstall.message[1],
406
268
  'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "conda-libmamba-solver>=25.4.0"'
407
269
  )
408
- assert.match(await fs.readFile(activatePath, 'utf8'), /:PINOKIO_APPLY_CONDA_ENV/)
409
270
  })
410
271
 
411
272
  test('Conda install replaces legacy miniconda with a compatibility alias to miniforge', async () => {
@@ -917,6 +917,37 @@ test('internal shell activation inspects only when shell.run guard option is set
917
917
  assert.equal(events.some((event) => event.type === 'notify'), true)
918
918
  })
919
919
 
920
+ test('Windows cmd suppresses echo only for explicit conda shell.run commands', () => {
921
+ const shell = new Shell({ bracketedPasteSupport: {} })
922
+ shell.platform = 'win32'
923
+ shell.shell = 'cmd.exe'
924
+ const command = 'conda_hook & conda deactivate & conda activate base & conda install -y -c conda-forge ffmpeg'
925
+
926
+ assert.equal(shell.shouldSuppressCmdEchoForConda({
927
+ message: 'conda install -y -c conda-forge ffmpeg',
928
+ }), true)
929
+ assert.equal(shell.shouldSuppressCmdEchoForConda({
930
+ message: 'python main.py',
931
+ }), false)
932
+ assert.equal(shell.shouldSuppressCmdEchoForConda({
933
+ message: 'conda_hook',
934
+ }), false)
935
+ assert.equal(shell.shouldSuppressCmdEchoForConda({
936
+ input: true,
937
+ message: 'conda install -y -c conda-forge ffmpeg',
938
+ }), false)
939
+
940
+ const prepared = shell.prepareCommandExecution({
941
+ message: 'conda install -y -c conda-forge ffmpeg',
942
+ }, command)
943
+ assert.equal(prepared.preview, command)
944
+ assert.equal(prepared.command, `@echo off\r\n${command}\r\n@echo on`)
945
+
946
+ assert.deepEqual(shell.prepareCommandExecution({
947
+ message: 'python main.py',
948
+ }, command), { command })
949
+ })
950
+
920
951
  test('rewrite warning is scoped to each user-visible shell flow', async () => {
921
952
  CondaRuntimeGuard.resetNoticeSessionsForTest()
922
953
  const { context, events, root } = createContext()