From 0916933cfdc608d1faa76737db7b509838054512 Mon Sep 17 00:00:00 2001 From: Adam Eivy Date: Sat, 15 Aug 2026 02:30:17 +0000 Subject: [PATCH 1/4] feat: classify python/go/docker/static app types in streamingDetect (#4137) --- .changelog/next/added-issue-4137.md | 1 + client/src/components/apps/constants.js | 36 +++- .../src/components/apps/tabs/OverviewTab.jsx | 8 +- client/src/pages/Apps.jsx | 7 +- client/src/pages/CreateApp.jsx | 15 +- client/src/pages/CreateApp.test.jsx | 42 ++++- server/services/pm2Standardizer.js | 8 +- server/services/pm2Standardizer.test.js | 16 ++ server/services/streamingDetect.js | 66 +++++++- server/services/streamingDetect.test.js | 159 +++++++++++++++++- 10 files changed, 334 insertions(+), 24 deletions(-) create mode 100644 .changelog/next/added-issue-4137.md diff --git a/.changelog/next/added-issue-4137.md b/.changelog/next/added-issue-4137.md new file mode 100644 index 0000000000..011c4d9842 --- /dev/null +++ b/.changelog/next/added-issue-4137.md @@ -0,0 +1 @@ +- App detection now classifies Python, Go, Docker, and static repos instead of labelling them `unknown`, and PM2 standardization (which writes a Node ecosystem config) is no longer offered for them diff --git a/client/src/components/apps/constants.js b/client/src/components/apps/constants.js index 343173db37..19849d8631 100644 --- a/client/src/components/apps/constants.js +++ b/client/src/components/apps/constants.js @@ -44,10 +44,38 @@ export function resolveLaunchPanelProcess(app, result) { return result?.results?.[processName]?.success === false ? null : processName; } -export const getAppTypeLabel = (type) => - type === 'ios-native' ? '📱 iOS' : - type === 'macos-native' ? '🖥️ macOS' : - type === 'swift' ? '🐦 Swift' : '🔨 Xcode'; +// Mirrors STANDARDIZABLE_TYPES in server/services/streamingDetect.js (a parity +// test asserts the two Sets match). POSITIVE list: the PM2 standardizer writes a +// NODE ecosystem config from a prompt that opens "You are analyzing a Node.js +// application", so a Python/Go/Docker/static repo must not be offered the flow. +// `unknown` and `desktop` stay in for continuity — see the server's rationale. +export const STANDARDIZABLE_TYPES = new Set([ + 'vite+express', 'vite', 'single-node-server', 'nextjs', 'desktop', 'unknown' +]); + +/** Whether the PM2 standardizer (which writes a NODE ecosystem config) applies. */ +export const isStandardizable = (type) => STANDARDIZABLE_TYPES.has(type ?? 'unknown'); + +// Every app type that can reach a UI label. Kept TOTAL (with a raw-type +// fallback) rather than a ternary chain ending in '🔨 Xcode' — that default +// meant any type not explicitly listed rendered as an Xcode project. +const APP_TYPE_LABELS = { + 'ios-native': '📱 iOS', + 'macos-native': '🖥️ macOS', + swift: '🐦 Swift', + xcode: '🔨 Xcode', + python: '🐍 Python', + go: '🐹 Go', + docker: '🐳 Docker', + static: '📄 Static', + desktop: '🎮 Desktop', + 'vite+express': '⚡ Vite + Express', + vite: '⚡ Vite', + 'single-node-server': '🟢 Node', + nextjs: '▲ Next.js' +}; + +export const getAppTypeLabel = (type) => APP_TYPE_LABELS[type] || type || 'Unknown'; // Where an app's autonomous work items live. Mirrors WORK_TRACKERS + // TRACKER_LABELS in server/lib/workTracker.js — shared by the Edit App picker diff --git a/client/src/components/apps/tabs/OverviewTab.jsx b/client/src/components/apps/tabs/OverviewTab.jsx index 54475f5b7f..0d89e88430 100644 --- a/client/src/components/apps/tabs/OverviewTab.jsx +++ b/client/src/components/apps/tabs/OverviewTab.jsx @@ -2,7 +2,7 @@ import { useState, useEffect, useMemo } from 'react'; import { Link } from 'react-router'; import { FolderOpen, Gamepad2, Terminal, Code, RefreshCw, Wrench, Archive, ArchiveRestore, Download, Tag, AlertTriangle, Rocket, Camera, Image, Sparkles } from 'lucide-react'; import toast from '../../ui/Toast'; -import { NON_PM2_TYPES } from '../constants'; +import { isStandardizable } from '../constants'; import ActivityLog from '../ActivityLog'; import SlashDoPanel from '../SlashDoPanel'; import Banner from '../../ui/Banner'; @@ -313,8 +313,10 @@ export default function OverviewTab({ app, onRefresh }) { {detectingIcon ? 'Scanning...' : 'Detect Icon'} {/* PortOS's own ecosystem.config.cjs is the canonical PORTS source — - it is never regenerated from an LLM analysis (the server refuses too). */} - {!NON_PM2_TYPES.has(app.type) && app.id !== api.PORTOS_APP_ID && ( + it is never regenerated from an LLM analysis (the server refuses too). + `isStandardizable` also keeps the button off non-Node repos, whose + ecosystem config the Node-shaped prompt has no business writing. */} + {isStandardizable(app.type) && app.id !== api.PORTOS_APP_ID && ( - {(!app.processes?.length || app.processes.some(p => !p.ports || Object.keys(p.ports).length === 0)) && ( + {/* The standardizer writes a NODE ecosystem config — + never offer it for a Python/Go/Docker/static repo + (the server refuses too). */} + {isStandardizable(app.type) && (!app.processes?.length || app.processes.some(p => !p.ports || Object.keys(p.ports).length === 0)) && (