pinokiod 3.271.0 → 3.273.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.
- package/kernel/ansi_stream_tracker.js +115 -0
- package/kernel/api/app/index.js +422 -0
- package/kernel/api/htmlmodal/index.js +94 -0
- package/kernel/app_launcher/index.js +115 -0
- package/kernel/app_launcher/platform/base.js +276 -0
- package/kernel/app_launcher/platform/linux.js +229 -0
- package/kernel/app_launcher/platform/macos.js +163 -0
- package/kernel/app_launcher/platform/unsupported.js +34 -0
- package/kernel/app_launcher/platform/windows.js +247 -0
- package/kernel/bin/conda-meta.js +93 -0
- package/kernel/bin/conda.js +2 -4
- package/kernel/bin/index.js +2 -4
- package/kernel/index.js +7 -0
- package/kernel/shell.js +212 -1
- package/package.json +1 -1
- package/server/index.js +491 -6
- package/server/public/common.js +224 -741
- package/server/public/create-launcher.js +754 -0
- package/server/public/htmlmodal.js +292 -0
- package/server/public/logs.js +715 -0
- package/server/public/resizeSync.js +117 -0
- package/server/public/style.css +653 -8
- package/server/public/tab-idle-notifier.js +34 -59
- package/server/public/tab-link-popover.js +7 -10
- package/server/public/terminal-settings.js +723 -9
- package/server/public/terminal_input_utils.js +72 -0
- package/server/public/terminal_key_caption.js +187 -0
- package/server/public/urldropdown.css +120 -3
- package/server/public/xterm-inline-bridge.js +116 -0
- package/server/socket.js +29 -0
- package/server/views/agents.ejs +1 -2
- package/server/views/app.ejs +55 -28
- package/server/views/bookmarklet.ejs +1 -1
- package/server/views/bootstrap.ejs +1 -0
- package/server/views/connect.ejs +1 -2
- package/server/views/create.ejs +63 -0
- package/server/views/editor.ejs +36 -4
- package/server/views/index.ejs +1 -2
- package/server/views/index2.ejs +1 -2
- package/server/views/init/index.ejs +36 -28
- package/server/views/install.ejs +20 -22
- package/server/views/layout.ejs +2 -8
- package/server/views/logs.ejs +155 -0
- package/server/views/mini.ejs +0 -18
- package/server/views/net.ejs +2 -2
- package/server/views/network.ejs +1 -2
- package/server/views/network2.ejs +1 -2
- package/server/views/old_network.ejs +1 -2
- package/server/views/pro.ejs +26 -23
- package/server/views/prototype/index.ejs +30 -34
- package/server/views/screenshots.ejs +1 -2
- package/server/views/settings.ejs +1 -20
- package/server/views/shell.ejs +59 -66
- package/server/views/terminal.ejs +118 -73
- package/server/views/tools.ejs +1 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const os = require('os')
|
|
4
|
+
const { execFile } = require('child_process')
|
|
5
|
+
const { promisify } = require('util')
|
|
6
|
+
const BaseAdapter = require('./base')
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile)
|
|
9
|
+
const OPEN_BINARY = '/usr/bin/open'
|
|
10
|
+
const DEFAULT_DIRECTORIES = [
|
|
11
|
+
'/Applications',
|
|
12
|
+
'/System/Applications',
|
|
13
|
+
'/System/Applications/Utilities',
|
|
14
|
+
'/System/Library/CoreServices/Applications',
|
|
15
|
+
path.join(os.homedir(), 'Applications')
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
class MacOSAdapter extends BaseAdapter {
|
|
19
|
+
constructor(kernel) {
|
|
20
|
+
super(kernel)
|
|
21
|
+
this.directories = DEFAULT_DIRECTORIES
|
|
22
|
+
this.walkSeen = new Set()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async buildIndex() {
|
|
26
|
+
this.entries.clear()
|
|
27
|
+
this.walkSeen.clear()
|
|
28
|
+
for (const dir of this.directories) {
|
|
29
|
+
await this.walkApplications(dir)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async walkApplications(root) {
|
|
34
|
+
if (!root) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
const normalized = path.resolve(root)
|
|
38
|
+
if (this.walkSeen.has(normalized)) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
this.walkSeen.add(normalized)
|
|
42
|
+
let entries
|
|
43
|
+
try {
|
|
44
|
+
entries = await fs.promises.readdir(normalized, { withFileTypes: true })
|
|
45
|
+
} catch (_) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (entry.name.startsWith('.')) {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
const fullPath = path.join(normalized, entry.name)
|
|
53
|
+
if (entry.name.toLowerCase().endsWith('.app')) {
|
|
54
|
+
await this.addBundle(fullPath)
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
await this.walkApplications(fullPath)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async addBundle(bundlePath) {
|
|
64
|
+
const infoPath = path.join(bundlePath, 'Contents', 'Info.plist')
|
|
65
|
+
let info = null
|
|
66
|
+
try {
|
|
67
|
+
info = await this.readPlist(infoPath)
|
|
68
|
+
} catch (_) {
|
|
69
|
+
}
|
|
70
|
+
const baseName = path.basename(bundlePath, '.app')
|
|
71
|
+
const displayName = (info && (info.CFBundleDisplayName || info.CFBundleName)) || baseName
|
|
72
|
+
const bundleId = info && info.CFBundleIdentifier ? info.CFBundleIdentifier : null
|
|
73
|
+
const execName = info && info.CFBundleExecutable ? info.CFBundleExecutable : null
|
|
74
|
+
const execPath = execName ? path.join(bundlePath, 'Contents', 'MacOS', execName) : null
|
|
75
|
+
let id = bundleId || `bundle-path:${bundlePath}`
|
|
76
|
+
if (this.entries.has(id)) {
|
|
77
|
+
let suffix = 1
|
|
78
|
+
while (this.entries.has(`${id}#${suffix}`)) {
|
|
79
|
+
suffix += 1
|
|
80
|
+
}
|
|
81
|
+
id = `${id}#${suffix}`
|
|
82
|
+
}
|
|
83
|
+
const aliases = [baseName]
|
|
84
|
+
if (bundleId) {
|
|
85
|
+
aliases.push(bundleId)
|
|
86
|
+
}
|
|
87
|
+
if (execName) {
|
|
88
|
+
aliases.push(execName)
|
|
89
|
+
}
|
|
90
|
+
this.addEntry({
|
|
91
|
+
id,
|
|
92
|
+
name: displayName,
|
|
93
|
+
aliases,
|
|
94
|
+
kind: 'macos-bundle',
|
|
95
|
+
bundleId,
|
|
96
|
+
path: bundlePath,
|
|
97
|
+
execPath,
|
|
98
|
+
detail: bundleId || bundlePath
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async readPlist(plistPath) {
|
|
103
|
+
try {
|
|
104
|
+
const { stdout } = await execFileAsync('plutil', ['-convert', 'json', '-o', '-', plistPath], {
|
|
105
|
+
maxBuffer: 2 * 1024 * 1024
|
|
106
|
+
})
|
|
107
|
+
if (!stdout) {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
return JSON.parse(stdout)
|
|
111
|
+
} catch (_) {
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async launch(entry, params = {}) {
|
|
117
|
+
const args = this.sanitizeArgs(params.args)
|
|
118
|
+
const openArgs = []
|
|
119
|
+
if (entry.bundleId) {
|
|
120
|
+
openArgs.push('-b', entry.bundleId)
|
|
121
|
+
} else if (entry.path) {
|
|
122
|
+
openArgs.push('-a', entry.path)
|
|
123
|
+
} else if (entry.name) {
|
|
124
|
+
openArgs.push('-a', entry.name)
|
|
125
|
+
}
|
|
126
|
+
if (args.length > 0) {
|
|
127
|
+
openArgs.push('--args', ...args)
|
|
128
|
+
}
|
|
129
|
+
const result = await this.spawnDetached(OPEN_BINARY, openArgs)
|
|
130
|
+
return {
|
|
131
|
+
success: true,
|
|
132
|
+
id: entry.id,
|
|
133
|
+
name: entry.name,
|
|
134
|
+
kind: entry.kind,
|
|
135
|
+
launcher: 'macos-open',
|
|
136
|
+
pid: result.pid || null,
|
|
137
|
+
detail: entry.bundleId || entry.path || entry.name
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async launchUnknown(params = {}) {
|
|
142
|
+
if (!params.app) {
|
|
143
|
+
return super.launchUnknown(params)
|
|
144
|
+
}
|
|
145
|
+
const args = this.sanitizeArgs(params.args)
|
|
146
|
+
const openArgs = ['-a', params.app]
|
|
147
|
+
if (args.length > 0) {
|
|
148
|
+
openArgs.push('--args', ...args)
|
|
149
|
+
}
|
|
150
|
+
const result = await this.spawnDetached(OPEN_BINARY, openArgs)
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
id: null,
|
|
154
|
+
name: params.app,
|
|
155
|
+
kind: 'macos-bundle',
|
|
156
|
+
launcher: 'macos-open',
|
|
157
|
+
pid: result.pid || null,
|
|
158
|
+
detail: params.app
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = MacOSAdapter
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const BaseAdapter = require('./base')
|
|
2
|
+
|
|
3
|
+
class UnsupportedAdapter extends BaseAdapter {
|
|
4
|
+
constructor(kernel) {
|
|
5
|
+
super(kernel)
|
|
6
|
+
this.reason = `App launching is not supported on platform ${this.platform}`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async buildIndex() {
|
|
10
|
+
this.entries.clear()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async search() {
|
|
14
|
+
throw new Error(this.reason)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async info() {
|
|
18
|
+
throw new Error(this.reason)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async refresh() {
|
|
22
|
+
throw new Error(this.reason)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async launch() {
|
|
26
|
+
throw new Error(this.reason)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async launchUnknown() {
|
|
30
|
+
throw new Error(this.reason)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = UnsupportedAdapter
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const os = require('os')
|
|
4
|
+
const { execFile } = require('child_process')
|
|
5
|
+
const { promisify } = require('util')
|
|
6
|
+
const BaseAdapter = require('./base')
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile)
|
|
9
|
+
|
|
10
|
+
class WindowsAdapter extends BaseAdapter {
|
|
11
|
+
constructor(kernel) {
|
|
12
|
+
super(kernel)
|
|
13
|
+
this.systemRoot = process.env.SystemRoot || 'C:\\Windows'
|
|
14
|
+
this.powershellBinary = this.resolvePowerShell()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
resolvePowerShell() {
|
|
18
|
+
const explicit = process.env.POWERSHELL || process.env.POWERSHELL_EXE
|
|
19
|
+
if (explicit) {
|
|
20
|
+
return explicit
|
|
21
|
+
}
|
|
22
|
+
const candidate = path.join(this.systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
|
|
23
|
+
try {
|
|
24
|
+
if (fs.existsSync(candidate)) {
|
|
25
|
+
return candidate
|
|
26
|
+
}
|
|
27
|
+
} catch (_) {
|
|
28
|
+
}
|
|
29
|
+
return 'powershell.exe'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
startMenuDirs() {
|
|
33
|
+
const dirs = []
|
|
34
|
+
const programData = process.env.ProgramData
|
|
35
|
+
if (programData) {
|
|
36
|
+
dirs.push(path.join(programData, 'Microsoft', 'Windows', 'Start Menu', 'Programs'))
|
|
37
|
+
}
|
|
38
|
+
const appData = process.env.APPDATA
|
|
39
|
+
if (appData) {
|
|
40
|
+
dirs.push(path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs'))
|
|
41
|
+
}
|
|
42
|
+
return dirs
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async buildIndex() {
|
|
46
|
+
this.entries.clear()
|
|
47
|
+
await this.collectShortcuts()
|
|
48
|
+
await this.collectStartApps()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async collectShortcuts() {
|
|
52
|
+
const dirs = this.startMenuDirs()
|
|
53
|
+
for (const dir of dirs) {
|
|
54
|
+
await this.walkShortcuts(dir)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async walkShortcuts(root) {
|
|
59
|
+
if (!root) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
const stack = [root]
|
|
63
|
+
const visited = new Set()
|
|
64
|
+
while (stack.length > 0) {
|
|
65
|
+
const current = stack.pop()
|
|
66
|
+
if (!current) {
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
const normalized = path.resolve(current)
|
|
70
|
+
if (visited.has(normalized)) {
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
visited.add(normalized)
|
|
74
|
+
let entries
|
|
75
|
+
try {
|
|
76
|
+
entries = await fs.promises.readdir(normalized, { withFileTypes: true })
|
|
77
|
+
} catch (_) {
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
if (entry.name.startsWith('.')) {
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
const fullPath = path.join(normalized, entry.name)
|
|
85
|
+
if (entry.isDirectory()) {
|
|
86
|
+
stack.push(fullPath)
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
if (entry.isFile() && entry.name.toLowerCase().endsWith('.lnk')) {
|
|
90
|
+
const id = `shortcut:${fullPath.replace(/\\/g, '/')}`
|
|
91
|
+
const name = path.basename(entry.name, '.lnk')
|
|
92
|
+
this.addEntry({
|
|
93
|
+
id,
|
|
94
|
+
name,
|
|
95
|
+
aliases: [name.replace(/ shortcut$/i, ''), fullPath],
|
|
96
|
+
kind: 'windows-shortcut',
|
|
97
|
+
path: fullPath,
|
|
98
|
+
detail: path.relative(root, fullPath).replace(/\\/g, '/')
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async collectStartApps() {
|
|
106
|
+
let apps = []
|
|
107
|
+
try {
|
|
108
|
+
apps = await this.readStartApps()
|
|
109
|
+
} catch (_) {
|
|
110
|
+
apps = []
|
|
111
|
+
}
|
|
112
|
+
for (const app of apps) {
|
|
113
|
+
if (!app || !app.Name || !app.AppId) {
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
const id = `appx:${app.AppId}`
|
|
117
|
+
this.addEntry({
|
|
118
|
+
id,
|
|
119
|
+
name: app.Name,
|
|
120
|
+
aliases: [app.AppId],
|
|
121
|
+
kind: 'windows-appx',
|
|
122
|
+
appId: app.AppId,
|
|
123
|
+
detail: app.AppId
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async readStartApps() {
|
|
129
|
+
const script = `
|
|
130
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
131
|
+
$apps = Get-StartApps | Select-Object -Property Name, AppId
|
|
132
|
+
$apps | ConvertTo-Json -Depth 4 -Compress
|
|
133
|
+
`
|
|
134
|
+
const { stdout } = await this.runPowerShell(script)
|
|
135
|
+
if (!stdout) {
|
|
136
|
+
return []
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const parsed = JSON.parse(stdout)
|
|
140
|
+
if (Array.isArray(parsed)) {
|
|
141
|
+
return parsed
|
|
142
|
+
}
|
|
143
|
+
if (parsed) {
|
|
144
|
+
return [parsed]
|
|
145
|
+
}
|
|
146
|
+
return []
|
|
147
|
+
} catch (_) {
|
|
148
|
+
return []
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
quotePowerShell(value) {
|
|
153
|
+
const str = String(value === undefined || value === null ? '' : value)
|
|
154
|
+
return `'${str.replace(/'/g, "''")}'`
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
buildPowerShellArray(items) {
|
|
158
|
+
if (!items || items.length === 0) {
|
|
159
|
+
return '@()'
|
|
160
|
+
}
|
|
161
|
+
const encoded = items.map((item) => this.quotePowerShell(item))
|
|
162
|
+
return `@(${encoded.join(',')})`
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async runPowerShell(script) {
|
|
166
|
+
const wrapped = `& {\n${script}\n}`
|
|
167
|
+
try {
|
|
168
|
+
return await execFileAsync(this.powershellBinary, [
|
|
169
|
+
'-NoProfile',
|
|
170
|
+
'-NoLogo',
|
|
171
|
+
'-NonInteractive',
|
|
172
|
+
'-ExecutionPolicy',
|
|
173
|
+
'Bypass',
|
|
174
|
+
'-Command',
|
|
175
|
+
wrapped
|
|
176
|
+
], {
|
|
177
|
+
windowsHide: true,
|
|
178
|
+
maxBuffer: 8 * 1024 * 1024
|
|
179
|
+
})
|
|
180
|
+
} catch (error) {
|
|
181
|
+
error.script = script
|
|
182
|
+
throw error
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async launch(entry, params = {}) {
|
|
187
|
+
const args = this.sanitizeArgs(params.args)
|
|
188
|
+
if (entry.kind === 'windows-shortcut') {
|
|
189
|
+
return this.launchShortcut(entry, args)
|
|
190
|
+
}
|
|
191
|
+
if (entry.kind === 'windows-appx') {
|
|
192
|
+
if (args.length > 0) {
|
|
193
|
+
throw new Error('Passing args to Windows Store applications is not supported')
|
|
194
|
+
}
|
|
195
|
+
return this.launchAppx(entry)
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`Unsupported Windows app kind: ${entry.kind}`)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async launchShortcut(entry, args) {
|
|
201
|
+
const argLiteral = this.buildPowerShellArray(args)
|
|
202
|
+
const script = `
|
|
203
|
+
$path = ${this.quotePowerShell(entry.path)}
|
|
204
|
+
$extra = ${argLiteral}
|
|
205
|
+
$ErrorActionPreference = 'Stop'
|
|
206
|
+
$ws = New-Object -ComObject WScript.Shell
|
|
207
|
+
$shortcut = $ws.CreateShortcut($path)
|
|
208
|
+
$target = $shortcut.TargetPath
|
|
209
|
+
if (-not $target) { throw 'Shortcut is missing a target' }
|
|
210
|
+
$argumentList = @()
|
|
211
|
+
if ($shortcut.Arguments) { $argumentList += $shortcut.Arguments }
|
|
212
|
+
if ($extra.Count -gt 0) { $argumentList += $extra }
|
|
213
|
+
$startInfo = @{ FilePath = $target }
|
|
214
|
+
if ($argumentList.Count -gt 0) { $startInfo.ArgumentList = $argumentList }
|
|
215
|
+
if ($shortcut.WorkingDirectory) { $startInfo.WorkingDirectory = $shortcut.WorkingDirectory }
|
|
216
|
+
Start-Process @startInfo | Out-Null
|
|
217
|
+
`
|
|
218
|
+
await this.runPowerShell(script)
|
|
219
|
+
return {
|
|
220
|
+
success: true,
|
|
221
|
+
id: entry.id,
|
|
222
|
+
name: entry.name,
|
|
223
|
+
kind: entry.kind,
|
|
224
|
+
launcher: 'powershell-shortcut',
|
|
225
|
+
detail: entry.path
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async launchAppx(entry) {
|
|
230
|
+
const literal = this.quotePowerShell(`shell:AppsFolder\\${entry.appId}`)
|
|
231
|
+
const script = `
|
|
232
|
+
$ErrorActionPreference = 'Stop'
|
|
233
|
+
Start-Process -FilePath ${literal} | Out-Null
|
|
234
|
+
`
|
|
235
|
+
await this.runPowerShell(script)
|
|
236
|
+
return {
|
|
237
|
+
success: true,
|
|
238
|
+
id: entry.id,
|
|
239
|
+
name: entry.name,
|
|
240
|
+
kind: entry.kind,
|
|
241
|
+
launcher: 'powershell-appx',
|
|
242
|
+
detail: entry.appId
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
module.exports = WindowsAdapter
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { execFile } = require('child_process')
|
|
4
|
+
|
|
5
|
+
async function buildCondaListFromMeta(minicondaPath, useCondaList) {
|
|
6
|
+
if (!useCondaList) {
|
|
7
|
+
const metaOutput = await readFromMeta(minicondaPath)
|
|
8
|
+
if (metaOutput) {
|
|
9
|
+
return { response: metaOutput, source: 'conda-meta' }
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return await runCondaList(minicondaPath)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function readFromMeta(minicondaPath) {
|
|
16
|
+
if (!minicondaPath) {
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
const metaDir = path.join(minicondaPath, 'conda-meta')
|
|
20
|
+
let entries
|
|
21
|
+
try {
|
|
22
|
+
entries = await fs.promises.readdir(metaDir, { withFileTypes: true })
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
const lines = [
|
|
27
|
+
'# packages in environment (generated from conda-meta)',
|
|
28
|
+
'#',
|
|
29
|
+
'# name version build channel'
|
|
30
|
+
]
|
|
31
|
+
let count = 0
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (!entry || !entry.isFile() || !entry.name.endsWith('.json')) {
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
const fullpath = path.join(metaDir, entry.name)
|
|
37
|
+
try {
|
|
38
|
+
const content = await fs.promises.readFile(fullpath, 'utf8')
|
|
39
|
+
const json = JSON.parse(content)
|
|
40
|
+
if (json && json.name && json.version) {
|
|
41
|
+
const build = json.build_string || json.build || 'meta'
|
|
42
|
+
const channel = json.channel || ''
|
|
43
|
+
lines.push(`${json.name} ${json.version} ${build} ${channel}`)
|
|
44
|
+
count++
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
// ignore malformed entries
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (count === 0) {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
return lines.join('\n')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function runCondaList(minicondaPath) {
|
|
57
|
+
const condaBinary = resolveCondaBinary(minicondaPath)
|
|
58
|
+
if (!condaBinary) {
|
|
59
|
+
return { response: '', source: 'conda-list' }
|
|
60
|
+
}
|
|
61
|
+
return await new Promise((resolve) => {
|
|
62
|
+
execFile(condaBinary, ['list'], { windowsHide: true }, (err, stdout) => {
|
|
63
|
+
if (err) {
|
|
64
|
+
resolve({ response: '', source: 'conda-list' })
|
|
65
|
+
} else {
|
|
66
|
+
resolve({ response: stdout || '', source: 'conda-list' })
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveCondaBinary(minicondaPath) {
|
|
73
|
+
if (process.platform === 'win32') {
|
|
74
|
+
if (minicondaPath) {
|
|
75
|
+
const scriptPath = path.join(minicondaPath, 'Scripts', 'conda.exe')
|
|
76
|
+
if (fs.existsSync(scriptPath)) {
|
|
77
|
+
return scriptPath
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return 'conda'
|
|
81
|
+
}
|
|
82
|
+
if (minicondaPath) {
|
|
83
|
+
const binPath = path.join(minicondaPath, 'bin', 'conda')
|
|
84
|
+
if (fs.existsSync(binPath)) {
|
|
85
|
+
return binPath
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return 'conda'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = {
|
|
92
|
+
buildCondaListFromMeta
|
|
93
|
+
}
|
package/kernel/bin/conda.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require('path')
|
|
|
3
3
|
const fetch = require('cross-fetch')
|
|
4
4
|
const { glob } = require('glob')
|
|
5
5
|
const semver = require('semver')
|
|
6
|
+
const { buildCondaListFromMeta } = require('./conda-meta')
|
|
6
7
|
class Conda {
|
|
7
8
|
description = "Pinokio uses Conda to install various useful programs in an isolated manner."
|
|
8
9
|
urls = {
|
|
@@ -122,10 +123,7 @@ report_errors: false`)
|
|
|
122
123
|
// }
|
|
123
124
|
// }
|
|
124
125
|
async check() {
|
|
125
|
-
let res = await this.kernel.bin.
|
|
126
|
-
// process.stdout.write(stream.raw)
|
|
127
|
-
// console.log("conda list check", { stream })
|
|
128
|
-
})
|
|
126
|
+
let res = await buildCondaListFromMeta(this.kernel.bin.path("miniconda"))
|
|
129
127
|
|
|
130
128
|
let lines = res.response.split(/[\r\n]+/)
|
|
131
129
|
let conda_check = {}
|
package/kernel/bin/index.js
CHANGED
|
@@ -22,6 +22,7 @@ const VS = require("./vs")
|
|
|
22
22
|
const Cuda = require("./cuda")
|
|
23
23
|
const Torch = require("./torch")
|
|
24
24
|
const { detectCommandLineTools } = require('./xcode-tools')
|
|
25
|
+
const { buildCondaListFromMeta } = require('./conda-meta')
|
|
25
26
|
const { glob } = require('glob')
|
|
26
27
|
const fakeUa = require('fake-useragent');
|
|
27
28
|
const fse = require('fs-extra')
|
|
@@ -349,10 +350,7 @@ class Bin {
|
|
|
349
350
|
if (to_reset_exists) {
|
|
350
351
|
this.correct_conda = false
|
|
351
352
|
} else {
|
|
352
|
-
res = await this.
|
|
353
|
-
// console.log("conda list check", { stream })
|
|
354
|
-
})
|
|
355
|
-
|
|
353
|
+
let res = await buildCondaListFromMeta(this.kernel.bin.path("miniconda"))
|
|
356
354
|
let lines = res.response.split(/[\r\n]+/)
|
|
357
355
|
for(let line of lines) {
|
|
358
356
|
if (start) {
|
package/kernel/index.js
CHANGED
|
@@ -39,6 +39,7 @@ const Peer = require('./peer')
|
|
|
39
39
|
const Git = require('./git')
|
|
40
40
|
const Connect = require('./connect')
|
|
41
41
|
const Favicon = require('./favicon')
|
|
42
|
+
const AppLauncher = require('./app_launcher')
|
|
42
43
|
const { DownloaderHelper } = require('node-downloader-helper');
|
|
43
44
|
const { ProxyAgent } = require('proxy-agent');
|
|
44
45
|
const fakeUa = require('fake-useragent');
|
|
@@ -867,6 +868,12 @@ class Kernel {
|
|
|
867
868
|
this.api = new Api(this)
|
|
868
869
|
this.python = new Python(this)
|
|
869
870
|
this.shell = new Shells(this)
|
|
871
|
+
this.appLauncher = new AppLauncher(this)
|
|
872
|
+
if (typeof this.appLauncher.init === 'function') {
|
|
873
|
+
this.appLauncher.init().catch((error) => {
|
|
874
|
+
console.warn('[Kernel] AppLauncher init failed:', error && error.message ? error.message : error)
|
|
875
|
+
})
|
|
876
|
+
}
|
|
870
877
|
this.pinokio_domain_value = (process.env.PINOKIO_DOMAIN || '').trim()
|
|
871
878
|
if (this.pinokio_domain_value) {
|
|
872
879
|
this.router = new PinokioDomainRouter(this)
|