pinokiod 7.5.9 → 7.5.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.
- package/kernel/api/shell/index.js +1 -4
- package/kernel/index.js +1 -1
- package/kernel/shell.js +3 -2
- package/kernel/shell_conda_runtime_guard.js +263 -105
- package/kernel/shells.js +1 -1
- package/package.json +1 -1
- package/release_notes/8.0.0/assets/app-ask-ai-drawer.png +0 -0
- package/release_notes/8.0.0/assets/app-detail-header-actions.png +0 -0
- package/release_notes/8.0.0/assets/autolaunch-dependencies.png +0 -0
- package/release_notes/8.0.0/assets/connect-huggingface-login.png +0 -0
- package/release_notes/8.0.0/assets/home-app-actions.png +0 -0
- package/release_notes/8.0.0/assets/home-sidebar-apps.png +0 -0
- package/release_notes/8.0.0/assets/logs-terminal-reporting.png +0 -0
- package/release_notes/8.0.0/assets/mobile-home-footer-nav.png +0 -0
- package/release_notes/8.0.0/assets/network-phone-access.png +0 -0
- package/release_notes/8.0.0/assets/plugins-agent-tools.png +0 -0
- package/release_notes/8.0.0/assets/skills-management.png +0 -0
- package/release_notes/8.0.0/assets/tools-runtime-management.png +0 -0
- package/release_notes/8.0.0/ui.md +91 -0
- package/server/public/logs.js +65 -5
- package/server/public/style.css +2 -2
- package/server/views/app.ejs +10 -1
- package/server/views/index.ejs +218 -14
- package/test/logs-ask-ai.test.js +245 -0
- package/test/shell-api.test.js +7 -6
- package/test/shell-conda-runtime-guard.test.js +477 -77
|
@@ -180,10 +180,7 @@ class Shell {
|
|
|
180
180
|
req.params.rows = req.client.rows
|
|
181
181
|
req.params.cols = req.client.cols
|
|
182
182
|
}
|
|
183
|
-
|
|
184
|
-
configurable: true,
|
|
185
|
-
value: true,
|
|
186
|
-
})
|
|
183
|
+
options[CondaRuntimeGuard.SHELL_RUN_GUARD_OPTION] = true
|
|
187
184
|
let response = await kernel.shell.run(req.params, options, async (stream, type) => {
|
|
188
185
|
// process.stdout.write(stream.raw)
|
|
189
186
|
ondata(stream, type)
|
package/kernel/index.js
CHANGED
package/kernel/shell.js
CHANGED
|
@@ -390,8 +390,9 @@ class Shell {
|
|
|
390
390
|
}
|
|
391
391
|
return chunks.join(" ")
|
|
392
392
|
}
|
|
393
|
-
async start(params, ondata) {
|
|
393
|
+
async start(params, ondata, options = {}) {
|
|
394
394
|
this.ondata = ondata
|
|
395
|
+
this.condaRuntimeGuard = !!(options && options[CondaRuntimeGuard.SHELL_RUN_GUARD_OPTION])
|
|
395
396
|
if (this.nudgeRestoreTimer) {
|
|
396
397
|
clearTimeout(this.nudgeRestoreTimer)
|
|
397
398
|
this.nudgeRestoreTimer = null
|
|
@@ -1354,7 +1355,7 @@ class Shell {
|
|
|
1354
1355
|
? this.kernel.bin.path("miniforge")
|
|
1355
1356
|
: this.kernel.path("bin/miniforge")
|
|
1356
1357
|
const flowSessionKey = [this.id, this.start_time].filter(Boolean).join(':')
|
|
1357
|
-
if (
|
|
1358
|
+
if (this.condaRuntimeGuard) {
|
|
1358
1359
|
CondaRuntimeGuard.applyCondaRuntimeGuard(params, {
|
|
1359
1360
|
cwd: params.path,
|
|
1360
1361
|
managedBasePrefix,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const unparse = require('yargs-unparser-custom-flag')
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const SHELL_RUN_GUARD_OPTION = 'condaRuntimeGuard'
|
|
5
5
|
const noticeSessions = new Set()
|
|
6
6
|
|
|
7
7
|
const MUTATING_COMMANDS = new Set([
|
|
@@ -24,6 +24,7 @@ const PROTECTED_PACKAGES = new Set([
|
|
|
24
24
|
'python',
|
|
25
25
|
'conda-libmamba-solver',
|
|
26
26
|
])
|
|
27
|
+
const PROTECTED_PACKAGE_NAMES = Array.from(PROTECTED_PACKAGES).sort((a, b) => b.length - a.length)
|
|
27
28
|
|
|
28
29
|
const VALUE_FLAGS = new Set([
|
|
29
30
|
'-c',
|
|
@@ -34,9 +35,15 @@ const VALUE_FLAGS = new Set([
|
|
|
34
35
|
'--name',
|
|
35
36
|
'-p',
|
|
36
37
|
'--prefix',
|
|
38
|
+
'--revision',
|
|
37
39
|
'--solver',
|
|
38
40
|
])
|
|
39
41
|
|
|
42
|
+
const FILE_FLAGS = new Set([
|
|
43
|
+
'-f',
|
|
44
|
+
'--file',
|
|
45
|
+
])
|
|
46
|
+
|
|
40
47
|
function pathApi(platform) {
|
|
41
48
|
return platform === 'win32' ? path.win32 : path.posix
|
|
42
49
|
}
|
|
@@ -217,12 +224,19 @@ function splitShellSegments(input, context = {}) {
|
|
|
217
224
|
return parts
|
|
218
225
|
}
|
|
219
226
|
|
|
220
|
-
function getExecutableToken(tokens) {
|
|
221
|
-
let index =
|
|
227
|
+
function getExecutableToken(tokens, startIndex = 0, context = {}) {
|
|
228
|
+
let index = startIndex
|
|
222
229
|
while (index < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[index])) {
|
|
223
230
|
index++
|
|
224
231
|
}
|
|
225
|
-
|
|
232
|
+
const token = String(tokens[index] || '')
|
|
233
|
+
const lower = token.toLowerCase()
|
|
234
|
+
const shellName = String(context.shellName || '').toLowerCase()
|
|
235
|
+
const platform = context.platform || process.platform
|
|
236
|
+
if (
|
|
237
|
+
(token === '&' && (shellName.includes('powershell') || shellName.includes('pwsh')))
|
|
238
|
+
|| (lower === 'call' && (platform === 'win32' || shellName.includes('cmd')))
|
|
239
|
+
) {
|
|
226
240
|
index++
|
|
227
241
|
}
|
|
228
242
|
return { executable: tokens[index] || '', index }
|
|
@@ -245,22 +259,60 @@ function isCondaExecutable(executable, context) {
|
|
|
245
259
|
return base === 'conda' || base === 'conda.exe' || base === 'conda.bat' || base === 'conda.cmd'
|
|
246
260
|
}
|
|
247
261
|
|
|
262
|
+
function isPythonExecutable(executable, context) {
|
|
263
|
+
const platform = context.platform || process.platform
|
|
264
|
+
const base = executableBasename(executable, platform)
|
|
265
|
+
return /^python(?:\d+(?:\.\d+)?)?(?:\.exe)?$/.test(base)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function isCondaModuleToken(token) {
|
|
269
|
+
return String(token || '').toLowerCase() === 'conda'
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function findCondaInvocation(tokens, context, startIndex = 0) {
|
|
273
|
+
const { executable, index: executableIndex } = getExecutableToken(tokens, startIndex, context)
|
|
274
|
+
if (isCondaExecutable(executable, context)) {
|
|
275
|
+
return { executableIndex, argsStart: executableIndex + 1 }
|
|
276
|
+
}
|
|
277
|
+
if (isPythonExecutable(executable, context)) {
|
|
278
|
+
if (tokens[executableIndex + 1] === '-m' && isCondaModuleToken(tokens[executableIndex + 2])) {
|
|
279
|
+
return { executableIndex, argsStart: executableIndex + 3 }
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return null
|
|
283
|
+
}
|
|
284
|
+
|
|
248
285
|
function resolveWrapperTarget(params, context) {
|
|
249
286
|
const conda = params ? params.conda : undefined
|
|
250
287
|
const platform = context.platform || process.platform
|
|
251
288
|
const cwd = context.cwd || (params && params.path) || process.cwd()
|
|
289
|
+
const managedBasePrefix = context.managedBasePrefix || ''
|
|
252
290
|
if (!conda) {
|
|
253
|
-
return { kind: 'managed-base', source: 'wrapper', prefix:
|
|
291
|
+
return { kind: 'managed-base', source: 'wrapper', prefix: managedBasePrefix }
|
|
292
|
+
}
|
|
293
|
+
if (conda && conda.skip === true) {
|
|
294
|
+
return { kind: 'unknown', source: 'wrapper-skip' }
|
|
254
295
|
}
|
|
255
296
|
if (typeof conda === 'string') {
|
|
256
|
-
|
|
297
|
+
const prefix = resolvePathFrom(cwd, conda, platform)
|
|
298
|
+
if (managedBasePrefix && isSamePath(prefix, managedBasePrefix, platform)) {
|
|
299
|
+
return { kind: 'managed-base', source: 'wrapper', prefix: managedBasePrefix }
|
|
300
|
+
}
|
|
301
|
+
return { kind: 'app-env', source: 'wrapper', prefix }
|
|
302
|
+
}
|
|
303
|
+
if (conda && conda.activate === 'minimal' && (conda.path || conda.name)) {
|
|
304
|
+
return { kind: 'managed-base', source: 'wrapper', prefix: managedBasePrefix }
|
|
257
305
|
}
|
|
258
306
|
if (conda && conda.path) {
|
|
259
|
-
|
|
307
|
+
const prefix = resolvePathFrom(cwd, conda.path, platform)
|
|
308
|
+
if (managedBasePrefix && isSamePath(prefix, managedBasePrefix, platform)) {
|
|
309
|
+
return { kind: 'managed-base', source: 'wrapper', prefix: managedBasePrefix }
|
|
310
|
+
}
|
|
311
|
+
return { kind: 'app-env', source: 'wrapper', prefix }
|
|
260
312
|
}
|
|
261
313
|
if (conda && conda.name) {
|
|
262
314
|
if (conda.name === 'base') {
|
|
263
|
-
return { kind: 'managed-base', source: 'wrapper', prefix:
|
|
315
|
+
return { kind: 'managed-base', source: 'wrapper', prefix: managedBasePrefix }
|
|
264
316
|
}
|
|
265
317
|
return {
|
|
266
318
|
kind: 'app-env',
|
|
@@ -271,8 +323,8 @@ function resolveWrapperTarget(params, context) {
|
|
|
271
323
|
return { kind: 'unknown', source: 'wrapper' }
|
|
272
324
|
}
|
|
273
325
|
|
|
274
|
-
function parseCommandTarget(tokens,
|
|
275
|
-
for (let i =
|
|
326
|
+
function parseCommandTarget(tokens, argsStart) {
|
|
327
|
+
for (let i = argsStart; i < tokens.length; i++) {
|
|
276
328
|
const token = tokens[i]
|
|
277
329
|
if (token === '-p' || token === '--prefix') {
|
|
278
330
|
return { type: 'prefix', value: tokens[i + 1] || '' }
|
|
@@ -309,13 +361,13 @@ function refineTargetFromCommandArgs(target, commandTarget, params, context) {
|
|
|
309
361
|
return { kind: 'app-env', source: 'command-target', prefix: resolved }
|
|
310
362
|
}
|
|
311
363
|
|
|
312
|
-
function resolveTarget(tokens,
|
|
313
|
-
const wrapperTarget = resolveWrapperTarget(params, context)
|
|
314
|
-
return refineTargetFromCommandArgs(wrapperTarget, parseCommandTarget(tokens,
|
|
364
|
+
function resolveTarget(tokens, argsStart, params, context, wrapperTargetOverride) {
|
|
365
|
+
const wrapperTarget = wrapperTargetOverride || resolveWrapperTarget(params, context)
|
|
366
|
+
return refineTargetFromCommandArgs(wrapperTarget, parseCommandTarget(tokens, argsStart), params, context)
|
|
315
367
|
}
|
|
316
368
|
|
|
317
|
-
function findSubcommand(tokens,
|
|
318
|
-
for (let i =
|
|
369
|
+
function findSubcommand(tokens, argsStart) {
|
|
370
|
+
for (let i = argsStart; i < tokens.length; i++) {
|
|
319
371
|
const token = String(tokens[i] || '').toLowerCase()
|
|
320
372
|
if (!token || token.startsWith('-')) {
|
|
321
373
|
if (VALUE_FLAGS.has(token)) {
|
|
@@ -338,13 +390,8 @@ function findSubcommand(tokens, executableIndex) {
|
|
|
338
390
|
return null
|
|
339
391
|
}
|
|
340
392
|
|
|
341
|
-
function
|
|
342
|
-
return subcommand && (
|
|
343
|
-
subcommand.name === 'create'
|
|
344
|
-
|| subcommand.name === 'env create'
|
|
345
|
-
|| subcommand.name === 'env remove'
|
|
346
|
-
|| subcommand.name === 'env update'
|
|
347
|
-
)
|
|
393
|
+
function isCreateSubcommand(subcommand) {
|
|
394
|
+
return subcommand && (subcommand.name === 'create' || subcommand.name === 'env create')
|
|
348
395
|
}
|
|
349
396
|
|
|
350
397
|
function normalizePackageSpec(token) {
|
|
@@ -355,12 +402,65 @@ function normalizePackageSpec(token) {
|
|
|
355
402
|
return scoped.split(/[=<>!~]/)[0].trim().toLowerCase()
|
|
356
403
|
}
|
|
357
404
|
|
|
405
|
+
function protectedPackageFromArchive(token, context) {
|
|
406
|
+
if (!token || token.startsWith('-')) {
|
|
407
|
+
return ''
|
|
408
|
+
}
|
|
409
|
+
const platform = context.platform || process.platform
|
|
410
|
+
const base = executableBasename(token, platform)
|
|
411
|
+
const lower = base.toLowerCase()
|
|
412
|
+
const ext = lower.endsWith('.tar.bz2')
|
|
413
|
+
? '.tar.bz2'
|
|
414
|
+
: lower.endsWith('.conda')
|
|
415
|
+
? '.conda'
|
|
416
|
+
: ''
|
|
417
|
+
if (!ext) {
|
|
418
|
+
return ''
|
|
419
|
+
}
|
|
420
|
+
for (const pkg of PROTECTED_PACKAGE_NAMES) {
|
|
421
|
+
const prefix = `${pkg}-`
|
|
422
|
+
if (!lower.startsWith(prefix)) {
|
|
423
|
+
continue
|
|
424
|
+
}
|
|
425
|
+
const body = lower.slice(prefix.length, -ext.length)
|
|
426
|
+
const buildSeparator = body.indexOf('-')
|
|
427
|
+
if (/^\d/.test(body) && buildSeparator > 0 && buildSeparator < body.length - 1) {
|
|
428
|
+
return pkg
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return ''
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function hasFlag(tokens, startIndex, flags) {
|
|
435
|
+
return tokens.slice(startIndex).some((token) => {
|
|
436
|
+
if (flags.has(token)) {
|
|
437
|
+
return true
|
|
438
|
+
}
|
|
439
|
+
if (String(token).startsWith('--') && String(token).includes('=')) {
|
|
440
|
+
return flags.has(String(token).slice(0, String(token).indexOf('=')))
|
|
441
|
+
}
|
|
442
|
+
return false
|
|
443
|
+
})
|
|
444
|
+
}
|
|
445
|
+
|
|
358
446
|
function hasAllFlag(tokens, startIndex) {
|
|
359
|
-
return tokens
|
|
447
|
+
return hasFlag(tokens, startIndex, new Set(['--all']))
|
|
360
448
|
}
|
|
361
449
|
|
|
362
450
|
function hasDryRunFlag(tokens, startIndex) {
|
|
363
|
-
return tokens
|
|
451
|
+
return hasFlag(tokens, startIndex, new Set(['--dry-run', '-d']))
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function hasUpdateAllFlag(tokens, startIndex) {
|
|
455
|
+
return hasFlag(tokens, startIndex, new Set(['--update-all', '--all']))
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function hasRevisionFlag(tokens, startIndex) {
|
|
459
|
+
return hasFlag(tokens, startIndex, new Set(['--revision']))
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function hasFileFlag(tokens, startIndex) {
|
|
463
|
+
return hasFlag(tokens, startIndex, FILE_FLAGS)
|
|
364
464
|
}
|
|
365
465
|
|
|
366
466
|
function explicitPackageNames(tokens, startIndex) {
|
|
@@ -388,40 +488,145 @@ function explicitPackageNames(tokens, startIndex) {
|
|
|
388
488
|
return packages
|
|
389
489
|
}
|
|
390
490
|
|
|
391
|
-
function protectedPackageFromSubcommand(tokens, subcommand) {
|
|
491
|
+
function protectedPackageFromSubcommand(tokens, subcommand, context) {
|
|
392
492
|
const packages = explicitPackageNames(tokens, subcommand.endIndex + 1)
|
|
393
|
-
|
|
493
|
+
const explicitPackage = packages.find((pkg) => PROTECTED_PACKAGES.has(pkg))
|
|
494
|
+
if (explicitPackage) {
|
|
495
|
+
return explicitPackage
|
|
496
|
+
}
|
|
497
|
+
if (subcommand.name !== 'install') {
|
|
498
|
+
return ''
|
|
499
|
+
}
|
|
500
|
+
for (let i = subcommand.endIndex + 1; i < tokens.length; i++) {
|
|
501
|
+
const token = tokens[i]
|
|
502
|
+
if (!token) {
|
|
503
|
+
continue
|
|
504
|
+
}
|
|
505
|
+
if (token.startsWith('--') && token.includes('=')) {
|
|
506
|
+
continue
|
|
507
|
+
}
|
|
508
|
+
if (VALUE_FLAGS.has(token)) {
|
|
509
|
+
i++
|
|
510
|
+
continue
|
|
511
|
+
}
|
|
512
|
+
if (token.startsWith('-')) {
|
|
513
|
+
continue
|
|
514
|
+
}
|
|
515
|
+
const archivePackage = protectedPackageFromArchive(token, context)
|
|
516
|
+
if (archivePackage) {
|
|
517
|
+
return archivePackage
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return ''
|
|
394
521
|
}
|
|
395
522
|
|
|
396
523
|
function isListedBroadMutation(tokens, subcommand, target) {
|
|
397
|
-
|
|
524
|
+
const startIndex = subcommand.endIndex + 1
|
|
525
|
+
if ((subcommand.name === 'update' || subcommand.name === 'upgrade') && hasUpdateAllFlag(tokens, subcommand.endIndex + 1)) {
|
|
526
|
+
return true
|
|
527
|
+
}
|
|
528
|
+
if (subcommand.name === 'install' && (hasUpdateAllFlag(tokens, startIndex) || hasRevisionFlag(tokens, startIndex))) {
|
|
529
|
+
return true
|
|
530
|
+
}
|
|
531
|
+
if ((subcommand.name === 'remove' || subcommand.name === 'uninstall') && hasAllFlag(tokens, startIndex)) {
|
|
532
|
+
return true
|
|
533
|
+
}
|
|
534
|
+
if ((subcommand.name === 'create' || subcommand.name === 'env create') && target.source === 'command-target') {
|
|
535
|
+
return true
|
|
536
|
+
}
|
|
537
|
+
if (subcommand.name === 'env remove' && target.source === 'command-target') {
|
|
538
|
+
return true
|
|
539
|
+
}
|
|
540
|
+
if (subcommand.name === 'env update') {
|
|
541
|
+
return true
|
|
542
|
+
}
|
|
543
|
+
if (['install', 'update', 'upgrade'].includes(subcommand.name) && hasFileFlag(tokens, startIndex)) {
|
|
398
544
|
return true
|
|
399
545
|
}
|
|
400
|
-
return
|
|
546
|
+
return false
|
|
401
547
|
}
|
|
402
548
|
|
|
403
|
-
function
|
|
404
|
-
|
|
405
|
-
|
|
549
|
+
function parseCondaRun(tokens, argsStart, params, context, wrapperTarget) {
|
|
550
|
+
let commandTarget = null
|
|
551
|
+
let commandStart = argsStart + 1
|
|
552
|
+
for (let i = argsStart + 1; i < tokens.length; i++) {
|
|
553
|
+
const token = tokens[i]
|
|
554
|
+
if (token === '-p' || token === '--prefix') {
|
|
555
|
+
commandTarget = { type: 'prefix', value: tokens[i + 1] || '' }
|
|
556
|
+
i++
|
|
557
|
+
commandStart = i + 1
|
|
558
|
+
continue
|
|
559
|
+
}
|
|
560
|
+
if (String(token).startsWith('--prefix=')) {
|
|
561
|
+
commandTarget = { type: 'prefix', value: token.slice('--prefix='.length) }
|
|
562
|
+
commandStart = i + 1
|
|
563
|
+
continue
|
|
564
|
+
}
|
|
565
|
+
if (token === '-n' || token === '--name') {
|
|
566
|
+
commandTarget = { type: 'name', value: tokens[i + 1] || '' }
|
|
567
|
+
i++
|
|
568
|
+
commandStart = i + 1
|
|
569
|
+
continue
|
|
570
|
+
}
|
|
571
|
+
if (String(token).startsWith('--name=')) {
|
|
572
|
+
commandTarget = { type: 'name', value: token.slice('--name='.length) }
|
|
573
|
+
commandStart = i + 1
|
|
574
|
+
continue
|
|
575
|
+
}
|
|
576
|
+
if (token === '--cwd') {
|
|
577
|
+
i++
|
|
578
|
+
commandStart = i + 1
|
|
579
|
+
continue
|
|
580
|
+
}
|
|
581
|
+
if (String(token).startsWith('--cwd=')) {
|
|
582
|
+
commandStart = i + 1
|
|
583
|
+
continue
|
|
584
|
+
}
|
|
585
|
+
if (token === '--') {
|
|
586
|
+
commandStart = i + 1
|
|
587
|
+
break
|
|
588
|
+
}
|
|
589
|
+
if (String(token).startsWith('-')) {
|
|
590
|
+
commandStart = i + 1
|
|
591
|
+
continue
|
|
592
|
+
}
|
|
593
|
+
commandStart = i
|
|
594
|
+
break
|
|
595
|
+
}
|
|
596
|
+
const target = refineTargetFromCommandArgs(wrapperTarget, commandTarget, params, context)
|
|
597
|
+
return { commandStart, target }
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function classifyCommandTokens(tokens, params = {}, context = {}, startIndex = 0, wrapperTargetOverride = null) {
|
|
601
|
+
const invocation = findCondaInvocation(tokens, context, startIndex)
|
|
602
|
+
if (!invocation) {
|
|
406
603
|
return { shouldSkip: false }
|
|
407
604
|
}
|
|
408
|
-
const
|
|
605
|
+
const wrapperTarget = wrapperTargetOverride || resolveWrapperTarget(params, context)
|
|
606
|
+
const subcommand = findSubcommand(tokens, invocation.argsStart)
|
|
607
|
+
if (subcommand && subcommand.name === 'run') {
|
|
608
|
+
const run = parseCondaRun(tokens, invocation.argsStart, params, context, wrapperTarget)
|
|
609
|
+
return classifyCommandTokens(tokens, params, context, run.commandStart, run.target)
|
|
610
|
+
}
|
|
611
|
+
const target = resolveTarget(tokens, invocation.argsStart, params, context, wrapperTarget)
|
|
409
612
|
if (target.kind !== 'managed-base') {
|
|
410
613
|
return { shouldSkip: false }
|
|
411
614
|
}
|
|
412
|
-
const subcommand = findSubcommand(tokens, executableIndex)
|
|
413
615
|
if (!subcommand || !subcommand.mutating) {
|
|
414
616
|
return { shouldSkip: false }
|
|
415
617
|
}
|
|
416
|
-
if (hasDryRunFlag(tokens,
|
|
618
|
+
if (hasDryRunFlag(tokens, invocation.argsStart)) {
|
|
417
619
|
return { shouldSkip: false }
|
|
418
620
|
}
|
|
419
621
|
|
|
420
|
-
if (
|
|
622
|
+
if (isCreateSubcommand(subcommand) && target.source !== 'command-target') {
|
|
623
|
+
return { shouldSkip: false }
|
|
624
|
+
}
|
|
625
|
+
if (subcommand.name === 'env remove' && target.source !== 'command-target') {
|
|
421
626
|
return { shouldSkip: false }
|
|
422
627
|
}
|
|
423
628
|
|
|
424
|
-
const protectedPackage = protectedPackageFromSubcommand(tokens, subcommand)
|
|
629
|
+
const protectedPackage = protectedPackageFromSubcommand(tokens, subcommand, context)
|
|
425
630
|
if (!protectedPackage && !isListedBroadMutation(tokens, subcommand, target)) {
|
|
426
631
|
return { shouldSkip: false }
|
|
427
632
|
}
|
|
@@ -435,72 +640,36 @@ function classifyCommandTokens(tokens, params = {}, context = {}) {
|
|
|
435
640
|
}
|
|
436
641
|
}
|
|
437
642
|
|
|
438
|
-
function noopCommand() {
|
|
439
|
-
return 'echo Pinokio skipped a Conda setup command. Pinokio is continuing.'
|
|
440
|
-
}
|
|
441
|
-
|
|
442
643
|
function classifySegment(segment, params, context) {
|
|
443
644
|
const parsed = shellTokenize(segment, context)
|
|
444
645
|
if (!parsed.closed) {
|
|
445
646
|
return { shouldSkip: false }
|
|
446
647
|
}
|
|
447
|
-
return
|
|
448
|
-
...classifyCommandTokens(parsed.tokens, params, context),
|
|
449
|
-
tokens: parsed.tokens,
|
|
450
|
-
}
|
|
648
|
+
return classifyCommandTokens(parsed.tokens, params, context)
|
|
451
649
|
}
|
|
452
650
|
|
|
453
651
|
function rewriteStringMessage(message, params, context) {
|
|
454
652
|
const parts = splitShellSegments(message, context)
|
|
455
653
|
const skipped = []
|
|
456
|
-
const hasUnsafeSeparator = parts.some((part) => part.type === 'unsafe-separator')
|
|
457
654
|
|
|
458
|
-
|
|
459
|
-
for (const part of parts) {
|
|
460
|
-
if (part.type !== 'segment') {
|
|
461
|
-
continue
|
|
462
|
-
}
|
|
463
|
-
const body = part.text.trim()
|
|
464
|
-
if (!body) {
|
|
465
|
-
continue
|
|
466
|
-
}
|
|
467
|
-
const classification = classifySegment(body, params, context)
|
|
468
|
-
if (classification.shouldSkip) {
|
|
469
|
-
skipped.push({
|
|
470
|
-
command: body,
|
|
471
|
-
reason: classification.reason,
|
|
472
|
-
target: classification.target,
|
|
473
|
-
})
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
if (skipped.length > 0) {
|
|
477
|
-
return { message: noopCommand(), skipped }
|
|
478
|
-
}
|
|
479
|
-
return { message, skipped }
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const rewritten = parts.map((part) => {
|
|
655
|
+
for (const part of parts) {
|
|
483
656
|
if (part.type !== 'segment') {
|
|
484
|
-
|
|
657
|
+
continue
|
|
485
658
|
}
|
|
486
|
-
const leading = (part.text.match(/^\s*/) || [''])[0]
|
|
487
|
-
const trailing = (part.text.match(/\s*$/) || [''])[0]
|
|
488
659
|
const body = part.text.trim()
|
|
489
660
|
if (!body) {
|
|
490
|
-
|
|
661
|
+
continue
|
|
491
662
|
}
|
|
492
663
|
const classification = classifySegment(body, params, context)
|
|
493
|
-
if (
|
|
494
|
-
|
|
664
|
+
if (classification.shouldSkip) {
|
|
665
|
+
skipped.push({
|
|
666
|
+
command: body,
|
|
667
|
+
reason: classification.reason,
|
|
668
|
+
target: classification.target,
|
|
669
|
+
})
|
|
495
670
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
reason: classification.reason,
|
|
499
|
-
target: classification.target,
|
|
500
|
-
})
|
|
501
|
-
return leading + noopCommand() + trailing
|
|
502
|
-
}).join('')
|
|
503
|
-
return { message: rewritten, skipped }
|
|
671
|
+
}
|
|
672
|
+
return { message: skipped.length > 0 ? '' : message, skipped }
|
|
504
673
|
}
|
|
505
674
|
|
|
506
675
|
function structuredTokens(message) {
|
|
@@ -525,7 +694,7 @@ function rewriteSingleMessage(message, params, context) {
|
|
|
525
694
|
return { message, skipped: [] }
|
|
526
695
|
}
|
|
527
696
|
return {
|
|
528
|
-
message:
|
|
697
|
+
message: '',
|
|
529
698
|
skipped: [{
|
|
530
699
|
command: tokens.join(' '),
|
|
531
700
|
reason: classification.reason,
|
|
@@ -565,29 +734,21 @@ function formatRawNotice(skip) {
|
|
|
565
734
|
}
|
|
566
735
|
|
|
567
736
|
function formatNotifyHtml(skipped) {
|
|
568
|
-
const count = skipped.length
|
|
569
737
|
const firstCommand = skipped[0] && skipped[0].command ? String(skipped[0].command) : ''
|
|
570
738
|
const truncatedCommand = firstCommand.length > 240 ? `${firstCommand.slice(0, 237)}...` : firstCommand
|
|
571
|
-
const title = count === 1
|
|
572
|
-
? 'Pinokio skipped a Conda setup command.'
|
|
573
|
-
: `Pinokio skipped ${count} Conda setup commands.`
|
|
574
739
|
const commandHtml = truncatedCommand
|
|
575
740
|
? [
|
|
576
|
-
'<
|
|
741
|
+
'<code style="display:block;box-sizing:border-box;max-width:100%;white-space:pre-wrap;margin:0;padding:6px 8px;border:1px solid rgba(255,255,255,.16);border-radius:4px;background:rgba(255,255,255,.06);font-size:12px;line-height:1.35;">',
|
|
577
742
|
escapeHtml(truncatedCommand),
|
|
578
|
-
'</
|
|
743
|
+
'</code>',
|
|
579
744
|
].join('')
|
|
580
745
|
: ''
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
: ''
|
|
584
|
-
return [
|
|
585
|
-
`<b>${escapeHtml(title)}</b>`,
|
|
586
|
-
escapeHtml('Pinokio already manages base Conda.'),
|
|
587
|
-
escapeHtml('Pinokio is continuing.'),
|
|
746
|
+
const rows = [
|
|
747
|
+
`<b style="display:block;">${escapeHtml('Command skipped')}</b>`,
|
|
588
748
|
commandHtml,
|
|
589
|
-
|
|
590
|
-
].filter(Boolean).join('
|
|
749
|
+
`<span>${escapeHtml('No action needed. Pinokio already includes Conda.')}</span>`,
|
|
750
|
+
].filter(Boolean).join('')
|
|
751
|
+
return `<div style="display:flex;flex-direction:column;gap:8px;">${rows}</div>`
|
|
591
752
|
}
|
|
592
753
|
|
|
593
754
|
function emitSkipNotices(params, skipped, context) {
|
|
@@ -610,10 +771,7 @@ function emitSkipNotices(params, skipped, context) {
|
|
|
610
771
|
}
|
|
611
772
|
|
|
612
773
|
function applyCondaRuntimeGuard(params, context = {}) {
|
|
613
|
-
if (!params
|
|
614
|
-
return { params, skipped: [] }
|
|
615
|
-
}
|
|
616
|
-
if (params.conda && params.conda.skip === true) {
|
|
774
|
+
if (!params) {
|
|
617
775
|
return { params, skipped: [] }
|
|
618
776
|
}
|
|
619
777
|
const message = params.message
|
|
@@ -641,7 +799,7 @@ function resetNoticeSessionsForTest() {
|
|
|
641
799
|
}
|
|
642
800
|
|
|
643
801
|
module.exports = {
|
|
644
|
-
|
|
802
|
+
SHELL_RUN_GUARD_OPTION,
|
|
645
803
|
applyCondaRuntimeGuard,
|
|
646
804
|
resetNoticeSessionsForTest,
|
|
647
805
|
}
|
package/kernel/shells.js
CHANGED
|
@@ -632,7 +632,7 @@ class Shells {
|
|
|
632
632
|
sh.mute = true
|
|
633
633
|
sh.kill()
|
|
634
634
|
}
|
|
635
|
-
})
|
|
635
|
+
}, options)
|
|
636
636
|
// If this shell ran under a workspace, rescan git repos for that workspace.
|
|
637
637
|
// Snapshots are now always user-initiated via the backups UI; here we only
|
|
638
638
|
// pin new repos to specific commits when this run was started from a
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|