refactor(cli): remove daemon update command and simplify status runtime info
This commit is contained in:
@@ -1,16 +1,10 @@
|
||||
import type { Command } from 'commander'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { createRequire } from 'node:module'
|
||||
import { getOrCreateServerId } from '@getpaseo/server'
|
||||
import { tryConnectToDaemon } from '../../utils/client.js'
|
||||
import type { CommandOptions, ListResult, OutputSchema } from '../../output/index.js'
|
||||
import { resolveLocalDaemonState, resolveTcpHostFromListen } from './local-daemon.js'
|
||||
import {
|
||||
formatNpmInvocation,
|
||||
resolveNpmInvocationFromNode,
|
||||
resolvePreferredNodePath,
|
||||
type NpmInvocation,
|
||||
} from './runtime-toolchain.js'
|
||||
import { resolveNodePathFromPid } from './runtime-toolchain.js'
|
||||
|
||||
interface DaemonStatus {
|
||||
serverId: string | null
|
||||
@@ -24,11 +18,9 @@ interface DaemonStatus {
|
||||
logPath: string
|
||||
runningAgents: number | null
|
||||
idleAgents: number | null
|
||||
runtimeNode: string
|
||||
runtimeNpm: string
|
||||
daemonNode: string
|
||||
cliNode: string
|
||||
cliVersion: string
|
||||
latestCliVersion: string
|
||||
updateStatus: string
|
||||
note?: string
|
||||
}
|
||||
|
||||
@@ -41,13 +33,7 @@ type CliPackageJson = {
|
||||
version?: unknown
|
||||
}
|
||||
|
||||
type LatestCliVersionResult = {
|
||||
version: string | null
|
||||
note?: string
|
||||
}
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const CLI_UPDATE_CHECK_TIMEOUT_MS = 3000
|
||||
|
||||
function normalizeError(error: unknown): string {
|
||||
if (error instanceof Error) {
|
||||
@@ -82,95 +68,6 @@ function resolveCliVersion(): string {
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
function parseVersionFromNpmOutput(raw: string): string | null {
|
||||
const trimmed = raw.trim()
|
||||
if (!trimmed) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed) as unknown
|
||||
if (typeof parsed === 'string' && parsed.trim().length > 0) {
|
||||
return parsed.trim()
|
||||
}
|
||||
} catch {
|
||||
// Fall back to plain output.
|
||||
}
|
||||
|
||||
return trimmed.replace(/^"+|"+$/g, '').trim() || null
|
||||
}
|
||||
|
||||
function parseSemver(version: string): [number, number, number] | null {
|
||||
const match = version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/)
|
||||
if (!match) {
|
||||
return null
|
||||
}
|
||||
|
||||
const major = Number(match[1])
|
||||
const minor = Number(match[2])
|
||||
const patch = Number(match[3])
|
||||
if (![major, minor, patch].every((part) => Number.isInteger(part) && part >= 0)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return [major, minor, patch]
|
||||
}
|
||||
|
||||
function compareSemver(left: string, right: string): number | null {
|
||||
const leftParts = parseSemver(left)
|
||||
const rightParts = parseSemver(right)
|
||||
if (!leftParts || !rightParts) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (let index = 0; index < leftParts.length; index += 1) {
|
||||
if (leftParts[index]! < rightParts[index]!) return -1
|
||||
if (leftParts[index]! > rightParts[index]!) return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function fetchLatestCliVersion(npm: NpmInvocation): LatestCliVersionResult {
|
||||
const result = spawnSync(
|
||||
npm.command,
|
||||
[...npm.argsPrefix, 'view', '@getpaseo/cli', 'version', '--json'],
|
||||
{
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
env: process.env,
|
||||
timeout: CLI_UPDATE_CHECK_TIMEOUT_MS,
|
||||
}
|
||||
)
|
||||
|
||||
if (result.error) {
|
||||
return {
|
||||
version: null,
|
||||
note: `update check failed: ${shortenMessage(normalizeError(result.error))}`,
|
||||
}
|
||||
}
|
||||
|
||||
if ((result.status ?? 1) !== 0) {
|
||||
const stderr = result.stderr?.trim()
|
||||
return {
|
||||
version: null,
|
||||
note: stderr
|
||||
? `update check failed: ${shortenMessage(stderr)}`
|
||||
: `update check failed: npm exited with code ${result.status ?? 1}`,
|
||||
}
|
||||
}
|
||||
|
||||
const version = parseVersionFromNpmOutput(result.stdout)
|
||||
if (!version) {
|
||||
return {
|
||||
version: null,
|
||||
note: 'update check failed: empty npm response',
|
||||
}
|
||||
}
|
||||
|
||||
return { version }
|
||||
}
|
||||
|
||||
function createStatusSchema(status: DaemonStatus): OutputSchema<StatusRow> {
|
||||
return {
|
||||
idField: 'key',
|
||||
@@ -208,11 +105,9 @@ function toStatusRows(status: DaemonStatus): StatusRow[] {
|
||||
{ key: 'Started', value: status.startedAt ?? '-' },
|
||||
{ key: 'Owner', value: status.owner ?? '-' },
|
||||
{ key: 'Logs', value: status.logPath },
|
||||
{ key: 'Node', value: status.runtimeNode },
|
||||
{ key: 'npm', value: status.runtimeNpm },
|
||||
{ key: 'Daemon Node', value: status.daemonNode },
|
||||
{ key: 'CLI Node', value: status.cliNode },
|
||||
{ key: 'CLI', value: status.cliVersion },
|
||||
{ key: 'Latest CLI', value: status.latestCliVersion },
|
||||
{ key: 'Update', value: status.updateStatus },
|
||||
]
|
||||
|
||||
if (status.runningAgents !== null && status.idleAgents !== null) {
|
||||
@@ -253,22 +148,24 @@ export async function runStatusCommand(
|
||||
const state = resolveLocalDaemonState({ home })
|
||||
|
||||
const owner = resolveOwnerLabel(state.pidInfo?.uid, state.pidInfo?.hostname)
|
||||
const resolvedNode = resolvePreferredNodePath({
|
||||
daemonPid: state.running ? state.pidInfo?.pid : null,
|
||||
fallbackNodePath: process.execPath,
|
||||
})
|
||||
let daemonNode: string
|
||||
if (!state.running) {
|
||||
daemonNode = '-'
|
||||
} else if (state.pidInfo?.pid) {
|
||||
const fromPid = resolveNodePathFromPid(state.pidInfo.pid)
|
||||
daemonNode = fromPid.nodePath ?? `unknown (${fromPid.error ?? 'could not resolve from PID'})`
|
||||
} else {
|
||||
daemonNode = 'unknown (no PID available)'
|
||||
}
|
||||
const cliNode = process.execPath
|
||||
let status: DaemonStatus['status'] = state.running ? 'running' : 'stopped'
|
||||
let runningAgents: number | null = null
|
||||
let idleAgents: number | null = null
|
||||
let note: string | undefined
|
||||
let runtimeNpm = '-'
|
||||
let latestCliVersion = 'unknown'
|
||||
let updateStatus = 'unknown'
|
||||
|
||||
if (!state.running && state.stalePidFile && state.pidInfo) {
|
||||
note = `Stale PID file found for PID ${state.pidInfo.pid}`
|
||||
}
|
||||
note = appendNote(note, resolvedNode.note)
|
||||
|
||||
if (state.running) {
|
||||
const host = resolveTcpHostFromListen(state.listen)
|
||||
@@ -295,38 +192,7 @@ export async function runStatusCommand(
|
||||
}
|
||||
}
|
||||
|
||||
let npmInvocation: NpmInvocation | null = null
|
||||
try {
|
||||
npmInvocation = resolveNpmInvocationFromNode(resolvedNode.nodePath)
|
||||
runtimeNpm = formatNpmInvocation(npmInvocation)
|
||||
} catch (err) {
|
||||
runtimeNpm = `unresolved (${shortenMessage(normalizeError(err))})`
|
||||
}
|
||||
|
||||
const cliVersion = resolveCliVersion()
|
||||
if (npmInvocation) {
|
||||
const latest = fetchLatestCliVersion(npmInvocation)
|
||||
if (latest.version) {
|
||||
latestCliVersion = latest.version
|
||||
if (cliVersion === 'unknown') {
|
||||
updateStatus = 'unknown (local CLI version unavailable)'
|
||||
} else {
|
||||
const comparison = compareSemver(cliVersion, latest.version)
|
||||
if (comparison === null) {
|
||||
updateStatus = 'unknown (version format not comparable)'
|
||||
} else if (comparison < 0) {
|
||||
updateStatus = `update available (${cliVersion} -> ${latest.version})`
|
||||
} else {
|
||||
updateStatus = 'up to date'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
latestCliVersion = 'unknown'
|
||||
updateStatus = latest.note ?? 'unknown'
|
||||
}
|
||||
} else {
|
||||
updateStatus = 'unknown (npm unresolved)'
|
||||
}
|
||||
|
||||
let serverId: string | null = null
|
||||
try {
|
||||
@@ -347,11 +213,9 @@ export async function runStatusCommand(
|
||||
logPath: state.logPath,
|
||||
runningAgents,
|
||||
idleAgents,
|
||||
runtimeNode: `${resolvedNode.nodePath} (${resolvedNode.source})`,
|
||||
runtimeNpm,
|
||||
daemonNode,
|
||||
cliNode,
|
||||
cliVersion,
|
||||
latestCliVersion,
|
||||
updateStatus,
|
||||
note,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user