pinokiod 3.209.0 → 3.211.0

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.
@@ -136,7 +136,10 @@ class Terminal {
136
136
  return { files: saved, errors: failures }
137
137
  }
138
138
 
139
- const marker = '[attachment] '
139
+ const marker = this.buildAttachmentPayload(saved, {
140
+ shellInstance,
141
+ params
142
+ })
140
143
  let shellEmitAttempted = false
141
144
  let shellEmitSucceeded = false
142
145
  if (params && params.id && kernel && kernel.shell && typeof kernel.shell.emit === 'function') {
@@ -173,6 +176,99 @@ class Terminal {
173
176
  return { files: saved, errors: failures, shellEmit: shellEmitSucceeded, shellEmitAttempted }
174
177
  }
175
178
 
179
+ buildAttachmentPayload(files, context = {}) {
180
+ const markerPrefix = ''
181
+ if (!Array.isArray(files) || files.length === 0) {
182
+ return markerPrefix
183
+ }
184
+
185
+ const shellName = this.resolveShellName(context)
186
+ const escapedPaths = files
187
+ .map((file) => {
188
+ if (!file || typeof file !== 'object') {
189
+ return null
190
+ }
191
+ const rawPath = this.pickBestPath(file)
192
+ if (!rawPath) {
193
+ return null
194
+ }
195
+ return this.escapePathForShell(shellName, rawPath)
196
+ })
197
+ .filter(Boolean)
198
+
199
+ if (escapedPaths.length === 0) {
200
+ return markerPrefix
201
+ }
202
+
203
+ return escapedPaths.join(' ')
204
+ }
205
+
206
+ resolveShellName(context) {
207
+ if (!context) {
208
+ return null
209
+ }
210
+ const { shellInstance, params } = context
211
+ if (shellInstance && typeof shellInstance.shell === 'string' && shellInstance.shell.trim()) {
212
+ return shellInstance.shell.trim()
213
+ }
214
+ if (params && typeof params.shell === 'string' && params.shell.trim()) {
215
+ return params.shell.trim()
216
+ }
217
+ return null
218
+ }
219
+
220
+ escapePathForShell(shellName, rawPath) {
221
+ if (!rawPath || typeof rawPath !== 'string') {
222
+ return ''
223
+ }
224
+ const normalizedPath = rawPath
225
+ if (!shellName) {
226
+ return this.escapePosixPath(normalizedPath)
227
+ }
228
+ const lowerShell = shellName.toLowerCase()
229
+ if (lowerShell.includes('powershell') || lowerShell.includes('pwsh')) {
230
+ return this.escapePowershellPath(normalizedPath)
231
+ }
232
+ if (lowerShell.includes('cmd.exe') || lowerShell === 'cmd') {
233
+ return this.escapeCmdPath(normalizedPath)
234
+ }
235
+ return this.escapePosixPath(normalizedPath)
236
+ }
237
+
238
+ pickBestPath(file) {
239
+ const candidates = [
240
+ file.cliRelativePath,
241
+ file.cliPath,
242
+ file.path,
243
+ file.displayPath,
244
+ file.homeRelativePath,
245
+ file.storedAs,
246
+ file.originalName,
247
+ typeof file.name === 'string' ? file.name : null
248
+ ]
249
+ for (const candidate of candidates) {
250
+ if (typeof candidate === 'string') {
251
+ const trimmed = candidate.trim()
252
+ if (trimmed.length > 0) {
253
+ return trimmed
254
+ }
255
+ }
256
+ }
257
+ return null
258
+ }
259
+
260
+ escapePosixPath(input) {
261
+ return `'${input.split("'").join("'\\''")}'`
262
+ }
263
+
264
+ escapePowershellPath(input) {
265
+ return `'${input.split("'").join("''")}'`
266
+ }
267
+
268
+ escapeCmdPath(input) {
269
+ return `"${input.replace(/(["^%])/g, '^$1')}"`
270
+ }
271
+
176
272
  resolveShellInstance(params, kernel) {
177
273
  if (!params || typeof params.id !== 'string') {
178
274
  return null
@@ -0,0 +1,99 @@
1
+ const { spawn } = require('child_process')
2
+ const os = require('os')
3
+
4
+ const cache = new Map()
5
+
6
+ const OK_MARKER = '__PINOKIO_BP_OK__'
7
+ const NO_MARKER = '__PINOKIO_BP_NO__'
8
+
9
+ function normalizeKey(shell, platform) {
10
+ return `${platform || os.platform()}::${(shell || '').toLowerCase()}`
11
+ }
12
+
13
+ function isWindowsShell(shell) {
14
+ const name = (shell || '').toLowerCase()
15
+ return name.includes('cmd.exe') || name === 'cmd' || name.includes('powershell') || name.includes('pwsh')
16
+ }
17
+
18
+ function isBash(shell) {
19
+ return (shell || '').toLowerCase().includes('bash')
20
+ }
21
+
22
+ function isZsh(shell) {
23
+ return (shell || '').toLowerCase().includes('zsh')
24
+ }
25
+
26
+ function runProbe(shell, args, script) {
27
+ return new Promise((resolve) => {
28
+ let stdout = ''
29
+ const child = spawn(shell, [...args, script], {
30
+ stdio: ['ignore', 'pipe', 'ignore']
31
+ })
32
+ child.stdout.setEncoding('utf8')
33
+ child.stdout.on('data', (chunk) => {
34
+ stdout += chunk
35
+ })
36
+ child.on('error', () => resolve(null))
37
+ child.on('close', () => resolve(stdout))
38
+ })
39
+ }
40
+
41
+ async function detect(shell, platform = os.platform()) {
42
+ const key = normalizeKey(shell, platform)
43
+ if (cache.has(key)) {
44
+ return cache.get(key)
45
+ }
46
+
47
+ let result
48
+ try {
49
+ result = await runDetection(shell, platform)
50
+ } catch (_) {
51
+ result = null
52
+ }
53
+ if (result === null) {
54
+ result = true
55
+ }
56
+ cache.set(key, result)
57
+ return result
58
+ }
59
+
60
+ async function runDetection(shell, platform) {
61
+ if (!shell) {
62
+ return true
63
+ }
64
+ if (isWindowsShell(shell)) {
65
+ return false
66
+ }
67
+
68
+ if (isBash(shell)) {
69
+ const script = `if bind -v 2>/dev/null | grep -q 'enable-bracketed-paste'; then bind 'set enable-bracketed-paste on' >/dev/null 2>&1; printf '${OK_MARKER}\n'; else printf '${NO_MARKER}\n'; fi`
70
+ const output = await runProbe(shell, ['--noprofile', '--norc', '-ic'], script)
71
+ return parseProbeOutput(output)
72
+ }
73
+
74
+ if (isZsh(shell)) {
75
+ const script = `if setopt -q bracketed-paste 2>/dev/null; then printf '${OK_MARKER}\n'; exit 0; fi; if setopt bracketed-paste 2>/dev/null; then printf '${OK_MARKER}\n'; exit 0; fi; printf '${NO_MARKER}\n'`
76
+ const output = await runProbe(shell, ['-f', '-c'], script)
77
+ return parseProbeOutput(output)
78
+ }
79
+
80
+ // For other shells assume supported unless clearly Windows-only.
81
+ return true
82
+ }
83
+
84
+ function parseProbeOutput(output) {
85
+ if (typeof output !== 'string') {
86
+ return null
87
+ }
88
+ if (output.includes(OK_MARKER)) {
89
+ return true
90
+ }
91
+ if (output.includes(NO_MARKER)) {
92
+ return false
93
+ }
94
+ return null
95
+ }
96
+
97
+ module.exports = {
98
+ detect
99
+ }
@@ -613,8 +613,9 @@ const init = async (options, kernel) => {
613
613
 
614
614
  const entriesToEnsure = [
615
615
  "ENVIRONMENT",
616
- ".*",
617
- "~*",
616
+ // ".*",
617
+ // "~*",
618
+ "/.pinokio-temp/",
618
619
  "/logs/",
619
620
  "/cache/",
620
621
  "/AGENTS.md",
package/kernel/shell.js CHANGED
@@ -40,6 +40,13 @@ class Shell {
40
40
  this.platform = os.platform()
41
41
  this.logs = {}
42
42
  this.shell = this.platform === 'win32' ? 'cmd.exe' : 'bash';
43
+ this.supportsBracketedPaste = this.computeBracketedPasteSupport(this.shell)
44
+ if (this.kernel && this.kernel.bracketedPasteSupport) {
45
+ const cached = this.kernel.bracketedPasteSupport[(this.shell || '').toLowerCase()]
46
+ if (typeof cached === 'boolean') {
47
+ this.supportsBracketedPaste = cached
48
+ }
49
+ }
43
50
  this.decsyncBuffer = ''
44
51
 
45
52
  // Windows: /D => ignore AutoRun Registry Key
@@ -276,6 +283,12 @@ class Shell {
276
283
  this.EOL = os.EOL
277
284
  if (this.params.shell) {
278
285
  this.shell = this.params.shell
286
+ const normalizedShell = (this.shell || '').toLowerCase()
287
+ if (this.kernel && this.kernel.bracketedPasteSupport && typeof this.kernel.bracketedPasteSupport[normalizedShell] === 'boolean') {
288
+ this.supportsBracketedPaste = this.kernel.bracketedPasteSupport[normalizedShell]
289
+ } else {
290
+ this.supportsBracketedPaste = this.computeBracketedPasteSupport(this.shell)
291
+ }
279
292
  if (/bash/i.test(this.shell)) {
280
293
  this.args = ["--noprofile", "--norc"]
281
294
  //this.args = [ "--login", "-i"]
@@ -579,6 +592,19 @@ class Shell {
579
592
  const regex = new RegExp(pattern, 'gi')
580
593
  return str.replaceAll(regex, '');
581
594
  }
595
+ computeBracketedPasteSupport(shellName) {
596
+ const name = (shellName || '').toLowerCase()
597
+ if (!name) {
598
+ return true
599
+ }
600
+ if (name.includes('cmd.exe') || name === 'cmd') {
601
+ return false
602
+ }
603
+ if (name.includes('powershell') || name.includes('pwsh')) {
604
+ return false
605
+ }
606
+ return true
607
+ }
582
608
  exists(abspath) {
583
609
  return new Promise(r=>fs.access(abspath, fs.constants.F_OK, e => r(!e)))
584
610
  }
package/kernel/shells.js CHANGED
@@ -9,10 +9,12 @@ const {
9
9
 
10
10
  const path = require('path')
11
11
  const Shell = require("./shell")
12
+ const { detect: detectBracketedPasteSupport } = require('./bracketed_paste_detector')
12
13
  class Shells {
13
14
  constructor(kernel) {
14
15
  this.kernel = kernel
15
16
  this.shells = []
17
+ this.bracketedPasteDetections = new Map()
16
18
 
17
19
  }
18
20
  /*
@@ -40,6 +42,14 @@ class Shells {
40
42
  env: this.kernel.bin.envs({}),
41
43
  })
42
44
 
45
+ await this.ensureBracketedPasteSupport(sh.shell)
46
+ if (this.kernel.bracketedPasteSupport) {
47
+ const cached = this.kernel.bracketedPasteSupport[(sh.shell || '').toLowerCase()]
48
+ if (typeof cached === 'boolean') {
49
+ sh.supportsBracketedPaste = cached
50
+ }
51
+ }
52
+
43
53
  this.kernel.envs = sh.env
44
54
  // also set the uppercase variables if they're not set
45
55
  for(let key in sh.env) {
@@ -67,6 +77,44 @@ class Shells {
67
77
  }
68
78
  })
69
79
  }
80
+ async ensureBracketedPasteSupport(shellName) {
81
+ if (!shellName) {
82
+ return
83
+ }
84
+ const lower = (shellName || '').toLowerCase()
85
+ if (!lower) {
86
+ return
87
+ }
88
+ if (!this.kernel.bracketedPasteSupport) {
89
+ this.kernel.bracketedPasteSupport = {}
90
+ }
91
+ if (Object.prototype.hasOwnProperty.call(this.kernel.bracketedPasteSupport, lower)) {
92
+ return this.kernel.bracketedPasteSupport[lower]
93
+ }
94
+ if (this.bracketedPasteDetections.has(lower)) {
95
+ return this.bracketedPasteDetections.get(lower)
96
+ }
97
+ const fallback = !(lower.includes('cmd.exe') || lower === 'cmd' || lower.includes('powershell') || lower.includes('pwsh'))
98
+ const detectionPromise = detectBracketedPasteSupport(shellName, this.kernel.platform || os.platform())
99
+ .then((support) => {
100
+ const value = typeof support === 'boolean' ? support : fallback
101
+ this.kernel.bracketedPasteSupport[lower] = value
102
+ return value
103
+ })
104
+ .catch((error) => {
105
+ console.warn('[shells.ensureBracketedPasteSupport] detection failed', {
106
+ shell: shellName,
107
+ error: error && error.message ? error.message : error
108
+ })
109
+ this.kernel.bracketedPasteSupport[lower] = fallback
110
+ return fallback
111
+ })
112
+ .finally(() => {
113
+ this.bracketedPasteDetections.delete(lower)
114
+ })
115
+ this.bracketedPasteDetections.set(lower, detectionPromise)
116
+ return detectionPromise
117
+ }
70
118
  async launch(params, options, ondata) {
71
119
  // if array, duplicate the action
72
120
  if (Array.isArray(params.message)) {
@@ -112,6 +160,9 @@ class Shells {
112
160
  let exec_path = (params.path ? params.path : ".") // use the current path if not specified
113
161
  let cwd = (options && options.cwd ? options.cwd : this.kernel.homedir) // if cwd exists, use it. Otherwise the cwd is pinokio home folder (~/pinokio)
114
162
  params.path = this.kernel.api.resolvePath(cwd, exec_path)
163
+
164
+ const plannedShell = params.shell || (this.kernel.platform === 'win32' ? 'cmd.exe' : 'bash')
165
+ await this.ensureBracketedPasteSupport(plannedShell)
115
166
  let sh = new Shell(this.kernel)
116
167
  if (options) {
117
168
  params.group = options.group // set group
@@ -387,9 +438,13 @@ class Shells {
387
438
  let session = this.get(params.id)
388
439
  if (session) {
389
440
  if (params.paste) {
390
- //session.emit("\x1b[?2004h\x1b[200~" + params.emit+ "\x1b[201~")
391
- session.emit("\x1b[200~" + params.emit+ "\x1b[201~")
392
- //session.emit(params.emit)
441
+ const payload = params.emit != null ? String(params.emit) : ''
442
+ if (session.supportsBracketedPaste !== false) {
443
+ //session.emit("\x1b[?2004h\x1b[200~" + params.emit+ "\x1b[201~")
444
+ session.emit("\x1b[200~" + payload + "\x1b[201~")
445
+ } else {
446
+ session.emit(payload)
447
+ }
393
448
  } else {
394
449
  session.emit(params.emit)
395
450
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.209.0",
3
+ "version": "3.211.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {