pinokiod 7.5.36 → 7.5.38

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,98 +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_PATHS = [
73
- path.join("condabin", "_conda_activate.bat"),
74
- path.join("Lib", "site-packages", "conda", "shell", "condabin", "_conda_activate.bat"),
75
- ]
76
- const WINDOWS_CONDA_ACTIVATE_BAT = [
77
- ":: Copyright (C) 2012 Anaconda, Inc",
78
- ":: SPDX-License-Identifier: BSD-3-Clause",
79
- ":: Helper routine for activation, deactivation, and reactivation.",
80
- "@ECHO OFF",
81
- "SETLOCAL",
82
- "",
83
- ":: Generate a unique temporary filename using a GUID to support parallel workflows",
84
- ":: (e.g., `start /b conda run ...`). %RANDOM% is insufficient because processes",
85
- ":: started simultaneously get identical time-based seeds, producing collisions.",
86
- ":: See: https://devblogs.microsoft.com/oldnewthing/20100617-00/?p=13673",
87
- "FOR /F \"delims=\" %%G IN ('powershell -NoProfile -Command \"[guid]::NewGuid()\"') DO SET \"__conda_guid=%%G\"",
88
- "SET \"__conda_tmp=%TEMP%\\__conda_tmp_%__conda_guid%.txt\"",
89
- "",
90
- ":: Run conda command and get its output",
91
- ":: WARNING: This cannot be simplified into a FOR /F parsing loop because of the way",
92
- ":: MSDOS associates the outer quotes for usebackq. E.g., the following:",
93
- ":: `\"%CONDA_EXE%\" shell.cmd.exe activate \"name\"`",
94
- ":: Which results in the following gibberish being run:",
95
- ":: %CONDA_EXE%\" shell.cmd.exe activate \"environment",
96
- ":: Producing an error like:",
97
- ":: The filename, directory name, or volume label syntax is incorrect.",
98
- ":: Instead we run the command and store the output for subsequent processing.",
99
- "\"%CONDA_EXE%\" %_CE_M% %_CE_CONDA% shell.cmd.exe %* > \"%__conda_tmp%\"",
100
- "IF NOT EXIST \"%__conda_tmp%\" (",
101
- " ECHO ERROR: Failed to create temp file for 'conda %*'.>&2",
102
- " ECHO.>&2",
103
- " ECHO This is likely a TEMP directory issue>&2",
104
- " ECHO ^(permissions, disk space, or invalid path^).>&2",
105
- " ECHO.>&2",
106
- " ECHO Ensure TEMP or TMP environment variables point to a writable location.>&2",
107
- " ECHO See: https://docs.conda.io/projects/conda/en/stable/user-guide/troubleshooting.html#temp-file-errors>&2",
108
- " ENDLOCAL & EXIT /B 1",
109
- ") ELSE IF %ERRORLEVEL% NEQ 0 (",
110
- " ECHO ERROR: 'conda %*' exited with code %ERRORLEVEL%.>&2",
111
- " CALL :DELETE \"%__conda_tmp%\"",
112
- " ENDLOCAL & EXIT /B 2",
113
- ")",
114
- "",
115
- ":: Check if conda produced output",
116
- "FOR /F \"delims=\" %%T IN (%__conda_tmp%) DO (",
117
- " :: T = \"%TEMP%\\<uuid>.env\"",
118
- " IF NOT EXIST \"%%T\" (",
119
- " ECHO ERROR: Activation file missing for 'conda %*'.>&2",
120
- " CALL :DELETE \"%__conda_tmp%\"",
121
- " ENDLOCAL & EXIT /B 3",
122
- " ) ELSE ENDLOCAL & @CALL :PINOKIO_APPLY_CONDA_ENV \"%%T\" \"%__conda_tmp%\" & @GOTO :EOF",
123
- ")",
124
- "",
125
- ":: If we get here, the FOR loop never ran which means no output",
126
- "ECHO ERROR: No output from 'conda %*'.>&2",
127
- "CALL :DELETE \"%__conda_tmp%\"",
128
- "ENDLOCAL & EXIT /B 5",
129
- "",
130
- ":PINOKIO_APPLY_CONDA_ENV",
131
- "@ECHO OFF",
132
- "@FOR /F \"tokens=1,* delims==\" %%A IN (%~1) DO (",
133
- "@IF \"%CONDA_DEBUG%\" == \"1\" ECHO DEBUG: \"%%A=%%B\">&2",
134
- "@IF \"%%A\"==\"_CONDA_SCRIPT\" (",
135
- "@CALL \"%%B\" || (",
136
- "@ECHO ERROR: Activation script \"%%B\" failed with code %ERRORLEVEL%.>&2",
137
- "@CALL :DELETE \"%~1\"",
138
- "@CALL :DELETE \"%~2\"",
139
- "@ECHO ON",
140
- "@EXIT /B 4",
141
- ")",
142
- ") ELSE IF \"%%B\"==\"\" (",
143
- "@SET \"%%A=\"",
144
- ") ELSE (",
145
- "@SET \"%%A=%%B\"",
146
- ")",
147
- ")",
148
- "@CALL :DELETE \"%~1\"",
149
- "@CALL :DELETE \"%~2\"",
150
- "@ECHO ON",
151
- "@EXIT /B 0",
152
- "",
153
- ":: Routine to delete a temp file if CONDA_DEBUG is not set",
154
- ":: usage: call :DELETE \"path/to/file\"",
155
- ":DELETE",
156
- "IF \"%CONDA_DEBUG%\" == \"1\" (",
157
- " ECHO DEBUG: Retaining temporary file \"%~1\".>&2",
158
- ") ELSE IF EXIST \"%~1\" (",
159
- " DEL /F /Q \"%~1\" 2>NUL",
160
- ")",
161
- "GOTO :EOF",
162
- "",
163
- ].join("\r\n")
164
72
 
