fix node npm detection (#1163)

This commit is contained in:
ekko
2026-05-30 20:19:01 +08:00
committed by GitHub
parent dcbf601e35
commit fc35c74eb3
18 changed files with 406 additions and 53 deletions
+1
View File
@@ -24,6 +24,7 @@ export interface CodingAgentMutationResult extends CodingAgentsStatus {
success: boolean
tool: CodingAgentToolStatus
message?: string
code?: string
}
export interface CodingAgentConfigFileContent {
+1
View File
@@ -35,6 +35,7 @@ export interface PreviewStatus {
export interface PreviewActionResponse extends PreviewStatus {
success: boolean
message?: string
code?: string
}
// Config-based model types
@@ -42,6 +42,23 @@ function applyErrorStatus(err: any) {
} catch {}
}
function errorCodeMessage(code?: string, fallback?: string): string {
if (code === 'node_environment_missing') return t('githubPreview.nodeEnvironmentMissing')
return fallback || t('githubPreview.actionFailed')
}
function parseErrorPayload(err: any): { message?: string; code?: string } | null {
const messageText = String(err?.message || '')
const jsonStart = messageText.indexOf('{')
if (jsonStart < 0) return null
try {
const parsed = JSON.parse(messageText.slice(jsonStart))
return parsed && typeof parsed === 'object' ? parsed : null
} catch {
return null
}
}
async function loadStatus() {
status.value = await fetchPreviewStatus()
if (!selectedTag.value && status.value.current_tag) {
@@ -71,19 +88,20 @@ async function handleRefresh() {
}
}
async function runAction(action: string, fn: () => Promise<PreviewStatus & { success?: boolean; message?: string }>, successKey: string) {
async function runAction(action: string, fn: () => Promise<PreviewStatus & { success?: boolean; message?: string; code?: string }>, successKey: string) {
actionLoading.value = action
try {
const res = await fn()
status.value = res
if (res.success === false) {
message.warning(res.message || t('githubPreview.actionFailed'))
message.warning(errorCodeMessage(res.code, res.message))
return
}
message.success(t(successKey))
} catch (err: any) {
applyErrorStatus(err)
message.error(err?.message || t('githubPreview.actionFailed'))
const payload = parseErrorPayload(err)
message.error(errorCodeMessage(payload?.code, payload?.message || err?.message))
} finally {
actionLoading.value = ''
}
+2
View File
@@ -1015,6 +1015,7 @@ jobTriggered: 'Job ausgelost',
yes: "Ja",
no: "Nein",
actionFailed: "Aktion fehlgeschlagen",
nodeEnvironmentMissing: "Node/npm wurde nicht erkannt. Bitte installiere Node.js und versuche es erneut.",
prepareSuccess: "Vorschaucode ist bereit",
installSuccess: "Abhängigkeiten installiert",
startSuccess: "Vorschau gestartet",
@@ -1038,6 +1039,7 @@ jobTriggered: 'Job ausgelost',
installing: "Installiere",
installSuccess: "Installiert",
installFailed: "Installation fehlgeschlagen",
nodeEnvironmentMissing: "Node/npm wurde nicht erkannt. Bitte installiere Node.js und versuche es erneut.",
deleteNow: "Loschen",
deleting: "Losche",
deleteSuccess: "Gelöscht",
+2
View File
@@ -1117,6 +1117,7 @@ export default {
yes: "Yes",
no: "No",
actionFailed: "Action failed",
nodeEnvironmentMissing: "Node/npm was not detected. Please install Node.js and try again.",
prepareSuccess: "Preview code is ready",
installSuccess: "Dependencies installed",
startSuccess: "Preview started",
@@ -1140,6 +1141,7 @@ export default {
installing: "Installing",
installSuccess: "Installed",
installFailed: "Install failed",
nodeEnvironmentMissing: "Node/npm was not detected. Please install Node.js and try again.",
deleteNow: "Delete",
deleting: "Deleting",
deleteSuccess: "Deleted",
+2
View File
@@ -1015,6 +1015,7 @@ jobTriggered: 'Job ejecutado',
yes: "Sí",
no: "No",
actionFailed: "Acción fallida",
nodeEnvironmentMissing: "No se detectó Node/npm. Instala Node.js y vuelve a intentarlo.",
prepareSuccess: "Código de vista previa listo",
installSuccess: "Dependencias instaladas",
startSuccess: "Vista previa iniciada",
@@ -1038,6 +1039,7 @@ jobTriggered: 'Job ejecutado',
installing: "Instalando",
installSuccess: "Instalado",
installFailed: "Error de instalación",
nodeEnvironmentMissing: "No se detectó Node/npm. Instala Node.js y vuelve a intentarlo.",
deleteNow: "Eliminar",
deleting: "Eliminando",
deleteSuccess: "Eliminado",
+2
View File
@@ -1015,6 +1015,7 @@ jobTriggered: 'Job declenche',
yes: "Oui",
no: "Non",
actionFailed: "Échec de laction",
nodeEnvironmentMissing: "Node/npm na pas été détecté. Installez Node.js puis réessayez.",
prepareSuccess: "Code de prévisualisation prêt",
installSuccess: "Dépendances installées",
startSuccess: "Prévisualisation démarrée",
@@ -1038,6 +1039,7 @@ jobTriggered: 'Job declenche',
installing: "Installation",
installSuccess: "Installé",
installFailed: "Échec de linstallation",
nodeEnvironmentMissing: "Node/npm na pas été détecté. Installez Node.js puis réessayez.",
deleteNow: "Supprimer",
deleting: "Suppression",
deleteSuccess: "Supprimé",
+2
View File
@@ -1015,6 +1015,7 @@ export default {
yes: "はい",
no: "いいえ",
actionFailed: "操作に失敗しました",
nodeEnvironmentMissing: "Node/npm が検出されませんでした。Node.js をインストールしてから再試行してください。",
prepareSuccess: "プレビューコードの準備が完了しました",
installSuccess: "依存関係をインストールしました",
startSuccess: "プレビューを起動しました",
@@ -1038,6 +1039,7 @@ export default {
installing: "インストール中",
installSuccess: "インストールしました",
installFailed: "インストールに失敗しました",
nodeEnvironmentMissing: "Node/npm が検出されませんでした。Node.js をインストールしてから再試行してください。",
deleteNow: "削除",
deleting: "削除中",
deleteSuccess: "削除しました",
+2
View File
@@ -1015,6 +1015,7 @@ export default {
yes: "예",
no: "아니요",
actionFailed: "작업 실패",
nodeEnvironmentMissing: "Node/npm 환경을 찾을 수 없습니다. Node.js를 설치한 뒤 다시 시도하세요.",
prepareSuccess: "미리보기 코드가 준비되었습니다",
installSuccess: "의존성이 설치되었습니다",
startSuccess: "미리보기가 시작되었습니다",
@@ -1038,6 +1039,7 @@ export default {
installing: "설치 중",
installSuccess: "설치됨",
installFailed: "설치 실패",
nodeEnvironmentMissing: "Node/npm 환경을 찾을 수 없습니다. Node.js를 설치한 뒤 다시 시도하세요.",
deleteNow: "삭제",
deleting: "삭제 중",
deleteSuccess: "삭제됨",
+2
View File
@@ -1015,6 +1015,7 @@ jobTriggered: 'Job acionado',
yes: "Sim",
no: "Não",
actionFailed: "Ação falhou",
nodeEnvironmentMissing: "Node/npm não foi detectado. Instale o Node.js e tente novamente.",
prepareSuccess: "Código de prévia pronto",
installSuccess: "Dependências instaladas",
startSuccess: "Prévia iniciada",
@@ -1038,6 +1039,7 @@ jobTriggered: 'Job acionado',
installing: "Instalando",
installSuccess: "Instalado",
installFailed: "Falha na instalação",
nodeEnvironmentMissing: "Node/npm não foi detectado. Instale o Node.js e tente novamente.",
deleteNow: "Excluir",
deleting: "Excluindo",
deleteSuccess: "Excluído",
@@ -1109,6 +1109,7 @@ export default {
yes: "是",
no: "否",
actionFailed: "操作失敗",
nodeEnvironmentMissing: "未偵測到可用的 Node/npm 環境,請先安裝 Node.js 後重試。",
prepareSuccess: "預覽程式碼已準備好",
installSuccess: "依賴安裝完成",
startSuccess: "預覽已啟動",
@@ -1132,6 +1133,7 @@ export default {
installing: "安裝中",
installSuccess: "安裝完成",
installFailed: "安裝失敗",
nodeEnvironmentMissing: "未偵測到可用的 Node/npm 環境,請先安裝 Node.js 後重試。",
deleteNow: "刪除",
deleting: "刪除中",
deleteSuccess: "刪除完成",
+2
View File
@@ -1109,6 +1109,7 @@ export default {
yes: "是",
no: "否",
actionFailed: "操作失败",
nodeEnvironmentMissing: "未检测到可用的 Node/npm 环境,请先安装 Node.js 后重试。",
prepareSuccess: "预览代码已准备好",
installSuccess: "依赖安装完成",
startSuccess: "预览已启动",
@@ -1132,6 +1133,7 @@ export default {
installing: "安装中",
installSuccess: "安装完成",
installFailed: "安装失败",
nodeEnvironmentMissing: "未检测到可用的 Node/npm 环境,请先安装 Node.js 后重试。",
deleteNow: "删除",
deleting: "删除中",
deleteSuccess: "删除完成",
@@ -307,6 +307,23 @@ function currentLaunchRequest() {
}
}
function codingAgentMessage(code?: string, fallback?: string, fallbackKey = 'codingAgents.installFailed'): string {
if (code === 'node_environment_missing') return t('codingAgents.nodeEnvironmentMissing')
return fallback || t(fallbackKey)
}
function parseErrorPayload(err: any): { message?: string; code?: string } | null {
const messageText = String(err?.message || '')
const jsonStart = messageText.indexOf('{')
if (jsonStart < 0) return null
try {
const parsed = JSON.parse(messageText.slice(jsonStart))
return parsed && typeof parsed === 'object' ? parsed : null
} catch {
return null
}
}
async function launchBuiltInTerminal() {
if (!useGlobalLaunchConfig.value && (!launchProvider.value || !launchModel.value)) {
message.error(t('codingAgents.selectProviderModel'))
@@ -352,10 +369,11 @@ async function handleInstall(id: CodingAgentId) {
if (result.success) {
message.success(t('codingAgents.installSuccess'))
} else {
message.error(result.message || t('codingAgents.installFailed'))
message.error(codingAgentMessage(result.code, result.message, 'codingAgents.installFailed'))
}
} catch (err: any) {
message.error(err?.message || t('codingAgents.installFailed'))
const payload = parseErrorPayload(err)
message.error(codingAgentMessage(payload?.code, payload?.message || err?.message, 'codingAgents.installFailed'))
} finally {
installing.value[id] = false
}
@@ -369,10 +387,11 @@ async function handleDelete(id: CodingAgentId) {
if (result.success) {
message.success(t('codingAgents.deleteSuccess'))
} else {
message.error(result.message || t('codingAgents.deleteFailed'))
message.error(codingAgentMessage(result.code, result.message, 'codingAgents.deleteFailed'))
}
} catch (err: any) {
message.error(err?.message || t('codingAgents.deleteFailed'))
const payload = parseErrorPayload(err)
message.error(codingAgentMessage(payload?.code, payload?.message || err?.message, 'codingAgents.deleteFailed'))
} finally {
deleting.value[id] = false
}
@@ -77,6 +77,9 @@ async function loadRecommendations() {
const response = await fetch(recommendationsPath.value)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const text = await response.text()
if (/^\s*<!doctype html/i.test(text) || /^\s*<html[\s>]/i.test(text)) {
throw new Error('Skill recommendations file was not found')
}
if (requestSeq === recommendationsRequestSeq) {
recommendations.value = text
}