# Seed Project

Source: https://docs.interop.io/desktop/developers/seed-project/index.html

## Overview

Available since io.Connect Desktop 10.0

The io.Connect seed project is the main approach for delivering, customizing, and packaging **io.Connect Desktop**. The seed project enables you to control the entire process of creating your own custom distribution package, reducing the time for adopting new platform versions.

The seed project provides the following benefits:

- installing the latest or a specific version of the platform components;
- integrating the platform license key;
- customizing the platform system configuration;
- customizing the platform assets like logos, icons, splash screen, loader, and more;
- customizing various branding features via the CLI configuration (the product name and description, the names and icons of the produced artifacts);
- development and production modes that support different configuration settings;
- templates for the customizable system apps (e.g., Workspaces App, Web Group App, Launchpad);
- adding your own custom apps to be packaged with your platform build;
- test templates for writing and executing your own tests;
- producing artifacts (installers, bundles) for Windows and macOS;
- code signing settings;
- publishing and auto update settings;
- CI/CD integration settings;

The [`@interopio/iocd-cli`](https://www.npmjs.com/package/@interopio/iocd-cli) library provides an io.Connect CLI which is a necessary tool for creating and managing a seed project.

> ⚠️ *Note that you can use `npx` to execute the CLI commands without installing the `@interopio/iocd-cli` library globally. Some of the CLI commands are available as script definitions in the `package.json` file of the `@interopio/iocd-cli` library and can be executed via `npm`.*

> ℹ️ *For more details on the available io.Connect CLI commands, see the [CLI](#cli) section.*

## Requirements

To be able to create and use an io.Connect seed project, the following requirements must be met:

- Node.js v22 or later;
- npm v9 or later;
- OS: Windows 10 or later, macOS 11 or later;
- License: the seed project requires a valid [license key](https://docs.interop.io/desktop/getting-started/what-is-io-connect-desktop/requirements/index.md#licensing) for **io.Connect Desktop**;
- Windows code signing: `signtool.exe` (part of the [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)) is required to sign binaries on Windows;

## Quick Start

Execute the following steps to quickly setup your seed project.

1. Create the seed project:

Use `npx` to execute the `create` package command directly:

```cmd
npx @interopio/iocd-cli@latest create
```

2. Follow the prompts and provide a product name, select the desired platform components, select which templates to add for customization, and provide a valid [license key](#configuration-license_key).

3. Install the necessary dependencies for the seed project:

```cmd
npm install
```

4. Prepare the project for usage by executing the following command. This will install the selected components and the dependencies for all customizable apps:

```cmd
npm run setup
```

5. To run your seed project in [development mode](#development) in order to test your platform and your interop-enabled apps, execute the following command:

```cmd
npm run dev
```

6. After customizing and testing your platform project, you can produce build artifacts both for Windows and for macOS by executing the following command:

```cmd
npm run build
```

7. Use the build artifacts to distribute and deploy your platform project.

> ⚠️ *Note that the **io.Connect Desktop** installation folder can be placed at any location. The io.Connect platform runs independently of its location in the file system. However, you should consider the [path length limitations on Windows](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) which allow for a maximum of a 256-character path string. The longest path in the **io.Connect Desktop** installation is 91 characters. You should consider this when deciding on a location for the installation bundle when using a bundle deployment, or if you decide to move the default installation folder to a new location when using an installer.*

## Project Structure

The following demonstrates the basic structure of the seed project:

```cmd
.
├── apps/                       # Contains the customizable system apps. You can place your own apps here to be bundled with the package.
├── components/                 # Downloaded platform components.
│   ├── iocd/                   # The main platform component (required).
│   └── <other-components>      # Additional licensed components (devTools, demos, and more).
├── config/                     # Seed project configuration files.
│   ├── forge.config.js         # Electron Forge configuration.
│   ├── iocd.cli.config.json    # io.Connect CLI configuration.
│   └── iocd.license.key        # License key file.
├── modifications/              # Contains all platform customizations (icons, images, and more) and configuration overrides.
│   ├── base/                   # Modification that will be applied in all modes (development/build).
│   ├── dev/                    # Modifications that will be applied only in development mode.
│   └── build/                  # Modifications that will be applied only during production builds.
├── tests/                      # Automated tests.
│   └── tests/
│       └── sample.spec.ts      # Example test suite.
├── .github/workflows/          # Pre-configured GitHub Actions workflow example for CI/CD automation.
├── package.json
└── README.md
```

## Components

The following table lists the currently available components that can be installed via the seed project:

| Component | Description | Platforms |
|-----------|-------------|-----------|
| `"iocd"` | **Required.** The main io.Connect Desktop application runtime. | Windows, macOS |
| `"devTools"` | Developer tools for io.Connect Desktop. | Windows, macOS |
| `"demos"` | A set of web-based demo applications showcasing various features of io.Connect Desktop. | Windows, macOS |
| `"java-demos"` | A set of Java demo applications showcasing io.Connect Desktop integration. | Windows, macOS |
| `"bbg"` | Bloomberg Terminal V3 integration adapter. | Windows, macOS |
| `"bbg-mdf"` | Bloomberg Market Data Feed integration adapter. | Windows |
| `"excel"` | Microsoft Excel integration adapter for io.Connect Desktop. | Windows |
| `"fidessa"` | Fidessa integration adapter for io.Connect Desktop. | Windows |
| `"citrix"` | Citrix integration adapter for io.Connect Desktop. | Windows |
| `"ibchat"` | IB Chat integration adapter for io.Connect Desktop. | Windows |
| `"outlook"` | Microsoft Outlook integration adapter for io.Connect Desktop. | Windows |
| `"word"` | Microsoft Word integration adapter for io.Connect Desktop. | Windows |
| `"ppt"` | Microsoft PowerPoint integration adapter for io.Connect Desktop. | Windows |

## Configuration

### Basic Settings

The metadata for your product can be configured in the `package.json` file:

```json
{
    "name": "my-product-slug",
    "version": "1.0.0",
    "description": "My product description.",
    "productName": "My Product Name",
    "author": "My Company"
}
```

The values specified in these fields are automatically populated by the io.Connect CLI when you execute the [`create` command](#cli-commands-create).

The following table describes the purpose of each property:

| Property | Type | Description |
|----------|------|-------------|
| `"author"` | `string` | Used as a company name (also used to auto-generate copyright). |
| `"description"` | `string` | Used as a product description in installers and metadata. |
| `"name"` | `string` | Used as a project identifier (e.g., folder name, executable name, package name, and more). |
| `"productName"` | `string` | Used as a human-readable product name displayed to users in UIs. |
| `"version"` | `string` | Used as a version for your platform app. |

The settings for configuring the io.Connect CLI are in the `iocd.cli.config.json` file located in the `/config` folder.

The following is a basic configuration for the io.Connect CLI specifying the store from which to download the platform components and the list of components and component versions to retrieve:

```json
{
    "components": {
        "store": {
            "github": {
                "repo": "interopio/iocd-components/releases"
            }
        },
        "list": {
            "iocd": "latest"
        }
    }
}
```

> ℹ️ *For details on all available configuration settings for the io.Connect CLI, see the complete JSON schema for the `iocd.cli.config.json` file. The schema is located in the `/node_modules/@interopio/iocd-cli/dist/schemas` folder. Add a `$schema` property to your configuration file to enable IDE autocompletion and validation.*

### License Key

**io.Connect Desktop** requires a valid license key to operate.

> ℹ️ *For details on how to obtain a license key for **io.Connect Desktop** 10.0, [contact us](https://interop.io/contact/).*

During the process of [creating the seed project](#quick_start), you will be prompted to provide a valid license key for **io.Connect Desktop**. Copy and paste your license key when prompted to continue the project creation. If you don't provide a valid license key, you won't be able to create a seed project.

The io.Connect CLI will create automatically an `iocd.license.key` file located in the `/config` folder of the seed project. You can use this file to manage your license key later.

> ⚠️ *Note that you must never commit the `iocd.license.key` file in your project repository to prevent leaking your license key. By default, this file is ignored in the `.gitignore` settings.*

For the purpose of setting up [CI/CD automation](#building_for_production-cicd_automation), you can also use the `IOCD_LICENSE_KEY` environment variable to store and obtain the license key from your GitHub secrets during build.

## Customization

### Modifications

Modifications are the recommended way to customize **io.Connect Desktop** components without directly editing component files. They allow you to change configurations, add apps, modify assets, and override default behavior while preserving your changes across component updates.

> ℹ️ *For more details and guides on applying modifications, see the following articles on the [Developer Community](https://community.interop.io/) site:*
>
> - [Multi-Environment Builds in the io.Connect Seed Project](https://community.interop.io/t/multi-environment-builds-in-io-connect-seed-project/577)
>
> - [How to Avoid Overwriting Component App Definitions](https://community.interop.io/t/component-config-files-in-modifications-base-get-overwritten-during-seed-project-build/578)

**Key Concepts:**

- **Modifications are stored separately** from components in the `modifications/` folder
- **Changes persist** when components are updated or reinstalled
- **Mode-specific modifications** allow different behavior in dev vs. build mode
- **Files are copied or merged** into components when running `npm run dev` or `npm run build`

**Directory Structure:**

```cmd
modifications/
├── base/                     # Applied in all modes.
│   └── iocd/
│       ├── config/
│       │   └── apps/
│       └── assets/
├── dev/                      # Applied only in dev mode
│   └── iocd/
│       └── config/
└── build/                    # Applied only in build mode.
    └── iocd/
        └── config/
```

**Processing Order:**

1. **Base modifications** (`modifications/base/`) are applied first
2. **Mode-specific modifications** (`modifications/dev/` or `modifications/build/`) are applied second, overriding base when needed

**Common Use Cases:**

- Replace logos and icons in `modifications/base/iocd/assets/`
- Configure system settings in `modifications/base/iocd/config/`
- Add app definitions in `modifications/base/iocd/apps/`
- Use different URLs for dev (localhost) vs. build (file://) in mode-specific folders

**Special File Types:**

- **`.json.merge`** - Deep merges with existing JSON instead of replacing
- **`.delete`** - Removes the corresponding file
- **`.replace`** marker - Replaces entire directory (place in directory to replace)

#### Deep Merging with `.json.merge` Files

The `.json.merge` extension enables you to selectively modify JSON configuration files without replacing them entirely. This is particularly useful when you want to override specific settings while preserving all other default values.

**How It Works**

When a file is named with the `.json.merge` extension (e.g., `system.json.merge`), it is deep-merged with the corresponding target file (`system.json`). The merge process:

1. Recursively traverses nested objects
2. Adds new properties from the merge file
3. Overrides existing properties with values from the merge file
4. Preserves properties not mentioned in the merge file

**Example: Object Deep Merge**

If the target `system.json` contains:
```json
{
    "logging": {
        "level": "info",
        "appender": "default",
        "maxFiles": 10
    },
    "features": {
        "autoSave": true,
        "notifications": true
    }
}
```

And your merge file `system.json.merge` contains:
```json
{
    "logging": {
        "level": "debug"
    },
    "features": {
        "devTools": true
    }
}
```

The result will be:
```json
{
    "logging": {
        "level": "debug",
        "appender": "default",
        "maxFiles": 10
    },
    "features": {
        "autoSave": true,
        "notifications": true,
        "devTools": true
    }
}
```

Notice that:
- `logging.level` is overridden to `"debug"`
- `logging.appender` and `logging.maxFiles` are preserved
- `features.devTools` is added
- `features.autoSave` and `features.notifications` are preserved

**Named Merge Files**

You can have multiple merge files (e.g., `*.json.merge-*`) targeting the same JSON file by adding a suffix after `.json.merge-`. This lets you organize your modifications into separate, focused merge files:

```
system.json.merge                    # Base merge (applied on all platforms)
system.json.merge-autoUpdate         # Auto update settings
system.json.merge-launchpad          # Launchpad-related overrides
system.json.merge-myFeature          # Custom feature settings
```

All of the above will merge into `system.json`. The target file is determined by the part before `.json.merge`.

When multiple merge files target the same JSON file, `modifications/base/` is applied before `modifications/<mode>/`. Within each directory, files are applied in the order returned by the file system.

**Platform-Specific Merge Files**

If a merge file suffix contains a platform identifier (`darwin` or `win32`), it will only be applied on that platform:

- `system.json.merge-darwin` - Applied only on macOS
- `system.json.merge-win32` - Applied only on Windows
- `system.json.merge-feature1-win32` - Applied only on Windows (with custom naming)

Merge files without a platform identifier in the suffix are applied on all platforms.

#### Array Merging in `.json.merge` Files

*Available since `@interopio/iocd-cli` 0.0.71.*

When using `.json.merge` files, arrays are **concatenated by default** with the existing arrays in the target file. This allows you to add new items to existing arrays without losing the original content.

You can control array merge behavior using special directive objects:

| Directive | Behavior | Description |
|-----------|----------|-------------|
| *(default)* | Concatenate | Arrays are merged by appending new items to existing ones |
| `$replace` | Replace entirely | The array is completely replaced with the new values |
| `$prepend` | Add to beginning | New items are added at the start of the existing array |
| `$append` | Add to end | Explicitly adds items to the end (same as default) |
| `$remove` | Remove items | Matching items are removed from the existing array |

**Example: Default Array Concatenation**

If the target `apps.json` contains:
```json
{
    "apps": ["app1", "app2"]
}
```

And your merge file `apps.json.merge` contains:
```json
{
    "apps": ["app3", "app4"]
}
```

The result will be:
```json
{
    "apps": ["app1", "app2", "app3", "app4"]
}
```

**Example: Replacing an Array**

To completely replace an array instead of concatenating, use the `$replace` directive:

```json
{
    "apps": { "$replace": ["only-this-app"] }
}
```

This is useful when you want to override a list entirely rather than extend it.

**Example: Prepending to an Array**

To add items at the beginning of an existing array, use the `$prepend` directive:

```json
{
    "startupScripts": { "$prepend": ["init.js", "setup.js"] }
}
```

This ensures your items appear before any existing items in the array.

**Example: Removing Items from an Array**

To remove specific items from an existing array, use the `$remove` directive:

```json
{
    "disabledPlugins": { "$remove": ["legacy-plugin", "deprecated-feature"] }
}
```

For objects within arrays, the removal uses deep comparison:

```json
{
    "apps": { "$remove": [{ "name": "unwanted-app" }] }
}
```

**Combining Object and Array Merging**

You can combine object deep merging with array directives in the same merge file:

```json
{
    "logging": {
        "level": "debug"
    },
    "features": {
        "enabledPlugins": ["my-plugin"],
        "disabledPlugins": { "$remove": ["old-plugin"] }
    },
    "startupApps": { "$prepend": ["dashboard"] }
}
```

This example:
- Overrides `logging.level` to `"debug"`
- Concatenates `"my-plugin"` to `features.enabledPlugins`
- Removes `"old-plugin"` from `features.disabledPlugins`
- Prepends `"dashboard"` to the `startupApps` array

#### System Configuration

The **io.Connect Desktop** system configuration (the `system.json` file) controls platform-wide settings like auto-updates, logging, and feature flags. To customize these settings, create modification files that merge with the default configuration.

**Location:**
```cmd
modifications/base/iocd/config/system.json.merge
```

The following example demonstrates how to enable auto updates:

```json
{
    "autoUpdater": {
        "enabled": true,
        "updateSource": {
            "type": "Service",
            "baseUrl": "https://updates.yourcompany.com"
        },
        "updateIntervalMinutes": 60
    }
}
```

The following example demonstrates how to configure the system logging:

```json
{
    "logging": {
        "level": "info",
        "appender": "default"
    }
}
```

**Mode-Specific Configurations**

Using different settings for development and production build mode:

**Dev mode** (`modifications/dev/iocd/config/system.json.merge`):

```json
{
    "logging": {
        "level": "debug"
    },
    "features": {
        "devTools": true
    }
}
```

**Build mode** (`modifications/build/iocd/config/system.json.merge`):

```json
{
    "logging": {
        "level": "error"
    },
    "features": {
        "telemetry": true
    }
}
```

When modifying the system configuration, consider the following:

- Use `.merge` extension to deep merge with existing configuration.
- Without `.merge`, the file completely replaces the original.
- Changes are applied when running `npm run dev` or `npm run build`.
- Environment variables like `${PRODUCT_VERSION}` are automatically expanded.

#### Icons & Images

The native application icon, tray icon, and platform branding images are controlled through the `modifications/base/iocd/assets/images/` folder. Create the `images/` folder if it doesn't exist, and add the image files you want to override.

Use the exact file names shown below, because the CLI, Electron Forge configuration, and platform runtime look for these paths during development and packaging.

##### Native App Icons

| File | Purpose | Requirements |
| --- | --- | --- |
| `logo.ico` | Windows executable icon. The CLI embeds it into the main executable during `npm run dev` and `npm run build`. The Electron Forge Squirrel maker also uses it as the setup executable icon. | Use a Windows `.ico` file with multiple sizes, such as 16x16, 32x32, 48x48, and 256x256. |
| `logo.icns` | macOS application icon. The CLI copies it into the app bundle's `Contents/Resources/` folder and sets `CFBundleIconFile` to `logo.icns`. The Electron Forge DMG maker also uses it for the generated `.dmg`. | Use a macOS `.icns` file with multiple sizes. You can create it with Icon Composer or `iconutil`. |
| `logo.png` | Image asset used by the platform UI and branding. | Use a PNG file, recommended size 512x512. This doesn't replace the native Windows `.exe` or macOS `.app` icon by itself. |

On Windows, close any running instance of the application before running the command again, because the CLI has to patch the executable icon and metadata. If the Windows executable icon is stored somewhere else, update the `win.exe.exeIconPath` setting in `config/iocd.cli.config.json` or set the `WIN_EXE_ICON_PATH` environment variable. The default path is recommended because it also keeps the Squirrel installer icon aligned with the app icon.

On macOS, keep the file name `logo.icns`. The CLI expects this name when it updates the app bundle and signs the result.

##### Tray Icons

```console
modifications/base/iocd/assets/
└── images/
    ├── tray-light.ico
    ├── tray-dark.ico
    ├── tray.ico
    ├── tray-alert-light.ico
    ├── tray-alert-dark.ico
    ├── tray-alert.ico
    ├── logo-alert.ico
    ├── trayTemplate.png
    ├── trayTemplate@2X.png
    ├── tray-alertTemplate.png
    └── tray-alertTemplate@2x.png
```

On Windows, the tray icon changes with the current platform theme:

| State | Lookup order |
| --- | --- |
| Light theme | `tray-light.ico`, then `tray.ico`, then `logo.ico` |
| Dark theme | `tray-dark.ico`, then `tray.ico`, then `logo.ico` |
| Light theme with active notifications | `tray-alert-light.ico`, then `tray-alert.ico`, then `logo-alert.ico`, then the current tray icon |
| Dark theme with active notifications | `tray-alert-dark.ico`, then `tray-alert.ico`, then `logo-alert.ico`, then the current tray icon |

On macOS, the platform uses template PNG files instead of `.ico` files:

| State | Lookup order | High-DPI companion |
| --- | --- | --- |
| Default | `trayTemplate.png` | `trayTemplate@2X.png` |
| Active notifications | `tray-alertTemplate.png`, then the current tray icon | `tray-alertTemplate@2x.png` |

The alert tray icon is used only when the platform has active notifications, the notification badge is enabled, and the tray notifications item is enabled. The `tray` section in `system.json` controls whether the tray menu and its items are shown; it doesn't provide custom icon file names.

After replacing any of these files, run the platform again or create a new package:

```cmd
npm run dev

# or
npm run build
```

### Apps

**io.Connect Desktop** includes several built-in apps that you can replace with your own custom versions:

- **Workspaces** - Workspace management UI
- **Groups** - Window grouping UI
- **Splash Screen** - Platform splash screen
- **Launchpad** - App launcher
- **Notifications** - Notification center

Apps in the `apps/` folder allow you to customize these built-in apps or add entirely new functionality. This approach keeps your core platform apps in the same repository, eliminating the need to manage them across multiple repositories and simplifying version control, deployment, and coordination.

#### Using Template Apps

Template apps can be selected during project creation or added later:

```cmd
# Add a template app
npx @interopio/iocd-cli@latest apps add workspaces

# List available templates
npx @interopio/iocd-cli@latest apps list
```

**Available templates:**

- `workspaces` - Workspace management
- `groups` - Window grouping
- `splash` - Loading screen
- `launchpad` - App launcher
- `tests` - Automated testing framework (automatically included in new projects)

Once added, build your implementation on top of the template in the `apps/` folder.

#### Creating Custom Apps

For custom apps, create a folder in `apps/` with an `iocd.app.json` file:

```cmd
apps/
└── my-custom-app/
    ├── iocd.app.json       # Defines modifications and build behavior
    ├── package.json
    └── src/
```

**Example `iocd.app.json`:**

In this example:
- **Dev mode** (triggered by `npm run dev`): Executes a `start` script (typically starts a dev server with hot-reload) and copies the development app configuration so the app definition points to the dev server
- **Build mode** (triggered by `npm run build`): Executes a `build` script (typically builds optimized production assets) and copies the final built assets to the modifications folder

```json
{
   "dev": {
        "script": "start",
        "modifications": [
            {
                "source": "/config/my-app-dev.json",
                "destination": "/modifications/dev/iocd/config/apps/my-app.json"
            }
        ]
    },
    "build": {
        "script": "build",
        "modifications": [
            {
                "source": "/dist/",
                "destination": "/modifications/build/iocd/assets/my-app"
            }
        ]
    }
}
```

The `iocd.app.json` file defines:
- **base**: Array of modifications applied in all modes
- **dev**: Object with optional `script` (npm script name) and `modifications` array
- **build**: Object with optional `script` (npm script name) and `modifications` array

#### Managing Apps

```cmd
# Install app dependencies
npx @interopio/iocd-cli@latest apps install

# Start apps in dev mode
npm run apps-dev

# Build apps for production
npx @interopio/iocd-cli@latest apps build
```

## Development

Development mode allows you to quickly start **io.Connect Desktop** with all your configured changes and apps, enabling rapid iteration and testing during development.

When you run dev mode, the CLI will:

1. **Apply all relevant modifications** - Base modifications and dev-specific modifications are copied to components
2. **Launch the platform** - Starts the platform with all changes applied

```cmd
npm run dev
```

> ⚠️ *Note that in order to run any [custom apps](#customization-apps) you may have in the `/apps` folder, you must run `npm run apps-dev` in a separate terminal to start them. If you don't start them, **io.Connect Desktop** may fail to start.*

To pass additional command line arguments to the launched **io.Connect Desktop** process, use the `--extraLaunchArgs` option:

```cmd
npm run dev -- --extraLaunchArgs "--inspect --someFlag"
```

### Installing & Updating Components

To install a component or update an existing component while in development mode, use the following command:

```cmd
npx @interopio/iocd-cli@latest components install <component-name>
```

This command will download and install the latest version of the specified component.

If you want to install a specific version, use:

```cmd
npx @interopio/iocd-cli@latest components install <component-name>@<version>
```

### Using Pre-Releases

You can also use pre-release versions of the platform components via the io.Connect CLI. The list of all published releases is available at [https://releases.interop.io/components.html](https://releases.interop.io/components.html).

To enable using a pre-release version, set the `"usePrereleases"` property to `true` in the `"components.store.github"` object in the `config/iocd.cli.config.json` file:

```json
{
    "components": {
        "store": {
            "github": {
                "repo": "interopio/iocd-components/releases",
                "usePrereleases": true
            }
        }
    }
}
```

After enabling the flag, install the desired pre-release version by specifying the exact version:

```cmd
npx @interopio/iocd-cli@latest components install iocd@10.1.0
```

> ⚠️ *Note that pre-release versions are intended for testing and evaluation purposes. They may contain experimental features or changes that aren't yet fully stable. It's recommended to use stable releases for production environments.*

#### Pre-Release Channels

*Available since `@interopio/iocd-cli` 0.0.80.*

Pre-release versions use channel tags to indicate their stability level:

- **`build`** - Build pre-releases (e.g., `v10.0.0-build.20250927121929`). These are automated build artifacts.
- **`dev`** - Development pre-releases (e.g., `v10.0.0-dev.260314T1935`). These are development snapshots.
- **`nightly`** - Nightly pre-releases (e.g., `v10.0.0-nightly.260319T0800`). These are nightly build snapshots.

When `"usePrereleases"` is `true` and you use `"latest"` as the version, the CLI will consider all pre-release channels by default. You can restrict which channels are allowed for `"latest"` resolution using the `"prereleaseChannels"` property:

```json
{
    "components": {
        "store": {
            "github": {
                "repo": "interopio/iocd-components/releases",
                "usePrereleases": true,
                "prereleaseChannels": ["build"]
            }
        },
        "list": {
            "iocd": "latest"
        }
    }
}
```

In this example, resolving `"latest"` will only consider stable releases and `-build` pre-releases. The `-dev` and `-nightly` channels will be excluded.

You can still install a specific pre-release version from any channel, regardless of the `"prereleaseChannels"` setting:

```cmd
npx @interopio/iocd-cli@latest components install iocd@10.1.0-nightly.260319T0800
```

> ⚠️ *The `"prereleaseChannels"` setting only applies to `"latest"` resolution. It is ignored when `"usePrereleases"` is `false`. When `"usePrereleases"` is `false`, no pre-release versions are available at all — not even for explicit version requests.*

### Downloading Components to a Local Store

*Available since `@interopio/iocd-cli` 0.0.76.*

You can download components from a remote store (GitHub or S3) to a local directory using the `download` command. By default, the command downloads the latest version of each component:

```cmd
npx @interopio/iocd-cli@latest components download ./my-local-store
```

To download only specific components, use the `--components` option with a semicolon-separated list. You can specify exact versions with `@<version>` or use `latest`:

```cmd
npx @interopio/iocd-cli@latest components download ./my-local-store --components "iocd@10.0.3;devTools@latest"
```

To overwrite existing files without prompting, use the `--force` flag:

```cmd
npx @interopio/iocd-cli@latest components download --force ./my-local-store
```

The downloaded components are saved in the local store format and can be used as a local component store by configuring the `"components.store.local"` property in `config/iocd.cli.config.json`.

## Testing

Testing is based on **WebdriverIO** and depends on the `@interopio/wdio-iocd-service` package, which enables starting and controlling **io.Connect Desktop** from WebdriverIO. This allows you to create and run comprehensive end-to-end (E2E) tests for your **io.Connect Desktop** platform.

### Running Tests

Tests are written in the `tests/` folder and can be executed using:

```cmd
# Run all tests
npm test

# Forward arguments to the underlying test runner
npm test -- --spec ./tests/sample.spec.ts

# Or using the WebdriverIO CLI directly
npx wdio run ./wdio.config.ts
```

### Writing Tests

Tests are written using WebdriverIO syntax with the **io.Connect Desktop** service handling app lifecycle. Example test structure:

```javascript
describe("io.Connect Desktop Platform", () => {
    it("should launch successfully", async () => {
        // Your test logic here.
        // The @interopio/wdio-iocd-service handles starting/stopping io.Connect Desktop.
    });
});
```

Add your test files to the `tests/` directory. They'll run automatically in CI/CD pipelines.

## Building for Production

The build process creates production-ready installers for distribution. It orchestrates multiple steps to produce signed, optimized packages ready for deployment.

**What happens during build:**

1. **Build all apps** - Executes build scripts for each app in production mode
2. **Reinstall components** - Downloads and installs fresh component versions
3. **Apply all modifications** - Copies base and build-specific modifications to components
4. **Code sign binaries** - Signs executables and libraries (if configured)
5. **Create build artifacts** - Generates installers (`.exe` setup on Windows, `.dmg` on macOS, `.zip` archives)
6. **Publish to release server** - Uploads artifacts for auto-updates (if configured)

**Build System:**

The build process is based on [Electron Forge](https://www.electronforge.io/), a complete toolchain for building and packaging Electron apps. The main configuration is located in `/config/forge.config.js`, where you can customize makers, publishers, and build behavior.

### Build Commands

```cmd
# Build installer
npm run build

# Build options
npm run build -- --output custom/path      # Custom output directory
npm run build -- --publish-only            # Skip build, only publish
npm run build-skip-install                 # Skip component installation
```

### Build Output

Depending on the OS, you can configure Electron Forge makers to produce different types of artifacts. The following configurations have been tested:

Additional components are packaged under the runtime `components/` directory inside the built io.Connect Desktop app. If your project includes only the required `iocd` component, the build output won't create an empty `components/` directory.

#### Windows

1. Squirrel.Windows installer (`.exe` setup)

- You can change the install GIF by replacing the **install.mp4** in assets folder.

- If using a Windows installer, you can hide the installer UI during the installation process by using the `--silent` command line argument:

```cmd
io-connect-desktop-setup.exe --silent
```

- The app will be installed into **%LocalAppData%\YourAppName**. Squirrel is opinionated and does not allow changing this path.

- By default the `/UserData` folder generated by the platform will stay in the **%LocalAppData%\interop.io\io.Connect Desktop** folder. You can change this by adding the following `system.json.merge-win32` file in `modifications/base/iocd/config/system.json.merge-win32` (the merge file has win32 suffix which indicates it is only applied on Windows OS):

```json
{
    "folders": {
        "userData": "%LocalAppData%/${PRODUCT_SLUG}/UserData/%IO_CD_ENV%-%IO_CD_REGION%",
        "cache": {
            "location": "%LocalAppData%/${PRODUCT_SLUG}/Cache/%IO_CD_ENV%-%IO_CD_REGION%",
            "copy": true
        }
    }
}
```

2. Portable ZIP archive

#### macOS

1. DMG disk image

2. ZIP archive

The full list of makers is available in the [Electron Forge documentation](https://www.electronforge.io/config/makers).

### Code Signing

Code signing ensures that your app is trusted by operating systems and users can verify that it hasn't been compromised. Both Windows and macOS require code signing for distribution.

The settings for code signing are located under the `"codeSign"` property of the `"win"` top-level key for Windows and the `"mac"` top-level key for macOS in the `iocd.cli.config.json` seed project configuration file.

#### Windows

**Prerequisites**

The io.Connect CLI uses `signtool.exe` (part of the [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)) to sign binaries. It must be available on your system `PATH` before running a build with code signing enabled.

In CI environments (e.g., GitHub Actions), `signtool.exe` is typically already available or added to the `PATH` by the workflow.

The `"type"` property of the `"codeSign"` object under the `"win"` top-level key accepts the following values:

| Value | Description |
|-------|-------------|
| `"custom"` | Use a custom signing script. |
| `"signtool"` | Use a certificate and a sign tool to sign. The certificate can be retrieved from a PFX certificate file or from the Windows Certificate Store. |
| `"off"` | Disable code signing. |

- **PFX Certificate File**

The is the most common method for code-signing on Windows. It uses a PFX certificate with a password. The PFX file contains both the certificate and the private key needed by the sign tool.

To configure signing via a PFX certificate file, provide the path to the file and the PFX password:

```json
// In `config/iocd.cli.config.json`.
{
    "win": {
        "codeSign": {
            "type": "signtool",
            "pfxPath": "path/to/certificate.pfx",
            "pfxPassword": "${WIN_PFX_PASS}"
        }
    }
}
```

- **Certificate from the Windows Certificate Store**

This method uses a certificate that already exists in the Windows Certificate Store. This is the approach you would use with services like DigiCert KeyLocker. For instance, with KeyLocker, you sync the certificate into the local Windows Certificate Store and sign using the certificate hash. The advantage is that the private key never leaves the HSM which is good for more security-sensitive environments.

To configure signing via a certificate from the Windows Certificate Store, provide the certificate SHA-1:

```json
// In `config/iocd.cli.config.json`.
{
    "win": {
        "codeSign": {
            "type": "signtool",
            "certificateSha1": "${WIN_CERT_SHA1}"
        }
    }
}
```

When configuring your code signing settings, consider the following:

> ⚠️ *Note that if using DigiCert KeyLocker, you must run `smctl windows certsync` before building to sync certificates to the Windows Certificate Store.*

- **Custom Script**

For advanced signing scenarios, you can provide a custom signing script:

```json
// In `config/iocd.cli.config.json`.
{
    "win": {
        "codeSign": {
            "type": "custom",
            "customCodeSignScriptPath": "path/to/custom-sign.js"
        }
    }
}
```

Your custom script should export a function that receives the binary path and config:

```javascript
// custom-sign.js
module.exports = async function(binaryPath, config) {
    // Your custom signing logic here.
    console.log(`Signing ${binaryPath}`);
    // Use any signing tool or API you need.
};
```

- **Signing Squirrel Installer Binaries**

The io.Connect CLI signs all platform binaries during the build process. However, the Squirrel.Windows maker produces additional stub binaries (e.g., `Setup.exe`, `Update.exe`, `<APP-NAME>.exe`) as part of the installer creation step. These stub binaries are generated *after* the CLI code signing phase and therefore aren't signed automatically.

To ensure the Squirrel-produced binaries are also signed, add the `windowsSign` configuration to your Squirrel maker in `config/forge.config.js`:

```javascript
const path = require("path");

module.exports = {
    makers: [
        {
            name: "@electron-forge/maker-squirrel",
            config: {
                // ...other Squirrel config,

                windowsSign: {
                    certificateFile: path.resolve(__dirname, "certs/certificate.pfx"),
                    certificatePassword: process.env.WINDOWS_CERTIFICATE_PASSWORD,
                    timestampServer: "http://timestamp.digicert.com",
                    signToolPath: "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.26100.0\\x64\\signtool.exe",
                },
            },
        },
    ],
};
```

> ⚠️ *Note that the `signToolPath` may vary depending on your Windows SDK version. Adjust the path to match your installed version.*

- **Excluding Folders from Signing**

*Available since `@interopio/iocd-cli` 0.0.74.*

You can exclude specific folders from code signing on Windows by using the `"excludeFolders"` property of the `"codeSign"` object. This is useful when certain folders contain third-party binaries that are already signed or don't need to be signed with your certificate.

The `"excludeFolders"` property accepts an array of glob patterns. Paths are matched relative to the io.Connect Desktop component directory using forward slashes. By default, no folders are excluded — all binaries found in the build output are signed.

Supported glob syntax:

| Pattern | Description |
|---------|-------------|
| `**` | Matches any number of path segments (including zero). |
| `*` | Matches anything within a single path segment. |
| `?` | Matches a single character. |
| `!` (prefix) | Negates a pattern (re-includes previously excluded paths). |

Patterns are evaluated in order and the last matching pattern wins (similar to `.gitignore`).

Example configuration that excludes all binaries under `components/` and `assets/gw-legacy/`, but re-includes `components/my-addon/`:

```json
// In `config/iocd.cli.config.json`.
{
    "win": {
        "codeSign": {
            "type": "signtool",
            "pfxPath": "path/to/certificate.pfx",
            "pfxPassword": "${WIN_PFX_PASS}",
            "excludeFolders": [
                "components/**",
                "assets/gw-legacy/**",
                "!components/my-addon/**"
            ]
        }
    }
}
```

- **Signing Squirrel-Generated Stub Executables**

During the build process, the io.Connect CLI signs all platform binaries and the final setup `.exe` installer. However, during the installer creation process, Squirrel.Windows internally generates stub executables — `Update.exe` (responsible for managing app updates) and `<app-name>.exe` (the app launcher shortcut target). These stubs are created by Squirrel as part of building the installer package, so they cannot be signed by the CLI's signing step which runs separately.

To sign these Squirrel-generated stubs, add a `windowsSign` object to the `@electron-forge/maker-squirrel` configuration in `config/forge.config.js`:

```javascript
// In `config/forge.config.js`.
const path = require("path");

module.exports = {
    makers: [
        {
            name: "@electron-forge/maker-squirrel",
            config: {
                // ... other Squirrel config options,

                windowsSign: {
                    certificateFile: path.resolve(__dirname, "certs/certificate.pfx"),
                    certificatePassword: process.env.WINDOWS_CERTIFICATE_PASSWORD,
                    timestampServer: "http://timestamp.digicert.com",
                    signToolPath: "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.26100.0\\x64\\signtool.exe",
                },
            },
        },
    ],
};
```

The following table describes the `windowsSign` properties:

| Property | Type | Description |
|----------|------|-------------|
| `certificateFile` | `string` | Path to the PFX certificate file used for signing the Squirrel-generated stubs. |
| `certificatePassword` | `string` | Password for the PFX certificate. Use an environment variable to avoid hardcoding secrets. |
| `timestampServer` | `string` | URL of the timestamp server to use for timestamping the signature. |
| `signToolPath` | `string` | *(Optional)* Explicit path to `signtool.exe`. Required only if `signtool.exe` is not on your system `PATH`. |

> ⚠️ *Note that the `signToolPath` may vary depending on the Windows SDK version installed on your system. Verify the path before configuring it.*

> ⚠️ *Never hardcode the `certificatePassword` value directly in your configuration file. Always use an environment variable (e.g., `process.env.WINDOWS_CERTIFICATE_PASSWORD`) and store the actual password in your CI/CD secrets or a secure credential store.*

> ⚠️ *Without this configuration, the `Update.exe` and `<app-name>.exe` stubs inside the installer will be unsigned. This may trigger Windows SmartScreen warnings or antivirus flags for end users.*

#### macOS

On macOS the process is a little different and more tightly controlled. It also requires an extra step called notarization.

**Code Signing Configuration**

The `"type"` property of the `"codeSign"` object under the `"mac"` top-level key accepts the following values:

| Value | Description |
|-------|-------------|
| `"certificate"` | Use a `.p12` certificate file. |
| `"custom"` | Use a custom signing script. |
| `"keychain"` | Use a certificate from macOS Keychain. |
| `"off"` | Disable code signing. |

- **Keychain**

This method uses a certificate from macOS Keychain. This is the mode to use if you want to use an Apple Developer ID certificate. You must download the certificate from the Apple Developer Portal, import it into your Keychain, and it will become available for code signing tools.

```json
// In `config/iocd.cli.config.json`.
{
    "mac": {
        "codeSign": {
            "type": "keychain",
            "identity": "Developer ID Application: Your Company (TEAM_ID)"
        }
    }
}
```

You can use the following properties of the `"codeSign"` object to specify your Developer ID Application identity and Keychain to use:

| Property | Type | Description |
|----------|------|-------------|
| `"identity"` | `string` | Developer ID Application identity or SHA-1 hash (optional, auto-selected if not specified). |
| `"keychain"` | `string` | Keychain name or path (optional, uses the default Keychain if not specified). |

- **Certificate**

This method uses a `.p12` certificate file that's imported into a temporary keychain during the build. The temporary keychain is removed after the build.

```json
// In `config/iocd.cli.config.json`.
{
    "mac": {
        "codeSign": {
            "type": "certificate",
            "certificatePath": "./certs/mac-cert.p12",
            "certificatePassword": "${MAC_CERT_PASSWORD}"
        }
    }
}
```

- **Custom Script**

For advanced signing scenarios, you can provide a custom signing script:

```json
// In `config/iocd.cli.config.json`.
{
    "mac": {
        "codeSign": {
            "type": "custom",
            "customCodeSignScriptPath": "path/to/custom-sign.js"
        }
    }
}
```

Your custom script should export a function that receives the app bundle path and config:

```javascript
// custom-sign.js
module.exports = async function(appBundlePath, config) {
  // Your custom signing logic here
  console.log(`Signing ${appBundlePath}`);
  // Use any signing tool or API you need
};
```

**Notarization Configuration**

Notarization is required for distributing any macOS app outside the App Store. This is how Apple verifies that the app is safe and hasn't been compromised. If an app isn't notarized, users will see warnings or may not be able to run it at all.

The notarization settings are located under the `"notarization"` property of the `"mac"` top-level key:

```json
// In `config/iocd.cli.config.json`.
{
    "mac": {
        "notarization": {
            "type": "notarytool",
            "appleId": "${MAC_NOTARIZATION_APPLE_ID}",
            "appleIdPassword": "${MAC_NOTARIZATION_APPLE_ID_PASSWORD}",
            "appleTeamId": "${MAC_NOTARIZATION_APPLE_TEAM_ID}"
        }
    }
}
```

> ⚠️ *Note that when configuring your notarization settings, you should consider the following:*
>
> - Notarization is required for distribution outside the Mac App Store.
> - Use an app-specific password (not your regular Apple ID password).
> - Generate an app-specific password at `appleid.apple.com`.
> - You can set `"type"` to `"off"` to skip notarization (for development builds).

- **Custom Script**

For advanced notarization scenarios, you can provide a custom notarization script:

```json
{
    "mac": {
        "notarization": {
            "type": "custom",
            "customNotarizationScriptPath": "path/to/custom-notarize.js"
        }
    }
}
```

Your custom script should export a function that receives the app bundle path and config:

```javascript
// custom-notarize.js
module.exports = async function(appBundlePath, config) {
    // Your custom notarization logic here.
    console.log(`Notarizing ${appBundlePath}`);
    // Use any notarization tool or API you need.
};
```

### Install Tasks

Available since io.Connect Desktop 10.0.4

**io.Connect Desktop** supports executing custom install and uninstall tasks via command-line triggers, allowing automated execution of scripts and commands during installation and uninstallation processes on both Windows and macOS.

This feature enables you to:

- Copy configuration files to system locations after installation
- Register protocol handlers after installation
- Download additional resources after installation
- Clean up user data and configurations before uninstallation
- Unregister protocols or file associations before uninstallation
- Backup user preferences before uninstallation

#### Overview

When using installers built with the seed project, **io.Connect Desktop** automatically responds to installation lifecycle events through command-line arguments. The Squirrel installer (used on Windows) automatically passes these arguments to the executable during install/uninstall operations.

The following command-line arguments trigger task execution:

| Argument | Platform | Description |
|----------|----------|-------------|
| `--post-install` | Cross-platform | Execute install tasks (manual trigger). |
| `--pre-uninstall` | Cross-platform | Execute uninstall tasks (manual trigger). |
| `--squirrel-install` | Windows | Initial installation (triggered by Squirrel installer). |
| `--squirrel-uninstall` | Windows | Application uninstall (triggered by Squirrel installer). |
| `--squirrel-updated` | Windows | Application update (triggered by Squirrel installer). |

> ℹ️ *The `--post-install` and `--pre-uninstall` flags can be used if you aren't using a Squirrel-based installer or if you want to manually test the install tasks.*

**Example:**

```cmd
# Windows
io-connect-desktop.exe --post-install

# macOS
open -a "io.Connect Desktop" --args --post-install
```

#### Configuration

Install and uninstall tasks are defined in platform-specific JSON configuration files within the `install/` folder:

- **`tasks.win32.json`** - Windows tasks
- **`tasks.darwin.json`** - macOS tasks

**Platform values:**
- `win32` - Windows
- `darwin` - macOS

**File location** depends on the deployment method:

- **Packaged Windows:** `[iocd-exe-directory]/install/` (next to the .exe)
- **Packaged macOS:** `IocdApp.app/Contents/install/` (in the Contents folder)

To add install tasks to your seed project, create the task files in the `modifications/build/iocd/install/` directory:

```cmd
modifications/
└── build/
    └── iocd/
        └── install/
            ├── tasks.win32.json  (Windows)
            └── tasks.darwin.json (macOS)
```

This ensures that the task files are included in your production build.

#### Task Schema

The task files follow this structure:

```json
{
  "installTasks": [
    {
      "type": "run",
      "args": {
        "filename": "path/to/executable",
        "args": "command line arguments",
        "dir": "working/directory",
        "hide": true,
        "unattended": false,
        "exitCode0": "success",
        "exitCode1": "Custom error message",
        "exitCodeN": "success"
      }
    }
  ],
  "uninstallTasks": [
    {
      "type": "run",
      "args": { /* same structure */ }
    }
  ]
}
```

Each task supports the following properties:

| Property | Type | Description |
|----------|------|-------------|
| `filename` | `string` | **Required.** Path to the executable or script to run. Supports environment variable expansion (e.g., `%TEMP%`, `%APPDATA%`). |
| `args` | `string` | Command-line arguments to pass to the executable. The full command is passed to the system shell for parsing. |
| `dir` | `string` | Working directory for the process. Defaults to the install folder if not specified. Supports environment variables. |
| `hide` | `boolean` | Hide the process window (Windows only). Default: `false`. |
| `unattended` | `boolean` | Suppress error dialogs. If `true`, errors are logged only. Default: `false`. |
| `exitCode0` to `exitCodeN` | `string` | Custom exit code handling. Use `"success"` to mark a non-zero exit code as success, or provide a custom error message. Default: exit code 0 is success, any other is an error. |

> ⚠️ **Important:** Task execution is synchronous and sequential. Tasks run one at a time in the order defined. If any task fails (non-zero exit code without a success mapping), execution stops immediately and no subsequent tasks run.

#### Examples

##### Windows Batch Script

```json
{
  "installTasks": [
    {
      "type": "run",
      "args": {
        "filename": "cmd.exe",
        "args": "/c copy-resources.bat",
        "hide": true,
        "exitCode0": "success",
        "exitCode1": "Error copying resources."
      }
    }
  ],
  "uninstallTasks": [
    {
      "type": "run",
      "args": {
        "filename": "cmd.exe",
        "args": "/c cleanup.bat",
        "hide": true
      }
    }
  ]
}
```

##### macOS Shell Script

```json
{
  "installTasks": [
    {
      "type": "run",
      "args": {
        "filename": "/bin/bash",
        "args": "setup.sh --silent",
        "hide": false
      }
    }
  ],
  "uninstallTasks": [
    {
      "type": "run",
      "args": {
        "filename": "/bin/bash",
        "args": "cleanup.sh",
        "dir": "/tmp"
      }
    }
  ]
}
```

##### Custom Exit Code Mapping

You can map specific exit codes to success or custom error messages:

```json
{
  "installTasks": [
    {
      "type": "run",
      "args": {
        "filename": "installer.exe",
        "args": "--verify",
        "exitCode0": "success",
        "exitCode1": "Error validating the installation.",
        "exitCode2": "Error contacting server.",
        "exitCode3": "success",
        "exitCode4": "Network timeout - please retry."
      }
    }
  ]
}
```

In this example:
- Exit codes 0 and 3 are considered successful
- Exit codes 1, 2, and 4 will show custom error messages
- Any other exit code will show a default error message

#### Environment Variable Expansion

All path-related properties (`filename`, `args`, `dir`) support environment variable expansion using the `%VAR%` syntax:

```json
{
  "args": {
    "filename": "%APPDATA%\\MyCompany\\install-script.bat",
    "dir": "%LOCALAPPDATA%\\MyCompany"
  }
}
```

**Platform-Provided Variables**

The following environment variables are automatically set by **io.Connect Desktop** and available for use in all install tasks:

| Variable | Description | Example Value |
|----------|-------------|---------------|
| `IO_CD_EXE_NAME` | Executable file name | `io-connect-desktop.exe` (Windows)<br/>`io-connect-desktop` (macOS) |
| `IO_CD_ROOT_DIR` | Root installation directory | `C:\Users\...\io-connect-desktop\app-10.0.4` (Windows)<br/>`/Applications/io.Connect Desktop.app/Contents` (macOS) |
| `IO_CD_INSTALL_DIR` | Directory containing install tasks | `C:\Users\...\io-connect-desktop\app-10.0.4\install` (Windows)<br/>`/Applications/io.Connect Desktop.app/Contents/install` (macOS) |

#### Troubleshooting

**Tasks not executing:**
- Verify that the `install/tasks.win32.json` (Windows) or `install/tasks.darwin.json` (macOS) file exists in the correct location
- Check that the file contains valid JSON
- Ensure the executable paths are correct and accessible

**Tasks failing:**
- Review the console logs for error messages
- Test the script manually to verify it works
- Check file permissions (especially on macOS - scripts need execute permission)
- Verify environment variables are correctly expanded

**Silent installation fails:**
- Ensure all tasks have `"unattended": true` to prevent error dialogs
- Test each task individually to identify which one is failing
- Consider adding custom exit code mappings for expected error conditions

### CI/CD Automation

Continuous Integration and Continuous Deployment (CI/CD) automates building, testing, and publishing your **io.Connect Desktop** platform. The seed project includes a GitHub Actions workflow located in `.github/workflows/build.yml`. It produces signed installers and can publish them to a release server.

### Auto Updates

Setting up [auto updates](https://docs.interop.io/desktop/developers/configuration/system/index.md#auto_updates) allows your users to automatically receive new versions of your platform.

**Requirements:**

Auto-updates are supported for specific build artifact types:
- **Windows**: Squirrel.Windows installers (`.exe` setup files)
- **macOS**: DMG on macOS

Other build outputs (portable ZIP on Windows, ZIP on macOS) do not support automatic updates.

#### Update Source Types

**io.Connect Desktop** supports two types of update sources:

1. StaticStorage (Simple - No Special Server Required)

This is the simpler approach that **does not require a special update server**. You only need a static file server (such as a CDN, web server, or cloud storage) where the RELEASES files can be accessed via HTTP/HTTPS.

**How It Works:**

- **macOS**: The platform appends `/RELEASES.json` to your `baseUrl` and expects a JSON file with update metadata
- **Windows**: The platform uses the `baseUrl` directly and expects a Squirrel.Windows RELEASES file at that location

**Example Configuration:**

Create or modify `modifications/base/iocd/config/system.json.merge`:

```json
{
    "autoUpdater": {
        "enabled": true,
        "updateSource": {
            "type": "StaticStorage",
            "baseUrl": "https://cdn.yourcompany.com/updates"
        },
        "updateIntervalMinutes": 60
    }
}
```

With this configuration:
- **macOS** will check: `https://cdn.yourcompany.com/updates/RELEASES.json`
- **Windows** will check: `https://cdn.yourcompany.com/updates` (for the RELEASES file)

**When to Use:**
- You want a simple deployment without maintaining a dedicated update service
- You have a static file server, CDN, or cloud storage (e.g., AWS S3, Azure Blob Storage, GitHub Pages)
- You don't need dynamic routing based on channels or complex version logic

2. Service (Advanced - Requires Update Service)

This approach requires a dedicated update service that can handle dynamic routing based on platform, version, and channel.

**How It Works:**

The platform constructs a dynamic URL in the format: `<baseUrl>/update/<platform>/<version>/<channel>`

**Example Configuration:**

Create or modify `modifications/base/iocd/config/system.json.merge`:

```json
{
    "autoUpdater": {
        "enabled": true,
        "updateSource": {
            "type": "Service",
            "baseUrl": "https://updates.yourcompany.com",
            "channel": "stable"
        },
        "updateIntervalMinutes": 60
    }
}
```

With this configuration, the platform will check URLs like:
- `https://updates.yourcompany.com/update/darwin/1.0.0/stable` (macOS)
- `https://updates.yourcompany.com/update/win32/1.0.0/stable` (Windows)

**When to Use:**
- You need different update channels (stable, beta, dev)
- You want server-side logic for update eligibility (e.g., phased rollouts, A/B testing)
- You need to track update requests or analytics
- You're already using an update service like Nucleus, Hazel, or a custom solution

#### Setting Up Auto Updates

Execute the following steps to set up platform auto updates:

- **Using StaticStorage** (Recommended for Most Users)

StaticStorage supports two approaches: **automated publishing with Electron Forge publishers** (recommended), or **manual file management** (advanced).

**Approach 1**: Automated with Electron Forge Publishers (Recommended)

This approach uses [Electron Forge publishers](https://www.electronforge.io/config/publishers) to automatically upload installers and generate RELEASES files.

1. **Choose your hosting platform**:
- [**S3 Publisher**](https://www.electronforge.io/config/publishers/s3) - Amazon S3 buckets
- [**GitHub Publisher**](https://www.electronforge.io/config/publishers/github) - GitHub Releases (for public repos)
- Other compatible static storage providers

2. **Configure the publisher** in `config/forge.config.js`:

**Example: S3 Publisher**
```javascript
module.exports = {
  publishers: [
    {
      name: '@electron-forge/publisher-s3',
      config: {
        bucket: 'my-app-updates',
        region: 'us-east-1',
        public: true,
        // Credentials via AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY env vars
      }
    }
  ],
  makers: [
    {
      name: '@electron-forge/maker-squirrel',
      config: {
        // For Windows delta updates
        remoteReleases: 'https://my-app-updates.s3.amazonaws.com'
      }
    },
    {
      name: '@electron-forge/maker-zip',
      config: {
        // For macOS RELEASES.json generation
        macUpdateManifestBaseUrl: 'https://my-app-updates.s3.amazonaws.com/darwin/x64'
      }
    }
  ]
};
```

**Example: GitHub Publisher**
```javascript
module.exports = {
  publishers: [
    {
      name: '@electron-forge/publisher-github',
      config: {
        repository: {
          owner: 'your-org',
          name: 'your-repo'
        },
        prerelease: false
      }
    }
  ]
};
```

> **Important:** The `macUpdateManifestBaseUrl` option [requires using an Electron Forge publisher](https://js.electronforge.io/interfaces/_electron_forge_maker_zip.MakerZIPConfig.html) (like S3 or GitHub). It will not work with purely manual file uploads.

3. **Configure io.Connect Desktop** to check for updates:

Create or modify `modifications/base/iocd/config/system.json.merge`:

```json
{
    "autoUpdater": {
        "enabled": true,
        "updateSource": {
            "type": "StaticStorage",
            "baseUrl": "https://my-app-updates.s3.amazonaws.com"
        },
        "updateIntervalMinutes": 60
    }
}
```

4. **Build and publish**:

```cmd
npm run build
```

The build process will:
- Generate installers and RELEASES files
- Automatically upload everything to your configured publisher
- Update the RELEASES.json with the new version (macOS)

5. **Test the auto updates**:
- Install your platform using the published installer
- Publish a new version
- Launch the platform and verify it detects and installs the update

**Approach 2**: Manual File Management (Advanced)

If you cannot use an Electron Forge publisher, you can manually manage files. This requires understanding the RELEASES file formats.

**Windows:**
The [Squirrel.Windows maker](https://www.electronforge.io/config/makers/squirrel.windows) automatically generates:
- `{ProductName} Setup.exe` - The installer
- `{ProductName}-full.nupkg` - The full package
- `RELEASES` - The update metadata file

Simply upload these files to your hosting location. The RELEASES file is plain text with nupkg metadata.

**macOS:**
For macOS without a publisher, you must **manually create and maintain** the RELEASES.json file. The format is:

```json
{
  "currentRelease": "1.0.0",
  "releases": [
    {
      "version": "1.0.0",
      "updateTo": {
        "version": "1.0.0",
        "pub_date": "2024-01-15T10:00:00Z",
        "notes": "Initial release",
        "name": "1.0.0",
        "url": "https://cdn.yourcompany.com/updates/MyApp-1.0.0-mac.zip"
      }
    }
  ]
}
```

Each time you release a new version, manually update this file to add the new release entry and update `currentRelease`.

**Manual Upload Steps:**
1. Build installers: `npm run build`
2. Find generated files in `dist/` or `out/` directory
3. For Windows: Upload `RELEASES`, `.exe`, and `.nupkg` files
4. For macOS: Create/update `RELEASES.json` manually and upload with `.zip` file
5. Ensure all URLs in RELEASES files are accessible
6. Test updates by installing the platform and verifying it detects new versions

- **Using Service (Advanced)**

1. **Set up an update service**:

You can use existing solutions or build your own:
- **Existing services**: Nucleus, Hazel, or other update servers (see [Electron update services](https://www.electronjs.org/docs/latest/tutorial/updates#using-other-update-services))
- **Custom solution**: Build your own update server that responds to requests at `<baseUrl>/update/<platform>/<version>/<channel>`

2. **Configure io.Connect Desktop** to check the update service:

Create or modify `modifications/base/iocd/config/system.json.merge`:

```json
{
    "autoUpdater": {
        "enabled": true,
        "updateSource": {
            "type": "Service",
            "baseUrl": "https://updates.yourcompany.com",
            "channel": "stable"
        },
        "updateIntervalMinutes": 60
    }
}
```

3. **Publish updates** to your service:

Choose one of the following approaches:

**Automatic publishing during build** - configure publishers in `config/forge.config.js`:

```javascript
module.exports = {
    publishers: [
        {
            name: "@electron-forge/publisher-electron-release-server",
            config: {
                baseUrl: "https://updates.yourcompany.com",
                username: process.env.RELEASE_SERVER_USERNAME,
                password: process.env.RELEASE_SERVER_PASSWORD
            }
        }
    ]
};
```

**Manual upload** - after building, manually upload the installer artifacts to your update service using its API or admin interface.

4. **Test the auto updates**:
- Install your platform using the signed installer
- Publish a new version to your update service
- Launch the platform and verify it detects and installs the update

## CLI

The io.Connect CLI is available via the [`@interopio/iocd-cli`](https://www.npmjs.com/package/@interopio/iocd-cli) library, which provides various commands for creating and manipulating a seed project for **io.Connect Desktop**.

To install the library, execute the following command:

```cmd
npm install -g @interopio/iocd-cli@latest
```

You can also use `npx` to directly execute package commands without global installation of the package:

```cmd
npx @interopio/iocd-cli@latest create
```

Some of the commands are defined under the `"scripts"` property of the generated seed project's `package.json` file and can be executed via `npm`:

```cmd
# Invokes `iocd dev`.
npm run dev

# Invokes `iocd test`.
npm run test

# Invokes `iocd setup`.
npm run setup

# Invokes `iocd build`.
npm run build

# Invokes `iocd build --skip-install`.
npm run build-skip-install

# Invokes `iocd apps dev`.
npm run apps-dev

# Invokes `iocd components install`.
npm run components-install
```

### Commands

The io.Connect CLI offers the following main commands:

| Command | Description |
|---------|-------------|
| `apps` | Command with sub-commands for managing the customizable system apps in the seed project. |
| `build <options>` | Builds and packages the **io.Connect Desktop** platform for distribution. |
| `components` | Command with sub-commands for managing the seed project components. |
| `create <options>` | Creates a new seed project. Accepts options for configuring the seed project. |
| `dev` | Starts **io.Connect Desktop** in development mode (the platform isn't packaged yet). |
| `help <command>` | Displays help for the specified command. |
| `license` | Displays current license key information. |
| `modifications` | Command with sub-commands for managing the seed project modifications. |
| `setup` | Prepares the seed project for first usage. This will install the specified components and will install the dependencies for all customizable system apps. |
| `test` | Runs the test suite. |

#### create

The `create` command accepts the following options:

| Option | Description |
|--------|-------------|
| `--source <url/path>` | Override component source during setup |
| `--non-interactive` | Run in non-interactive mode using provided options |
| `--product-name <name>` | Product name for non-interactive mode |
| `--folder-name <name>` | Folder name for non-interactive mode |
| `--components <components>` | Comma-separated list of components (e.g., "iocd,demos,devTools") |
| `--applications <applications>` | Comma-separated list of apps (e.g., "groups,workspaces") |
| `--license-key <key>` | License key for non-interactive mode. Required unless `IOCD_LICENSE_KEY` is set or `config/iocd.license.key` exists. |
| `--use-current-folder` | Install in current folder (non-interactive mode) |
| `-h`, `--help` | Display help for command |

#### dev

The `dev` command accepts the following options:

| Option | Description |
|--------|-------------|
| `--extraLaunchArgs <arguments>` | Extra launch arguments passed to the **io.Connect Desktop** executable. The arguments are split by space. |
| `-h`, `--help` | Display help for command |

#### build

The `build` command accepts the following options:

| Option | Description |
|--------|-------------|
| `--output <path>` | Output directory for packages, relative to the project root. If omitted, packages remain under `dist/make`. |
| `--publish-only` | Skip the build phase and only publish packages (does nothing if no publish is configured) (default: false) |
| `--skip-install` | Skip installing components during the build process (default: false) |
| `-h`, `--help` | Display help for command |

#### components

The `components` command has the following sub-commands:

| Command | Description |
|---------|-------------|
| `uninstall <name>` | Remove a component from the project |
| `install [name]` | Install all components listed in `iocd.cli.config.json` or a specific component by name |
| `list` | List all installed components |
| `browse` | Browse all available components from currently selected store |
| `pin` | Pin all "latest" component versions to their current specific versions for reproducible builds |
| `download [options] [output-dir]` | Download components from the current store to a local directory in local store format. By default, the latest version of each component is downloaded. Use `-c` or `--components` with a semicolon-separated list to download specific components and versions, and `-f` or `--force` to overwrite existing files without prompting. |
| `help [command]` | Display help for command |

#### apps

The `apps` command has the following sub-commands:

| Command | Description |
|---------|-------------|
| `install [options]` | Install dependencies for all template apps |
| `build [options]` | Build all template apps and copy output to modifications folder. Requires prior installation of dependencies. |
| `dev [options]` | Start all template apps in development mode |
| `list` | List all template apps that are added to the seed project, and all available templates |
| `add <appName>` | Add a new template app from the registry |
| `help [command]` | Display help for command |

#### modifications

The `modifications` command has the following sub-commands:

| Command | Description |
|---------|-------------|
| `apply [component]` | Apply all modifications for the selected mode. The optional component argument is currently reserved and component-specific apply isn't implemented. |
| `help [command]` | Display help for command |

The `modifications apply` command accepts the following options:

| Option | Description |
|--------|-------------|
| `--mode <mode>` | Mode to apply modifications for: `dev` or `build`. Defaults to `dev`. |
| `-h`, `--help` | Display help for command |

## Troubleshooting

This guide addresses common issues when running the io.Connect Desktop CLI

### 1. Node.js SSL/TLS Certificate Errors

**Symptoms:**
- `SELF_SIGNED_CERT_IN_CHAIN` errors
- `unable to verify the first certificate`
- CLI cannot connect to GitHub or npm registry

**Root Cause:**
Corporate proxies often intercept HTTPS traffic and re-sign requests with an internal certificate. Node.js doesn't trust these certificates by default, causing HTTPS requests to fail.

**Solutions:**

#### Option A: Upgrade to Node.js 24+ (Recommended)

Starting from Node.js 24, Node uses the operating system's trust store. If your corporate certificate is already trusted by your OS (which is usually the case), no manual configuration is needed.

```cmd
# Check your Node.js version
node --version

# If below v24, upgrade Node.js
```

For Node.js 24+, you can also explicitly enable system certificates:

**Windows (PowerShell - current session):**
```powershell
$env:NODE_OPTIONS = "--use-system-ca"
npm run setup
```

**Windows (PowerShell - permanent for current user):**
```powershell
[Environment]::SetEnvironmentVariable("NODE_OPTIONS", "--use-system-ca", "User")
# Restart your terminal for changes to take effect
```

**macOS/Linux (current session):**
```cmd
NODE_OPTIONS="--use-system-ca" npm run setup
```

**macOS/Linux (permanent):**
```cmd
# Add to your shell profile (~/.zshrc or ~/.bashrc)
echo 'export NODE_OPTIONS="--use-system-ca $NODE_OPTIONS"' >> ~/.zshrc
source ~/.zshrc
```

#### Option B: Disable TLS Verification (Development Only)

> ⚠️ **Warning:** This disables security checks. Use only in development environments.

**Windows (PowerShell - current session):**
```powershell
$env:NODE_TLS_REJECT_UNAUTHORIZED = "0"
npm run setup
```

**Windows (PowerShell - permanent for current user):**
```powershell
[Environment]::SetEnvironmentVariable("NODE_TLS_REJECT_UNAUTHORIZED", "0", "User")
# Restart your terminal for changes to take effect
```

**macOS/Linux (current session):**
```cmd
NODE_TLS_REJECT_UNAUTHORIZED=0 npm run setup
```

**macOS/Linux (permanent - not recommended):**
```cmd
echo 'export NODE_TLS_REJECT_UNAUTHORIZED=0' >> ~/.zshrc
source ~/.zshrc
```

### 2. Node.js Proxy Configuration Issues

**Symptoms:**
- `npm run setup` fails with network errors
- GitHub API requests fail even though the browser can access GitHub
- `ECONNREFUSED` or timeout errors
- CLI cannot download components from GitHub
- "Network error: fetch failed" with retries

**Root Cause:**
Node.js does not automatically use system proxy settings (WinHTTP on Windows, system preferences on macOS). Even if your browser works behind a proxy, Node.js applications need explicit proxy configuration.

**Solution:**

The CLI (version 0.0.54+) **automatically detects and uses your system proxy settings**. If you're experiencing proxy-related issues, ensure you're running the latest version of the CLI:

```cmd
npm i @interopio/iocd-cli@latest
```

After updating, run the setup command again. The CLI will automatically route requests through your system's configured proxy.

**Verification:**

If you want to verify your proxy settings are correctly configured at the system level:

**Windows (PowerShell):**
```powershell
# Check system proxy (WinHTTP)
netsh winhttp show proxy

# Test if GitHub API is reachable
curl https://api.github.com/repos/interopio/iocd-components/releases?per_page=1
```

**macOS/Linux:**
```cmd
# Check network configuration
scutil --proxy

# Test connectivity
curl -I https://api.github.com
```

If the browser/curl works but the CLI still fails after updating, there may be a TLS certificate issue (see next section).

### 3. npm Registry Errors

**Symptoms:**
- `E400 Bad Request` when installing packages
- `npm error 400 Bad Request - GET https://registry.npmjs.org/@interopio%2f...`
- Timeouts when downloading npm packages

**Root Cause:**
npm uses its own SSL policy and doesn't automatically inherit Node.js TLS settings. Corporate proxies may also block or interfere with npm registry requests.

**Solutions:**

#### Disable npm Strict SSL

```cmd
npm config set strict-ssl false
```

#### Configure Corporate npm Registry

If your organization has an internal npm registry or proxy:

```cmd
# Set your corporate registry
npm config set registry https://your-corporate-registry.com/

# Or use a scoped registry for @interopio packages
npm config set @interopio:registry https://registry.npmjs.org/
```

#### View Current npm Configuration

```cmd
npm config get registry
npm config list
```

---

### 4. npm Proxy Configuration

**Symptoms:**
- `npm install` fails with `ECONNREFUSED` or timeout errors
- Package downloads hang or fail while CLI operations (like downloading components) work fine
- `npm ERR! network` errors during dependency installation

**Root Cause:**
The CLI uses its own HTTP client with automatic proxy support from environment variables. However, npm has its own network stack and may not inherit these settings. npm requires explicit proxy configuration through its own config system.

**Solution:**

The CLI automatically uses proxy settings from `HTTP_PROXY`/`HTTPS_PROXY` environment variables for its own network operations (downloading components from GitHub). However, npm package installations are handled by npm itself, which may need separate proxy configuration:

```cmd
npm config set proxy http://your-proxy-server:port
npm config set https-proxy http://your-proxy-server:port
```

> **Note:** In most enterprise environments, IT departments already configure `HTTP_PROXY`/`HTTPS_PROXY` environment variables system-wide. If these are set, the CLI will use them automatically. Check with your IT team if you're unsure whether these are configured.

### 5. Build fails with "License not found":

- Ensure that the `IOCD_LICENSE_KEY` environment variable is set or create an `iocd.license.key` file in the `/config` folder with your license key.

### 6. Code signing fails on Windows:

- Verify that `signtool.exe` is available on your system `PATH`. Run `signtool` in your terminal to check. If the command is not found, install the [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) or run your build from a Developer Command Prompt for Visual Studio. See the [Code Signing > Windows](#building_for_production-code_signing-windows) section for more details.
- Verify that the certificate is valid and not expired.
- Check that `"pfxPassword"` is set correctly.
- Verify that exactly one of `"pfxPath"` or `"certificateSha1"` is configured (not both).

### 7. Notarization for macOS fails:

- Use an app-specific password, not a regular Apple ID password.
- Generate an app-specific password at `appleid.apple.com`.
- Verify that your Apple Developer Program membership is active.
- Ensure that `"appleTeamId"` matches your Developer Team ID.

### 8. The platform won't start in development mode:

- Run `npm run setup` to verify component installation.
- Verify that that the `components/iocd` directory exists.
- Review the logs in the system `/temp` directory.

### 9. Components not downloading:

- Verify network connectivity.
- Verify the component store configuration in the `iocd.cli.config.json` file located in the `/config` folder.
- For GitHub stores: ensure that the repository access and credentials are correct.

## CLI Changelog

### 0.0

> #### 0.0.84
>
> *Release date: 07.05.2026*
>
> **New Features**
>
> - Extend the `components download` command to support downloading only specific components with specific versions, rather than downloading all components. This allows more targeted downloads when only certain components are needed.
>
> **Improvements & Bug Fixes**
>
> - Move unsigned executable caching from sign-time to install-time, improving build performance by avoiding redundant caching operations during the code signing phase.
> - Change tests to use the `download` command instead of their own implementation, improving test consistency and reducing code duplication.

> #### 0.0.83
>
> *Release date: 21.04.2026*
>
> **New Features**
>
> - Support forwarding arguments to the test script, allowing users to pass additional options (e.g., `--spec`, `--suite`) directly to the underlying test runner when executing `npm run test`.

> #### 0.0.82
>
> *Release date: 08.04.2026*
>
> **Improvements & Bug Fixes**
>
> - Add schema validation with static warning and runtime warning, helping users catch configuration errors early by validating the `iocd.cli.config.json` file against the JSON schema.
> - Update dependencies to resolve npm audit vulnerabilities.

> #### 0.0.81
>
> *Release date: 20.03.2026*
>
> **Improvements & Bug Fixes**
>
> - Fix stable build-tagged releases being filtered out incorrectly when resolving component versions, ensuring that stable releases with build metadata are properly recognized.

> #### 0.0.80
>
> *Release date: 19.03.2026*
>
> **New Features**
>
> - Add support for `nightly` prerelease tag in component versions (e.g., `v10.0.0-nightly.20250927121929`), in addition to the existing `build` and `dev` tags.
> - Add `prereleaseChannels` config option to restrict which prerelease channels are allowed when resolving `"latest"` (e.g., `["build"]` to only use build tags). See [Pre-Release Channels](#development-using_prereleases-prerelease_channels) for details.

> #### 0.0.79
>
> *Release date: 17.03.2026*
>
> **Bug Fixes**
>
> - Resolve npm audit vulnerabilities.

> #### 0.0.78
>
> *Release date: 16.03.2026*
>
> **New Features**
>
> - Add support for `dev` prerelease tag in component versions (e.g., `v10.0.0-dev.20250927121929`), in addition to the existing `build` tag.

> #### 0.0.77
>
> *Release date: 16.03.2026*
>
> **Bug Fixes**
>
> - Match prerelease versions via `fullVersion` in the version selector, allowing exact prerelease version requests (e.g., `iocd@10.1.0-dev.260314T1935`).
> - CI fixes for npm trusted publisher and prerelease publishing.

> #### 0.0.76
>
> *Release date: 10.03.2026*
>
> **New Features**
>
> - The `components download` command now downloads only the latest version of each component by default.

> #### 0.0.75
>
> *Release date: 10.03.2026*
>
> **New Features**
>
> - Do not create an empty `components` folder in the packaged `iocd` output when no components other than `iocd` are included in the project.

> #### 0.0.74
>
> *Release date: 02.03.2026*
>
> **New Features**
>
> - Add `excludeFolders` option for Windows code signing — allows specifying glob patterns to exclude folders from code signing. Supports `**`, `*`, `?` wildcards and `!` negation patterns (last matching pattern wins). By default, no folders are excluded.

> #### 0.0.73
>
> *Release date: 27.02.2026*
>
> **New Features**
>
> - Skip code signing for binaries that are already signed, reducing build time and avoiding re-signing issues.
>
> **Improvements & Bug Fixes**
>
> - Apply npm audit fixes to test template package-lock files to resolve security vulnerabilities.

> #### 0.0.72
>
> *Release date: 30.01.2026*
>
> **New Features**
>
> - Register adapter components (bbg-mdf, excel, fidessa, outlook, word) in the CLI component registry with platform support metadata.

> #### 0.0.71
>
> *Release date: 29.01.2026*
>
> **New Features**
>
> - Add array merge directives (`$replace`, `$prepend`, `$append`, `$remove`) for `.json.merge` files, giving fine-grained control over how arrays are combined during modifications.

> #### 0.0.70
>
> *Release date: 27.01.2026*
>
> **Improvements & Bug Fixes**
>
> - Trigger a version.

> #### 0.0.69
>
> *Release date: 22.01.2026*
>
> **New Features**
>
> - Enable cross-compilation of Windows installers on Linux by adding rcedit support and removing platform-specific OS calls.

> #### 0.0.68
>
> *Release date: 21.01.2026*
>
> **Improvements & Bug Fixes**
>
> - Change platform default to win32 for components (was darwin).

> #### 0.0.67
>
> *Release date: 21.01.2026*
>
> **Improvements & Bug Fixes**
>
> - Change platform default to win32 for components (was darwin).

> #### 0.0.66
>
> *Release date: 14.01.2026*
>
> **Improvements & Bug Fixes**
>
> - Fix Windows executable icon patching to source the icon from the modifications folder instead of the default location.

> #### 0.0.65
>
> *Release date: 13.01.2026*
>
> **Improvements & Bug Fixes**
>
> - Fix modification tracker so that deleting a modification source file correctly removes the corresponding output file from the component.

> #### 0.0.64
>
> *Release date: 10.01.2026*
>
> **Improvements & Bug Fixes**
>
> - Replace OS-specific shell-based ZIP extraction (PowerShell on Windows, unzip on macOS) with the cross-platform `extract-zip` npm package.

> #### 0.0.63
>
> *Release date: 09.01.2026*
>
> **New Features**
>
> - Read `entitlements.mac.plist` and `.nuspectemplate` from the CLI. These files were previously in the templates dir, now are bundled with the CLI and read from there. This allows easier updates and less confusion when users create new projects.

> #### 0.0.62
>
> *Release date: 05.01.2026*
>
> **New Features**
>
> - Support for specifying a custom tests directory in the CLI configuration.

> #### 0.0.60
>
> *Release date: 31.12.2025*
>
> **New Features**
>
> - Automatically reset all modifications before running `dev` or `build` commands to ensure a clean state.
> - Display notification when a newer version of the CLI is available.
>
> **Improvements & Bug Fixes**
>
> - Relaxed config schema validation to allow partial configuration files.
> - Updated `.gitignore` handling to only ignore the top-level `components` folder, not nested ones.

> #### 0.0.59
>
> *Release date: 12.12.2025*
>
> **New Features**
>
> - Support passing additional arguments to the Electron process in `dev` mode.
> - New `--output` argument for the `build` command to specify a custom output directory.
> - Export CLI commands for use as a library in other projects.
>
> **Improvements & Bug Fixes**
>
> - Automatically install dependencies when adding new apps to a project.
> - Updated Launchpad Vite configuration for improved build performance.

> #### 0.0.57
>
> *Release date: 09.12.2025*
>
> **New Features**
>
> - Auto include tests when setting up a new project with `create` command.

> #### 0.0.56
>
> *Release date: 06.12.2025*
>
> **Improvements & Bug Fixes**
>
> - Improved error logging message.

> #### 0.0.55
>
> *Release date: 06.12.2025*
>
> **Improvements & Bug Fixes**
>
> - Fix for versions that contain a letter in the build timestamp.

> #### 0.0.54
>
> *Release date: 04.12.2025*
>
> **New Features**
>
> - Add HTTP/HTTPS proxy support for all CLI network requests, enabling use behind corporate proxies and firewalls.

> #### 0.0.53
>
> *Release date: 01.12.2025*
>
> **New Features**
>
> - Log Node.js and CLI versions at the beginning of each log file for easier debugging.

> #### 0.0.52
>
> *Release date: 30.11.2025*
>
> **New Features**
>
> - Register the Bloomberg (BBG) component in the CLI component registry for download and inclusion in builds.

> #### 0.0.51
>
> *Release date: 28.11.2025*
>
> **New Features**
>
> - Add an overwrite mode for component modifications, allowing files to completely replace targets instead of being merged.

> #### 0.0.49
>
> *Release date: 26.11.2025*
>
> **Improvements & Bug Fixes**
>
> - Change configs to rely on `package.json` where possible.
> - Add UI version.
> - Updates in wdio.config for tests.
> - Fix for eslint setup.

> #### 0.0.43
>
> *Release date: 20.11.2025*
>
> **New Features**
>
> - Support variable substitution of product slug and product name in modification files, enabling dynamic per-product configuration.

> #### 0.0.42
>
> *Release date: 20.11.2025*
>
> **New Features**
>
> - Add OS-specific modification folders (`modifications-win32`, `modifications-darwin`) so different platforms can have distinct configuration overrides.
> - Embed product version in the Windows setup executable filename.
>
> **Improvements & Bug Fixes**
>
> - Various fixes; fix build.

> #### 0.0.35
>
> *Release date: 16.11.2025*
>
> **Improvements & Bug Fixes**
>
> - Update Node.js requirement to 22.0.0.
> - Minor fix.

> #### 0.0.33
>
> *Release date: 16.11.2025*
>
> **Improvements & Bug Fixes**
>
> - Updated README.
> - Simplify config.
> - Changed pfx to signtool.

> #### 0.0.29
>
> *Release date: 13.11.2025*
>
> **New Features**
>
> - Enable shell mode for child process execution to support system PATH resolution.

> #### 0.0.28
>
> *Release date: 13.11.2025*
>
> **Improvements & Bug Fixes**
>
> - Using signtool for signing; remove explicit digicert support.

> #### 0.0.27
>
> *Release date: 10.11.2025*
>
> **New Features**
>
> - Add `license.copyLicenseToOutput` config option to automatically include the license file in the build output.

> #### 0.0.26
>
> *Release date: 06.11.2025*
>
> **New Features**
>
> - Add end-to-end test infrastructure for CLI commands.
> - Add license file handling and validation.
> - Add JSON schema for CLI configuration file (`iocd.cli.config.json`) validation.

> #### 0.0.24
>
> *Release date: 30.10.2025*
>
> **New Features**
>
> - Add end-to-end test support with small improvements.
> - Basic test scaffolding.
> - Download all components.

> #### 0.0.23
>
> *Release date: 15.10.2025*
>
> **Improvements & Bug Fixes**
>
> - Logger fix.
> - Setup Apple code-signing.

> #### 0.0.22
>
> *Release date: 13.10.2025*
>
> **Improvements & Bug Fixes**
>
> - Fix icons and ability to sign using certificate on Mac.

> #### 0.0.20
>
> *Release date: 12.10.2025*
>
> **New Features**
>
> - Automatically generate and manage `.gitignore` files for new projects.

> #### 0.0.18
>
> *Release date: 12.10.2025*
>
> **New Features**
>
> - Allow the `create` command to initialize a project in the current directory if it is empty.

> #### 0.0.17
>
> *Release date: 12.10.2025*
>
> **New Features**
>
> - Add build configuration templates for Windows and macOS installers.

> #### 0.0.15
>
> *Release date: 11.10.2025*
>
> **New Features**
>
> - Add `--skip-components` CLI flag to the build command, allowing builds without downloading or updating components.

> #### 0.0.14
>
> *Release date: 11.10.2025*
>
> **Improvements & Bug Fixes**
>
> - Add GitHub token to the GitHub request.

> #### 0.0.9
>
> *Release date: 09.10.2025*
>
> **New Features**
>
> - Add support for top-level modifications that apply across all components, in addition to per-component modifications.

> #### 0.0.6
>
> *Release date: 07.10.2025*
>
> **New Features**
>
> - Integrate Sentry error tracking for automatic crash reporting and diagnostics.
> - Add internal presentation materials.

> #### 0.0.4
>
> *Release date: 04.10.2025*
>
> **New Features**
>
> - Pin component versions after initial project setup to ensure reproducible builds.
> - Add per-component modification folders (`modifications-xxx`) for targeted config overrides.
> - Auto-update support with `system.json.merge` template generation.

> #### 0.0.3
>
> *Release date: 28.09.2025*
>
> **New Features**
>
> - Added demos component support.
> - Add flag for prerelease.
> - Added doc about components storage.

> #### 0.0.2
>
> *Release date: 26.09.2025*
>
> **New Features**
>
> - License checks.
> - Licensing.

> #### 0.0.1
>
> *Release date: 22.09.2025*
>
> **New Features**
>
> - Initial release.
> - CLI project setup with create command.
> - Components selection in the create phase.
> - Ability to add apps.
> - S3 support for components storage.
> - Local storage support with same structure as S3.
> - Launchpad component.
> - Components remove functionality.
> - Installer/build command for Windows and macOS.
> - Code signing support for macOS and Windows.
> - Splash screen template.
> - Auto-update configuration support.
> - GitHub workflow for CI/CD.
