dify/cli/src/version/probe.ts
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

148 lines
3.9 KiB
TypeScript

import type { ServerVersionResponse } from '@dify/contracts/api/openapi/types.gen'
import type { CompatVerdict } from './compat.js'
import type { Channel } from './info.js'
import type { ActiveContext } from '@/auth/hosts'
import { META_PROBE_TIMEOUT_MS, MetaClient } from '@/api/meta'
import { Registry } from '@/auth/hosts'
import { createHttpClient } from '@/http/client'
import { arch, platform } from '@/sys/index'
import { activeHostInfo, openAPIBase } from '@/util/host'
import { difyCompat, evaluateCompat } from './compat.js'
import { versionInfo } from './info.js'
export type ClientBlock = {
readonly version: string
readonly commit: string
readonly buildDate: string
readonly channel: Channel
readonly platform: string
readonly arch: string
}
export type ServerBlock = {
readonly endpoint: string
readonly reachable: boolean
readonly version?: string
readonly edition?: ServerVersionResponse['edition']
}
export type CompatBlock = CompatVerdict & {
readonly minDify: string
readonly maxDify: string
}
export type VersionReport = {
readonly client: ClientBlock
readonly server: ServerBlock
readonly compat: CompatBlock
}
// /openapi/v1/_version is intentionally unauthenticated, so the probe does not
// take a bearer. Same signature shape as the auto-nudge probe — easy to swap.
export type MetaProbe = (endpoint: string) => Promise<ServerVersionResponse>
export type RunVersionProbeOptions = {
readonly skipServer: boolean
readonly loadActive?: () => Promise<ActiveContext | undefined>
readonly probe?: MetaProbe
}
const defaultLoadActive = async (): Promise<ActiveContext | undefined> => {
return (await Registry.load()).resolveActive()
}
function buildDefaultProbe(insecure: boolean): MetaProbe {
return async (endpoint) => {
const http = createHttpClient({
baseURL: openAPIBase(endpoint),
timeoutMs: META_PROBE_TIMEOUT_MS,
retryAttempts: 0,
insecure,
})
return new MetaClient(http).serverVersion()
}
}
function buildClientBlock(): ClientBlock {
return {
version: versionInfo.version,
commit: versionInfo.commit,
buildDate: versionInfo.buildDate,
channel: versionInfo.channel,
platform: platform(),
arch: arch(),
}
}
function unreachableServer(endpoint: string): ServerBlock {
return { endpoint, reachable: false }
}
function compatBlock(verdict: CompatVerdict): CompatBlock {
return {
minDify: difyCompat.minDify,
maxDify: difyCompat.maxDify,
status: verdict.status,
detail: verdict.detail,
}
}
export async function runVersionProbe(opts: RunVersionProbeOptions): Promise<VersionReport> {
const client = buildClientBlock()
if (opts.skipServer) {
return {
client,
server: { endpoint: '', reachable: false },
compat: compatBlock({ status: 'unknown', detail: 'server probe skipped' }),
}
}
const loadActive = opts.loadActive ?? defaultLoadActive
let active: ActiveContext | undefined
let loadFailed = false
try {
active = await loadActive()
} catch {
loadFailed = true
}
if (active === undefined || active.host === '') {
const detail = loadFailed ? 'hosts file unreadable' : 'no host configured'
return {
client,
server: { endpoint: '', reachable: false },
compat: compatBlock({ status: 'unknown', detail }),
}
}
const { host: endpoint, insecure } = activeHostInfo(active)
const probe = opts.probe ?? buildDefaultProbe(insecure)
let serverInfo: ServerVersionResponse | undefined
try {
serverInfo = await probe(endpoint)
} catch {
serverInfo = undefined
}
if (serverInfo === undefined)
return {
client,
server: unreachableServer(endpoint),
compat: compatBlock({ status: 'unknown', detail: 'server unreachable' }),
}
return {
client,
server: {
endpoint,
reachable: true,
version: serverInfo.version,
edition: serverInfo.edition,
},
compat: compatBlock(evaluateCompat(serverInfo.version)),
}
}