tis-meshagent-template icon

Mesh Agent Template

Silent install package for Mesh Agent Template

0.2.1.3-83
System and network
System and network

tis-meshagent

Prerequisites

⚠️ This package requires a previously configured MeshCentral server.
Before installing, make sure your MeshCentral server is correctly set up and operational.

You can follow one of the official installation guides below, depending on your environment:


Configuration

Agent retrieval

⚠️ Make sure you are logged into the MeshCentral web interface before proceeding.

To download the agent binaries, go to your server’s URL, for example:
https://mesh.fschelfaut.lan/meshagents

Download both x86 and x64 Windows agents.


Package modification

Once the binaries are downloaded, run the update-package function.

  1. Rename the package as desired.
  2. Edit the configuration by modifying the necessary fields in the Value column (press F2 to edit).
    The parameters that must be modified are: mesh_server, mesh_port, mesh_deviceGroup
  3. Select the meshagent binary for x86 (if not already present in the package root).
  4. Select the meshagent binary for x64 (if not already present in the package root).

The package configuration is complete!
You can now build and upload the package to your WAPT repository.

Preprod packages are packages built on LUTI. They remain in PREPROD usually for 5 days, after which a new VirusTotal scan is performed.
If the package passes this last check, it is promoted to PROD and published on the store.

  • package: tis-meshagent-template
  • name: Mesh Agent Template
  • version: 0.2.1.3-83
  • categories: System and network
  • maintainer: WAPT Team,Tranquil IT,Jimmy PELÉ,Hubert TOUVET
  • licence: Apache 2.0
  • locale: all
  • target_os: windows
  • impacted_process: MeshAgent,meshagent
  • architecture: all
  • signature_date:
  • size: 14.97 Ko
  • installed_size: 4.16 Mo
  • conflicts :

package           : tis-meshagent-template
version           : 0.2.1.3-83
architecture      : all
section           : base
priority          : optional
name              : Mesh Agent Template
categories        : System and network
maintainer        : WAPT Team,Tranquil IT,Jimmy PELÉ,Hubert TOUVET
description       : Run update-package-sources then build-upload and deploy
depends           : 
conflicts         : tis-mesh_agent_service
maturity          : PREPROD
locale            : all
target_os         : windows
min_wapt_version  : 2.1
sources           : 
installed_size    : 4158016
impacted_process  : MeshAgent,meshagent
description_fr    : 
description_pl    : 
description_de    : 
description_es    : 
description_pt    : 
description_it    : 
description_nl    : 
description_ru    : 
audit_schedule    : 
editor            : 
keywords          : 
licence           : Apache 2.0
homepage          : 
package_uuid      : abde5ebc-9c0d-4677-8fac-9a08e9d41ea5
valid_from        : 
valid_until       : 
forced_install_on : 
changelog         : 
min_os_version    : 
max_os_version    : 
icon_sha256sum    : 75919930641ca94d1e743ff8f41ac4f2f853ca4a4582c9bac1e8aa749fd735c1
signer            : test
signer_fingerprint: b82fc8ef4a4475c0f69ac168176c2bfc58f572eb716c4eadd65e4785c155dd8e
signature_date    : 2025-10-13T08:48:09.000000
signed_attributes : package,version,architecture,section,priority,name,categories,maintainer,description,depends,conflicts,maturity,locale,target_os,min_wapt_version,sources,installed_size,impacted_process,description_fr,description_pl,description_de,description_es,description_pt,description_it,description_nl,description_ru,audit_schedule,editor,keywords,licence,homepage,package_uuid,valid_from,valid_until,forced_install_on,changelog,min_os_version,max_os_version,icon_sha256sum,signer,signer_fingerprint,signature_date,signed_attributes
signature         : HjS5edv5m0e3krx/7626CAJ8fAwcdRAbLUmdpPFdacLikUkvFOEfg3eqy0hYEPxhvixBV3wOxKg7XrxOjJAFe1K4+DiUHEEGEP9BGT8g2dEqohM0Pwjp1VvbB6e18vYNzzhDOGwakpqRXlGaM4HvphaCPmISZJwBtiq9eKqLewBHe8HqTQLxnh3LgIWtQZaCEMQzyugIgDWMFqDzBRVZ8VOZ9Lgl7bK3iFIN0xebwEJPLPruMGhYtgJd1g+pvJn7qf9Pa/D/ozyOt7wB/esNSxfcvw/o6TKUuMHS0nSiphwXxzR+g0+yumCd3YfM7d0LiQhYhIeTO5Idb7Q+kOZbXg==

