# @dinhhv/mescli

Command-line tool for KMesSolution: deploy builds to HostRunner apps and manage MesAuth roles, users, client apps, and permissions directly from the terminal.

## Install

```powershell
npm install -g @dinhhv/mescli
```

Requires Node.js 18+.

## Quick start

```powershell
# Drop into the interactive shell
mescli

# Or run one-shot commands
mescli login
mescli hr publish
mescli auth role get
```

## Interactive shell (REPL)

Running `mescli` with no arguments enters a REPL. On entry, mescli checks `https://registry.npmjs.org/@dinhhv/mescli/latest` and offers to self-update if a newer version is available.

```
mescli v0.10.0 — KMesSolution publisher
Logged in as dinhhv (https://auth.domain.com)
Type `help` to list commands, `exit` to leave.

mescli>
```

Each sub-group (`hr`, `auth`) also has its own sub-shell:

```
mescli> hr        →  mescli hr>   (HostRunner commands)
mescli> auth      →  mescli auth> (Auth management commands)
```

---

## Top-level commands

| Command | What it does |
|---|---|
| `mescli login` | Authenticate to MesAuth. Caches tokens at `~/.mescli/credentials.json`. |
| `mescli logout` | Clear cached credentials. |
| `mescli whoami` | Print cached user + token expiry. |
| `mescli update` | Self-update via `npm install -g @dinhhv/mescli@latest`. |
| `mescli hr [cmd]` | HostRunner commands — see [HostRunner](#hostrunner-hr) section. |
| `mescli auth [cmd]` | Auth management — see [Auth management](#auth-management-auth) section. |

---

## HostRunner (`hr`)

Manage deployments to HostRunner apps.

```powershell
mescli hr init          # confirm/enter server, pick runner + app, writes .mescli/profile.json
mescli hr publish       # build → zip → upload → publish (main app + all peers)
mescli hr peers add     # add another deploy target (runner+app on any host)
mescli hr peers list    # show the main app + configured peers
mescli hr status        # app online/offline + latest bundle info
mescli hr version list  # backup versions, newest first (1-based index; --json for scripting)
mescli hr version rollback <index|id>   # republish an old backup (asks to confirm; -y skips)
mescli hr config        # show/update profile fields (config <key> <val>; config <key> --clear to remove)
mescli hr pull          # download latest source bundle
mescli hr push          # upload source folder
mescli hr offline       # take app offline
mescli hr online        # bring app online
```

Run `mescli hr` alone to enter the `mescli hr>` interactive sub-shell. Type `hr help` for the full command list.

### Profile

`.mescli/profile.json` in your project root. Created by `hr init`.

```json
{
  "schemaVersion": 1,
  "server": "https://domain.com/api1",
  "runnerId": "...",
  "appId": "...",
  "buildCommand": "dotnet publish -c Release -o ./out/publish",
  "outputDir": "./out/publish",
  "versionName": "{git.shortSha}-{date}"
}
```

`publish` flags: `--skip-build`, `--skip-peers`, `--peers-only`, `--peer <index>`, `--profile <path>`, `--version-name <name>`, `--notes "..."`, `--dry-run`.

### Multiple deploy targets (peers)

Publish **one** source project to **many** HostRunner hosts without mirror folders. Add an optional `peers[]` array to the profile — each peer is another runner/app that the same built artifact is deployed to:

```json
{
  "schemaVersion": 1,
  "server": "https://mes.kefico.vn/api1",
  "runnerId": "...", "appId": "...", "appName": "Auth",
  "buildCommand": "dotnet publish -c Release",
  "outputDir": "./bin/publish",
  "peers": [
    { "name": "FX",  "appId": "...", "appName": "auth", "runnerName": "FX",
      "server": "https://fx.kefico.vn/api1", "authAppServer": "https://fx.kefico.vn/auth" },
    { "name": "MBO", "appId": "...", "appName": "auth", "runnerName": "MBO" },
    { "name": "autha", "appId": "...", "buildCommand": "dotnet publish -c Release --self-contained true", "outputDir": "./bin/publish-sc" }
  ]
}
```

- Only `appId` is required per peer; every other field inherits from the top-level profile when omitted.
- `mescli hr publish` deploys the **main app + all peers** by default. Use `--skip-peers` (main only), `--peers-only` (all peers, no main), or `--peer <index>` to publish a **single peer** by its 1-based index from `peers list`.
- **Build once, reuse**: targets sharing the same `buildCommand` + `outputDir` build a single artifact uploaded to each. A peer that overrides either (e.g. `--self-contained`) builds separately.
- **Fail-fast**: publishing stops at the first failed target.
- **Per-target auth**: each host has its own HostRunner + auth API, so `publish` and `status` authenticate against each target's `authAppServer` (peer auth, the same federation `mescli api`/`auth` use). A peer on a **different host** than the main app should set its own `authAppServer` (and optionally `authAppId`); same-host peers inherit. You stay logged in once (`mescli login`) — the CLI federates per host transparently.
- **`hrAuthServer` (main app only)**: when the runner that *publishes* the main app lives under a different auth host than the app's own auth, set `hrAuthServer` to the runner's auth host. `mescli hr` (publish/status) then federates the **main** target against `hrAuthServer` while `mescli api`/`auth app` keep using `authAppServer`. Example: MBO's API is authed by `https://mbo.kefico.vn/auth` (`authAppServer`) but its runner is published under `https://mes.kefico.vn/auth` (`hrAuthServer`). Falls back to `authAppServer` when unset; set with `mescli hr config hrAuthServer <url>`.
- `status` shows the main app + all peers, each peer-authed.
- Manage peers with `mescli hr peers add` (enter a server → pick runner → pick app → set auth host), `mescli hr peers remove [<selector>]`, and `mescli hr peers list`.

### Versions & rollback

Every successful publish snapshots the previous deployment as a backup version on the host. `mescli hr version list` shows them newest-first with a 1-based index; `mescli hr version rollback <index|id>` republishes the chosen backup over the live destination (same execution pipeline as publish — you'll see the steps stream). Use `--peer <index>` to target a peer instead of the main app, `--dry-run` to resolve the selector and print the plan without triggering anything, `-y` to skip the confirmation, and `--json` on `list` for machine-readable output. Retention is capped by the app's `maxBackups` (`mescli hr app get maxBackups`) and pruned by `mescli hr app clean`.

### Source bundle: project references

`hr push` auto-detects `<ProjectReference>` entries in `*.csproj` files and bundles any project that lives outside the source folder. The bundle root expands to the lowest common ancestor so `mescli hr pull` restores the exact directory layout on the target. Override detection with `includeProjects` in the profile.

---

## Auth management (`auth`)

Manage MesAuth roles, users, client apps, and permissions remotely. All commands use the auth server and credentials from `~/.mescli/credentials.json`.

Run `mescli auth` alone to enter the `mescli auth>` interactive sub-shell.

### Role commands

```
mescli auth role get              List all roles
mescli auth role select           Pick a role → [role: Admin]> sub-shell
```

**Role sub-shell** (`[role: <Name>]>`):

| Command | What it does |
|---|---|
| `perm get` | List permissions granted to this role (paginated) |
| `perm grant` | Add a permission (autocomplete search) |
| `perm remove` | Remove a permission (select) |
| `user get` | List users in this role (paginated) |
| `user add [<username>]` | Search by username → confirm → add to role |
| `user remove [<username>]` | Filter role members → confirm → remove |

### User commands

```
mescli auth user get [<prefix>]       List users, optionally filtered
mescli auth user select [<prefix>]    Search → pick → [user: jdoe]> sub-shell
```

**User sub-shell** (`[user: <username>]>`):

| Command | What it does |
|---|---|
| `role get` | List roles assigned to this user |
| `role add` | Pick from unassigned roles → add |
| `role remove` | Pick from current roles → remove |

`user get` uses server-side pagination — the backend returns pages of 20 users; `Enter / p / j N / q` navigates pages without re-fetching everything.

### App commands

```
mescli auth app get               List all client apps
mescli auth app select            Pick an app → [app: SHOP]> sub-shell
```

**App sub-shell** (`[app: <appId>]>`):

| Command | What it does |
|---|---|
| `token get` | List active tokens (name, wellknow-trust, registered date) |
| `token create` | Prompt for name → create token |
| `token revoke` | Pick a token → revoke |
| `token perm get` | Pick token → paginated permission list |
| `token perm add` | Pick token → autocomplete from available perms → grant |
| `token perm remove` | Pick token → select from granted → revoke |
| `log [info\|warning\|error]` | View system logs for this app (server-paged, 50/page) |

`log` without a level shows all levels. Levels are color-coded: `INFO` (green), `WARN` (yellow), `ERR` (red).

### Paginated list navigation

Any list with more entries than the page size shows a navigation footer:

```
  [page 1/4  72 items]  Enter=next  p=prev  j <N>=jump  q=quit
```

Client-side lists (role perms, token perms) fetch all at once and paginate in memory. Server-side lists (users, system logs) fetch each page on demand.

---

## Auth model

- `mescli login` posts `grant_type=password` to `<authServer>/auth/token` and caches `accessToken` + `refreshToken`.
- Every API call attaches `Authorization: Bearer <accessToken>` (user mode) or `X-APP-ID` / `X-APP-KEY` headers (app mode).
- On `401`, the CLI exchanges the refresh token and retries once. If rejected, it exits and asks you to run `login` again.
- App-mode login: `mescli login --appid <id> --appkey <key>`.

## Files mescli reads/writes

| Path | Purpose |
|---|---|
| `~/.mescli/credentials.json` | Access + refresh tokens (mode 0600 on POSIX). |
| `~/.mescli/config.json` | `defaultServer`, `defaultAuthServer` — used by `hr init`. |
| `<project>/.mescli/profile.json` | Project-specific runner/app target + build settings. |

## Windows / PowerShell

`npm install -g` creates `mescli`, `mescli.cmd`, and `mescli.ps1` shims — the same `mescli` command works from PowerShell, cmd.exe, or Git Bash.

`hr publish` runs `buildCommand` in a child process. Default shell is `cmd.exe` on Windows. Set `"shell": "powershell"` or `"shell": "pwsh"` in the profile for PowerShell-specific build commands.