165
73
  class Conda {
166
74
  description = "Pinokio uses Conda to install various useful programs in an isolated manner."
@@ -253,16 +161,6 @@ class Conda {
253
161
  }
254
162
  }
255
163
  }
256
- async ensureWindowsCondaActivateBat() {
257
- if (this.kernel.platform !== "win32" || !(await this.hasCondaMeta())) {
258
- return
259
- }
260
- for (const relativePath of WINDOWS_CONDA_ACTIVATE_RELATIVE_PATHS) {
261
- const activatePath = this.kernel.bin.path(CONDA_ROOT_DIR, relativePath)
262
- await fs.promises.mkdir(path.dirname(activatePath), { recursive: true })
263
- await fs.promises.writeFile(activatePath, WINDOWS_CONDA_ACTIVATE_BAT)
264
- }
265
- }
266
164
  async init() {
267
165
  if (this.kernel.homedir) {
268
166
  console.log("condarc init")
@@ -285,7 +183,6 @@ report_errors: false`)
285
183
  await this.ensureCompatibilityAlias()
286
184
  }
287
185
  await this.ensureWindowsOpenSslHooks()
288
- await this.ensureWindowsCondaActivateBat()
289
186
  }
290
187
  }
291
188
  async check() {
@@ -434,7 +331,6 @@ report_errors: false`)
434
331
  )
435
332
  }
436
333
  await this.ensureWindowsOpenSslHooks()
437
- await this.ensureWindowsCondaActivateBat()
438
334
  await this.ensureCompatibilityAlias()
439
335
  ondata({ raw: `Install finished\r\n` })
440
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,31 @@ 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
+ prepareCommandExecution(params, command) {
355
+ if (!this.shouldSuppressCmdEchoForConda(params)) {
356
+ return { command }
357
+ }
358
+ return {
359
+ command,
360
+ preview: command,
361
+ quietCmd: true,
362
+ }
363
+ }
338
364
  isUnresolvedTemplate(value) {
339
365
  return typeof value === "string" && /^\{\{[\s\S]*\}\}$/.test(value)
340
366
  }
@@ -413,6 +439,10 @@ class Shell {
413
439
  this.decsyncBuffer = ''
414
440
  this.stateSync.reset()
415
441
  this.envArgsPreviewed = false
442
+ this.commandEchoPreview = null
443
+ this.commandEchoPreviewed = false
444
+ this.exec_cmd = null
445
+ this.quietCmdExecution = false
416
446
 
417
447
  /*
418
448
  params := {
@@ -1399,8 +1429,17 @@ class Shell {
1399
1429
  async exec(params) {
1400
1430
  this.parser = new ShellParser()
1401
1431
  this.emitEnvArgsPreview(params)
1432
+ const originalParams = {
1433
+ input: params && params.input,
1434
+ message: params && params.message,
1435
+ }
1402
1436
  params = await this.activate(params)
1403
1437
  this.cmd = this.build(params)
1438
+ const preparedCommand = this.prepareCommandExecution(originalParams, this.cmd)
1439
+ this.exec_cmd = preparedCommand.command
1440
+ this.commandEchoPreview = preparedCommand.preview || null
1441
+ this.commandEchoPreviewed = false
1442
+ this.quietCmdExecution = !!preparedCommand.quietCmd
1404
1443
  let res = await new Promise((resolve, reject) => {
1405
1444
  this.resolve = resolve
1406
1445
  this.reject = reject
@@ -1420,7 +1459,10 @@ class Shell {
1420
1459
  if (!this.ptyProcess) {
1421
1460
  // ptyProcess doesn't exist => create
1422
1461
  this.done = false
1423
- this.ptyProcess = pty.spawn(this.shell, this.args, config)
1462
+ const shellArgs = this.quietCmdExecution && this.isCmdShell()
1463
+ ? this.args.concat(this.args.some((arg) => /^\/q$/i.test(arg)) ? [] : ["/Q"])
1464
+ : this.args
1465
+ this.ptyProcess = pty.spawn(this.shell, shellArgs, config)
1424
1466
  this.ptyProcess.onData((data) => {
1425
1467
  if (!this.monitor) {
1426
1468
  this.monitor = ""
@@ -1853,7 +1895,11 @@ ${cleaned}
1853
1895
  }
1854
1896
  if (this.ptyProcess) {
1855
1897
  this.stateSync.noteInput()
1856
- this.ptyProcess.write(`${this.cmd}${this.EOL}`)
1898
+ if (this.commandEchoPreview && !this.commandEchoPreviewed) {
1899
+ this.commandEchoPreviewed = true
1900
+ this.queue.push(`${this.commandEchoPreview}${this.EOL}`)
1901
+ }
1902
+ this.ptyProcess.write(`${this.exec_cmd || this.cmd}${this.EOL}`)
1857
1903
  // setTimeout(() => {
1858
1904
  // this.ptyProcess.write('\x1B[?2004h');
1859
1905
  // }, 500)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.5.36",
3
+ "version": "7.5.38",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -125,24 +125,6 @@ const EXPECTED_WINDOWS_OPENSSL_HOOKS = {
125
125
  ].join('\n'),
126
126
  }
127
127
 
128
- const WINDOWS_CONDA_ACTIVATE_PATHS = [
129
- path.join('bin', 'miniforge', 'condabin', '_conda_activate.bat'),
130
- path.join(
131
- 'bin',
132
- 'miniforge',
133
- 'Lib',
134
- 'site-packages',
135
- 'conda',
136
- 'shell',
137
- 'condabin',
138
- '_conda_activate.bat'
139
- ),
140
- ]
141
- const WINDOWS_CONDA_ACTIVATE_LEAK_BLOCK = [
142
- ' ) ELSE ENDLOCAL & (',
143
- ' FOR /F "tokens=1,* delims==" %%A IN (%%T) DO (',
144
- ].join('\r\n')
145
-
146
128
  async function seedWindowsOpenSslHooks(root, condaRootName) {
147
129
  const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
148
130
  for (const hookRelativePath of Object.keys(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
@@ -161,15 +143,6 @@ async function seedWindowsOpenSslHooks(root, condaRootName) {
161
143
  }
162
144
  }
163
145
 
164
- async function seedWindowsCondaActivateScripts(root, content = 'corrupted helper\r\n') {
165
- const activatePaths = WINDOWS_CONDA_ACTIVATE_PATHS.map((activatePath) => path.join(root, activatePath))
166
- for (const activatePath of activatePaths) {
167
- await fs.mkdir(path.dirname(activatePath), { recursive: true })
168
- await fs.writeFile(activatePath, content)
169
- }
170
- return activatePaths
171
- }
172
-
173
146
  async function assertWindowsOpenSslHooksPatched(root, condaRootName) {
174
147
  const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
175
148
  for (const [hookRelativePath, expectedContent] of Object.entries(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
@@ -230,24 +203,6 @@ test('Conda init patches Windows OpenSSL hooks for legacy Miniconda roots', asyn
230
203
  await assertWindowsOpenSslHooksPatched(root, 'miniconda')
231
204
  })
232
205
 
233
- test('Conda init replaces Windows CMD activation helper files', async () => {
234
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-replace-'))
235
- await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
236
- const activatePaths = await seedWindowsCondaActivateScripts(root)
237
- const conda = createConda(createKernel(root))
238
-
239
- await conda.init()
240
- await conda.init()
241
-
242
- const contents = await Promise.all(activatePaths.map((activatePath) => fs.readFile(activatePath, 'utf8')))
243
- assert.equal(contents[0], contents[1])
244
- assert.equal(contents[0].includes(WINDOWS_CONDA_ACTIVATE_LEAK_BLOCK), false)
245
- assert.match(contents[0], /ENDLOCAL & @CALL :PINOKIO_APPLY_CONDA_ENV "%%T" "%__conda_tmp%" & @GOTO :EOF/)
246
- assert.match(contents[0], /:PINOKIO_APPLY_CONDA_ENV\r\n@ECHO OFF/)
247
- assert.match(contents[0], /@CALL "%%B" \|\| \(/)
248
- assert.match(contents[0], /@ECHO ON\r\n@EXIT \/B 0/)
249
- })
250
-
251
206
  test('Conda pins Python 3.10.20 consistently across platforms', async () => {
252
207
  assert.equal(PYTHON_INSTALL_SPEC, 'python=3.10.20')
253
208
  assert.equal(WINDOWS_PYTHON_SSL_FIX_SPEC, 'python=3.10.20=*_1_cpython')
@@ -312,9 +267,6 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
312
267
  condaInstall.message[1],
313
268
  'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "conda-libmamba-solver>=25.4.0"'
314
269
  )
315
- for (const activatePath of WINDOWS_CONDA_ACTIVATE_PATHS.map((activatePath) => path.join(root, activatePath))) {
316
- assert.match(await fs.readFile(activatePath, 'utf8'), /:PINOKIO_APPLY_CONDA_ENV/)
317
- }
318
270
  })
319
271
 
320
272
  test('Conda install replaces legacy miniconda with a compatibility alias to miniforge', async () => {
@@ -917,6 +917,38 @@ 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, command)
945
+ assert.equal(prepared.quietCmd, true)
946
+
947
+ assert.deepEqual(shell.prepareCommandExecution({
948
+ message: 'python main.py',
949
+ }, command), { command })
950
+ })
951
+
920
952
  test('rewrite warning is scoped to each user-visible shell flow', async () => {
921
953
  CondaRuntimeGuard.resetNoticeSessionsForTest()
922
954
  const { context, events, root } = createContext()