# -*- coding: utf-8 -*-
from setuphelpers import *

config_file = "mesh-conf.json"


def install():
    # Declaring local variables
    package_version = control.get_software_version()
    app_name = control.name
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))
    if mesh_config_dict["mesh_companyName"] != mesh_config_dict["mesh_serviceName"]:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"], mesh_config_dict["mesh_serviceName"])
    else:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"])
    app_path = makepath(app_dir, mesh_config_dict["mesh_fileName"] + ".exe")
    app_uninstallkey = mesh_config_dict["mesh_serviceName"]

    if iswin64():
        bin_name = glob.glob("*64-*.exe")[0]
    else:
        bin_name = glob.glob("*32-*.exe")[0]

    if not running_as_system():
        print(
            f'WARNING: {app_name} will NOT be installed as SYSTEM account, that will cause issues since it is installed as user: {get_current_user()} ("Waiting for key: {mesh_config_dict["mesh_serviceName"]}" will not complete), please uninstall it manually before deployment'
        )

    def get_app_version(key):
        return get_version_from_binary(app_path, "FileVersion")

    # Installing the software
    install_exe_if_needed(
        bin_name,
        silentflags="-fullinstall",
        key=app_uninstallkey,
        min_version=package_version,
        get_version=get_app_version,
        accept_returncodes=[0, 1, 3010, 3221226356],
    )

    # Adding QuietUninstallString in registry for software
    quiet_uninstall_string = f'"{app_path}" {"-fulluninstall"}'
    wait_uninstallkey_present(app_uninstallkey)
    register_uninstall(app_uninstallkey, quiet_uninstall_string=quiet_uninstall_string)


def uninstall():
    # Declaring local variables
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))
    app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"])
    app_uninstallkey = mesh_config_dict["mesh_serviceName"]

    # Removing remaining folders if possible
    if isdir(app_dir):
        killalltasks(control.get_impacted_process_list())
        wait_uninstallkey_absent(app_uninstallkey)
        print("Removing: %s" % (app_dir))
        remove_tree(app_dir)


def audit():
    # Declaring local variables
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))
    if mesh_config_dict["mesh_companyName"] != mesh_config_dict["mesh_serviceName"]:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"], mesh_config_dict["mesh_serviceName"])
    else:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"])
    app_path = makepath(app_dir, mesh_config_dict["mesh_fileName"] + ".exe")
    app_name = mesh_config_dict["mesh_displayName"]
    mesh_regkey = r"SOFTWARE\Open Source\%s" % mesh_config_dict["mesh_serviceName"]
    meshserverurl = registry_readstring(HKEY_LOCAL_MACHINE, mesh_regkey, "MeshServerUrl")
    nodeid = registry_readstring(HKEY_LOCAL_MACHINE, mesh_regkey, "NodeId")
    remotedesktopurl = "https://%s:%s/?viewmode=11&gotonode=%s&hide=25" % (mesh_config_dict["mesh_server"], mesh_config_dict["mesh_port"], nodeid)

    print(remotedesktopurl)
    """
    // SERVICE_STOPPED				  1    The service is not running.
    // SERVICE_START_PENDING		  2    The service is starting.
    // SERVICE_STOP_PENDING			  3    The service is stopping.
    // SERVICE_RUNNING				  4    The service is running.
    // SERVICE_CONTINUE_PENDING		  5    The service continue is pending.
    // SERVICE_PAUSE_PENDING		  6    The service pause is pending.
    // SERVICE_PAUSED				  7    The service is paused.
    // SERVICE_NOT_INSTALLED		100    The service is not installed.
    """
    # nodeid = run([app_path, "-nodeid"]).splitlines()[0]
    # run(f'"{app_path}" -exec "console.log(_MSH().meshServiceName);process.exit();"')
    state = run(f'"{app_path}" {"state"}')  # , accept_returncodes=[0, 1, 2, 3, 4, 5, 6, 7, 100]
    state_str = state.strip()
    print("%s service state is:" % app_name)
    print(state_str)
    if state_str.lower() == "RUNNING".lower():
        result = "OK"
    elif state_str.lower() == "NOT INSTALLED".lower():
        result = "WARNING"
        print(
            'INFO: "Not installed" Service state may be incorrect status, you may need to update your Mesh Server and Mesh Agent (more info here: https://github.com/Ylianst/MeshAgent/issues/87)'
        )
    else:
        result = "WARNING"

    try:
        WAPT.write_audit_data_if_changed("mesh", "meshserverurl", meshserverurl, keep_days=365)
        WAPT.write_audit_data_if_changed("mesh", "nodeid", nodeid, keep_days=365)
        WAPT.write_audit_data_if_changed("mesh", "agenthash", registry_readstring(HKEY_LOCAL_MACHINE, mesh_regkey, "AgentHash"), keep_days=365)
        WAPT.write_audit_data_if_changed("mesh", "remotedesktopurl", remotedesktopurl, keep_days=365)
    except:
        print("ERROR: write_audit_data failed")
        result = "ERROR"

    return result


def get_persistent_package_file(fname):
    if isdir(makepath(os.getcwd(), "WAPT", "persistent")):
        return makepath(os.getcwd(), "WAPT", "persistent", fname)
    else:
        return makepath(WAPT.persistent_root_dir, control.package_uuid, fname)


def get_persistent_package_dir():
    if isdir(makepath(os.getcwd(), "WAPT", "persistent")):
        return makepath(os.getcwd(), "WAPT", "persistent")
    else:
        return makepath(WAPT.persistent_root_dir, control.package_uuid)

# -*- coding: utf-8 -*-
from setuphelpers import *
import waptguihelper
import json


def get_persistent_package_file(fname):
    if isdir(makepath(os.getcwd(), "WAPT", "persistent")):
        return makepath(os.getcwd(), "WAPT", "persistent", fname)
    else:
        return makepath(WAPT.persistent_root_dir, control.package_uuid, fname)


config_file = "mesh-conf.json"
mesh_config_dict_old = json_load_file(get_persistent_package_file(config_file))


def update_package():
    # Declaring local variables
    package_updated = False

    # Gathering informations for the package
    if "template" in control.package:
        package = waptguihelper.input_dialog(
            control.package, "Please change the template suffix with a term corresponding to your Mesh Agent", control.package
        )
        control.package = package
        control.save_control_to_wapt()

    dialog_response = waptguihelper.grid_dialog(
        "Adapt your Agent parameters (corresponding to: meshcentral-data/config.json)",
        format_dict_to_grid(mesh_config_dict_old),
        0,
        '{"columns":[{"propertyname":"parameter","datatype":"String","required":false,"readonly":true,"width":153},{"propertyname":"value","datatype":"String","required":false,"readonly":false,"width":172}]}',
    )
    json_write_file(get_persistent_package_file(config_file), format_grid_to_dict(dialog_response))
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))

    # Getting binaries
    if glob.glob("*.exe"):
        ask_for_binaries = waptguihelper.message_dialog(control.package, "Do you want to deploy new Mesh Agent binaries ?", waptguihelper.MB_YESNO)
    else:
        ask_for_binaries = 6

    # Deleting binaries
    if ask_for_binaries == 6:
        for f in glob.glob("*.exe") + glob.glob("*.msi"):
            print("Removing: %s" % f)
            remove_file(f)

        bin_path_x86 = waptguihelper.filename_dialog(
            "Please provide x86 Mesh Agent",
            "",
            "%s32-%s.exe" % (mesh_config_dict["mesh_fileName"].replace(" ", ""), mesh_config_dict["mesh_deviceGroup"].replace(" ", "")),
            "EXE Files|*.exe",
        )
        bin_path_x64 = waptguihelper.filename_dialog(
            "Please provide x64 Mesh Agent",
            "",
            "%s64-%s.exe" % (mesh_config_dict["mesh_fileName"].replace(" ", ""), mesh_config_dict["mesh_deviceGroup"].replace(" ", "")),
            "EXE Files|*.exe",
        )
        bin_name_x64 = bin_path_x64.split(os.sep)[-1].split(".")[0]
        if bin_path_x86:
            bin_name_x86 = bin_path_x86.split(os.sep)[-1].split(".")[0]
            filecopyto(bin_path_x86, basedir)
            if not bin_name_x86 in control.impacted_process:
                control.impacted_process = control.impacted_process + "," + bin_name_x86
                control.save_control_to_wapt()
        filecopyto(bin_path_x64, basedir)
        if not bin_name_x64 in control.impacted_process:
            control.impacted_process = control.impacted_process + "," + bin_name_x64
            control.save_control_to_wapt()

        # Changing version of the package
        version = get_file_properties(bin_path_x64).get("FileVersion", control.get_software_version())
        if Version(version) > Version(control.get_software_version()):
            print("Software version updated (from: %s to: %s)" % (control.get_software_version(), Version(version)))
            package_updated = True
        else:
            print("Software version up-to-date (%s)" % Version(version))
        control.set_software_version(version)
        control.save_control_to_wapt()
    if not mesh_config_dict["mesh_fileName"] in control.impacted_process:
        control.impacted_process = control.impacted_process + "," + mesh_config_dict["mesh_fileName"]
        control.save_control_to_wapt()

    # Editing control
    if not "template" in control.package:
        control.name = mesh_config_dict["mesh_displayName"]
        control.description = mesh_config_dict["mesh_displayName"]
        control.save_control_to_wapt()

    # Validating or not update-package-sources
    return package_updated


