pinokiod 8.2.5 → 8.2.7

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.
@@ -187,11 +187,8 @@ class Anywhere {
187
187
  async autostart() {
188
188
  // a pid file left behind by a crash or reboot must never point at an unrelated process
189
189
  if (!(await this.daemonAlive())) fs.rmSync(this.pidPath(), { force: true })
190
- if (!(await this.installed())) {
191
- await this.stop()
192
- return false
193
- }
194
190
  if (!(await this.enabled())) return false
191
+ if (!(await this.installed())) return false
195
192
  await this.start()
196
193
  return true
197
194
  }
@@ -202,11 +199,6 @@ class Anywhere {
202
199
  }
203
200
  async _start() {
204
201
  this.error = null
205
- if (!(await this.installed())) {
206
- await this.stop()
207
- this.error = "Update the tailscaled tool before starting Pinokio Anywhere."
208
- throw new Error(this.error)
209
- }
210
202
  await this.loadConfig()
211
203
  await fs.promises.mkdir(this.stateDir(), { recursive: true })
212
204
  const bin = this.binPath()
@@ -11,7 +11,7 @@ const TLS_PACKAGE_MINIMUMS = {
11
11
  // Bootstrap URL and hash: https://discuss.python.org/t/109576
12
12
  const PIP_MIN_VERSION = '26.2.2'
13
13
  const PIP_INSTALL_SPEC = 'pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70'
14
- const PIP_INSTALL_COMMAND = `python -I -m pip --isolated --disable-pip-version-check install --no-index --no-deps "${PIP_INSTALL_SPEC}"`
14
+ const PIP_INSTALL_COMMAND = `python -I -m pip --isolated install --no-index "${PIP_INSTALL_SPEC}"`
15
15
  const TLS_PACKAGE_SPECS = Object.entries(TLS_PACKAGE_MINIMUMS).map(([name, version]) => `${name}>=${version}`)
16
16
  const hasSecureTlsPackages = (versions = {}) => Object.entries(TLS_PACKAGE_MINIMUMS).every(([name, minimum]) => {
17
17
  const version = semver.coerce(versions[name])
@@ -46,9 +46,7 @@ const isExpectedPythonPinned = (platform, version, build) => {
46
46
 
47
47
  module.exports = {
48
48
  CONDA_PIN_VERSION,
49
- TLS_PACKAGE_MINIMUMS,
50
49
  PIP_MIN_VERSION,
51
- PIP_INSTALL_SPEC,
52
50
  PIP_INSTALL_COMMAND,
53
51
  TLS_PACKAGE_SPECS,
54
52
  hasSecureTlsPackages,
@@ -9,7 +9,6 @@ const { buildCondaListFromMeta, managedCondaRuns, hasSecureManagedPip } = requir
9
9
  const {
10
10
  CONDA_PIN_VERSION,
11
11
  PIP_INSTALL_COMMAND,
12
- TLS_PACKAGE_MINIMUMS,
13
12
  TLS_PACKAGE_SPECS,
14
13
  hasSecureTlsPackages,
15
14
  PYTHON_INSTALL_SPEC,
@@ -183,16 +182,14 @@ report_errors: false`)
183
182
  // Refresh a working runtime in place, preserving named app environments.
184
183
  await this.init()
185
184
  const bootstrap = await this.kernel.bin.exec({
186
- message: [PIP_INSTALL_COMMAND, `python -I -m pip --isolated install --no-deps --only-binary=:all: --index-url https://pypi.org/simple "certifi>=${TLS_PACKAGE_MINIMUMS.certifi}"`],
185
+ message: PIP_INSTALL_COMMAND,
187
186
  }, ondata)
188
- if (bootstrap?.error?.length) throw new Error("Conda certificate bootstrap failed; existing environments were preserved.")
187
+ if (bootstrap?.error?.length || !(await hasSecureManagedPip(this.kernel.bin.path(CONDA_ROOT_DIR), this.kernel.platform))) throw new Error("Conda certificate bootstrap failed; existing environments were preserved.")
189
188
  const python = this.kernel.bin.path(CONDA_ROOT_DIR, this.kernel.platform === 'win32' ? 'python.exe' : 'bin/python')
190
- const output = await new Promise((resolve, reject) => execFile(python,
191
- ['-I', '-c', 'import certifi; from importlib.metadata import version; print(certifi.where()); print(version("certifi"))'],
189
+ const caFile = await new Promise((resolve, reject) => execFile(python,
190
+ ['-I', '-c', 'from pip._vendor import certifi; print(certifi.where())'],
192
191
  { env: managedPythonEnv(), windowsHide: true, timeout: 15000 },
193
192
  (error, stdout) => error ? reject(error) : resolve(stdout.trim())))
194
- const [caFile, certifiVersion] = output.split(/[\r\n]+/)
195
- if (!semver.valid(certifiVersion) || !semver.gte(certifiVersion, TLS_PACKAGE_MINIMUMS.certifi)) throw new Error("The certificate bootstrap did not complete.")
196
193
  const result = await this.kernel.bin.exec({
197
194
  message: [`conda install -y --override-channels -c conda-forge "${this.kernel.platform === "win32" ? WINDOWS_PYTHON_SSL_FIX_SPEC : PYTHON_INSTALL_SPEC}" "conda-libmamba-solver>=25.4.0" pip ${TLS_PACKAGE_SPECS.map(spec => `"${spec}"`).join(" ")}`, PIP_INSTALL_COMMAND],
198
195
  env: { CONDA_SSL_VERIFY: caFile },
@@ -18,7 +18,6 @@ class Tailscaled {
18
18
  return this.kernel.bin.path("miniforge", "bin", "tailscaled")
19
19
  }
20
20
  async install(req, ondata) {
21
- if (this.kernel.anywhere) await this.kernel.anywhere.stop()
22
21
  await this.kernel.bin.exec({
23
22
  message: [
24
23
  // a stale libmamba index cache reports the package as missing even
package/kernel/shell.js CHANGED
@@ -21,7 +21,7 @@ const ShellParser = require('./shell_parser')
21
21
  const AnsiStreamTracker = require('./ansi_stream_tracker')
22
22
  const ShellStateSync = require('./shell_state_sync')
23
23
  const ShellRunTemplate = require('./api/shell_run_template')
24
- const { PYTHON_INSTALL_SPEC, PIP_INSTALL_COMMAND, PIP_MIN_VERSION } = require('./bin/conda-pins')
24
+ const { PYTHON_INSTALL_SPEC, PIP_MIN_VERSION } = require('./bin/conda-pins')
25
25
  const CondaRuntimeGuard = require('./shell_conda_runtime_guard')
26
26
  const home = os.homedir()
27
27
  const WINDOWS_CMD_CONDA_COMMAND_PATTERN = /(?:^|[&|()]\s*)@?\s*(?:call\s+)?(?:"[^"]*[\\/]conda(?:\.bat|\.exe)?"|(?:[a-z]:)?[^&|()\s"]*[\\/]conda(?:\.bat|\.exe)?|conda(?:\.bat|\.exe)?)\s+/i
@@ -1399,16 +1399,13 @@ class Shell {
1399
1399
  deactivate_path,
1400
1400
  // timeout,
1401
1401
  activate_command,
1402
- this.isPowerShell()
1403
- ? `${PIP_INSTALL_COMMAND}; if ($LASTEXITCODE -ne 0) { exit 1 }`
1404
- : `${PIP_INSTALL_COMMAND} || exit 1`,
1405
1402
  // timeout,
1406
1403
  ]
1407
1404
  }
1408
1405
  }
1409
1406
  if (!use_uv) {
1410
- const checkPip = `python -I -c "from importlib.metadata import distributions; assert all(tuple(map(int, d.version.split('.')[:3])) >= (${PIP_MIN_VERSION.split(".").join(", ")}) for d in distributions(name='pip')), 'Update pip in this app environment before running it.'"`
1411
- venv_activation.push(this.isPowerShell() ? `${checkPip}; if ($LASTEXITCODE -ne 0) { exit 1 }` : `${checkPip} || exit 1`)
1407
+ const updatePip = `uv pip install --exclude-newer-package pip=false "pip>=${PIP_MIN_VERSION}"`
1408
+ venv_activation.push(this.isPowerShell() ? `${updatePip}; if ($LASTEXITCODE -ne 0) { exit 1 }` : `${updatePip} || exit 1`)
1412
1409
  }
1413
1410
  } else {
1414
1411
  venv_activation = []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "8.2.5",
3
+ "version": "8.2.7",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -441,18 +441,3 @@ test('sign-out resets the login state, and a link the server rejected is replace
441
441
  fs.rmSync(tmp, { recursive: true, force: true })
442
442
  }
443
443
  })
444
-
445
-
446
- test('old Tailscale daemons are stopped and cannot be adopted through direct start', async t => {
447
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-anywhere-version-'))
448
- t.after(() => fs.rmSync(root, { recursive: true, force: true }))
449
- const anywhere = new Anywhere(fakeKernel(root, { installed: false }))
450
- let stopped = 0
451
- anywhere.daemonAlive = async () => true
452
- anywhere.enabled = async () => true
453
- anywhere.stop = async () => { stopped++ }
454
- assert.equal(await anywhere.autostart(), false)
455
- assert.equal(stopped, 1)
456
- await assert.rejects(anywhere.start(), /Update the tailscaled tool/)
457
- assert.equal(stopped, 2)
458
- })
@@ -14,9 +14,7 @@ const {
14
14
  WINDOWS_PYTHON_SSL_FIX_SPEC,
15
15
  isExpectedCondaPinned,
16
16
  isExpectedPythonPinned,
17
- TLS_PACKAGE_MINIMUMS,
18
17
  TLS_PACKAGE_SPECS,
19
- PIP_INSTALL_SPEC,
20
18
  PIP_MIN_VERSION,
21
19
  } = require('../kernel/bin/conda-pins')
22
20
 
@@ -26,7 +24,8 @@ test.beforeEach(t => {
26
24
  if (args[0] !== '-I' || args[1] !== '-c') return realExecFile(binary, args, options, done)
27
25
  const prefix = path.basename(binary) === 'python.exe' ? path.dirname(binary) : path.dirname(path.dirname(binary))
28
26
  if (args[2].includes('certifi.where()')) {
29
- done(null, path.join(prefix, 'certificates.pem') + '\n2026.10.19\n')
27
+ assert.match(args[2], /^from pip\._vendor import certifi;/)
28
+ done(null, path.join(prefix, 'certificates.pem') + '\n')
30
29
  return
31
30
  }
32
31
  fs.readFile(path.join(prefix, 'test-pip-version'), 'utf8').then(
@@ -73,8 +72,8 @@ async function writeHealthyCondaMeta(root, platform = 'win32', condaVersion = CO
73
72
  await writeCondaMeta(root, 'conda', condaVersion, 'py310')
74
73
  await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
75
74
  await writeCondaMeta(root, 'python', '3.10.20', platform === 'win32' ? 'h4de0772_1_cpython' : 'cpython')
76
- for (const [name, version] of Object.entries(TLS_PACKAGE_MINIMUMS)) {
77
- await writeCondaMeta(root, name, version)
75
+ for (const name of ['ca-certificates', 'certifi']) {
76
+ await writeCondaMeta(root, name, '2026.10.19')
78
77
  }
79
78
  await fs.writeFile(path.join(root, 'bin/miniforge/test-pip-version'), PIP_MIN_VERSION)
80
79
  }
@@ -282,7 +281,7 @@ test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () =>
282
281
  assert.equal(await pathExists(path.join(root, 'bin', 'miniconda')), true)
283
282
  const condaInstall = calls.find((call) => Array.isArray(call.message))
284
283
  assert.ok(condaInstall)
285
- assert.equal(condaInstall.message[2], `python -I -m pip --isolated --disable-pip-version-check install --no-index --no-deps "${PIP_INSTALL_SPEC}"`)
284
+ assert.match(condaInstall.message[2], /^python -I -m pip --isolated install --no-index "pip @ https:\/\/files\.pythonhosted\.org\/packages\/[a-f0-9/]+\/pip-26\.2\.2-py3-none-any\.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"$/)
286
285
  assert.equal(
287
286
  condaInstall.message[1],
288
287
  'conda install -y --override-channels -c conda-forge "conda=26.5.3" "python=3.10.20" "conda-libmamba-solver>=25.4.0" pip "ca-certificates>=2026.10.19" "certifi>=2026.10.19"'
@@ -620,8 +619,8 @@ test('stale certificate packages trigger the existing Conda setup checks', async
620
619
  t.after(() => fs.rm(root, { recursive: true, force: true }))
621
620
  await writeHealthyCondaMeta(root, 'darwin')
622
621
  await writeFakeManagedConda(root, 'darwin')
623
- for (const [name, version] of Object.entries(TLS_PACKAGE_MINIMUMS)) {
624
- await fs.rm(path.join(root, 'bin/miniforge/conda-meta', `${name}-${version}.json`))
622
+ for (const name of ['ca-certificates', 'certifi']) {
623
+ await fs.rm(path.join(root, 'bin/miniforge/conda-meta', `${name}-2026.10.19.json`))
625
624
  await writeCondaMeta(root, name, '2026.4.22')
626
625
  }
627
626
  assert.equal(await createConda(createKernel(root, 'darwin')).check(), false)
@@ -655,7 +654,7 @@ test('old pip cannot pass Conda readiness even with current CA packages', async
655
654
  assert.equal(bin.correct_conda, false)
656
655
  })
657
656
 
658
- test('certificate refresh preserves named environments and uses the new CA bundle', async t => {
657
+ test("certificate refresh preserves named environments and uses pip's bundled CA", async t => {
659
658
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-ca-refresh-'))
660
659
  t.after(() => fs.rm(root, { recursive: true, force: true }))
661
660
  await writeHealthyCondaMeta(root, 'darwin')
@@ -670,6 +669,7 @@ test('certificate refresh preserves named environments and uses the new CA bundl
670
669
  await createConda(kernel).install({}, () => {})
671
670
  assert.equal(await fs.readFile(marker, 'utf8'), 'existing app')
672
671
  assert.equal(calls.length, 2)
672
+ assert.match(calls[0].message, /^python -I -m pip --isolated install --no-index /)
673
673
  assert.equal(calls[1].env.CONDA_SSL_VERIFY, path.join(root, 'bin/miniforge/certificates.pem'))
674
674
  })
675
675
 
@@ -679,9 +679,12 @@ test('failed pip bootstrap stops certificate refresh without replacing environme
679
679
  await writeHealthyCondaMeta(root, 'darwin')
680
680
  await writeFakeManagedConda(root, 'darwin')
681
681
  const kernel = createKernel(root, 'darwin')
682
- kernel.bin.exec = async () => ({ error: ['ERROR: hash verification failed'] })
683
682
  kernel.bin.download = async () => assert.fail('must preserve the runtime')
684
- await assert.rejects(createConda(kernel).install({}, () => {}), /bootstrap failed/)
683
+ await fs.writeFile(path.join(root, 'bin/miniforge/test-pip-version'), '26.2.1')
684
+ for (const response of [{ error: ['ERROR: hash verification failed'] }, {}]) {
685
+ kernel.bin.exec = async () => response
686
+ await assert.rejects(createConda(kernel).install({}, () => {}), /bootstrap failed/)
687
+ }
685
688
  assert.equal(await pathExists(path.join(root, 'bin/miniforge/conda-meta')), true)
686
689
  })
687
690
 
@@ -3,9 +3,11 @@ const fs = require('node:fs/promises')
3
3
  const os = require('node:os')
4
4
  const path = require('node:path')
5
5
  const test = require('node:test')
6
+ const { spawnSync } = require('node:child_process')
6
7
 
7
8
  const ShellRunTemplate = require('../kernel/api/shell_run_template')
8
9
  const Shell = require('../kernel/shell')
10
+ const { PIP_MIN_VERSION } = require('../kernel/bin/conda-pins')
9
11
 
10
12
  function createKernel(root = process.cwd()) {
11
13
  return {
@@ -379,47 +381,86 @@ test('redactEnvArgs summarizes protected argv env values', () => {
379
381
  })
380
382
 
381
383
 
382
- test('new venv bootstrap failure prevents app commands even with chain:*', { skip: process.platform === 'win32' }, async t => {
383
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-venv-bootstrap-'))
384
+ test('venvs preserve creation behavior and enforce the pip minimum across shells', async t => {
385
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-venv-selection-'))
384
386
  t.after(() => fs.rm(root, { recursive: true, force: true }))
385
- const kernel = createKernel(root)
386
- kernel.bin = { path: (...parts) => path.join(root, 'bin', ...parts) }
387
- const shell = createShell(kernel)
388
- shell.shell = 'bash'
389
- shell.env = {}
390
- shell.conda_hook = () => ':'
391
- const fakeBin = path.join(root, 'fake-bin')
392
- await fs.mkdir(fakeBin)
393
- await fs.writeFile(path.join(fakeBin, 'python'), '#!/bin/sh\nif [ "$1" = "-m" ]; then mkdir -p "$3/bin"; touch "$3/bin/activate"; exit 0; fi\nexit 19\n', { mode: 0o755 })
394
- const params = await shell.activate({ path: root, conda: { skip: true }, venv: 'env', chain: '*', message: `touch '${root}/app-ran'` })
395
- const result = require('node:child_process').spawnSync('bash', ['--noprofile', '--norc', '-c', shell.build(params)], { env: { ...process.env, PATH: fakeBin + ':' + process.env.PATH }, encoding: 'utf8' })
396
- assert.notEqual(result.status, 0, result.stderr)
397
- assert.equal(await fs.access(path.join(root, 'app-ran')).then(() => true, () => false), false)
398
- })
399
-
400
- test('existing app environments do not run the creation-only pip bootstrap', async t => {
401
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-existing-env-'))
402
- t.after(() => fs.rm(root, { recursive: true, force: true }))
403
- await fs.mkdir(path.join(root, 'env'))
404
- const shell = createShell(createKernel(root))
405
- shell.shell = 'bash'
406
- const params = await shell.activate({ path: root, conda: { skip: true }, venv: 'env', message: 'echo app' })
407
- assert.doesNotMatch(shell.build(params), /pip.*install/)
387
+ const cases = [
388
+ { venv: 'default-env' },
389
+ { venv: { path: 'object-default-env' } },
390
+ { venv: 'explicit-env', venv_python: '3.10', version: '3.10' },
391
+ { venv: { path: 'object-env', python: '3.11' }, version: '3.11' },
392
+ { venv: 'existing-env', existing: true },
393
+ { venv: 'existing-env', venv_python: '3.10', version: '3.10', existing: true },
394
+ {},
395
+ ]
396
+ await fs.mkdir(path.join(root, 'existing-env'))
397
+ for (const [platform, shellName] of [
398
+ ['linux', 'bash'],
399
+ ['darwin', 'bash'],
400
+ ['win32', 'cmd.exe'],
401
+ ['win32', 'powershell'],
402
+ ['win32', 'bash'],
403
+ ]) {
404
+ for (const scenario of cases) {
405
+ const shell = createShell(createKernel(root))
406
+ shell.platform = platform
407
+ shell.shell = shellName
408
+ const params = await shell.activate({
409
+ path: root,
410
+ conda: { skip: true },
411
+ venv: scenario.venv,
412
+ venv_python: scenario.venv_python,
413
+ message: 'echo PINOKIO_REQUIREMENTS_RAN',
414
+ })
415
+ const command = shell.build(params)
416
+ assert.doesNotMatch(command, /--seed|--no-config|--python-preference|python -I|files\.pythonhosted\.org/)
417
+ assert.match(command, /echo PINOKIO_REQUIREMENTS_RAN$/)
418
+ if (!scenario.venv || scenario.existing) {
419
+ assert.doesNotMatch(command, /python -m venv|uv venv/)
420
+ } else if (scenario.version) {
421
+ assert.ok(command.includes(` --python ${scenario.version}`), command)
422
+ assert.doesNotMatch(command, /python -m venv/)
423
+ } else {
424
+ assert.match(command, /python -m venv /)
425
+ }
426
+ const updatePip = `uv pip install --exclude-newer-package pip=false "pip>=${PIP_MIN_VERSION}"`
427
+ if (scenario.venv && !scenario.version) {
428
+ assert.equal(command.split(updatePip).length - 1, 1)
429
+ assert.ok(command.indexOf(updatePip) > command.lastIndexOf('activate'))
430
+ assert.ok(command.includes(shell.isPowerShell()
431
+ ? `${updatePip}; if ($LASTEXITCODE -ne 0) { exit 1 }`
432
+ : `${updatePip} || exit 1`))
433
+ } else {
434
+ assert.doesNotMatch(command, /uv pip install/)
435
+ }
436
+ assert.equal(shell.env.UV_PYTHON_PREFERENCE, 'only-managed')
437
+ }
438
+ }
408
439
  })
409
440
 
410
441
 
411
- test('existing venv with outdated pip cannot bypass the guard on a retry', { skip: process.platform === 'win32' }, async t => {
412
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-venv-retry-'))
442
+ test('failed pip updates stop the application even with an unconditional command chain', async t => {
443
+ if (process.platform === 'win32') return t.skip('executes the POSIX shell failure guard')
444
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-pip-failure-'))
413
445
  t.after(() => fs.rm(root, { recursive: true, force: true }))
414
- await fs.mkdir(path.join(root, 'env/bin'), { recursive: true })
415
- await fs.writeFile(path.join(root, 'env/bin/activate'), '')
416
- const fakeBin = path.join(root, 'fake-bin')
417
- await fs.mkdir(fakeBin)
418
- await fs.writeFile(path.join(fakeBin, 'python'), '#!/bin/sh\nexit 19\n', { mode: 0o755 })
446
+ const binDir = path.join(root, 'bin')
447
+ await fs.mkdir(path.join(root, 'env', 'bin'), { recursive: true })
448
+ await fs.mkdir(binDir)
449
+ await fs.writeFile(path.join(root, 'env', 'bin', 'activate'), 'true\n')
450
+ await fs.writeFile(path.join(binDir, 'uv'), '#!/bin/sh\nexit "$PINOKIO_TEST_UV_EXIT_CODE"\n', { mode: 0o755 })
419
451
  const shell = createShell(createKernel(root))
420
452
  shell.shell = 'bash'
421
- const params = await shell.activate({ path: root, conda: { skip: true }, venv: 'env', chain: '*', message: `touch '${root}/app-ran'` })
422
- const result = require('node:child_process').spawnSync('bash', ['--noprofile', '--norc', '-c', shell.build(params)], { env: { ...process.env, PATH: fakeBin + ':' + process.env.PATH }, encoding: 'utf8' })
423
- assert.notEqual(result.status, 0, result.stderr)
424
- assert.equal(await fs.access(path.join(root, 'app-ran')).then(() => true, () => false), false)
453
+ const params = await shell.activate({
454
+ path: root, conda: { skip: true }, venv: 'env',
455
+ chain: '*', message: 'echo PINOKIO_APPLICATION_RAN',
456
+ })
457
+ const command = shell.build(params)
458
+ for (const exitCode of [0, 7]) {
459
+ const result = spawnSync('/bin/bash', ['-c', command], {
460
+ encoding: 'utf8',
461
+ env: { ...process.env, PATH: `${binDir}${path.delimiter}${process.env.PATH}`, PINOKIO_TEST_UV_EXIT_CODE: String(exitCode) },
462
+ })
463
+ assert.equal(result.status, exitCode ? 1 : 0, result.stderr)
464
+ assert.equal(result.stdout.includes('PINOKIO_APPLICATION_RAN'), exitCode === 0)
465
+ }
425
466
  })