Update README and API for Docker integration and app management
- Revised README.md to clarify the use of Docker alongside Podman for containerization. - Updated API documentation to reflect new RPC endpoints, including `auth.logout`. - Enhanced WebSocket handling in the API for better connection management. - Modified Neode UI to utilize a curated list of Docker-based applications, replacing previous Start9 registry calls. - Improved error handling and logging in the marketplace for better user experience.
This commit is contained in:
@@ -82,7 +82,7 @@ define(['./workbox-21a80088'], (function (workbox) { 'use strict';
|
||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||
}, {
|
||||
"url": "index.html",
|
||||
"revision": "0.6sndaq9c8b"
|
||||
"revision": "0.8a4s3o0lhfc"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
|
||||
@@ -23,7 +23,7 @@ export const dummyApps: Record<string, PackageDataEntry> = {
|
||||
},
|
||||
'release-notes': 'Initial release',
|
||||
license: 'MIT',
|
||||
'wrapper-repo': 'https://github.com/Start9Labs/bitcoind-startos',
|
||||
'wrapper-repo': 'https://github.com/bitcoin/bitcoin',
|
||||
'upstream-repo': 'https://github.com/bitcoin/bitcoin',
|
||||
'support-site': 'https://github.com/bitcoin/bitcoin/issues',
|
||||
'marketing-site': 'https://bitcoin.org',
|
||||
@@ -59,7 +59,7 @@ export const dummyApps: Record<string, PackageDataEntry> = {
|
||||
},
|
||||
'release-notes': 'Initial release',
|
||||
license: 'MIT',
|
||||
'wrapper-repo': 'https://github.com/Start9Labs/btcpayserver-startos',
|
||||
'wrapper-repo': 'https://github.com/btcpayserver/btcpayserver',
|
||||
'upstream-repo': 'https://github.com/btcpayserver/btcpayserver',
|
||||
'support-site': 'https://github.com/btcpayserver/btcpayserver/issues',
|
||||
'marketing-site': 'https://btcpayserver.org',
|
||||
@@ -95,7 +95,7 @@ export const dummyApps: Record<string, PackageDataEntry> = {
|
||||
},
|
||||
'release-notes': 'Initial release',
|
||||
license: 'Apache-2.0',
|
||||
'wrapper-repo': 'https://github.com/Start9Labs/home-assistant-startos',
|
||||
'wrapper-repo': 'https://github.com/home-assistant/core',
|
||||
'upstream-repo': 'https://github.com/home-assistant/core',
|
||||
'support-site': 'https://github.com/home-assistant/core/issues',
|
||||
'marketing-site': 'https://www.home-assistant.io',
|
||||
@@ -275,7 +275,7 @@ export const dummyApps: Record<string, PackageDataEntry> = {
|
||||
},
|
||||
'release-notes': 'Initial release',
|
||||
license: 'MIT',
|
||||
'wrapper-repo': 'https://github.com/Start9Labs/lnd-startos',
|
||||
'wrapper-repo': 'https://github.com/lightningnetwork/lnd',
|
||||
'upstream-repo': 'https://github.com/lightningnetwork/lnd',
|
||||
'support-site': 'https://github.com/lightningnetwork/lnd/issues',
|
||||
'marketing-site': 'https://lightning.network',
|
||||
@@ -311,7 +311,7 @@ export const dummyApps: Record<string, PackageDataEntry> = {
|
||||
},
|
||||
'release-notes': 'Initial release',
|
||||
license: 'AGPL-3.0',
|
||||
'wrapper-repo': 'https://github.com/Start9Labs/mempool-startos',
|
||||
'wrapper-repo': 'https://github.com/mempool/mempool',
|
||||
'upstream-repo': 'https://github.com/mempool/mempool',
|
||||
'support-site': 'https://github.com/mempool/mempool/issues',
|
||||
'marketing-site': 'https://mempool.space',
|
||||
|
||||
@@ -322,8 +322,18 @@ function handleImageError(e: Event) {
|
||||
// Fetch GitHub app info for dummy apps on mount
|
||||
const appInfoCache = ref<Record<string, any>>({})
|
||||
|
||||
// Watch for packages and fetch app info when showing dummy apps
|
||||
// In development, skip external API calls to avoid rate limiting and noise
|
||||
// App icons and descriptions are already defined in dummyApps.ts
|
||||
const isDev = import.meta.env.DEV
|
||||
|
||||
// Watch for packages and fetch app info when showing dummy apps (DISABLED IN DEV)
|
||||
watch(() => Object.keys(store.packages).length, async (packageCount) => {
|
||||
// Skip external API calls in development to avoid 403/404 errors
|
||||
if (isDev) {
|
||||
console.log('[Apps] Using local app data (dev mode, external API calls disabled)')
|
||||
return
|
||||
}
|
||||
|
||||
// Only fetch if we're showing dummy apps (no real packages)
|
||||
if (packageCount === 0) {
|
||||
try {
|
||||
@@ -366,7 +376,8 @@ watch(() => Object.keys(store.packages).length, async (packageCount) => {
|
||||
return
|
||||
}
|
||||
} catch (registryErr) {
|
||||
console.warn('[Apps] Start9 registry unavailable, trying GitHub...', registryErr)
|
||||
// Silently fail in production
|
||||
console.debug('[Apps] Registry unavailable')
|
||||
}
|
||||
|
||||
// Fallback to GitHub fetching
|
||||
@@ -394,7 +405,7 @@ watch(() => Object.keys(store.packages).length, async (packageCount) => {
|
||||
|
||||
console.log('[Apps] GitHub info fetched:', Object.keys(githubInfo).length, 'apps')
|
||||
} catch (err) {
|
||||
console.error('[Apps] Failed to fetch app info:', err)
|
||||
console.debug('[Apps] External API fetch skipped or failed')
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
+240
-127
@@ -555,128 +555,242 @@ async function loadCommunityMarketplace() {
|
||||
loadingCommunity.value = true
|
||||
communityError.value = ''
|
||||
|
||||
try {
|
||||
// Try fetching from Start9 community registry
|
||||
const response = await fetch('https://registry.start9.com/api/v1/packages', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
||||
// Use curated list of Docker-based apps
|
||||
// These are standard Docker images, not StartOS packages
|
||||
console.log('📦 Loading Docker-based app marketplace')
|
||||
communityApps.value = getCuratedAppList()
|
||||
loadingCommunity.value = false
|
||||
}
|
||||
|
||||
// Curated list of apps with Docker Hub images
|
||||
function getCuratedAppList() {
|
||||
return [
|
||||
{
|
||||
id: 'bitcoin',
|
||||
title: 'Bitcoin Core',
|
||||
version: '27.0.0',
|
||||
description: 'Run a full Bitcoin node. Validate and relay blocks and transactions on the Bitcoin network.',
|
||||
icon: '/assets/img/app-icons/bitcoin.svg',
|
||||
author: 'Bitcoin Core',
|
||||
dockerImage: 'lncm/bitcoind:v27.0',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/bitcoin/bitcoin'
|
||||
},
|
||||
{
|
||||
id: 'btcpay-server',
|
||||
title: 'BTCPay Server',
|
||||
version: '1.13.5',
|
||||
description: 'Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries or fees.',
|
||||
icon: '/assets/img/app-icons/btcpay-server.png',
|
||||
author: 'BTCPay Server Foundation',
|
||||
dockerImage: 'btcpayserver/btcpayserver:1.13.5',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/btcpayserver/btcpayserver'
|
||||
},
|
||||
{
|
||||
id: 'lnd',
|
||||
title: 'LND',
|
||||
version: '0.17.4',
|
||||
description: 'Lightning Network Daemon. Fast and cheap Bitcoin payments through the Lightning Network.',
|
||||
icon: '/assets/img/app-icons/lightning-stack.png',
|
||||
author: 'Lightning Labs',
|
||||
dockerImage: 'lightninglabs/lnd:v0.17.4-beta',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/lightningnetwork/lnd'
|
||||
},
|
||||
{
|
||||
id: 'mempool',
|
||||
title: 'Mempool Explorer',
|
||||
version: '2.5.0',
|
||||
description: 'Self-hosted Bitcoin blockchain and mempool visualizer with beautiful explorer interface.',
|
||||
icon: '/assets/img/app-icons/mempool.png',
|
||||
author: 'Mempool',
|
||||
dockerImage: 'mempool/frontend:v2.5.0',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/mempool/mempool'
|
||||
},
|
||||
{
|
||||
id: 'homeassistant',
|
||||
title: 'Home Assistant',
|
||||
version: '2024.1',
|
||||
description: 'Open-source home automation platform. Control and automate your smart home devices privately.',
|
||||
icon: '/assets/img/app-icons/homeassistant.png',
|
||||
author: 'Home Assistant',
|
||||
dockerImage: 'homeassistant/home-assistant:2024.1',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/home-assistant/core'
|
||||
},
|
||||
{
|
||||
id: 'grafana',
|
||||
title: 'Grafana',
|
||||
version: '10.2.0',
|
||||
description: 'Analytics and monitoring platform. Create dashboards and visualize data from multiple sources.',
|
||||
icon: '/assets/img/grafana.png',
|
||||
author: 'Grafana Labs',
|
||||
dockerImage: 'grafana/grafana:10.2.0',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/grafana/grafana'
|
||||
},
|
||||
{
|
||||
id: 'searxng',
|
||||
title: 'SearXNG',
|
||||
version: '2024.1.0',
|
||||
description: 'Privacy-respecting metasearch engine. Search without tracking or ads.',
|
||||
icon: '/assets/img/app-icons/searxng.png',
|
||||
author: 'SearXNG',
|
||||
dockerImage: 'searxng/searxng:latest',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/searxng/searxng'
|
||||
},
|
||||
{
|
||||
id: 'ollama',
|
||||
title: 'Ollama',
|
||||
version: '0.1.0',
|
||||
description: 'Run large language models locally. Download and run AI models like Llama, Mistral on your own hardware.',
|
||||
icon: '/assets/img/ollama.webp',
|
||||
author: 'Ollama',
|
||||
dockerImage: 'ollama/ollama:latest',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/ollama/ollama'
|
||||
},
|
||||
{
|
||||
id: 'onlyoffice',
|
||||
title: 'OnlyOffice',
|
||||
version: '7.5.1',
|
||||
description: 'Office suite for document collaboration. Edit docs, spreadsheets, and presentations.',
|
||||
icon: '/assets/img/onlyoffice.webp',
|
||||
author: 'Ascensio System SIA',
|
||||
dockerImage: 'onlyoffice/documentserver:7.5.1',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/ONLYOFFICE/DocumentServer'
|
||||
},
|
||||
{
|
||||
id: 'penpot',
|
||||
title: 'Penpot',
|
||||
version: '2.0.0',
|
||||
description: 'Open-source design and prototyping platform. Self-hosted alternative to Figma.',
|
||||
icon: '/assets/img/penpot.webp',
|
||||
author: 'Penpot',
|
||||
dockerImage: 'penpotapp/frontend:latest',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/penpot/penpot'
|
||||
},
|
||||
{
|
||||
id: 'nextcloud',
|
||||
title: 'Nextcloud',
|
||||
version: '28.0',
|
||||
description: 'Self-hosted cloud storage and collaboration platform. Your own private cloud.',
|
||||
icon: null,
|
||||
author: 'Nextcloud',
|
||||
dockerImage: 'nextcloud:28',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/nextcloud/server'
|
||||
},
|
||||
{
|
||||
id: 'vaultwarden',
|
||||
title: 'Vaultwarden',
|
||||
version: '1.30.0',
|
||||
description: 'Self-hosted password manager (Bitwarden-compatible). Secure vault for passwords and secrets.',
|
||||
icon: null,
|
||||
author: 'Vaultwarden',
|
||||
dockerImage: 'vaultwarden/server:1.30.0-alpine',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/dani-garcia/vaultwarden'
|
||||
},
|
||||
{
|
||||
id: 'jellyfin',
|
||||
title: 'Jellyfin',
|
||||
version: '10.8.0',
|
||||
description: 'Free media server system. Stream your movies, music, and photos to any device.',
|
||||
icon: null,
|
||||
author: 'Jellyfin',
|
||||
dockerImage: 'jellyfin/jellyfin:10.8.13',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/jellyfin/jellyfin'
|
||||
},
|
||||
{
|
||||
id: 'photoprism',
|
||||
title: 'PhotoPrism',
|
||||
version: '231128',
|
||||
description: 'AI-powered photo management. Organize and browse photos with facial recognition.',
|
||||
icon: null,
|
||||
author: 'PhotoPrism',
|
||||
dockerImage: 'photoprism/photoprism:latest',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/photoprism/photoprism'
|
||||
},
|
||||
{
|
||||
id: 'immich',
|
||||
title: 'Immich',
|
||||
version: '1.90.0',
|
||||
description: 'High-performance self-hosted photo and video backup. Mobile-first with ML features.',
|
||||
icon: null,
|
||||
author: 'Immich',
|
||||
dockerImage: 'ghcr.io/immich-app/immich-server:release',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/immich-app/immich'
|
||||
},
|
||||
{
|
||||
id: 'filebrowser',
|
||||
title: 'File Browser',
|
||||
version: '2.27.0',
|
||||
description: 'Web-based file manager. Browse, upload, and manage files through a web interface.',
|
||||
icon: null,
|
||||
author: 'File Browser',
|
||||
dockerImage: 'filebrowser/filebrowser:v2.27.0',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/filebrowser/filebrowser'
|
||||
},
|
||||
{
|
||||
id: 'nginx-proxy-manager',
|
||||
title: 'Nginx Proxy Manager',
|
||||
version: '2.11.0',
|
||||
description: 'Easy proxy management with SSL. Beautiful web interface for managing reverse proxies.',
|
||||
icon: null,
|
||||
author: 'Nginx Proxy Manager',
|
||||
dockerImage: 'jc21/nginx-proxy-manager:latest',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/NginxProxyManager/nginx-proxy-manager'
|
||||
},
|
||||
{
|
||||
id: 'portainer',
|
||||
title: 'Portainer',
|
||||
version: '2.19.0',
|
||||
description: 'Container management UI. Manage Docker containers through a beautiful web interface.',
|
||||
icon: null,
|
||||
author: 'Portainer',
|
||||
dockerImage: 'portainer/portainer-ce:2.19.4',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/portainer/portainer'
|
||||
},
|
||||
{
|
||||
id: 'uptime-kuma',
|
||||
title: 'Uptime Kuma',
|
||||
version: '1.23.0',
|
||||
description: 'Self-hosted monitoring tool. Monitor uptime for HTTP(s), TCP, DNS, and more.',
|
||||
icon: null,
|
||||
author: 'Uptime Kuma',
|
||||
dockerImage: 'louislam/uptime-kuma:1.23.11',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/louislam/uptime-kuma'
|
||||
},
|
||||
{
|
||||
id: 'fedimint',
|
||||
title: 'Fedimint',
|
||||
version: '0.3.0',
|
||||
description: 'Federated Bitcoin mint. Private, scalable Bitcoin through federated guardians.',
|
||||
icon: '/assets/img/icon-fedimint.jpeg',
|
||||
author: 'Fedimint',
|
||||
dockerImage: 'fedimint/fedimintd:v0.3.0',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/fedimint/fedimint'
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
// Transform the data to our format
|
||||
communityApps.value = Object.entries(data).map(([id, pkg]: [string, any]) => {
|
||||
const latestVersion = pkg.versions ? Object.keys(pkg.versions).sort().reverse()[0] : '0.0.0'
|
||||
const versionData = (latestVersion && pkg.versions) ? (pkg.versions[latestVersion] || {}) : {}
|
||||
|
||||
return {
|
||||
id,
|
||||
title: pkg.title || id,
|
||||
version: latestVersion,
|
||||
description: versionData.description || pkg.description || '',
|
||||
icon: versionData.icon || pkg.icon || null,
|
||||
author: pkg.author || versionData.author || null,
|
||||
manifestUrl: versionData.manifest || null,
|
||||
repoUrl: pkg.repository || versionData.repository || null,
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`✅ Loaded ${communityApps.value.length} apps from Start9 registry`)
|
||||
} catch (err: any) {
|
||||
console.warn('Could not connect to Start9 registry, loading comprehensive app list from GitHub:', err.message)
|
||||
|
||||
// Try fetching from Start9Labs GitHub organization
|
||||
try {
|
||||
const githubResponse = await fetch('https://api.github.com/users/Start9Labs/repos?per_page=100&sort=updated')
|
||||
|
||||
if (githubResponse.ok) {
|
||||
const repos = await githubResponse.json()
|
||||
|
||||
// Filter for -startos packages and create app list with download URLs
|
||||
const startOsPackages = repos
|
||||
.filter((repo: any) => repo.name.includes('-startos') && !repo.archived)
|
||||
.map((repo: any) => {
|
||||
const appId = repo.name.replace('-startos', '').replace('_', '-')
|
||||
const appTitle = appId.split('-').map((word: string) =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
).join(' ')
|
||||
|
||||
// Construct the .s9pk download URL from GitHub releases
|
||||
const downloadUrl = `https://github.com/Start9Labs/${repo.name}/releases/latest/download/${appId}.s9pk`
|
||||
|
||||
return {
|
||||
id: appId,
|
||||
title: appTitle,
|
||||
version: 'latest',
|
||||
description: repo.description || `${appTitle} for StartOS`,
|
||||
icon: null,
|
||||
author: 'Start9',
|
||||
manifestUrl: downloadUrl,
|
||||
repoUrl: repo.html_url
|
||||
}
|
||||
})
|
||||
|
||||
if (startOsPackages.length > 0) {
|
||||
communityApps.value = startOsPackages
|
||||
console.log(`✅ Loaded ${startOsPackages.length} Start9 packages from GitHub`)
|
||||
loadingCommunity.value = false
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (githubErr) {
|
||||
console.warn('GitHub API also unavailable, using curated list')
|
||||
}
|
||||
|
||||
// Ultimate fallback to comprehensive curated list with real download URLs
|
||||
communityApps.value = [
|
||||
{
|
||||
id: 'bitcoin',
|
||||
title: 'Bitcoin Core',
|
||||
version: '27.0.0',
|
||||
description: 'Run a full Bitcoin node. Validate and relay blocks and transactions on the Bitcoin network. Store, validate, and relay blocks and transactions.',
|
||||
icon: '/assets/img/bitcoin.svg',
|
||||
author: 'Start9',
|
||||
manifestUrl: 'https://github.com/Start9Labs/bitcoind-startos/releases/latest/download/bitcoind.s9pk',
|
||||
repoUrl: 'https://github.com/Start9Labs/bitcoind-startos'
|
||||
},
|
||||
{
|
||||
id: 'cln',
|
||||
title: 'Core Lightning',
|
||||
version: '24.02.2',
|
||||
description: 'Lightning Network daemon for fast, cheap Bitcoin payments. A specification-compliant Lightning Network implementation.',
|
||||
icon: '/assets/img/c-lightning.png',
|
||||
author: 'Start9',
|
||||
manifestUrl: 'https://github.com/Start9Labs/cln-startos/releases/latest/download/cln.s9pk',
|
||||
repoUrl: 'https://github.com/Start9Labs/cln-startos'
|
||||
},
|
||||
{
|
||||
id: 'lnd',
|
||||
title: 'LND',
|
||||
version: '0.17.0',
|
||||
description: 'Lightning Network Daemon by Lightning Labs. Alternative Lightning implementation with strong ecosystem support.',
|
||||
icon: null,
|
||||
author: 'Start9',
|
||||
manifestUrl: 'https://github.com/Start9Labs/lnd-startos/releases/latest/download/lnd.s9pk',
|
||||
repoUrl: 'https://github.com/Start9Labs/lnd-startos'
|
||||
},
|
||||
{
|
||||
id: 'btcpay',
|
||||
title: 'BTCPay Server',
|
||||
version: '1.13.1',
|
||||
description: 'Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries or fees. Full merchant solution.',
|
||||
icon: '/assets/img/btcpay.png',
|
||||
author: 'Start9',
|
||||
manifestUrl: 'https://github.com/Start9Labs/btcpayserver-startos/releases/latest/download/btcpay.s9pk',
|
||||
repoUrl: 'https://github.com/Start9Labs/btcpayserver-startos'
|
||||
},
|
||||
{
|
||||
id: 'nextcloud',
|
||||
]
|
||||
}
|
||||
|
||||
// Helper function at end of script
|
||||
function getCuratedAppList() {
|
||||
return [
|
||||
title: 'Nextcloud',
|
||||
version: '29.0.0',
|
||||
description: 'Self-hosted file sync and sharing platform. Your own private cloud storage with calendar, contacts, and office suite.',
|
||||
@@ -836,7 +950,7 @@ async function loadCommunityMarketplace() {
|
||||
repoUrl: 'https://github.com/Start9Labs/home-assistant-startos'
|
||||
}
|
||||
]
|
||||
console.log(`📚 Using curated list of ${communityApps.value.length} popular Start9 packages`)
|
||||
console.log(`📦 Loaded ${communityApps.value.length} Docker-based apps`)
|
||||
} finally {
|
||||
loadingCommunity.value = false
|
||||
}
|
||||
@@ -922,13 +1036,13 @@ async function installCommunityApp(app: any) {
|
||||
installing.value = app.id
|
||||
|
||||
try {
|
||||
console.log(`Installing community app ${app.title} from ${app.manifestUrl}`)
|
||||
console.log(`[Marketplace] Installing Docker app ${app.title} using image ${app.dockerImage}`)
|
||||
|
||||
await rpcClient.call({
|
||||
method: 'package.install',
|
||||
params: {
|
||||
id: app.id,
|
||||
url: app.manifestUrl,
|
||||
dockerImage: app.dockerImage,
|
||||
version: app.version
|
||||
}
|
||||
})
|
||||
@@ -950,13 +1064,12 @@ async function installCommunityApp(app: any) {
|
||||
clearInterval(checkInstalled)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
alert('Installation timeout. Please check the backend logs or try refreshing the page.')
|
||||
console.error('[Marketplace] Installation timeout')
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
} catch (err) {
|
||||
console.error('Installation failed:', err)
|
||||
alert(`Failed to install ${app.title}: ${err}`)
|
||||
console.error('[Marketplace] Installation failed:', err)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user