pinokiod 5.1.10 → 5.1.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/fs/download_worker.js +158 -0
- package/kernel/api/fs/index.js +95 -91
- package/kernel/api/index.js +3 -0
- package/kernel/bin/index.js +5 -2
- package/kernel/environment.js +19 -2
- package/kernel/git.js +972 -1
- package/kernel/index.js +65 -30
- package/kernel/peer.js +1 -2
- package/kernel/plugin.js +0 -8
- package/kernel/procs.js +92 -36
- package/kernel/prototype.js +45 -22
- package/kernel/shells.js +30 -6
- package/kernel/sysinfo.js +33 -13
- package/kernel/util.js +61 -24
- package/kernel/workspace_status.js +131 -7
- package/package.json +1 -1
- package/pipe/index.js +1 -1
- package/server/index.js +1169 -350
- package/server/public/create-launcher.js +157 -2
- package/server/public/install.js +135 -41
- package/server/public/style.css +32 -1
- package/server/public/tab-link-popover.js +45 -14
- package/server/public/terminal-settings.js +51 -35
- package/server/public/urldropdown.css +89 -3
- package/server/socket.js +12 -7
- package/server/views/agents.ejs +4 -3
- package/server/views/app.ejs +798 -30
- package/server/views/bootstrap.ejs +2 -1
- package/server/views/checkpoints.ejs +1014 -0
- package/server/views/checkpoints_registry_beta.ejs +260 -0
- package/server/views/columns.ejs +4 -4
- package/server/views/connect.ejs +1 -0
- package/server/views/d.ejs +74 -4
- package/server/views/download.ejs +28 -28
- package/server/views/editor.ejs +4 -5
- package/server/views/env_editor.ejs +1 -1
- package/server/views/file_explorer.ejs +1 -1
- package/server/views/index.ejs +3 -1
- package/server/views/init/index.ejs +2 -1
- package/server/views/install.ejs +2 -1
- package/server/views/net.ejs +9 -7
- package/server/views/network.ejs +15 -14
- package/server/views/pro.ejs +5 -2
- package/server/views/prototype/index.ejs +2 -1
- package/server/views/registry_link.ejs +76 -0
- package/server/views/rows.ejs +4 -4
- package/server/views/screenshots.ejs +1 -0
- package/server/views/settings.ejs +1 -0
- package/server/views/shell.ejs +4 -6
- package/server/views/terminal.ejs +528 -38
- package/server/views/tools.ejs +1 -0
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/1764297248545 +0 -45
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/1764335557118 +0 -45
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/1764335834126 +0 -45
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/events +0 -12
- package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/latest +0 -45
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const { DownloaderHelper } = require('node-downloader-helper')
|
|
3
|
+
const randomUseragent = require('random-useragent')
|
|
4
|
+
|
|
5
|
+
const safeSend = (msg) => {
|
|
6
|
+
try {
|
|
7
|
+
if (process.send) {
|
|
8
|
+
process.send(msg)
|
|
9
|
+
}
|
|
10
|
+
} catch (_) {
|
|
11
|
+
// ignore IPC errors
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
process.on('message', async (config) => {
|
|
16
|
+
const { url, folder, fileName, stallMs } = config || {}
|
|
17
|
+
|
|
18
|
+
if (!url || !folder) {
|
|
19
|
+
safeSend({
|
|
20
|
+
type: 'error',
|
|
21
|
+
error: { message: 'download_worker: invalid config (url/folder required)', stack: '' }
|
|
22
|
+
})
|
|
23
|
+
process.exit(1)
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await fs.promises.mkdir(folder, { recursive: true }).catch(() => {})
|
|
29
|
+
} catch (_) {
|
|
30
|
+
// if mkdir fails, DownloaderHelper will surface an error later
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const userAgent = randomUseragent.getRandom((ua) => ua.browserName === 'Chrome') ||
|
|
34
|
+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36'
|
|
35
|
+
|
|
36
|
+
const options = {
|
|
37
|
+
headers: {
|
|
38
|
+
'user-agent': userAgent
|
|
39
|
+
},
|
|
40
|
+
override: {
|
|
41
|
+
skip: true,
|
|
42
|
+
skipSmaller: false
|
|
43
|
+
},
|
|
44
|
+
resumeIfFileExists: true,
|
|
45
|
+
removeOnStop: false,
|
|
46
|
+
removeOnFail: false,
|
|
47
|
+
retry: { maxRetries: 10, delay: 5000 }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (fileName) {
|
|
51
|
+
options.fileName = fileName
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const dl = new DownloaderHelper(url, folder, options)
|
|
55
|
+
|
|
56
|
+
const STALL_MS = typeof stallMs === 'number' ? stallMs : 60000
|
|
57
|
+
let lastProgressAt = Date.now()
|
|
58
|
+
let finished = false
|
|
59
|
+
let stallTimer = setInterval(() => {
|
|
60
|
+
if (finished) return
|
|
61
|
+
const elapsed = Date.now() - lastProgressAt
|
|
62
|
+
if (elapsed > STALL_MS) {
|
|
63
|
+
safeSend({
|
|
64
|
+
type: 'stall',
|
|
65
|
+
elapsed
|
|
66
|
+
})
|
|
67
|
+
try {
|
|
68
|
+
dl.stop()
|
|
69
|
+
} catch (_) {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}, STALL_MS)
|
|
73
|
+
|
|
74
|
+
const cleanup = () => {
|
|
75
|
+
finished = true
|
|
76
|
+
if (stallTimer) {
|
|
77
|
+
clearInterval(stallTimer)
|
|
78
|
+
stallTimer = null
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
dl.on('download', (downloadInfo) => {
|
|
83
|
+
safeSend({ type: 'download', info: downloadInfo })
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// High-frequency progress updates stay in the worker to drive the stall watchdog,
|
|
87
|
+
// but we only forward throttled updates to the parent to avoid flooding IPC.
|
|
88
|
+
dl.on('progress', (stats) => {
|
|
89
|
+
lastProgressAt = Date.now()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
dl.on('progress.throttled', (stats) => {
|
|
93
|
+
lastProgressAt = Date.now()
|
|
94
|
+
safeSend({ type: 'progress.throttled', stats })
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
dl.on('stateChanged', (state) => {
|
|
98
|
+
safeSend({ type: 'stateChanged', state })
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
dl.on('redirected', (newUrl, oldUrl) => {
|
|
102
|
+
safeSend({ type: 'redirected', newUrl, oldUrl })
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
dl.on('resume', (isResumed) => {
|
|
106
|
+
safeSend({ type: 'resume', isResumed })
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
dl.on('timeout', () => {
|
|
110
|
+
safeSend({ type: 'timeout' })
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
dl.on('warning', (err) => {
|
|
114
|
+
safeSend({
|
|
115
|
+
type: 'warning',
|
|
116
|
+
error: {
|
|
117
|
+
message: err && err.message ? err.message : String(err),
|
|
118
|
+
stack: err && err.stack ? err.stack : ''
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
dl.on('skip', (skipInfo) => {
|
|
124
|
+
cleanup()
|
|
125
|
+
safeSend({ type: 'skip', info: skipInfo })
|
|
126
|
+
process.exit(0)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
dl.on('end', (downloadInfo) => {
|
|
130
|
+
cleanup()
|
|
131
|
+
safeSend({ type: 'end', info: downloadInfo })
|
|
132
|
+
process.exit(0)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
dl.on('error', (err) => {
|
|
136
|
+
cleanup()
|
|
137
|
+
safeSend({
|
|
138
|
+
type: 'error',
|
|
139
|
+
error: {
|
|
140
|
+
message: err && err.message ? err.message : String(err),
|
|
141
|
+
stack: err && err.stack ? err.stack : String(err)
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
process.exit(1)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
dl.start().catch((err) => {
|
|
148
|
+
cleanup()
|
|
149
|
+
safeSend({
|
|
150
|
+
type: 'error',
|
|
151
|
+
error: {
|
|
152
|
+
message: err && err.message ? err.message : String(err),
|
|
153
|
+
stack: err && err.stack ? err.stack : String(err)
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
process.exit(1)
|
|
157
|
+
})
|
|
158
|
+
})
|
package/kernel/api/fs/index.js
CHANGED
|
@@ -4,11 +4,10 @@ const fs = require("fs")
|
|
|
4
4
|
const fse = require('fs-extra')
|
|
5
5
|
const { rimraf } = require('rimraf')
|
|
6
6
|
const Pdrive = require('pdrive')
|
|
7
|
-
const { DownloaderHelper } = require('node-downloader-helper');
|
|
8
|
-
const randomUseragent = require('random-useragent');
|
|
9
7
|
const symlinkDir = require('symlink-dir')
|
|
10
8
|
const retry = require('async-retry');
|
|
11
9
|
const { createHash } = require('crypto');
|
|
10
|
+
const { fork } = require('child_process')
|
|
12
11
|
const Environment = require("../../environment")
|
|
13
12
|
const Util = require("../../util")
|
|
14
13
|
|
|
@@ -559,108 +558,113 @@ class FS {
|
|
|
559
558
|
path: <the eact file path to store the file under>
|
|
560
559
|
}
|
|
561
560
|
*/
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
return ua.browserName === 'Chrome';
|
|
568
|
-
});
|
|
561
|
+
const params = req.params || {}
|
|
562
|
+
const url = params.url || params.uri
|
|
563
|
+
if (!url) {
|
|
564
|
+
throw new Error('fs.download: url or uri is required')
|
|
565
|
+
}
|
|
569
566
|
|
|
567
|
+
let folder
|
|
568
|
+
let filename
|
|
570
569
|
if (params.dir) {
|
|
571
570
|
folder = kernel.api.filePath(params.dir, req.cwd)
|
|
572
|
-
await fs.promises.mkdir(folder, { recursive: true }).catch((e) => { })
|
|
573
|
-
dl = new DownloaderHelper(url, folder, {
|
|
574
|
-
headers: {
|
|
575
|
-
"user-agent": userAgent,
|
|
576
|
-
},
|
|
577
|
-
// httpsRequestOptions: {
|
|
578
|
-
// rejectUnauthorized: false
|
|
579
|
-
// },
|
|
580
|
-
override: {
|
|
581
|
-
skip: true,
|
|
582
|
-
skipSmaller: false,
|
|
583
|
-
},
|
|
584
|
-
resumeIfFileExists: true,
|
|
585
|
-
removeOnStop: false,
|
|
586
|
-
removeOnFail: false,
|
|
587
|
-
retry: { maxRetries: 10, delay: 5000 },
|
|
588
|
-
})
|
|
589
571
|
} else if (params.path) {
|
|
590
|
-
|
|
572
|
+
const filepath = kernel.api.filePath(params.path, req.cwd)
|
|
591
573
|
folder = path.dirname(filepath)
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
headers: {
|
|
596
|
-
"user-agent": userAgent,
|
|
597
|
-
},
|
|
598
|
-
// httpsRequestOptions: {
|
|
599
|
-
// rejectUnauthorized: false
|
|
600
|
-
// },
|
|
601
|
-
override: {
|
|
602
|
-
skip: true,
|
|
603
|
-
skipSmaller: false,
|
|
604
|
-
},
|
|
605
|
-
fileName: filename,
|
|
606
|
-
resumeIfFileExists: true,
|
|
607
|
-
removeOnStop: false,
|
|
608
|
-
removeOnFail: false,
|
|
609
|
-
retry: { maxRetries: 10, delay: 5000 },
|
|
610
|
-
})
|
|
574
|
+
filename = path.basename(filepath)
|
|
575
|
+
} else {
|
|
576
|
+
throw new Error('fs.download: either dir or path must be specified')
|
|
611
577
|
}
|
|
578
|
+
|
|
612
579
|
ondata({ raw: `\r\nDownloading ${url} to ${folder}...\r\n` })
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
let str = ""
|
|
627
|
-
for(let i=0; i<p; i++) {
|
|
628
|
-
str += "#"
|
|
629
|
-
}
|
|
630
|
-
for(let i=p; i<100; i++) {
|
|
631
|
-
str += "-"
|
|
580
|
+
|
|
581
|
+
const workerPath = path.resolve(__dirname, 'download_worker.js')
|
|
582
|
+
await fs.promises.mkdir(folder, { recursive: true }).catch(() => {})
|
|
583
|
+
|
|
584
|
+
await new Promise((resolve, reject) => {
|
|
585
|
+
let settled = false
|
|
586
|
+
const finish = (err) => {
|
|
587
|
+
if (settled) return
|
|
588
|
+
settled = true
|
|
589
|
+
if (err) {
|
|
590
|
+
reject(err)
|
|
591
|
+
} else {
|
|
592
|
+
resolve()
|
|
632
593
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
ondata({ raw: msg })
|
|
638
|
-
})
|
|
639
|
-
dl.on('skip', (skipInfo) => {
|
|
640
|
-
const msg = `\r\n[Download Skipped] File already exists: ${JSON.stringify(skipInfo)}\r\n`
|
|
641
|
-
ondata({ raw: msg })
|
|
642
|
-
resolve()
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const child = fork(workerPath, [], {
|
|
597
|
+
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
643
598
|
})
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
599
|
+
|
|
600
|
+
child.on('message', (msg) => {
|
|
601
|
+
if (!msg || typeof msg !== 'object') return
|
|
602
|
+
switch (msg.type) {
|
|
603
|
+
case 'download': {
|
|
604
|
+
const payload = msg.info || {}
|
|
605
|
+
// Minimal start message; no extra JSON noise.
|
|
606
|
+
ondata({ raw: `\r\n[Download Started] ${payload.fileName || ''}\r\n` })
|
|
607
|
+
break
|
|
608
|
+
}
|
|
609
|
+
case 'progress.throttled': {
|
|
610
|
+
const stats = msg.stats || {}
|
|
611
|
+
const p = typeof stats.progress === 'number' ? Math.floor(stats.progress) : null
|
|
612
|
+
if (p !== null && p >= 0 && p <= 100) {
|
|
613
|
+
let bar = ''
|
|
614
|
+
for (let i = 0; i < p; i++) bar += '#'
|
|
615
|
+
for (let i = p; i < 100; i++) bar += '-'
|
|
616
|
+
ondata({ raw: `\r${bar}` })
|
|
617
|
+
}
|
|
618
|
+
break
|
|
619
|
+
}
|
|
620
|
+
case 'stall': {
|
|
621
|
+
const elapsed = msg.elapsed || 0
|
|
622
|
+
ondata({ raw: `\r\n[Download Watchdog] No progress for ${(elapsed / 1000).toFixed(1)}s (url: ${url})\r\n` })
|
|
623
|
+
break
|
|
624
|
+
}
|
|
625
|
+
case 'skip': {
|
|
626
|
+
const info = msg.info || {}
|
|
627
|
+
ondata({ raw: `\r\n[Download Skipped] File already exists: ${JSON.stringify(info)}\r\n` })
|
|
628
|
+
finish()
|
|
629
|
+
break
|
|
630
|
+
}
|
|
631
|
+
case 'end': {
|
|
632
|
+
const info = msg.info || {}
|
|
633
|
+
ondata({ raw: `\r\n[Download End] ${JSON.stringify(info)}\r\n` })
|
|
634
|
+
ondata({ raw: `\r\nDownload Complete!\r\n` })
|
|
635
|
+
finish()
|
|
636
|
+
break
|
|
637
|
+
}
|
|
638
|
+
case 'error': {
|
|
639
|
+
const err = msg.error || {}
|
|
640
|
+
const text = err.stack || err.message || String(err)
|
|
641
|
+
ondata({ raw: `\r\n[Download Error] ${text}\r\n` })
|
|
642
|
+
finish(new Error(err.message || 'download error'))
|
|
643
|
+
break
|
|
644
|
+
}
|
|
645
|
+
default:
|
|
646
|
+
break
|
|
647
|
+
}
|
|
651
648
|
})
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
649
|
+
|
|
650
|
+
child.on('exit', (code) => {
|
|
651
|
+
if (settled) return
|
|
652
|
+
if (code === 0) {
|
|
653
|
+
finish()
|
|
654
|
+
} else {
|
|
655
|
+
finish(new Error(`download worker exited with code ${code}`))
|
|
656
|
+
}
|
|
655
657
|
})
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
658
|
+
|
|
659
|
+
child.on('error', (err) => {
|
|
660
|
+
if (settled) return
|
|
661
|
+
finish(err)
|
|
659
662
|
})
|
|
660
663
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
+
child.send({
|
|
665
|
+
url,
|
|
666
|
+
folder,
|
|
667
|
+
fileName: filename || null
|
|
664
668
|
})
|
|
665
669
|
})
|
|
666
670
|
}
|
package/kernel/api/index.js
CHANGED
|
@@ -787,6 +787,9 @@ class Api {
|
|
|
787
787
|
|
|
788
788
|
}
|
|
789
789
|
async step (request, rawrpc, input, i, total, args) {
|
|
790
|
+
if (this.kernel.sysReady) {
|
|
791
|
+
await this.kernel.sysReady
|
|
792
|
+
}
|
|
790
793
|
await Promise.all([this.init(), this.kernel.update_sysinfo()])
|
|
791
794
|
|
|
792
795
|
// clear global regular expression object RegExp.lastMatch (memory leak prevention)
|
package/kernel/bin/index.js
CHANGED
|
@@ -1023,6 +1023,10 @@ class Bin {
|
|
|
1023
1023
|
return relevant.platform && relevant.arch && relevant.gpu
|
|
1024
1024
|
}
|
|
1025
1025
|
async check(config) {
|
|
1026
|
+
if (typeof this.kernel.binCheckDepth !== 'number') {
|
|
1027
|
+
this.kernel.binCheckDepth = 0
|
|
1028
|
+
}
|
|
1029
|
+
this.kernel.binCheckDepth++
|
|
1026
1030
|
let requirements = this.requirements(config)
|
|
1027
1031
|
let requirements_pending = !this.installed_initialized
|
|
1028
1032
|
let install_required = true
|
|
@@ -1087,7 +1091,7 @@ class Bin {
|
|
|
1087
1091
|
requirements = requirements.filter((r) => {
|
|
1088
1092
|
return r.relevant
|
|
1089
1093
|
})
|
|
1090
|
-
|
|
1094
|
+
this.kernel.binCheckDepth--
|
|
1091
1095
|
return {
|
|
1092
1096
|
error,
|
|
1093
1097
|
title: config.bin.title,
|
|
@@ -1097,7 +1101,6 @@ class Bin {
|
|
|
1097
1101
|
install_required,
|
|
1098
1102
|
requirements_pending
|
|
1099
1103
|
}
|
|
1100
|
-
|
|
1101
1104
|
}
|
|
1102
1105
|
winBuildNumber() {
|
|
1103
1106
|
let osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release());
|
package/kernel/environment.js
CHANGED
|
@@ -587,12 +587,29 @@ const init = async (options, kernel) => {
|
|
|
587
587
|
await fs.promises.writeFile(destination, rendered_recipe)
|
|
588
588
|
}
|
|
589
589
|
}
|
|
590
|
-
|
|
590
|
+
const geminiIgnorePath = path.resolve(root, ".geminiignore")
|
|
591
|
+
const geminiIgnoreContent = `ENVIRONMENT
|
|
591
592
|
!/logs
|
|
592
593
|
!/GEMINI.md
|
|
593
594
|
!/SPEC.md
|
|
594
595
|
!/app
|
|
595
|
-
!${kernel.homedir}`
|
|
596
|
+
!${kernel.homedir}`
|
|
597
|
+
let shouldWriteGeminiIgnore = false
|
|
598
|
+
try {
|
|
599
|
+
const existingGeminiIgnore = await fs.promises.readFile(geminiIgnorePath, "utf8")
|
|
600
|
+
if (existingGeminiIgnore !== geminiIgnoreContent) {
|
|
601
|
+
shouldWriteGeminiIgnore = true
|
|
602
|
+
}
|
|
603
|
+
} catch (error) {
|
|
604
|
+
if (error && error.code === "ENOENT") {
|
|
605
|
+
shouldWriteGeminiIgnore = true
|
|
606
|
+
} else {
|
|
607
|
+
throw error
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (shouldWriteGeminiIgnore) {
|
|
611
|
+
await fs.promises.writeFile(geminiIgnorePath, geminiIgnoreContent)
|
|
612
|
+
}
|
|
596
613
|
}
|
|
597
614
|
|
|
598
615
|
const gitDir = path.resolve(root, ".git")
|