def format_dict_to_grid(dict_data, key_name="parameter", value_name="value"):
    dict_list_data = []
    for section in dict_data:
        dict_list_data.append({key_name: section, value_name: dict_data[section]})
    return json.dumps(dict_list_data)


def format_grid_to_dict(dict_list_data, key_name="parameter", value_name="value"):
    dict_data = {}
    for entry in dict_list_data:
        dict_data.update({entry[key_name]: entry[value_name]})
    return dict_data

c8eedcc82defa2c7cbbfb938cf20885f06c9fe60f55377439b7be3629a3bddae : WAPT/README.md
d16c03ffade79629765e4a7090b01b6a598106df34bd57414d86cc0d36bf10d9 : WAPT/README_fr.md
01ca7fe94636e5a08fcb73849d3b5df25d51e2c82f4dd1a08f01798b25899819 : WAPT/certificate.crt
c93319868827d9ec45281e366ae3118b62d3138ddb93892f2088e7976364b36f : WAPT/changelog.txt
ce1d6c43b36851e71818cacc2a540c7f873b9a9cc5a8606fd5fd06a1be8126ab : WAPT/control
75919930641ca94d1e743ff8f41ac4f2f853ca4a4582c9bac1e8aa749fd735c1 : WAPT/icon.png
df263c72463ec99b91797ea95d4086c5f618f6b18d1d82eaa851eb186491810d : WAPT/persistent/mesh-conf.json
e2b1c188f4f53ca667c2c7e49cb92d111aa1eb4408c4ff43b90c18f50251e1d9 : luti.json
4cd62dada82b6bcb161275f1b94f6da8e1655ffc2ec675e8be9e2f41def234fa : setup.py
a1e856f29393cb2906d5431d3dc5da639291a8885412907ab9c1a46ff122ae7d : update_package.py

0.2.1.3-81

Package can now be fully customized interactively with update_package
Configuration is now based on JSON file "mesh-conf.json" graphically edited in update_package
Now using persistent folder to call JSON file locally with get_persistent_package_file()
Autofilling impacted_process
Fix uninstall by adding QuietUninstallString 
Reverse waptguihelper.grid_dialog for easy editing
Now asking for port instead of mixing it with server