import node_assert from "node:assert";
import node_fs, { accessSync, constants } from "node:fs";
import { z } from "@midscene/core";
import { createDefaultMobileActions, defineAction, resolveTextInputOptions, sendTextSequentially, shouldInputSequentially } from "@midscene/core/device";
import { getTmpFile, sleep } from "@midscene/core/utils";
import { createImgBase64ByFormat } from "@midscene/shared/img";
import { getDebug } from "@midscene/shared/logger";
import { mergeAndNormalizeAppNameMapping, normalizeForComparison, repeat } from "@midscene/shared/utils";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { Agent } from "@midscene/core/agent";
import { agentBehaviorInitArgShape, extractAgentBehaviorInitArgs, getAgentInitArgsSignature, shouldRebuildAgentForInitArgs } from "@midscene/shared/agent-tools/agent-behavior-init-args";
import { BaseMidsceneTools } from "@midscene/shared/agent-tools/base-tools";
import { overrideAIConfig } from "@midscene/shared/env";
import node_path from "node:path";
import { select as prompts_select } from "@inquirer/prompts";
import { createScreenshotPreviewDescriptor, definePlaygroundPlatform } from "@midscene/playground";
import { PLAYGROUND_SERVER_PORT } from "@midscene/shared/constants";
import { findAvailablePort } from "@midscene/shared/node";
function _define_property(obj, key, value) {
    if (key in obj) Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
    });
    else obj[key] = value;
    return obj;
}
const execFileAsync = promisify(execFile);
const debugHdc = getDebug('harmony:hdc');
const supportedStringKeyEvents = new Set([
    'Back',
    'Home',
    'Power'
]);
const numericKeyEventPattern = /^\d+$/;
const uiInputErrorPattern = /(?:(?:Invalid parameters|Missing parameter|Too many parameters)\.?|Please confirm that the coordinate values are correct\.?)/i;
const minUiInputSpeed = 200;
const maxUiInputSpeed = 40000;
function roundUiCoordinate(value, name) {
    if (!Number.isFinite(value) || value < 0) throw new Error(`HDC ${name} coordinate must be a non-negative finite number`);
    return Math.round(value);
}
function roundUiInputSpeed(value, operation) {
    if (!Number.isFinite(value) || value < minUiInputSpeed || value > maxUiInputSpeed) throw new Error(`HDC ${operation} speed must be a finite number between ${minUiInputSpeed} and ${maxUiInputSpeed}`);
    return Math.round(value);
}
function parseBundleInfo(output, bundleName) {
    const jsonStart = output.indexOf('{');
    if (jsonStart < 0) throw new Error(`Cannot parse bundle information for ${bundleName}`);
    try {
        return JSON.parse(output.slice(jsonStart));
    } catch (error) {
        throw new Error(`Cannot parse bundle information for ${bundleName}`, {
            cause: error
        });
    }
}
function isLauncherAbility(ability) {
    return Boolean(ability.skills?.some((skill)=>skill.entities?.includes('entity.system.home') && skill.actions?.some((action)=>'action.system.home' === action || 'ohos.want.action.home' === action)));
}
function resolveModuleName(module) {
    return [
        module.moduleName,
        module.name
    ].find((name)=>Boolean(name));
}
function componentNamesMatch(left, right) {
    return left === right || left.endsWith(`.${right}`) || right.endsWith(`.${left}`);
}
function declaredTargetFromModule(module) {
    const moduleName = resolveModuleName(module);
    if (!moduleName) return;
    const abilities = module.abilityInfos ?? [];
    const declaredAbilityName = [
        module.mainElementName,
        module.mainAbility
    ].find((name)=>Boolean(name));
    if (declaredAbilityName) {
        const matchedAbility = abilities.find((ability)=>ability.name && componentNamesMatch(ability.name, declaredAbilityName));
        const isDeclaredExtension = module.extensionInfos?.some((extension)=>extension.name && componentNamesMatch(extension.name, declaredAbilityName));
        if (!matchedAbility && (abilities.length > 0 || isDeclaredExtension)) return;
        return {
            abilityName: matchedAbility?.name ?? declaredAbilityName,
            moduleName
        };
    }
}
function launcherTargetFromModule(module) {
    const moduleName = resolveModuleName(module);
    if (!moduleName) return;
    const launcherAbility = module.abilityInfos?.find(isLauncherAbility);
    return launcherAbility?.name ? {
        abilityName: launcherAbility.name,
        moduleName
    } : void 0;
}
function resolveHdcPath(hdcPath) {
    if (hdcPath) return hdcPath;
    if (process.env.HDC_HOME) {
        const envPath = `${process.env.HDC_HOME}/hdc`;
        debugHdc(`Using HDC from HDC_HOME: ${envPath}`);
        return envPath;
    }
    const homeDir = process.env.HOME || process.env.USERPROFILE || '';
    const commonPaths = [
        `${homeDir}/Library/HarmonyOS/next/command-line-tools/sdk/default/openharmony/toolchains/hdc`,
        `${homeDir}/Library/HarmonyOS/sdk/hmscore/3.1.0/toolchains/hdc`
    ];
    for (const p of commonPaths)try {
        accessSync(p, constants.X_OK);
        debugHdc(`Found HDC at: ${p}`);
        return p;
    } catch  {}
    return 'hdc';
}
class HdcClient {
    buildArgs(args) {
        if (this.deviceId) return [
            '-t',
            this.deviceId,
            ...args
        ];
        return args;
    }
    async exec(...args) {
        let release;
        const prev = this.execMutex;
        this.execMutex = new Promise((r)=>{
            release = r;
        });
        await prev;
        const fullArgs = this.buildArgs(args);
        debugHdc(`hdc ${fullArgs.join(' ')}`);
        try {
            const { stdout, stderr } = await execFileAsync(this.hdcPath, fullArgs, {
                timeout: this.timeout,
                maxBuffer: 52428800
            });
            if (stderr?.trim()) debugHdc(`hdc stderr: ${stderr.trim()}`);
            debugHdc(`hdc ${fullArgs.join(' ')} end`);
            return stdout;
        } catch (error) {
            if (error.killed && error.stdout?.trim()) {
                debugHdc('hdc process was killed but stdout is available, treating as success');
                return error.stdout;
            }
            debugHdc(`hdc error: ${error.message}`);
            throw new Error(`HDC command failed: hdc ${fullArgs.join(' ')}: ${error.message}`, {
                cause: error
            });
        } finally{
            release();
        }
    }
    async shell(command) {
        return this.exec('shell', command);
    }
    assertUiInputSucceeded(operation, output, detail) {
        const errorMatch = output.match(uiInputErrorPattern);
        if (!errorMatch) return;
        const detailText = detail ? ` for ${detail}` : '';
        throw new Error(`HDC uiInput ${operation} failed${detailText}: ${errorMatch[0]}`);
    }
    async runUiInput(operation, args, detail) {
        const output = await this.shell(`uitest uiInput ${operation} ${args}`);
        this.assertUiInputSucceeded(operation, output, detail);
    }
    buildPointerMovementArgs(operation, fromX, fromY, toX, toY, speed) {
        const args = [
            roundUiCoordinate(fromX, 'fromX'),
            roundUiCoordinate(fromY, 'fromY'),
            roundUiCoordinate(toX, 'toX'),
            roundUiCoordinate(toY, 'toY')
        ];
        if (void 0 !== speed) args.push(roundUiInputSpeed(speed, operation));
        return args.join(' ');
    }
    async fileSend(localPath, remotePath) {
        await this.exec('file', 'send', localPath, remotePath);
    }
    async fileRecv(remotePath, localPath) {
        await this.exec('file', 'recv', remotePath, localPath);
    }
    async screenshot(remotePath) {
        return await this.shell(`snapshot_display -f ${remotePath}`);
    }
    async click(x, y) {
        await this.runUiInput('click', `${roundUiCoordinate(x, 'x')} ${roundUiCoordinate(y, 'y')}`);
    }
    async doubleClick(x, y) {
        await this.runUiInput('doubleClick', `${roundUiCoordinate(x, 'x')} ${roundUiCoordinate(y, 'y')}`);
    }
    async longClick(x, y) {
        await this.runUiInput('longClick', `${roundUiCoordinate(x, 'x')} ${roundUiCoordinate(y, 'y')}`);
    }
    async swipe(fromX, fromY, toX, toY, speed) {
        await this.runUiInput('swipe', this.buildPointerMovementArgs('swipe', fromX, fromY, toX, toY, speed));
    }
    async fling(fromX, fromY, toX, toY, speed) {
        await this.runUiInput('fling', this.buildPointerMovementArgs('fling', fromX, fromY, toX, toY, speed));
    }
    async drag(fromX, fromY, toX, toY, speed) {
        await this.runUiInput('drag', this.buildPointerMovementArgs('drag', fromX, fromY, toX, toY, speed));
    }
    async inputText(x, y, text) {
        const escapedText = text.replace(/'/g, "'\\''");
        await this.runUiInput('inputText', `${roundUiCoordinate(x, 'x')} ${roundUiCoordinate(y, 'y')} '${escapedText}'`);
    }
    async keyEvent(...keys) {
        if (keys.length < 1 || keys.length > 3) throw new Error('HDC keyEvent requires between 1 and 3 keys');
        if (keys.length > 1 && keys.some((key)=>supportedStringKeyEvents.has(key))) throw new Error('HDC system key events cannot be combined');
        for (const key of keys)if (!supportedStringKeyEvents.has(key) && !numericKeyEventPattern.test(key)) throw new Error(`Invalid HDC key event: ${key}`);
        const joinedKeys = keys.join(' ');
        await this.runUiInput('keyEvent', joinedKeys, joinedKeys);
    }
    async clearTextField() {
        const output = await this.shell('uitest uiInput keyEvent 2072 2017;uitest uiInput keyEvent 2055');
        this.assertUiInputSucceeded('clearTextField', output);
    }
    async startAbility(bundleName, abilityName, moduleName) {
        const moduleArg = moduleName ? ` -m ${moduleName}` : '';
        const output = await this.shell(`aa start -a ${abilityName} -b ${bundleName}${moduleArg}`);
        if (output.includes('error:')) throw new Error(`Failed to start ${bundleName}/${abilityName}: ${output.trim()}`);
    }
    async queryLaunchAbility(bundleName) {
        const output = await this.shell(`bm dump -n ${bundleName}`);
        const bundleInfo = parseBundleInfo(output, bundleName);
        const modules = bundleInfo.hapModuleInfos ?? [];
        const entryModuleNames = new Set([
            bundleInfo.entryModuleName,
            bundleInfo.mainEntry
        ].filter((name)=>Boolean(name)));
        const preferredModules = modules.filter((module)=>entryModuleNames.has(module.moduleName ?? '') || entryModuleNames.has(module.name ?? ''));
        const entryModules = modules.filter((module)=>!preferredModules.includes(module) && (1 === module.moduleType || 'entry' === module.moduleType));
        const remainingModules = modules.filter((module)=>!preferredModules.includes(module) && !entryModules.includes(module));
        const entryModulesByPriority = [
            ...preferredModules,
            ...entryModules
        ];
        for (const module of entryModulesByPriority){
            const target = declaredTargetFromModule(module);
            if (target) return target;
        }
        for (const module of entryModulesByPriority){
            const target = launcherTargetFromModule(module);
            if (target) return target;
        }
        for (const module of remainingModules){
            const target = launcherTargetFromModule(module);
            if (target) return target;
        }
        for (const module of remainingModules){
            const target = declaredTargetFromModule(module);
            if (target) return target;
        }
        throw new Error(`Cannot find a launchable ability for ${bundleName}`);
    }
    async launchBundle(bundleName) {
        const cachedTarget = this.launchAbilityCache.get(bundleName);
        const target = cachedTarget ?? await this.queryLaunchAbility(bundleName);
        try {
            await this.startAbility(bundleName, target.abilityName, target.moduleName);
            this.launchAbilityCache.set(bundleName, target);
        } catch (error) {
            this.launchAbilityCache.delete(bundleName);
            if (!cachedTarget) throw error;
            const refreshedTarget = await this.queryLaunchAbility(bundleName);
            if (refreshedTarget.abilityName === cachedTarget.abilityName && refreshedTarget.moduleName === cachedTarget.moduleName) throw error;
            debugHdc(`Cached launch ability changed from ${cachedTarget.moduleName}/${cachedTarget.abilityName} to ${refreshedTarget.moduleName}/${refreshedTarget.abilityName}`);
            await this.startAbility(bundleName, refreshedTarget.abilityName, refreshedTarget.moduleName);
            this.launchAbilityCache.set(bundleName, refreshedTarget);
        }
    }
    async forceStop(bundleName) {
        const output = await this.shell(`aa force-stop ${bundleName}`);
        if (output.includes('error:')) throw new Error(`Failed to force stop ${bundleName}: ${output.trim()}`);
    }
    async getScreenInfo() {
        const stdout = await this.shell('hidumper -s RenderService -a screen');
        const renderDimensionPattern = 'render\\s+(?:size|resolution)\\s*[:=]\\s*(\\d{3,5})x(\\d{3,5})';
        const activeFoldMatch = stdout.match(/foldScreenId:(\d+),\s*isConnected:\d+,\s*isPowerOn:1/);
        if (activeFoldMatch) {
            const activeId = activeFoldMatch[1];
            const screenRegex = new RegExp(`screen\\[\\d+\\]:\\s*id=${activeId},.*?${renderDimensionPattern}`);
            const screenMatch = stdout.match(screenRegex);
            if (screenMatch) {
                debugHdc(`Foldable screen detected, active screen id=${activeId}: ${screenMatch[1]}x${screenMatch[2]}`);
                return {
                    width: Number.parseInt(screenMatch[1], 10),
                    height: Number.parseInt(screenMatch[2], 10)
                };
            }
        }
        const renderSizeMatch = stdout.match(new RegExp(renderDimensionPattern));
        if (renderSizeMatch) return {
            width: Number.parseInt(renderSizeMatch[1], 10),
            height: Number.parseInt(renderSizeMatch[2], 10)
        };
        const displayStdout = await this.shell('hidumper -s DisplayManagerService -a');
        const displayMatch = displayStdout.match(/activeModes.*?(\d{3,5}),\s*(\d{3,5})/);
        if (displayMatch) return {
            width: Number.parseInt(displayMatch[1], 10),
            height: Number.parseInt(displayMatch[2], 10)
        };
        throw new Error(`Failed to get screen size from HDC. RenderService output: ${stdout}`);
    }
    async listTargets() {
        const stdout = await this.exec('list', 'targets');
        return stdout.trim().split('\n').map((line)=>line.trim()).filter((line)=>line.length > 0 && !line.startsWith('['));
    }
    constructor(options){
        _define_property(this, "hdcPath", void 0);
        _define_property(this, "deviceId", void 0);
        _define_property(this, "timeout", void 0);
        _define_property(this, "execMutex", Promise.resolve());
        _define_property(this, "launchAbilityCache", new Map());
        this.hdcPath = resolveHdcPath(options.hdcPath);
        this.deviceId = options.deviceId ?? '';
        this.timeout = options.timeout ?? 60000;
    }
}
const namedHarmonyKeyCodeMap = {
    Enter: '2054',
    Backspace: '2055',
    Tab: '2049',
    Escape: '2070',
    Home: 'Home',
    ArrowUp: '2012',
    ArrowDown: '2013',
    ArrowLeft: '2014',
    ArrowRight: '2015',
    Space: '2050',
    Delete: '2071',
    Alt: '2045',
    Shift: '2047',
    Control: '2072',
    Meta: '2076',
    Back: 'Back',
    Power: 'Power'
};
const keyNameAliasMap = new Map([
    [
        'return',
        'Enter'
    ],
    [
        'esc',
        'Escape'
    ],
    [
        'up',
        'ArrowUp'
    ],
    [
        'down',
        'ArrowDown'
    ],
    [
        'left',
        'ArrowLeft'
    ],
    [
        'right',
        'ArrowRight'
    ],
    [
        'ctrl',
        'Control'
    ]
]);
const caseInsensitiveNamedKeyCodeMap = new Map(Object.entries(namedHarmonyKeyCodeMap).map(([key, code])=>[
        key.toLowerCase(),
        code
    ]));
function numericKeyCode(value) {
    return String(value);
}
function resolveSingleHarmonyKeyCodes(keyName) {
    const trimmedKeyName = keyName.trim();
    if (!trimmedKeyName) {
        if (0 === keyName.length) throw new Error('Unsupported HarmonyOS key: empty key name');
        throw unsupportedKeyboardPressKey(keyName);
    }
    const alias = keyNameAliasMap.get(trimmedKeyName.toLowerCase());
    const normalizedKeyName = alias ?? trimmedKeyName;
    if (/^[0-9]$/.test(normalizedKeyName)) return [
        numericKeyCode(2000 + Number(normalizedKeyName))
    ];
    const letterKeyMatch = normalizedKeyName.match(/^[a-z]$/i);
    if (letterKeyMatch) {
        const alphabetIndex = letterKeyMatch[0].toUpperCase().charCodeAt(0) - 65;
        return [
            numericKeyCode(2017 + alphabetIndex)
        ];
    }
    const namedCode = caseInsensitiveNamedKeyCodeMap.get(normalizedKeyName.toLowerCase());
    if (namedCode) return [
        namedCode
    ];
    throw unsupportedKeyboardPressKey(keyName);
}
function unsupportedKeyboardPressKey(keyName) {
    return new Error(`Unsupported HarmonyOS keyboardPress key: ${JSON.stringify(keyName)}. Use typeText() for text input, or pass a supported key name such as "Enter", "A", or "1".`);
}
function resolveHarmonyKeyCodes(keyName) {
    const trimmedKeyName = keyName.trim();
    if (!trimmedKeyName && ' ' !== keyName) return resolveSingleHarmonyKeyCodes(keyName);
    if ('+' === trimmedKeyName) return resolveSingleHarmonyKeyCodes(keyName);
    if (trimmedKeyName.includes('+')) throw new Error(`HarmonyOS keyboardPress does not support key combinations: ${JSON.stringify(keyName)}`);
    return resolveSingleHarmonyKeyCodes(keyName);
}
function device_define_property(obj, key, value) {
    if (key in obj) Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
    });
    else obj[key] = value;
    return obj;
}
const defaultScrollUntilTimes = 10;
const defaultFastSwipeSpeed = 2000;
const maxScrollDistance = 9999999;
const scrollQuadrantDivisions = 4;
const screenEdgeMargin = 50;
const debugDevice = getDebug('harmony:device');
let screenshotResizeScaleWarned = false;
class HarmonyDevice {
    actionSpace() {
        const mobileActionContext = {
            input: this.inputPrimitives,
            size: ()=>this.size(),
            sleep: async (timeMs)=>{
                await sleep(timeMs);
            }
        };
        const defaultActions = [
            ...createDefaultMobileActions(mobileActionContext)
        ];
        const platformSpecificActions = Object.values(createPlatformActions(this));
        const customActions = this.customActions ?? [];
        return [
            ...defaultActions,
            ...platformSpecificActions,
            ...customActions
        ];
    }
    async performActionScroll(param) {
        const element = param.locate;
        const startingPoint = element ? {
            left: element.center[0],
            top: element.center[1]
        } : void 0;
        const scrollToEventName = param?.scrollType;
        if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
        else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
        else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
        else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
        else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
        else {
            if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance ?? void 0, startingPoint);
            else if ('left' === param.direction) await this.scrollLeft(param.distance ?? void 0, startingPoint);
            else if ('right' === param.direction) await this.scrollRight(param.distance ?? void 0, startingPoint);
            else throw new Error(`Unknown scroll direction: ${param.direction}`);
            else await this.scrollDown(param?.distance ?? void 0, startingPoint);
            await sleep(500);
        }
    }
    describe() {
        return this.descriptionText || `DeviceId: ${this.deviceId}`;
    }
    async connect() {
        const hdc = await this.getHdc();
        return hdc;
    }
    async getHdc() {
        if (this.destroyed) throw new Error(`HarmonyDevice ${this.deviceId} has been destroyed and cannot execute HDC commands`);
        if (this.hdc) return this.hdc;
        if (this.connecting) return this.connecting;
        this.connecting = (async ()=>{
            debugDevice(`Initializing HDC with device ID: ${this.deviceId}`);
            try {
                this.hdc = new HdcClient({
                    hdcPath: this.options?.hdcPath,
                    deviceId: this.deviceId
                });
                const screenInfo = await this.hdc.getScreenInfo();
                this.cachedScreenSize = screenInfo;
                this.descriptionText = `DeviceId: ${this.deviceId}\nScreenSize: ${screenInfo.width}x${screenInfo.height}`;
                debugDevice('HDC initialized successfully', this.descriptionText);
                return this.hdc;
            } catch (e) {
                debugDevice(`Failed to initialize HDC: ${e}`);
                throw new Error(`Unable to connect to device ${this.deviceId}: ${e}`);
            } finally{
                this.connecting = null;
            }
        })();
        return this.connecting;
    }
    setAppNameMapping(mapping) {
        this.appNameMapping = mergeAndNormalizeAppNameMapping({}, mapping);
    }
    resolveMappedAppTarget(appName) {
        const normalizedAppName = normalizeForComparison(appName);
        return this.appNameMapping[normalizedAppName];
    }
    resolveBundleNameForTerminate(target) {
        const mappedTarget = this.resolveMappedAppTarget(target);
        if (mappedTarget) return mappedTarget.split('/')[0];
        const [bundleOrAppName] = target.split('/');
        return (this.resolveMappedAppTarget(bundleOrAppName) ?? bundleOrAppName).split('/')[0];
    }
    async launch(uri) {
        const hdc = await this.getHdc();
        this.uri = uri;
        try {
            debugDevice(`Launching app: ${uri}`);
            const target = uri.includes('://') ? uri : this.resolveMappedAppTarget(uri) ?? uri;
            if (target.includes('://')) {
                const sanitizedUri = target.replace(/[`$\\;"'|&<>(){}]/g, '');
                await hdc.shell(`aa start -U ${sanitizedUri}`);
            } else if (target.includes('/')) {
                const [bundleName, abilityName] = target.split('/');
                await hdc.startAbility(bundleName, abilityName);
            } else await hdc.launchBundle(target);
            debugDevice(`Successfully launched: ${uri}`);
        } catch (error) {
            debugDevice(`Error launching ${uri}: ${error}`);
            throw new Error(`Failed to launch ${uri}: ${error.message}`, {
                cause: error
            });
        }
        return this;
    }
    async terminate(uri) {
        const bundleName = this.resolveBundleNameForTerminate(uri);
        const hdc = await this.getHdc();
        try {
            debugDevice(`Terminating app: ${bundleName}`);
            await hdc.forceStop(bundleName);
            debugDevice(`Successfully terminated: ${bundleName}`);
        } catch (error) {
            debugDevice(`Error terminating ${bundleName}: ${error}`);
            throw new Error(`Failed to terminate ${bundleName}: ${error.message}`, {
                cause: error
            });
        }
    }
    async getScreenSize() {
        if (this.cachedScreenSize) return this.cachedScreenSize;
        const hdc = await this.getHdc();
        const screenInfo = await hdc.getScreenInfo();
        this.cachedScreenSize = screenInfo;
        return screenInfo;
    }
    async size() {
        const screenInfo = await this.getScreenSize();
        return {
            width: screenInfo.width,
            height: screenInfo.height
        };
    }
    async screenshotBase64() {
        debugDevice('screenshotBase64 begin');
        const hdc = await this.getHdc();
        if (!this.localScreenshotPath) this.localScreenshotPath = getTmpFile('jpeg');
        const maxAttempts = 2;
        for(let attempt = 1; attempt <= maxAttempts; attempt++){
            const snapshotOutput = await hdc.screenshot(this.remoteScreenshotPath);
            const dimMatch = snapshotOutput.match(/width\s+(\d+),\s*height\s+(\d+)/);
            if (dimMatch) {
                const w = Number.parseInt(dimMatch[1], 10);
                const h = Number.parseInt(dimMatch[2], 10);
                if (this.cachedScreenSize && (this.cachedScreenSize.width !== w || this.cachedScreenSize.height !== h)) {
                    debugDevice(`Screen size changed: ${this.cachedScreenSize.width}x${this.cachedScreenSize.height} -> ${w}x${h}`);
                    this.cachedScreenSize = {
                        width: w,
                        height: h
                    };
                }
            }
            await hdc.fileRecv(this.remoteScreenshotPath, this.localScreenshotPath);
            const screenshotBuffer = await node_fs.promises.readFile(this.localScreenshotPath);
            if (screenshotBuffer && screenshotBuffer.length > 0) {
                debugDevice(`Screenshot captured: ${screenshotBuffer.length} bytes`);
                return createImgBase64ByFormat('jpeg', screenshotBuffer.toString('base64'));
            }
            debugDevice(`Screenshot buffer empty (attempt ${attempt}/${maxAttempts})`);
            if (attempt < maxAttempts) await sleep(200);
        }
        throw new Error('Screenshot buffer is empty after retries');
    }
    async tapPoint(point) {
        this.lastTapPosition = {
            x: point.x,
            y: point.y
        };
        const hdc = await this.getHdc();
        await hdc.click(point.x, point.y);
    }
    async doubleTapPoint(point) {
        const hdc = await this.getHdc();
        await hdc.doubleClick(point.x, point.y);
    }
    async longPressPoint(point) {
        const hdc = await this.getHdc();
        await hdc.longClick(point.x, point.y);
    }
    async typeText(text, element, shouldReplace, options) {
        if (!text) return;
        const resolvedInputOptions = resolveTextInputOptions(options, this.options);
        const hdc = await this.getHdc();
        const typeDelay = resolvedInputOptions.keyboardTypeDelay;
        const inputSequentially = shouldInputSequentially(resolvedInputOptions);
        let x;
        let y;
        if (element) [x, y] = element.center;
        else if (this.lastTapPosition) {
            x = this.lastTapPosition.x;
            y = this.lastTapPosition.y;
        } else {
            const { width, height } = await this.size();
            x = Math.round(width / 2);
            y = Math.round(height / 2);
        }
        if (shouldReplace) {
            await hdc.click(x, y);
            await sleep(100);
            await hdc.clearTextField();
            await sleep(100);
        }
        if (inputSequentially) await sendTextSequentially(text, {
            sendCharacter: (character)=>hdc.inputText(x, y, character),
            wait: sleep
        }, {
            delayMs: typeDelay,
            delayAfterLast: 'legacy' === resolvedInputOptions.inputStrategy
        });
        else await hdc.inputText(x, y, text);
        const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
        if (shouldAutoDismissKeyboard) await this.hideKeyboard(options);
    }
    async clearInput(element) {
        const hdc = await this.getHdc();
        if (element) {
            await hdc.click(element.center[0], element.center[1]);
            await sleep(100);
        }
        await hdc.clearTextField();
    }
    async pressKey(key) {
        const keyCodes = resolveHarmonyKeyCodes(key);
        const hdc = await this.getHdc();
        await hdc.keyEvent(...keyCodes);
    }
    async scroll(deltaX, deltaY, speed) {
        if (0 === deltaX && 0 === deltaY) throw new Error('Scroll distance cannot be zero in both directions');
        const { width, height } = await this.size();
        const n = scrollQuadrantDivisions;
        const startX = Math.round(deltaX < 0 ? width / n * (n - 1) : width / n);
        const startY = Math.round(deltaY < 0 ? height / n * (n - 1) : height / n);
        const maxPositiveDeltaX = startX;
        const maxNegativeDeltaX = width - startX;
        const maxPositiveDeltaY = startY;
        const maxNegativeDeltaY = height - startY;
        deltaX = Math.max(-maxNegativeDeltaX, Math.min(deltaX, maxPositiveDeltaX));
        deltaY = Math.max(-maxNegativeDeltaY, Math.min(deltaY, maxPositiveDeltaY));
        const endX = Math.round(Math.max(screenEdgeMargin, Math.min(width - screenEdgeMargin, startX - deltaX)));
        const endY = Math.round(Math.max(screenEdgeMargin, Math.min(height - screenEdgeMargin, startY - deltaY)));
        const hdc = await this.getHdc();
        await hdc.fling(startX, startY, endX, endY, speed ?? defaultFastSwipeSpeed);
    }
    async scrollInDirection(direction, distance, startPoint) {
        const { width, height } = await this.size();
        const isVertical = 'up' === direction || 'down' === direction;
        const scrollDistance = Math.round(distance ?? (isVertical ? height : width));
        if (startPoint) {
            const hdc = await this.getHdc();
            const sx = Math.round(startPoint.left);
            const sy = Math.round(startPoint.top);
            const endPoints = {
                down: {
                    x: sx,
                    y: Math.max(screenEdgeMargin, sy - scrollDistance)
                },
                up: {
                    x: sx,
                    y: Math.min(height - screenEdgeMargin, sy + scrollDistance)
                },
                left: {
                    x: Math.min(width - screenEdgeMargin, sx + scrollDistance),
                    y: sy
                },
                right: {
                    x: Math.max(screenEdgeMargin, sx - scrollDistance),
                    y: sy
                }
            };
            const end = endPoints[direction];
            await hdc.fling(sx, sy, end.x, end.y, defaultFastSwipeSpeed);
            return;
        }
        const deltas = {
            down: [
                0,
                scrollDistance
            ],
            up: [
                0,
                -scrollDistance
            ],
            left: [
                -scrollDistance,
                0
            ],
            right: [
                scrollDistance,
                0
            ]
        };
        const [dx, dy] = deltas[direction];
        await this.scroll(dx, dy);
    }
    async scrollDown(distance, startPoint) {
        await this.scrollInDirection('down', distance, startPoint);
    }
    async scrollUp(distance, startPoint) {
        await this.scrollInDirection('up', distance, startPoint);
    }
    async scrollLeft(distance, startPoint) {
        await this.scrollInDirection('left', distance, startPoint);
    }
    async scrollRight(distance, startPoint) {
        await this.scrollInDirection('right', distance, startPoint);
    }
    async scrollUntilEdge(direction, startPoint) {
        if (startPoint) {
            const { width, height } = await this.size();
            const hdc = await this.getHdc();
            const sx = Math.round(startPoint.left);
            const sy = Math.round(startPoint.top);
            const flingTargets = {
                up: {
                    x: sx,
                    y: Math.round(height) - screenEdgeMargin
                },
                down: {
                    x: sx,
                    y: screenEdgeMargin
                },
                left: {
                    x: Math.round(width) - screenEdgeMargin,
                    y: sy
                },
                right: {
                    x: screenEdgeMargin,
                    y: sy
                }
            };
            const target = flingTargets[direction];
            await repeat(defaultScrollUntilTimes, ()=>hdc.fling(sx, sy, target.x, target.y, defaultFastSwipeSpeed));
            await sleep(1000);
            return;
        }
        const deltas = {
            up: [
                0,
                -maxScrollDistance
            ],
            down: [
                0,
                maxScrollDistance
            ],
            left: [
                -maxScrollDistance,
                0
            ],
            right: [
                maxScrollDistance,
                0
            ]
        };
        const [dx, dy] = deltas[direction];
        await repeat(defaultScrollUntilTimes, ()=>this.scroll(dx, dy, defaultFastSwipeSpeed));
        await sleep(1000);
    }
    async scrollUntilTop(startPoint) {
        await this.scrollUntilEdge('up', startPoint);
    }
    async scrollUntilBottom(startPoint) {
        await this.scrollUntilEdge('down', startPoint);
    }
    async scrollUntilLeft(startPoint) {
        await this.scrollUntilEdge('left', startPoint);
    }
    async scrollUntilRight(startPoint) {
        await this.scrollUntilEdge('right', startPoint);
    }
    async back() {
        const hdc = await this.getHdc();
        await hdc.keyEvent('Back');
    }
    async home() {
        const hdc = await this.getHdc();
        await hdc.keyEvent('Home');
    }
    async recentApps() {
        const hdc = await this.getHdc();
        await hdc.keyEvent('10011');
    }
    async hideKeyboard(options) {
        const hdc = await this.getHdc();
        const keyboardDismissStrategy = options?.keyboardDismissStrategy ?? this.options?.keyboardDismissStrategy ?? 'esc-first';
        if ('back-first' === keyboardDismissStrategy) return void await hdc.keyEvent('Back');
        await hdc.keyEvent(...resolveHarmonyKeyCodes('Escape'));
    }
    async getDeviceLocalTimeString(format = 'YYYY-MM-DD HH:mm:ss') {
        const hdc = await this.getHdc();
        try {
            const stdout = await hdc.shell('date +%Y-%m-%dT%H:%M:%S');
            const match = stdout.trim().match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})$/);
            if (!match) throw new Error(`Invalid device time format: ${stdout}`);
            const [, year, month, day, hours, minutes, seconds] = match;
            const timeString = format.replace('YYYY', year).replace('MM', month).replace('DD', day).replace('HH', hours).replace('mm', minutes).replace('ss', seconds);
            debugDevice(`Got device local time: ${timeString}`);
            return `${timeString} (${format})`;
        } catch (error) {
            debugDevice(`Failed to get device local time: ${error}`);
            throw new Error(`Failed to get device local time: ${error}`);
        }
    }
    async destroy() {
        if (this.destroyed) return;
        this.destroyed = true;
        this.cachedScreenSize = null;
        this.hdc = null;
        this.connecting = null;
    }
    constructor(deviceId, options){
        device_define_property(this, "deviceId", void 0);
        device_define_property(this, "hdc", null);
        device_define_property(this, "connecting", null);
        device_define_property(this, "destroyed", false);
        device_define_property(this, "descriptionText", void 0);
        device_define_property(this, "customActions", void 0);
        device_define_property(this, "cachedScreenSize", null);
        device_define_property(this, "appNameMapping", {});
        device_define_property(this, "lastTapPosition", null);
        device_define_property(this, "interfaceType", 'harmony');
        device_define_property(this, "uri", void 0);
        device_define_property(this, "options", void 0);
        device_define_property(this, "inputPrimitives", {
            pointer: {
                tap: (point)=>this.tapPoint(point),
                doubleClick: (point)=>this.doubleTapPoint(point),
                longPress: (point)=>this.longPressPoint(point),
                dragAndDrop: async (from, to)=>{
                    const hdc = await this.getHdc();
                    await hdc.drag(from.x, from.y, to.x, to.y);
                }
            },
            keyboard: {
                keyboardPress: (keyName)=>this.pressKey(keyName),
                typeText: (value, opts)=>{
                    const harmonyOpts = opts;
                    return harmonyOpts?.focusOnly ? Promise.resolve() : this.typeText(value, harmonyOpts?.target, harmonyOpts?.replace ?? true, {
                        autoDismissKeyboard: harmonyOpts?.autoDismissKeyboard,
                        keyboardDismissStrategy: harmonyOpts?.keyboardDismissStrategy,
                        keyboardTypeDelay: harmonyOpts?.keyboardTypeDelay,
                        inputStrategy: harmonyOpts?.inputStrategy
                    });
                },
                clearInput: (target)=>this.clearInput(target),
                cursorMove: async (direction, times = 1)=>{
                    const arrowKey = 'left' === direction ? 'ArrowLeft' : 'ArrowRight';
                    for(let i = 0; i < times; i++)await this.pressKey(arrowKey);
                }
            },
            touch: {
                swipe: async (start, end, opts)=>{
                    const duration = opts?.duration;
                    const repeatCount = opts?.repeat ?? 1;
                    const hdc = await this.getHdc();
                    for(let i = 0; i < repeatCount; i++)await hdc.swipe(start.x, start.y, end.x, end.y, duration ? Math.round(duration) : void 0);
                }
            },
            scroll: {
                scroll: (param)=>this.performActionScroll(param)
            }
        });
        device_define_property(this, "remoteScreenshotPath", '/data/local/tmp/ms_screen.jpeg');
        device_define_property(this, "localScreenshotPath", null);
        node_assert(deviceId, 'deviceId is required for HarmonyDevice');
        this.deviceId = deviceId;
        this.options = options;
        this.customActions = options?.customActions;
        if (options?.screenshotResizeScale !== void 0 && !screenshotResizeScaleWarned) {
            screenshotResizeScaleWarned = true;
            console.warn('[midscene] screenshotResizeScale is deprecated. Use screenshotShrinkFactor in AgentOpt instead.');
        }
    }
}
const runHdcShellParamSchema = z.object({
    command: z.string().describe('HDC shell command to execute')
});
const launchParamSchema = z.object({
    uri: z.string().describe('App name, bundle name, or URL to launch. Prioritize using the exact bundle name or URL the user has provided. If none provided, use the accurate app name.')
});
const terminateParamSchema = z.object({
    uri: z.string().describe('Bundle name or app name to terminate. Prioritize using the exact bundle name the user provided. If the bundle is unknown, use the accurate app name shown on screen, such as Settings or Music.')
});
const createPlatformActions = (device)=>({
        RunHdcShell: defineAction({
            name: 'RunHdcShell',
            description: 'Execute HDC shell command on HarmonyOS device',
            interfaceAlias: 'runHdcShell',
            paramSchema: runHdcShellParamSchema,
            sample: {
                command: 'hidumper -s WindowManagerService -a'
            },
            call: async (param)=>{
                if (!param.command || '' === param.command.trim()) throw new Error('RunHdcShell requires a non-empty command parameter');
                const hdc = await device.getHdc();
                return await hdc.shell(param.command);
            }
        }),
        Launch: defineAction({
            name: 'Launch',
            description: 'Launch a HarmonyOS app or URL',
            interfaceAlias: 'launch',
            paramSchema: launchParamSchema,
            sample: {
                uri: 'com.example.app'
            },
            call: async (param)=>{
                if (!param.uri || '' === param.uri.trim()) throw new Error('Launch requires a non-empty uri parameter');
                await device.launch(param.uri);
            }
        }),
        Terminate: defineAction({
            name: 'Terminate',
            description: 'Terminate (force-stop) a HarmonyOS app by bundle name or mapped app name',
            interfaceAlias: 'terminate',
            paramSchema: terminateParamSchema,
            call: async (param)=>{
                if (!param.uri || '' === param.uri.trim()) throw new Error('Terminate requires a non-empty uri parameter');
                await device.terminate(param.uri);
            }
        }),
        HarmonyBackButton: defineAction({
            name: 'HarmonyBackButton',
            description: 'Trigger the system "back" operation on HarmonyOS devices',
            call: async ()=>{
                await device.back();
            }
        }),
        HarmonyHomeButton: defineAction({
            name: 'HarmonyHomeButton',
            description: 'Trigger the system "home" operation on HarmonyOS devices',
            call: async ()=>{
                await device.home();
            }
        }),
        HarmonyRecentAppsButton: defineAction({
            name: 'HarmonyRecentAppsButton',
            description: 'Trigger the system "recent apps" operation on HarmonyOS devices',
            call: async ()=>{
                await device.recentApps();
            }
        })
    });
const defaultAppNameMapping = {
    设置: 'com.huawei.hmos.settings',
    Settings: 'com.huawei.hmos.settings',
    相机: 'com.huawei.hmos.camera',
    Camera: 'com.huawei.hmos.camera',
    图库: 'com.huawei.hmos.photos',
    Gallery: 'com.huawei.hmos.photos',
    日历: 'com.huawei.hmos.calendar',
    Calendar: 'com.huawei.hmos.calendar',
    时钟: 'com.huawei.hmos.clock',
    Clock: 'com.huawei.hmos.clock',
    计算器: 'com.huawei.hmos.calculator',
    Calculator: 'com.huawei.hmos.calculator',
    文件管理: 'com.huawei.hmos.filemanager',
    备忘录: 'com.huawei.hmos.notepad',
    联系人: 'com.huawei.hmos.contacts',
    电话: 'com.huawei.hmos.phone',
    信息: 'com.huawei.hmos.message',
    邮件: 'com.huawei.hmos.email',
    浏览器: 'com.huawei.hmos.browser',
    Browser: 'com.huawei.hmos.browser',
    应用市场: 'com.huawei.appmarket',
    AppGallery: 'com.huawei.appmarket',
    华为音乐: 'com.huawei.hmsapp.music',
    Music: 'com.huawei.hmsapp.music',
    华为视频: 'com.huawei.hmos.video',
    天气: 'com.huawei.hmos.weather',
    Weather: 'com.huawei.hmos.weather',
    抖音: 'com.ss.hm.ugc.aweme',
    支付宝: 'com.alipay.mobile.client',
    高德地图: 'com.amap.hmapp',
    百度: 'com.baidu.baiduapp',
    携程: 'com.ctrip.harmonynext'
};
const debugUtils = getDebug('harmony:utils');
async function getConnectedDevices(hdcPath, options = {}) {
    try {
        const hdc = new HdcClient({
            hdcPath,
            timeout: options.timeout
        });
        const targets = await hdc.listTargets();
        const devices = targets.map((deviceId)=>({
                deviceId
            }));
        debugUtils(`Found ${devices.length} connected devices: `, devices);
        return devices;
    } catch (error) {
        console.error('Failed to get device list:', error);
        throw new Error(`Unable to get connected HarmonyOS device list, please ensure HDC is properly configured: ${error.message}`, {
            cause: error
        });
    }
}
function agent_define_property(obj, key, value) {
    if (key in obj) Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
    });
    else obj[key] = value;
    return obj;
}
const debugAgent = getDebug('harmony:agent');
class HarmonyAgent extends Agent {
    async launch(uri) {
        const action = this.wrapActionInActionSpace('Launch');
        return action({
            uri
        });
    }
    async terminate(uri) {
        const action = this.wrapActionInActionSpace('Terminate');
        return action({
            uri
        });
    }
    async runHdcShell(command) {
        const action = this.wrapActionInActionSpace('RunHdcShell');
        return action({
            command
        });
    }
    createActionWrapper(name) {
        const action = this.wrapActionInActionSpace(name);
        return (...args)=>action(args[0]);
    }
    constructor(device, opts){
        super(device, opts), agent_define_property(this, "back", void 0), agent_define_property(this, "home", void 0), agent_define_property(this, "recentApps", void 0);
        const appNameMapping = mergeAndNormalizeAppNameMapping(defaultAppNameMapping, opts?.appNameMapping);
        device.setAppNameMapping(appNameMapping);
        this.back = this.createActionWrapper('HarmonyBackButton');
        this.home = this.createActionWrapper('HarmonyHomeButton');
        this.recentApps = this.createActionWrapper('HarmonyRecentAppsButton');
    }
}
async function agentFromHdcDevice(deviceId, opts) {
    if (!deviceId) {
        const devices = await getConnectedDevices(opts?.hdcPath);
        if (0 === devices.length) throw new Error('No HarmonyOS devices found. Please connect a HarmonyOS device and ensure HDC is properly configured. Run `hdc list targets` to verify device connection.');
        deviceId = devices[0].deviceId;
        debugAgent('deviceId not specified, will use the first device (id = %s)', deviceId);
    }
    const device = new HarmonyDevice(deviceId, opts || {});
    await device.connect();
    return new HarmonyAgent(device, opts);
}
function agent_tools_define_property(obj, key, value) {
    if (key in obj) Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
    });
    else obj[key] = value;
    return obj;
}
const debug = getDebug('agent-tools:harmony');
function adaptHarmonyInitArgs(extracted) {
    if (!extracted) return;
    const initArgs = {
        ...'string' == typeof extracted.deviceId ? {
            deviceId: extracted.deviceId
        } : {},
        ...extractAgentBehaviorInitArgs(extracted) ?? {}
    };
    return Object.keys(initArgs).length > 0 ? initArgs : void 0;
}
class HarmonyMidsceneTools extends BaseMidsceneTools {
    getCliReportSessionName() {
        return 'midscene-harmony';
    }
    createTemporaryDevice() {
        return new HarmonyDevice('temp-for-action-space', {});
    }
    async ensureAgent(initArgs) {
        const deviceId = initArgs?.deviceId;
        const nextSignature = getAgentInitArgsSignature(initArgs);
        if (this.agent && shouldRebuildAgentForInitArgs(this.lastInitArgsSignature, nextSignature)) {
            try {
                await this.agent.destroy?.();
            } catch (error) {
                debug('Failed to destroy agent during cleanup:', error);
            }
            this.agent = void 0;
        }
        if (this.agent) return this.agent;
        debug('Creating Harmony agent with deviceId:', deviceId || 'auto-detect');
        const reportOptions = this.readCliReportAgentOptions();
        const agent = await agentFromHdcDevice(deviceId, {
            autoDismissKeyboard: false,
            ...extractAgentBehaviorInitArgs(initArgs) ?? {},
            ...reportOptions ?? {}
        });
        this.agent = agent;
        this.lastInitArgsSignature = nextSignature;
        return agent;
    }
    preparePlatformTools() {
        return [
            {
                name: 'harmony_connect',
                description: 'Connect to HarmonyOS device via HDC. If deviceId not provided, uses the first available device.',
                schema: this.getAgentInitArgSchema(),
                cli: this.getAgentInitArgCliMetadata(),
                handler: async (args)=>{
                    const initArgs = this.extractAgentInitParam(args);
                    const deviceId = initArgs?.deviceId;
                    const reportSession = this.createNewCliReportSession(deviceId ?? 'auto');
                    this.commitCliReportSession(reportSession);
                    if (this.agent) {
                        try {
                            await this.agent.destroy?.();
                        } catch (error) {
                            debug('Failed to destroy agent during connect:', error);
                        }
                        this.agent = void 0;
                        this.lastInitArgsSignature = void 0;
                    }
                    const agent = await this.ensureAgent(initArgs);
                    const screenshot = await agent.page.screenshotBase64();
                    return {
                        content: [
                            {
                                type: 'text',
                                text: `Connected to HarmonyOS device${deviceId ? `: ${deviceId}` : ' (auto-detected)'}`
                            },
                            ...this.buildScreenshotContent(screenshot)
                        ],
                        isError: false
                    };
                }
            },
            {
                name: 'harmony_disconnect',
                description: 'Disconnect from current HarmonyOS device and release HDC resources',
                schema: {},
                handler: this.createDisconnectHandler('HarmonyOS device')
            }
        ];
    }
    constructor(...args){
        super(...args), agent_tools_define_property(this, "lastInitArgsSignature", void 0), agent_tools_define_property(this, "initArgSpec", {
            namespace: 'harmony',
            shape: {
                deviceId: z.string().optional().describe('HarmonyOS device ID (from hdc list targets)'),
                ...agentBehaviorInitArgShape
            },
            cli: {
                preferBareKeys: true
            },
            adapt: adaptHarmonyInitArgs
        });
    }
}
const HARMONY_NO_DEVICE_MESSAGE = 'No HarmonyOS devices found. Connect a device via USB, ensure HDC is configured, and run `hdc list targets` to verify.';
function createNoDeviceError() {
    return new Error(HARMONY_NO_DEVICE_MESSAGE);
}
async function getHdcTargets() {
    const devices = await getConnectedDevices();
    return devices.map((device, index)=>({
            id: device.deviceId,
            label: device.deviceId,
            isDefault: 0 === index,
            status: 'device'
        }));
}
async function getHdcTargetsSafe() {
    try {
        const targets = await getHdcTargets();
        return {
            targets
        };
    } catch (error) {
        return {
            targets: [],
            error: error instanceof Error ? error.message : String(error)
        };
    }
}
async function selectDevice() {
    console.log('🔍 Scanning for HarmonyOS devices...');
    const targets = await getHdcTargets();
    if (0 === targets.length) throw createNoDeviceError();
    if (1 === targets.length) {
        console.log(`📱 Found device: ${targets[0].id}`);
        return targets[0].id;
    }
    return prompts_select({
        message: '📱 Multiple devices found. Please select one:',
        choices: targets.map((target)=>({
                name: target.label,
                value: target.id
            }))
    });
}
const harmonyPlaygroundPlatform = definePlaygroundPlatform({
    id: 'harmony',
    title: 'Midscene HarmonyOS Playground',
    description: "HarmonyOS playground platform descriptor",
    async prepare (options) {
        const staticDir = options?.staticDir || node_path.join(__dirname, '../../static');
        const availablePort = await findAvailablePort(PLAYGROUND_SERVER_PORT);
        if (availablePort !== PLAYGROUND_SERVER_PORT) console.log(`⚠️  Port ${PLAYGROUND_SERVER_PORT} is busy, using port ${availablePort} instead`);
        const createSessionManager = ()=>({
                async getSetupSchema () {
                    const explicitDeviceId = options?.deviceId;
                    const discovery = explicitDeviceId ? {
                        targets: [
                            {
                                id: explicitDeviceId,
                                label: explicitDeviceId,
                                isDefault: true,
                                status: 'device'
                            }
                        ]
                    } : await getHdcTargetsSafe();
                    const { error, targets } = discovery;
                    return {
                        title: 'Welcome to\nMidscene.js Playground!',
                        description: 'Select an available HDC device to create the current HarmonyOS Agent',
                        primaryActionLabel: 'Create Agent',
                        autoSubmitWhenReady: 1 === targets.length,
                        notice: error ? {
                            type: 'warning',
                            message: 'HarmonyOS device discovery failed',
                            description: error
                        } : void 0,
                        fields: [
                            {
                                key: 'deviceId',
                                label: 'HDC device',
                                type: 'select',
                                required: true,
                                options: targets.map((target)=>({
                                        label: target.label,
                                        value: target.id,
                                        description: target.description
                                    })),
                                defaultValue: targets.find((target)=>target.isDefault)?.id,
                                placeholder: 'Select a connected HarmonyOS device'
                            }
                        ],
                        targets
                    };
                },
                async listTargets () {
                    if (options?.deviceId) return [
                        {
                            id: options.deviceId,
                            label: options.deviceId,
                            isDefault: true,
                            status: 'device'
                        }
                    ];
                    return (await getHdcTargetsSafe()).targets;
                },
                async createSession (input) {
                    const targets = options?.deviceId ? [
                        {
                            id: options.deviceId,
                            label: options.deviceId,
                            isDefault: true,
                            status: 'device'
                        }
                    ] : await getHdcTargets();
                    const deviceId = options?.deviceId || ('string' == typeof input?.deviceId && input.deviceId ? input.deviceId : targets.find((target)=>target.isDefault)?.id);
                    if (!deviceId) throw createNoDeviceError();
                    const connectAgent = async ()=>{
                        const device = new HarmonyDevice(deviceId);
                        await device.connect();
                        return new HarmonyAgent(device, options?.getAgentOptions?.());
                    };
                    const agent = await connectAgent();
                    return {
                        agent,
                        agentFactory: connectAgent,
                        preview: createScreenshotPreviewDescriptor({
                            title: 'HarmonyOS device preview'
                        }),
                        displayName: deviceId,
                        metadata: {
                            deviceId
                        }
                    };
                }
            });
        if (options?.deferConnection) return {
            platformId: 'harmony',
            title: 'Midscene HarmonyOS Playground',
            sessionManager: createSessionManager(),
            launchOptions: {
                port: availablePort,
                openBrowser: false,
                verbose: false,
                staticPath: staticDir
            },
            preview: createScreenshotPreviewDescriptor({
                title: 'HarmonyOS device preview'
            }),
            metadata: {
                ...options.deviceId ? {
                    deviceId: options.deviceId
                } : {},
                sessionConnected: false,
                setupState: 'required'
            }
        };
        const selectedDeviceId = options?.deviceId || await selectDevice();
        return {
            platformId: 'harmony',
            title: 'Midscene HarmonyOS Playground',
            agentFactory: async ()=>{
                const device = new HarmonyDevice(selectedDeviceId);
                await device.connect();
                return new HarmonyAgent(device, options?.getAgentOptions?.());
            },
            launchOptions: {
                port: availablePort,
                openBrowser: false,
                verbose: false,
                staticPath: staticDir
            },
            preview: createScreenshotPreviewDescriptor({
                title: 'HarmonyOS device preview'
            }),
            metadata: {
                deviceId: selectedDeviceId
            }
        };
    }
});
export { HarmonyAgent, HarmonyDevice, HarmonyMidsceneTools, agentFromHdcDevice, getConnectedDevices, harmonyPlaygroundPlatform, overrideAIConfig };
