# Modules Guide

## Available Modules

| Module | Port | Dependencies | Description |
|--------|------|-------------|-------------|
| `notifications` | 8087 | — | In-app notification store shared by other modules |
| `calendar` | 8084 | notifications | Google Calendar sync, reminders, and event queries |
| `status` | 8086 | notifications | Host, service, network, and website health monitoring |
| `telegram-bridge` | — | — | Routes Telegram messages to/from the conductor agent |
| `coding-pair` | TBD | — | Specialist coding agent pair (coming soon) |
| `imessage-bridge` | TBD | — | iMessage routing (coming soon) |
| `monitoring-agent` | TBD | — | AI-powered anomaly detection (coming soon) |

### notifications

A lightweight Express server backed by a SQLite database. Other modules post notifications here; the dashboard reads them. No configuration required.

Endpoints: `POST /notify`, `GET /notifications`, `DELETE /notifications/:id`

### calendar

Syncs with Google Calendar via the Google Calendar API. Provides a REST API for querying events, creating reminders, and triggering notifications when events are approaching. Requires a Google OAuth credentials file and a token file.

Endpoints: `GET /events`, `POST /events`, `GET /reminders`, `POST /sync`

### status

A multi-collector monitoring service. Collectors run on a configurable schedule and check:
- **hosts** — SSH or local process checks
- **network** — ICMP ping targets
- **services** — TCP port reachability
- **sites** — HTTP status code checks

Alerts are deduplicated and evaluated against configurable rules before being sent to the notifications module.

Endpoints: `GET /health`, `GET /hosts`, `GET /services`, `GET /sites`, `GET /alerts`

### telegram-bridge

A Telegram bot that forwards incoming messages to the conductor agent via the channel server, and relays the conductor's replies back to the Telegram chat. Requires a bot token from @BotFather and a list of authorized user IDs.

## Adding a Module After Installation

Use the `add-module.sh` script:

```bash
./scripts/add-module.sh <module-name>
```

Example:

```bash
./scripts/add-module.sh telegram-bridge
```

The script:
1. Runs the module's `setup.js` to collect configuration
2. Copies the module to `<installPath>/services/<module-name>/`
3. Runs `npm install && npm run build`
4. Registers the module in `config/manifest.json`
5. Regenerates the PM2 ecosystem config
6. Starts the new service

## Removing a Module

```bash
./scripts/remove-module.sh <module-name>
```

You will be asked whether to delete the module's files and database. Removing a module that others depend on will print a warning — remove dependents first.

## Creating a Custom Module

Custom modules follow the same contract as built-in modules. Here is the full pattern.

### Directory Structure

```
modules/my-module/
├── manifest.json
├── package.json
├── setup.js
├── tsconfig.json       # optional, if using TypeScript
└── src/
    └── index.ts        # (or index.js for plain JS)
```

### manifest.json Schema

```json
{
  "name": "my-module",
  "version": "1.0.0",
  "description": "One-line description shown in the installer",
  "dependencies": [],
  "requiredPorts": [8090],
  "serviceType": "pm2",
  "setupScript": "setup.js"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Unique identifier, lowercase, hyphenated |
| `version` | string | Semver |
| `description` | string | Shown in module selection list |
| `dependencies` | string[] | Names of modules that must be installed first |
| `requiredPorts` | number[] | Ports this module binds; checked for conflicts |
| `serviceType` | `"pm2"` | Only `pm2` is currently supported |
| `setupScript` | string | Relative path to the setup script |

### setup.js Pattern

The setup script is called by the installer with no arguments. It must:
1. Prompt the user for any required configuration
2. Return a plain object with the config values

```js
import { askText, askYesNo } from '../../installer/prompts.js';

export async function setup() {
  const apiKey = await askText('Enter your API key:');
  const enableFeatureX = await askYesNo('Enable feature X?', true);

  return {
    MY_MODULE_API_KEY: apiKey,
    MY_MODULE_FEATURE_X: enableFeatureX ? '1' : '0',
  };
}
```

The returned object is written to `<installPath>/services/my-module/data/config.json` and also available as environment variables when the PM2 service starts.

### index.ts Pattern

```typescript
import express from 'express';
import Database from 'better-sqlite3';
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const config = JSON.parse(readFileSync(join(__dirname, '../data/config.json'), 'utf8'));

const app = express();
app.use(express.json());

const db = new Database(join(__dirname, '../data/my-module.db'));

app.get('/health', (_req, res) => {
  res.json({ status: 'ok', service: 'my-module' });
});

const port = parseInt(process.env.PORT || '8090', 10);
app.listen(port, () => {
  console.log(`my-module listening on port ${port}`);
});
```

### Registering Your Module

Place your module directory inside `modules/`. It will appear automatically in the installer's module selection list. To install it into a running system:

```bash
./scripts/add-module.sh my-module
```
