pinokiod 3.270.0 → 3.272.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.
Files changed (56) hide show
  1. package/kernel/ansi_stream_tracker.js +115 -0
  2. package/kernel/api/app/index.js +422 -0
  3. package/kernel/api/htmlmodal/index.js +94 -0
  4. package/kernel/app_launcher/index.js +115 -0
  5. package/kernel/app_launcher/platform/base.js +276 -0
  6. package/kernel/app_launcher/platform/linux.js +229 -0
  7. package/kernel/app_launcher/platform/macos.js +163 -0
  8. package/kernel/app_launcher/platform/unsupported.js +34 -0
  9. package/kernel/app_launcher/platform/windows.js +247 -0
  10. package/kernel/bin/conda-meta.js +93 -0
  11. package/kernel/bin/conda.js +2 -4
  12. package/kernel/bin/index.js +2 -4
  13. package/kernel/index.js +9 -2
  14. package/kernel/peer.js +186 -19
  15. package/kernel/shell.js +212 -1
  16. package/package.json +1 -1
  17. package/server/index.js +491 -6
  18. package/server/public/common.js +224 -741
  19. package/server/public/create-launcher.js +754 -0
  20. package/server/public/htmlmodal.js +292 -0
  21. package/server/public/logs.js +715 -0
  22. package/server/public/resizeSync.js +117 -0
  23. package/server/public/style.css +651 -6
  24. package/server/public/tab-idle-notifier.js +34 -59
  25. package/server/public/tab-link-popover.js +7 -10
  26. package/server/public/terminal-settings.js +723 -9
  27. package/server/public/terminal_input_utils.js +72 -0
  28. package/server/public/terminal_key_caption.js +187 -0
  29. package/server/public/urldropdown.css +120 -3
  30. package/server/public/xterm-inline-bridge.js +116 -0
  31. package/server/socket.js +29 -0
  32. package/server/views/agents.ejs +1 -2
  33. package/server/views/app.ejs +55 -28
  34. package/server/views/bookmarklet.ejs +1 -1
  35. package/server/views/bootstrap.ejs +1 -0
  36. package/server/views/connect.ejs +1 -2
  37. package/server/views/create.ejs +63 -0
  38. package/server/views/editor.ejs +36 -4
  39. package/server/views/index.ejs +1 -2
  40. package/server/views/index2.ejs +1 -2
  41. package/server/views/init/index.ejs +36 -28
  42. package/server/views/install.ejs +20 -22
  43. package/server/views/layout.ejs +2 -8
  44. package/server/views/logs.ejs +155 -0
  45. package/server/views/mini.ejs +0 -18
  46. package/server/views/net.ejs +2 -2
  47. package/server/views/network.ejs +1 -2
  48. package/server/views/network2.ejs +1 -2
  49. package/server/views/old_network.ejs +1 -2
  50. package/server/views/pro.ejs +26 -23
  51. package/server/views/prototype/index.ejs +30 -34
  52. package/server/views/screenshots.ejs +1 -2
  53. package/server/views/settings.ejs +1 -20
  54. package/server/views/shell.ejs +59 -66
  55. package/server/views/terminal.ejs +118 -73
  56. 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
+ }
@@ -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.exec({ message: `conda list` }, (stream) => {
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 = {}
@@ -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.exec({ message: `conda list` }, (stream) => {
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');
@@ -803,6 +804,7 @@ class Kernel {
803
804
  async init(options) {
804
805
 
805
806
  let home = this.store.get("home") || process.env.PINOKIO_HOME
807
+ this.homedir = home
806
808
 
807
809
  // reset shells if they exist
808
810
  if (this.shell) {
@@ -820,10 +822,9 @@ class Kernel {
820
822
  this.kv = new KV(this)
821
823
  this.cloudflare = new Cloudflare()
822
824
  this.peer = new Peer(this)
825
+ await this.peer.initialize(this)
823
826
  this.git = new Git(this)
824
827
 
825
- this.homedir = home
826
-
827
828
  // if (home) {
828
829
  // this.homedir = home
829
830
  // } else {
@@ -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)