pinokiod 7.2.17 → 7.3.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/Dockerfile +2 -0
- package/kernel/api/index.js +1 -42
- package/kernel/api/process/index.js +44 -99
- package/kernel/bin/conda-python.js +30 -0
- package/kernel/bin/conda.js +22 -3
- package/kernel/bin/index.js +11 -1
- package/kernel/environment.js +2 -182
- package/kernel/git.js +13 -0
- package/kernel/index.js +1 -64
- package/kernel/plugin.js +58 -6
- package/kernel/shell.js +2 -21
- package/kernel/util.js +0 -60
- package/package.json +1 -1
- package/server/index.js +149 -176
- package/server/lib/content_validation.js +25 -28
- package/server/public/common.js +29 -103
- package/server/public/create-launcher.js +31 -4
- package/server/public/electron.css +6 -0
- package/server/public/style.css +0 -337
- package/server/public/task-launcher.js +32 -5
- package/server/public/universal-launcher.js +26 -3
- package/server/socket.js +11 -22
- package/server/views/app.ejs +30 -167
- package/server/views/d.ejs +33 -0
- package/server/views/editor.ejs +4 -25
- package/server/views/partials/main_sidebar.ejs +0 -1
- package/server/views/shell.ejs +3 -11
- package/server/views/terminal.ejs +3 -23
- package/server/views/terminals.ejs +0 -1
- package/spec/INSTRUCTION_SYNC.md +5 -5
- package/kernel/api/shell_run_template.js +0 -273
- package/kernel/api/uri/index.js +0 -51
- package/kernel/plugin_sources.js +0 -236
- package/kernel/watch/context.js +0 -42
- package/kernel/watch/drivers/fs.js +0 -71
- package/kernel/watch/drivers/poll.js +0 -33
- package/kernel/watch/index.js +0 -158
- package/server/features/drafts/index.js +0 -41
- package/server/features/drafts/parser.js +0 -169
- package/server/features/drafts/public/drafts.js +0 -1504
- package/server/features/drafts/registry_import.js +0 -412
- package/server/features/drafts/routes.js +0 -68
- package/server/features/drafts/service.js +0 -261
- package/server/features/drafts/watcher.js +0 -76
- package/server/features/index.js +0 -13
- package/server/lib/workspace_catalog.js +0 -151
- package/server/lib/workspace_runtime.js +0 -390
- package/server/routes/workspaces.js +0 -44
- package/server/views/partials/workspace_row.ejs +0 -61
- package/server/views/workspaces.ejs +0 -812
- package/system/plugin/antigravity/antigravity.png +0 -0
- package/system/plugin/antigravity/pinokio.js +0 -37
- package/system/plugin/claude/claude.png +0 -0
- package/system/plugin/claude/pinokio.js +0 -63
- package/system/plugin/claude-auto/claude.png +0 -0
- package/system/plugin/claude-auto/pinokio.js +0 -74
- package/system/plugin/claude-desktop/icon.jpeg +0 -0
- package/system/plugin/claude-desktop/pinokio.js +0 -39
- package/system/plugin/codex/openai.webp +0 -0
- package/system/plugin/codex/pinokio.js +0 -58
- package/system/plugin/codex-auto/openai.webp +0 -0
- package/system/plugin/codex-auto/pinokio.js +0 -65
- package/system/plugin/codex-desktop/icon.png +0 -0
- package/system/plugin/codex-desktop/pinokio.js +0 -39
- package/system/plugin/crush/crush.png +0 -0
- package/system/plugin/crush/pinokio.js +0 -31
- package/system/plugin/cursor/cursor.jpeg +0 -0
- package/system/plugin/cursor/pinokio.js +0 -39
- package/system/plugin/gemini/gemini.jpeg +0 -0
- package/system/plugin/gemini/pinokio.js +0 -40
- package/system/plugin/gemini-auto/gemini.jpeg +0 -0
- package/system/plugin/gemini-auto/pinokio.js +0 -43
- package/system/plugin/qwen/pinokio.js +0 -50
- package/system/plugin/qwen/qwen.png +0 -0
- package/system/plugin/vscode/pinokio.js +0 -36
- package/system/plugin/vscode/vscode.png +0 -0
- package/system/plugin/windsurf/pinokio.js +0 -39
- package/system/plugin/windsurf/windsurf.png +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const fs = require("fs")
|
|
2
2
|
const path = require("path")
|
|
3
3
|
const clearModule = require("clear-module")
|
|
4
|
-
const PluginSources = require("../../kernel/plugin_sources")
|
|
5
4
|
|
|
6
5
|
const {
|
|
7
6
|
TASK_CONFIG_FILENAME,
|
|
@@ -118,7 +117,23 @@ function createContentValidationService({ kernel }) {
|
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
const normalizePluginPath = (value) => {
|
|
121
|
-
|
|
120
|
+
let normalized = typeof value === "string" ? value.trim() : ""
|
|
121
|
+
if (!normalized) {
|
|
122
|
+
return ""
|
|
123
|
+
}
|
|
124
|
+
normalized = normalized.replace(/\\/g, "/")
|
|
125
|
+
normalized = normalized.replace(/^\/run(?=\/)/, "")
|
|
126
|
+
if (!normalized.startsWith("/")) {
|
|
127
|
+
normalized = `/${normalized}`
|
|
128
|
+
}
|
|
129
|
+
normalized = normalized.replace(/\/{2,}/g, "/").replace(/\/+$/, "")
|
|
130
|
+
if (!normalized) {
|
|
131
|
+
return ""
|
|
132
|
+
}
|
|
133
|
+
if (!normalized.endsWith("/pinokio.js")) {
|
|
134
|
+
normalized = `${normalized}/pinokio.js`
|
|
135
|
+
}
|
|
136
|
+
return normalized
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
const normalizeBundledPluginSpec = (value) => {
|
|
@@ -154,9 +169,9 @@ function createContentValidationService({ kernel }) {
|
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
if (config && typeof config.icon === "string" && config.icon.trim()) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
context.image =
|
|
172
|
+
if (normalizedPath.startsWith("/plugin/")) {
|
|
173
|
+
const relativeDir = path.posix.dirname(normalizedPath.slice(1))
|
|
174
|
+
context.image = `/asset/${relativeDir}/${config.icon.trim()}`
|
|
160
175
|
} else if (normalizedPath.startsWith("/api/")) {
|
|
161
176
|
const segments = normalizedPath.replace(/^\/+/, "").split("/")
|
|
162
177
|
const appName = segments[1] || ""
|
|
@@ -177,10 +192,7 @@ function createContentValidationService({ kernel }) {
|
|
|
177
192
|
const fallbackTitle = normalizedPath
|
|
178
193
|
? path.basename(path.posix.dirname(normalizedPath))
|
|
179
194
|
: "Plugin"
|
|
180
|
-
|
|
181
|
-
const isHomePlugin = normalizedPath.startsWith("/plugin/")
|
|
182
|
-
const isApiPlugin = normalizedPath.startsWith("/api/")
|
|
183
|
-
if (!normalizedPath || (!isSystemPlugin && !isHomePlugin && !isApiPlugin)) {
|
|
195
|
+
if (!normalizedPath || (!normalizedPath.startsWith("/plugin/") && !normalizedPath.startsWith("/api/"))) {
|
|
184
196
|
return buildInvalid({
|
|
185
197
|
type: "plugin",
|
|
186
198
|
subjectTitle: fallbackTitle,
|
|
@@ -193,21 +205,8 @@ function createContentValidationService({ kernel }) {
|
|
|
193
205
|
detailUrl: "/plugins",
|
|
194
206
|
})
|
|
195
207
|
}
|
|
196
|
-
if (PluginSources.isLegacyPluginCodePath(normalizedPath)) {
|
|
197
|
-
return buildInvalid({
|
|
198
|
-
type: "plugin",
|
|
199
|
-
subjectTitle: fallbackTitle,
|
|
200
|
-
errors: [
|
|
201
|
-
buildError(
|
|
202
|
-
"This managed plugin path is no longer used.",
|
|
203
|
-
"Open the built-in Pinokio plugin instead."
|
|
204
|
-
),
|
|
205
|
-
],
|
|
206
|
-
detailUrl: "/plugins",
|
|
207
|
-
})
|
|
208
|
-
}
|
|
209
208
|
|
|
210
|
-
const absolutePath =
|
|
209
|
+
const absolutePath = kernel.path(normalizedPath.replace(/^\/+/, ""))
|
|
211
210
|
const folderPath = path.dirname(absolutePath)
|
|
212
211
|
const loaded = await loadJsFile(absolutePath)
|
|
213
212
|
if (!loaded.exists) {
|
|
@@ -268,8 +267,9 @@ function createContentValidationService({ kernel }) {
|
|
|
268
267
|
))
|
|
269
268
|
}
|
|
270
269
|
if (normalizedPath.startsWith("/plugin/")) {
|
|
270
|
+
const isManagedDefaultPlugin = normalizedPath.startsWith("/plugin/code/")
|
|
271
271
|
const declaredPath = typeof config.path === "string" ? config.path.trim() : ""
|
|
272
|
-
if (declaredPath !== "plugin") {
|
|
272
|
+
if (!isManagedDefaultPlugin && declaredPath !== "plugin") {
|
|
273
273
|
errors.push(buildError(
|
|
274
274
|
'Standalone plugins must set `path: "plugin"`.',
|
|
275
275
|
'Add `path: "plugin"` to pinokio.js.',
|
|
@@ -567,7 +567,7 @@ function createContentValidationService({ kernel }) {
|
|
|
567
567
|
})
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
const validateRunPath = async (pathComponents
|
|
570
|
+
const validateRunPath = async (pathComponents) => {
|
|
571
571
|
if (!Array.isArray(pathComponents) || pathComponents.length === 0) {
|
|
572
572
|
return buildValid()
|
|
573
573
|
}
|
|
@@ -576,9 +576,6 @@ function createContentValidationService({ kernel }) {
|
|
|
576
576
|
return buildValid()
|
|
577
577
|
}
|
|
578
578
|
if (normalized[0] === "plugin") {
|
|
579
|
-
if (options && options.system) {
|
|
580
|
-
return validatePluginByPath(`${PluginSources.SYSTEM_RUN_PREFIX}/${normalized.join("/")}`)
|
|
581
|
-
}
|
|
582
579
|
return validatePluginByPath(`/${normalized.join("/")}`)
|
|
583
580
|
}
|
|
584
581
|
if (normalized[0] === "api" && normalized.length > 1) {
|
package/server/public/common.js
CHANGED
|
@@ -16,7 +16,6 @@ const requirementsGuardedRoutePrefixes = [
|
|
|
16
16
|
'/api/',
|
|
17
17
|
'/_api/',
|
|
18
18
|
'/run/',
|
|
19
|
-
'/pinokio/run/',
|
|
20
19
|
'/tools',
|
|
21
20
|
'/bundle/',
|
|
22
21
|
'/connect/',
|
|
@@ -105,96 +104,6 @@ function createMinimalLoadingSwal () {
|
|
|
105
104
|
});
|
|
106
105
|
return close;
|
|
107
106
|
}
|
|
108
|
-
|
|
109
|
-
const PINOKIO_WAIT_FOOTER_ID = 'pinokio-process-wait-footer-status'
|
|
110
|
-
const PINOKIO_INSTALL_SPINNER_VARIANTS = ["grid-shift", "single-vacancy", "pulse-swap", "corner-chase", "flip-2x2", "stack-ripple", "pixel-orbit", "binary-shuffle"]
|
|
111
|
-
const PINOKIO_INSTALL_STATUS_SPINNER_HTML = `
|
|
112
|
-
<span class="install-status-spinner" aria-hidden="true">
|
|
113
|
-
<span class="install-status-grid-anchor install-status-grid-anchor-1"></span>
|
|
114
|
-
<span class="install-status-grid-anchor install-status-grid-anchor-2"></span>
|
|
115
|
-
<span class="install-status-grid-anchor install-status-grid-anchor-3"></span>
|
|
116
|
-
<span class="install-status-grid-anchor install-status-grid-anchor-4"></span>
|
|
117
|
-
<span class="install-status-grid-tile install-status-grid-tile-1"></span>
|
|
118
|
-
<span class="install-status-grid-tile install-status-grid-tile-2"></span>
|
|
119
|
-
<span class="install-status-grid-tile install-status-grid-tile-3"></span>
|
|
120
|
-
<span class="install-status-grid-tile install-status-grid-tile-4"></span>
|
|
121
|
-
<span class="install-status-grid-chaser"></span>
|
|
122
|
-
<span class="install-status-grid-pixel"></span>
|
|
123
|
-
</span>`
|
|
124
|
-
let lastPinokioWaitSpinnerVariant = null
|
|
125
|
-
|
|
126
|
-
function escapePinokioStatusHtml(value) {
|
|
127
|
-
return String(value == null ? '' : value)
|
|
128
|
-
.replace(/&/g, '&')
|
|
129
|
-
.replace(/</g, '<')
|
|
130
|
-
.replace(/>/g, '>')
|
|
131
|
-
.replace(/"/g, '"')
|
|
132
|
-
.replace(/'/g, ''')
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function ensurePinokioWaitFooterStatus() {
|
|
136
|
-
let status = document.getElementById(PINOKIO_WAIT_FOOTER_ID)
|
|
137
|
-
if (status) {
|
|
138
|
-
return status
|
|
139
|
-
}
|
|
140
|
-
status = document.createElement('div')
|
|
141
|
-
status.id = PINOKIO_WAIT_FOOTER_ID
|
|
142
|
-
status.className = 'pinokio-install-inline-status pinokio-process-wait-footer-status'
|
|
143
|
-
status.hidden = true
|
|
144
|
-
status.setAttribute('aria-live', 'polite')
|
|
145
|
-
|
|
146
|
-
const anchor = document.querySelector('.terminal-container')
|
|
147
|
-
|| document.querySelector('main')
|
|
148
|
-
|| document.querySelector('#terminal')?.parentElement
|
|
149
|
-
|| document.querySelector('footer')
|
|
150
|
-
|| document.body
|
|
151
|
-
|
|
152
|
-
if (anchor && anchor !== document.body) {
|
|
153
|
-
anchor.insertAdjacentElement('afterend', status)
|
|
154
|
-
} else {
|
|
155
|
-
document.body.appendChild(status)
|
|
156
|
-
}
|
|
157
|
-
return status
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function showPinokioWaitFooterStatus(data = {}) {
|
|
161
|
-
const title = data.title || 'Waiting'
|
|
162
|
-
const description = data.description || data.message || ''
|
|
163
|
-
const status = ensurePinokioWaitFooterStatus()
|
|
164
|
-
const candidates = PINOKIO_INSTALL_SPINNER_VARIANTS.filter((variant) => variant !== lastPinokioWaitSpinnerVariant)
|
|
165
|
-
const pool = candidates.length > 0 ? candidates : PINOKIO_INSTALL_SPINNER_VARIANTS
|
|
166
|
-
lastPinokioWaitSpinnerVariant = pool[Math.floor(Math.random() * pool.length)]
|
|
167
|
-
document.documentElement.dataset.installSpinnerVariant = lastPinokioWaitSpinnerVariant
|
|
168
|
-
status.className = 'pinokio-install-inline-status pinokio-process-wait-footer-status is-progress'
|
|
169
|
-
status.innerHTML = `
|
|
170
|
-
<div class="install-status-shell">
|
|
171
|
-
${PINOKIO_INSTALL_STATUS_SPINNER_HTML}
|
|
172
|
-
<div class="install-status-copy">
|
|
173
|
-
<div class="install-status-title">${escapePinokioStatusHtml(title)}</div>
|
|
174
|
-
${description ? `<div class="install-status-detail">${escapePinokioStatusHtml(description)}</div>` : ''}
|
|
175
|
-
</div>
|
|
176
|
-
</div>
|
|
177
|
-
`
|
|
178
|
-
status.hidden = false
|
|
179
|
-
document.body.classList.add('pinokio-install-status-visible')
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function hidePinokioWaitFooterStatus() {
|
|
183
|
-
const status = document.getElementById(PINOKIO_WAIT_FOOTER_ID)
|
|
184
|
-
if (!status) {
|
|
185
|
-
return
|
|
186
|
-
}
|
|
187
|
-
status.hidden = true
|
|
188
|
-
status.innerHTML = ''
|
|
189
|
-
document.body.classList.remove('pinokio-install-status-visible')
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (typeof window !== 'undefined') {
|
|
193
|
-
window.PinokioWaitFooterStatus = {
|
|
194
|
-
show: showPinokioWaitFooterStatus,
|
|
195
|
-
hide: hidePinokioWaitFooterStatus
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
107
|
function check_ready (targetUrl = null, options = {}) {
|
|
199
108
|
createLauncherDebugLog('check_ready start');
|
|
200
109
|
return fetch("/pinokio/requirements_ready").then((res) => {
|
|
@@ -2419,14 +2328,6 @@ if (typeof hotkeys === 'function') {
|
|
|
2419
2328
|
playNextSound();
|
|
2420
2329
|
};
|
|
2421
2330
|
|
|
2422
|
-
window.PinokioPlayNotificationSound = (sound) => {
|
|
2423
|
-
if (sound === false || isFalseyString(sound)) {
|
|
2424
|
-
return false;
|
|
2425
|
-
}
|
|
2426
|
-
enqueueSound(typeof sound === 'string' && sound ? sound : '/chime.mp3');
|
|
2427
|
-
return true;
|
|
2428
|
-
};
|
|
2429
|
-
|
|
2430
2331
|
const handlePacket = (packet) => {
|
|
2431
2332
|
if (!packet || packet.id !== CHANNEL_ID || packet.type !== 'notification') {
|
|
2432
2333
|
return;
|
|
@@ -3581,6 +3482,32 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
3581
3482
|
tools: null,
|
|
3582
3483
|
toolsPromise: null
|
|
3583
3484
|
};
|
|
3485
|
+
const ASK_AI_FALLBACK_TOOLS = [
|
|
3486
|
+
{
|
|
3487
|
+
value: 'claude',
|
|
3488
|
+
label: 'Claude Code',
|
|
3489
|
+
iconSrc: '/asset/plugin/code/claude/claude.png',
|
|
3490
|
+
href: '/run/plugin/code/claude/pinokio.js',
|
|
3491
|
+
category: 'CLI',
|
|
3492
|
+
isDefault: true
|
|
3493
|
+
},
|
|
3494
|
+
{
|
|
3495
|
+
value: 'codex',
|
|
3496
|
+
label: 'OpenAI Codex',
|
|
3497
|
+
iconSrc: '/asset/plugin/code/codex/openai.webp',
|
|
3498
|
+
href: '/run/plugin/code/codex/pinokio.js',
|
|
3499
|
+
category: 'CLI',
|
|
3500
|
+
isDefault: false
|
|
3501
|
+
},
|
|
3502
|
+
{
|
|
3503
|
+
value: 'gemini',
|
|
3504
|
+
label: 'Google Gemini CLI',
|
|
3505
|
+
iconSrc: '/asset/plugin/code/gemini/gemini.jpeg',
|
|
3506
|
+
href: '/run/plugin/code/gemini/pinokio.js',
|
|
3507
|
+
category: 'CLI',
|
|
3508
|
+
isDefault: false
|
|
3509
|
+
}
|
|
3510
|
+
];
|
|
3584
3511
|
|
|
3585
3512
|
initializeUniversalLauncherIntegration();
|
|
3586
3513
|
initializeCreateLauncherIntegration();
|
|
@@ -3993,7 +3920,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
3993
3920
|
return typeof pathname === 'string'
|
|
3994
3921
|
&& (
|
|
3995
3922
|
pathname.startsWith('/run/plugin/')
|
|
3996
|
-
|| pathname.startsWith('/pinokio/run/plugin/')
|
|
3997
3923
|
|| (pathname.startsWith('/run/api/') && /\/pinokio\.js$/i.test(pathname))
|
|
3998
3924
|
);
|
|
3999
3925
|
}
|
|
@@ -4085,11 +4011,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
4085
4011
|
})
|
|
4086
4012
|
.then((payload) => {
|
|
4087
4013
|
const mapped = mapPluginMenuToAskAiTools(payload && Array.isArray(payload.menu) ? payload.menu : []);
|
|
4088
|
-
return mapped;
|
|
4014
|
+
return mapped.length > 0 ? mapped : ASK_AI_FALLBACK_TOOLS.slice();
|
|
4089
4015
|
})
|
|
4090
4016
|
.catch((error) => {
|
|
4091
|
-
console.warn('Failed to load Ask AI plugins', error);
|
|
4092
|
-
return
|
|
4017
|
+
console.warn('Failed to load Ask AI plugins, using fallback list', error);
|
|
4018
|
+
return ASK_AI_FALLBACK_TOOLS.slice();
|
|
4093
4019
|
})
|
|
4094
4020
|
.finally(() => {
|
|
4095
4021
|
askAiState.toolsPromise = null;
|
|
@@ -5,6 +5,33 @@
|
|
|
5
5
|
return;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
const FALLBACK_TOOLS = [
|
|
9
|
+
{
|
|
10
|
+
value: 'claude',
|
|
11
|
+
label: 'Claude Code',
|
|
12
|
+
iconSrc: '/asset/plugin/code/claude/claude.png',
|
|
13
|
+
isDefault: true,
|
|
14
|
+
href: '/run/plugin/code/claude/pinokio.js',
|
|
15
|
+
category: 'CLI',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
value: 'codex',
|
|
19
|
+
label: 'OpenAI Codex',
|
|
20
|
+
iconSrc: '/asset/plugin/code/codex/openai.webp',
|
|
21
|
+
isDefault: false,
|
|
22
|
+
href: '/run/plugin/code/codex/pinokio.js',
|
|
23
|
+
category: 'CLI',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: 'gemini',
|
|
27
|
+
label: 'Google Gemini CLI',
|
|
28
|
+
iconSrc: '/asset/plugin/code/gemini/gemini.jpeg',
|
|
29
|
+
isDefault: false,
|
|
30
|
+
href: '/run/plugin/code/gemini/pinokio.js',
|
|
31
|
+
category: 'CLI',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
8
35
|
const CATEGORY_ORDER = ['CLI', 'IDE'];
|
|
9
36
|
const MODAL_VARIANTS = {
|
|
10
37
|
CREATE: 'create',
|
|
@@ -56,7 +83,7 @@
|
|
|
56
83
|
|
|
57
84
|
let value = '';
|
|
58
85
|
if (href) {
|
|
59
|
-
// Normalize href to a
|
|
86
|
+
// Normalize href to a plugin-relative path for the backend (e.g., code/codex)
|
|
60
87
|
const normalized = href.replace(/^\/run/, '').replace(/^\/+/, '');
|
|
61
88
|
const parts = normalized.split('/').filter(Boolean);
|
|
62
89
|
// Expect /plugin/<path...>/pinokio.js -> want <path...>
|
|
@@ -112,11 +139,11 @@
|
|
|
112
139
|
.then((data) => {
|
|
113
140
|
const menu = data && Array.isArray(data.menu) ? data.menu : [];
|
|
114
141
|
const tools = mapPluginMenuToCreateLauncherTools(menu);
|
|
115
|
-
return tools;
|
|
142
|
+
return tools.length > 0 ? tools : FALLBACK_TOOLS.slice();
|
|
116
143
|
})
|
|
117
144
|
.catch((error) => {
|
|
118
|
-
console.warn('
|
|
119
|
-
return
|
|
145
|
+
console.warn('Falling back to default plugins for create launcher modal', error);
|
|
146
|
+
return FALLBACK_TOOLS.slice();
|
|
120
147
|
})
|
|
121
148
|
.finally(() => {
|
|
122
149
|
loadingTools = null;
|