import { z } from "zod"; import { scanInstalledApps, type InstalledApp } from "../infra/installed-apps.js"; const DEFAULT_LIMIT = 100; const MAX_LIMIT = 200; const DeviceAppsParamsSchema = z .object({ query: z.string().trim().min(1).optional(), limit: z .number() .int() .transform((value) => Math.min(MAX_LIMIT, Math.max(1, value))) .optional(), includeSystem: z.boolean().optional(), }) .strict(); type DeviceAppsPayload = { count: number; totalMatched: number; truncated: boolean; apps: InstalledApp[]; }; type DeviceAppsInvokeResult = | { ok: true; payload: DeviceAppsPayload } | { ok: false; code: string; message: string }; export async function invokeDeviceApps(params: { paramsJSON?: string | null; sharingEnabled: boolean; platform?: NodeJS.Platform; scan?: typeof scanInstalledApps; }): Promise { if (!params.sharingEnabled) { return { ok: false, code: "INSTALLED_APPS_SHARING_DISABLED", message: "INSTALLED_APPS_SHARING_DISABLED: enable Installed Apps in node-host settings", }; } let request: z.infer; try { request = DeviceAppsParamsSchema.parse(JSON.parse(params.paramsJSON || "{}")); } catch (error) { return { ok: false, code: "INVALID_REQUEST", message: String(error) }; } const scan = params.scan ?? scanInstalledApps; const inventory = await scan({ platform: params.platform ?? process.platform }); if (inventory.status === "unsupported") { return { ok: false, code: "UNAVAILABLE", message: "UNAVAILABLE: installed application inventory is only available on macOS", }; } const query = request.query?.toLocaleLowerCase("en-US"); const matching = inventory.apps.filter( (app) => (request.includeSystem === true || !app.system) && (!query || app.label.toLocaleLowerCase("en-US").includes(query) || app.bundleId?.toLocaleLowerCase("en-US").includes(query)), ); const apps = matching.slice(0, request.limit ?? DEFAULT_LIMIT); return { ok: true, payload: { count: apps.length, totalMatched: matching.length, truncated: matching.length > apps.length, apps, }, }; }