@mu-cabin/muc-cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +416 -0
- package/dist/muc.mjs +25018 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mu-cabin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# muc-cli
|
|
2
|
+
|
|
3
|
+
Terminal client for 东航 MUC IM (the `com.meicloud.im.dh` desktop app, also
|
|
4
|
+
called MUC.app). Drives the same REST surface the desktop talks to, plus the
|
|
5
|
+
binary chat protocol on raw TCP 8101 for sending messages, withdrawing, and
|
|
6
|
+
syncing sessions.
|
|
7
|
+
|
|
8
|
+
REST commands always coexist with the running desktop and phone — the server
|
|
9
|
+
allows concurrent `accessToken`s. Chat-channel commands share online slots
|
|
10
|
+
with real devices; see [Coexistence](#coexistence) below before sending.
|
|
11
|
+
|
|
12
|
+
## What it is
|
|
13
|
+
|
|
14
|
+
A single command (`muc`) packaging:
|
|
15
|
+
|
|
16
|
+
- **Send** — 1:1 and group text, files, images, and voice clips.
|
|
17
|
+
- **Read** — server-side chat history, profile lookups, group lists, sticker
|
|
18
|
+
packs, and directory search (people, departments, apps, service numbers).
|
|
19
|
+
- **Manage** — login / token refresh, withdraw a message, clear a session,
|
|
20
|
+
download a chat-attached file.
|
|
21
|
+
- **Self-describe** — every command has a `--json` mode emitting a stable
|
|
22
|
+
envelope; `muc manifest --json` returns the full command graph + per-command
|
|
23
|
+
output JSON Schema for agent / script consumers.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm i -g @mu-cabin/muc-cli
|
|
29
|
+
muc --version
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Requirements: **Node ≥ 18**. macOS, Linux, and Windows are all supported.
|
|
33
|
+
DES-CBC password encryption is `crypto-js` (pure JS) — no
|
|
34
|
+
`--openssl-legacy-provider` flag needed.
|
|
35
|
+
|
|
36
|
+
`~/.muc/credentials.json` and `~/.muc/config.json` are written with mode
|
|
37
|
+
`600` and the parent dir with mode `700`. POSIX honors that; on Windows the
|
|
38
|
+
modes are silently ignored — protect the files with NTFS ACLs if you're on a
|
|
39
|
+
shared host.
|
|
40
|
+
|
|
41
|
+
## First-time setup
|
|
42
|
+
|
|
43
|
+
Before you can `login`, run `muc init` to seed the tenant configuration. The
|
|
44
|
+
CLI is generic across MUC deployments — your MUC admin provides the
|
|
45
|
+
tenant-specific values.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
muc init # interactive: walks through APP_KEY, APP_ID, signing secrets, hosts
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
What `muc init` collects:
|
|
52
|
+
|
|
53
|
+
| Field | Source | Notes |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `appKey` | admin | 8 hex chars |
|
|
56
|
+
| `appId` | admin | 24 hex chars |
|
|
57
|
+
| `signSecretRest` | admin | secret for the regular-API signing scheme |
|
|
58
|
+
| `signSecretSecure` | admin | secret for the `/contacts/secure/...` signing scheme |
|
|
59
|
+
| `pwdDesKey` | admin (optional) | DES-CBC key for password encryption; defaults to `appKey` |
|
|
60
|
+
| `restHost` | default `https://muc.ceair.com/` | press enter to accept |
|
|
61
|
+
| `ssoHost` | default `https://store.ceair.com/` | reserved (advanced) |
|
|
62
|
+
| `imHost` / `imPort` | default `muc.ceair.com` / `8101` | TCP chat channel |
|
|
63
|
+
| `fileHost` / `filePort` | default `muc.ceair.com` / `6101` | TCP file channel |
|
|
64
|
+
|
|
65
|
+
Built-in defaults are provided **for hosts and ports only** — never for
|
|
66
|
+
`appKey`, `appId`, or signing secrets.
|
|
67
|
+
|
|
68
|
+
### Non-interactive (CI, scripts)
|
|
69
|
+
|
|
70
|
+
You can supply values via flags or env vars:
|
|
71
|
+
|
|
72
|
+
Get the four secrets from your MUC admin first; the snippets below use
|
|
73
|
+
placeholder names — substitute the real values your admin gives you.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Flag form
|
|
77
|
+
muc init --no-interactive \
|
|
78
|
+
--app-key "$APP_KEY" \
|
|
79
|
+
--app-id "$APP_ID" \
|
|
80
|
+
--sign-secret-rest "$SIGN_SECRET_REST" \
|
|
81
|
+
--sign-secret-secure "$SIGN_SECRET_SECURE" \
|
|
82
|
+
--json
|
|
83
|
+
|
|
84
|
+
# Env-var form
|
|
85
|
+
MUC_APP_KEY="$APP_KEY" \
|
|
86
|
+
MUC_APP_ID="$APP_ID" \
|
|
87
|
+
MUC_SIGN_SECRET_REST="$SIGN_SECRET_REST" \
|
|
88
|
+
MUC_SIGN_SECRET_SECURE="$SIGN_SECRET_SECURE" \
|
|
89
|
+
muc init --no-interactive
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Environment variables: `MUC_APP_KEY`, `MUC_APP_ID`, `MUC_SIGN_SECRET_REST`,
|
|
93
|
+
`MUC_SIGN_SECRET_SECURE`, `MUC_PWD_DES_KEY`, `MUC_REST_HOST`, `MUC_SSO_HOST`,
|
|
94
|
+
`MUC_IM_HOST`, `MUC_IM_PORT`, `MUC_FILE_HOST`, `MUC_FILE_PORT`.
|
|
95
|
+
|
|
96
|
+
> **Env-var leakage warning.** Values exported into the environment are
|
|
97
|
+
> visible to other processes on the same machine via `/proc/<pid>/environ`
|
|
98
|
+
> on Linux, and they land in shell history if you don't prefix the command
|
|
99
|
+
> with a space (and `HISTCONTROL=ignorespace` is set). For production setups
|
|
100
|
+
> prefer the **interactive** prompt or **flag-based** invocation — you can
|
|
101
|
+
> protect the flag values with secret-manager retrieval that never touches
|
|
102
|
+
> the env.
|
|
103
|
+
|
|
104
|
+
### Inspect, repair, reset
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
muc init --check # validate; exit 0 ok, 6 missing, 7 invalid
|
|
108
|
+
muc init --check --json # same, machine-readable
|
|
109
|
+
muc init --show # print config (secrets always redacted)
|
|
110
|
+
muc init --show --json # JSON envelope — secrets are also redacted here
|
|
111
|
+
# (read ~/.muc/config.json directly for raw values)
|
|
112
|
+
muc init --reset --yes # delete ~/.muc/config.json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Resolution order per field: CLI flag → env var → existing saved value → built-in
|
|
116
|
+
default (hosts/ports only) → interactive prompt (TTY) → fail.
|
|
117
|
+
|
|
118
|
+
## Login
|
|
119
|
+
|
|
120
|
+
`muc login` is **non-interactive on the credential side** — it never prompts
|
|
121
|
+
for a password. Pass it via flag, stdin, env var, or `--token-pwd` (reuse
|
|
122
|
+
the saved tokenPwd).
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Plaintext flag (avoid; lands in shell history)
|
|
126
|
+
muc login --account WANGLULU8 --password '…'
|
|
127
|
+
|
|
128
|
+
# Safer: read password from stdin, no flag value
|
|
129
|
+
printf '%s' "$MY_PWD" | muc login --account WANGLULU8 --password-stdin
|
|
130
|
+
|
|
131
|
+
# Reuse saved tokenPwd (no password needed; ~30 day TTL)
|
|
132
|
+
muc login --token-pwd
|
|
133
|
+
|
|
134
|
+
# Env-var form
|
|
135
|
+
MUC_ACCOUNT=WANGLULU8 MUC_PASSWORD='…' muc login
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- `account` is your MUC uid (uppercase ASCII, e.g. `WANGLULU8`); falls back
|
|
139
|
+
to `$MUC_ACCOUNT` or the previously-saved uid if `--account` is omitted.
|
|
140
|
+
- One of `--password` / `--password-stdin` / `--token-pwd` / `$MUC_PASSWORD`
|
|
141
|
+
is required — running `muc login` with no creds fails fast with
|
|
142
|
+
`AUTH_REQUIRED` (exit 3).
|
|
143
|
+
- The password is encrypted client-side (DES-CBC) before being sent — same
|
|
144
|
+
scheme the desktop client uses.
|
|
145
|
+
- A successful login saves to `~/.muc/credentials.json` (mode `600`):
|
|
146
|
+
`accessToken` (≈7 day TTL) and `tokenPwd` (≈30 day TTL, used for silent
|
|
147
|
+
re-login with `--token-pwd`).
|
|
148
|
+
- A stable per-CLI device id is generated at `~/.muc/device-id` on first
|
|
149
|
+
run. It is intentionally **different** from the desktop's machine-id so
|
|
150
|
+
REST sessions stay independent.
|
|
151
|
+
|
|
152
|
+
## Output contract
|
|
153
|
+
|
|
154
|
+
- **Default:** human-readable plain text on stdout, errors on stderr in the
|
|
155
|
+
form `error: <msg>` / `hint: <hint>` / `code: <code> (exit <n>)`.
|
|
156
|
+
- **`--json`** (anywhere on the command line): every command emits a single
|
|
157
|
+
stable JSON envelope on stdout — `{ok:true, schema_version:1, data}` or
|
|
158
|
+
`{ok:false, schema_version:1, code, msg, hint?}` — and stderr stays
|
|
159
|
+
silent. Agents and scripts should always pass `--json`.
|
|
160
|
+
- **Exit codes** are typed and identical in both modes: `0` success, `1`
|
|
161
|
+
internal bug, `2` usage, `3` auth required / expired, `4` not found, `5`
|
|
162
|
+
remote / network error, `6` config missing, `7` config invalid.
|
|
163
|
+
- `--help` / `--version` are human escape hatches and bypass the JSON
|
|
164
|
+
envelope by design. The canonical agent surface is `muc manifest --json`.
|
|
165
|
+
|
|
166
|
+
## Commands
|
|
167
|
+
|
|
168
|
+
### Setup
|
|
169
|
+
|
|
170
|
+
| Command | What it does |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `muc init` | Seed / inspect / reset tenant config (`~/.muc/config.json`). Run before `login`. See [First-time setup](#first-time-setup). |
|
|
173
|
+
|
|
174
|
+
### REST commands (no impact on the desktop app)
|
|
175
|
+
|
|
176
|
+
| Command | What it does |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `muc login` | Authenticate, persist `accessToken` + `tokenPwd`. |
|
|
179
|
+
| `muc whoami` | Show the saved profile + token freshness. Local only — does not read tenant config. |
|
|
180
|
+
| `muc search <keyword>` | Org / employee directory search (12 results max, server-side). |
|
|
181
|
+
| `muc search-dept <keyword>` | Department directory search (with employee count + path). |
|
|
182
|
+
| `muc search-app <keyword>` | Search the user's MUC widgets/apps drawer. |
|
|
183
|
+
| `muc search-sno <keyword>` | Search service-numbers (服务号); shows `★` for already-subscribed. |
|
|
184
|
+
| `muc whois <UID>` | Employee info + ext info (`secureApi`). Renders `cn / id / mail / departmentName / positionName / empStatusText`. `--no-ext` skips the slower `getExtInfo` call. |
|
|
185
|
+
| `muc groups` | Starred groups (flat list keyed by `team_id`) + department groups (keyed by `groupId / deptName`). |
|
|
186
|
+
| `muc emoticons` | Sticker packages installed on your account. |
|
|
187
|
+
| `muc history --peer UID \| --team TEAM_ID \| --to-self` | Server-side chat history via the roaming-messages endpoint. Stateless: each call fetches from the server. Default window: 30 days, 50 messages newest-first. |
|
|
188
|
+
| `muc download --file-key <key> [--out <path>]` | Download a chat-attached file from the V5 file server (TCP 6101). **Read-only** — opens a fresh per-request session, no chat login, doesn't kick anyone. Pass `--mid <mid>` instead of `--file-key` plus `--peer/--team/--to-self` to look the file up via roaming history. Verifies md5 on completion. |
|
|
189
|
+
| `muc manifest` | Print the full command graph + per-command output JSON Schema. Pair with `--json` for agents. |
|
|
190
|
+
|
|
191
|
+
`history` flags: scope with one of `--peer UID`, `--team TEAM_ID`,
|
|
192
|
+
`--sid <UID1|UID2 or teamId>`, or `--to-self`. `--limit N` (default 50),
|
|
193
|
+
`--days N` (window from now, default 30), `--before <ts|ISO>` to fetch
|
|
194
|
+
older than a moment, `--sub-type N` to filter to one type (e.g. `1` text,
|
|
195
|
+
`7` file, `8` image), `--desc` for newest-first (default: oldest at top).
|
|
196
|
+
The fetched `timestamp` is normalized to ms.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
muc history --peer YANWENYU --limit 100
|
|
200
|
+
muc history --to-self --days 7
|
|
201
|
+
muc history --team 9009876543210000 --before 2026-04-20 --json | jq .
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Chat-channel commands (TCP 8101) — see [Coexistence](#coexistence)
|
|
205
|
+
|
|
206
|
+
| Command | What it does |
|
|
207
|
+
|---|---|
|
|
208
|
+
| `muc tcp-ping [--wait N] [--debug]` | TCP connect + NEGOTIATE only. Smoke test, no login frame, **zero** kick risk. |
|
|
209
|
+
| `muc sync [--os OS]` | After chat-login, run `syncDone` / `syncSession` / `syncWithdraws`. Read-only. |
|
|
210
|
+
| `muc search-team <keyword> [--enrich]` | Search groups you belong to via the chat channel; `--enrich` follows up with `team/get_team` per id to print names + member counts. |
|
|
211
|
+
| `muc send (--to-self \| --peer UID \| --team TEAM_ID) <text> [--yes-not-self]` | Send a plain-text message. **Visible to the recipient.** |
|
|
212
|
+
| `muc send-file <file> [target] [--yes-not-self]` | Upload a file (TCP 6101) and broadcast as `MESSAGE_CHAT_FILE` (subType=7). Returns persistent `fileKey` + `mid`. **Visible.** Uses both 6101 (upload) and 8101 (broadcast) — costs the chosen `--os` slot for the broadcast. |
|
|
213
|
+
| `muc send-image <file> [target] [--width N --height N]` | Same as `send-file` but as `MESSAGE_CHAT_IMAGE` (subType=8). Width/height auto-probed from PNG/JPEG/GIF/BMP headers; pass `--width/--height` to override. |
|
|
214
|
+
| `muc send-voice <file> --duration <ms> [target]` | Same as `send-file` but as `MESSAGE_CHAT_AUDIO` (subType=9). `--duration <ms>` is required (the desktop renders the bubble's clip length from this metadata, not from the file). |
|
|
215
|
+
| `muc withdraw --mid <MID> [target]` | Recall a sent message. Original gets replaced server-side with "您撤回了一条消息". Target flag must match the original message's chat. |
|
|
216
|
+
| `muc clear-session [target]` | Delete a session from your client-side history (syncs to all your devices). **Doesn't delete server-side history** — `history` still returns the messages. |
|
|
217
|
+
|
|
218
|
+
`--os` defaults to `iOS`. Valid values: `Windows`, `OS X`, `Mac`, `iOS`,
|
|
219
|
+
`Android`. **`--os` does not provide coexistence — read the section below
|
|
220
|
+
before using any chat-channel command if you have a real device logged in.**
|
|
221
|
+
|
|
222
|
+
Sending to anyone other than yourself requires the explicit `--yes-not-self`
|
|
223
|
+
flag — a guard against accidentally messaging real humans during testing.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
muc tcp-ping --wait 8 # safe protocol smoke test
|
|
227
|
+
muc send "hello from the CLI" # to-self (default)
|
|
228
|
+
muc send --peer ZHANG_W2 "ping" --yes-not-self
|
|
229
|
+
muc send --team 9009876543210000 "team message" --yes-not-self
|
|
230
|
+
|
|
231
|
+
muc send-file ./report.pdf --to-self # self test
|
|
232
|
+
muc send-image ./screenshot.png --peer ZHANG_W2 --yes-not-self # auto width/height
|
|
233
|
+
muc send-voice ./clip.amr --duration 3500 --to-self # 3.5 s clip
|
|
234
|
+
|
|
235
|
+
muc download --file-key /chat/m/WANGLULU8/.../<md5> --out ./got.bin # by explicit key
|
|
236
|
+
muc download --mid 69ef1ae07e5462c465c05e00 --to-self # look up body via roaming
|
|
237
|
+
muc download --mid <MID> --peer YANWENYU --out ./inbox/ # save into a directory
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`download` is the only file-channel command that **doesn't** open a chat
|
|
241
|
+
session — it speaks only port 6101 (and a one-time bucket lookup on first
|
|
242
|
+
call). Run it from any environment without disturbing your desktop or phone.
|
|
243
|
+
|
|
244
|
+
## Coexistence
|
|
245
|
+
|
|
246
|
+
**REST commands always coexist** with a running desktop and phone — the
|
|
247
|
+
server allows concurrent `accessToken`s. `whoami`, `search`, `search-dept`,
|
|
248
|
+
`search-app`, `search-sno`, `history`, `whois`, `groups`, `emoticons`, and
|
|
249
|
+
`download` are REST-only (or 6101-only, in `download`'s case) and safe to
|
|
250
|
+
run anytime.
|
|
251
|
+
|
|
252
|
+
**Chat-channel commands** (`sync`, `search-team`, `send`, `send-file`,
|
|
253
|
+
`send-image`, `send-voice`, `withdraw`, `clear-session`) open a TCP-8101
|
|
254
|
+
session and share a per-user **two-slot** model with real devices:
|
|
255
|
+
|
|
256
|
+
| Slot | `--os` values |
|
|
257
|
+
|---|---|
|
|
258
|
+
| PC | `Windows`, `"OS X"`, `Mac` |
|
|
259
|
+
| Mobile | `iOS` (default), `Android` |
|
|
260
|
+
|
|
261
|
+
A second login in the same slot kicks the previous client off the realtime
|
|
262
|
+
channel — the IM panel goes offline, but tokens stay valid (reopen to
|
|
263
|
+
recover). The Mac desktop signs in as `"OS X"` (PC slot); the iPhone signs
|
|
264
|
+
in as `iOS` (Mobile slot).
|
|
265
|
+
|
|
266
|
+
The CLI defaults to `--os iOS` so it coexists with a Mac desktop. If your
|
|
267
|
+
iPhone is also signed in, both slots are taken and any TCP command will
|
|
268
|
+
kick one of them — fall back to REST-only commands.
|
|
269
|
+
|
|
270
|
+
`tcp-ping` only does TCP connect + NEGOTIATE (no `auth/login`), so it can't
|
|
271
|
+
kick anything. Run it first to confirm the wire protocol is happy on your
|
|
272
|
+
network.
|
|
273
|
+
|
|
274
|
+
If you do get kicked, reopen the desktop's IM panel or relaunch the mobile
|
|
275
|
+
app to recover — tokens stay valid.
|
|
276
|
+
|
|
277
|
+
## Examples
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
$ printf '%s' "$MY_PWD" | muc login --account WANGLULU8 --password-stdin
|
|
281
|
+
uid: WANGLULU8
|
|
282
|
+
empId: 5394483846707200
|
|
283
|
+
name: 王璐璐
|
|
284
|
+
accessToken: TV5l…u9kQ (redacted; use --json for raw)
|
|
285
|
+
accessTokenExpireAt: 1745678340000
|
|
286
|
+
tokenPwdRefreshed: true
|
|
287
|
+
tokenPwdExpireAt: 1748097540000
|
|
288
|
+
|
|
289
|
+
$ muc whoami
|
|
290
|
+
uid: WANGLULU8
|
|
291
|
+
empId: 5394483846707200
|
|
292
|
+
name: 王璐璐
|
|
293
|
+
deviceId: 8e413b4141…
|
|
294
|
+
deviceName: WANGLULU8-mbp.local (muc-cli)
|
|
295
|
+
accessToken: TV5l…u9kQ (redacted; use --json for raw)
|
|
296
|
+
accessTokenExpireAt: 1745678340000
|
|
297
|
+
tokenPwdSet: true
|
|
298
|
+
tokenPwdExpireAt: 1748097540000
|
|
299
|
+
savedAt: 1745073540000
|
|
300
|
+
credentialsPath: ~/.muc/credentials.json
|
|
301
|
+
|
|
302
|
+
$ muc search 张
|
|
303
|
+
ZHANG_W2 张伟 飞行部 / 机长室
|
|
304
|
+
ZHANGSAN03 张三 地面服务部
|
|
305
|
+
…
|
|
306
|
+
|
|
307
|
+
$ muc whois ZHANG_W2
|
|
308
|
+
uid ZHANG_W2
|
|
309
|
+
name 张伟
|
|
310
|
+
empId 4892173049281024
|
|
311
|
+
mobile 1380013…
|
|
312
|
+
email zhang.wei@ceair.com
|
|
313
|
+
dept 飞行部 / 机长室
|
|
314
|
+
title 机长
|
|
315
|
+
|
|
316
|
+
$ muc groups
|
|
317
|
+
starred:
|
|
318
|
+
[常用] (3)
|
|
319
|
+
9001234567890123 IT 值班群
|
|
320
|
+
9001234567890124 运行联络
|
|
321
|
+
9001234567890125 部门通知
|
|
322
|
+
dept groups: 1
|
|
323
|
+
9009876543210000 飞行部 / 机长室
|
|
324
|
+
|
|
325
|
+
$ muc emoticons
|
|
326
|
+
1001 默认表情包 (32)
|
|
327
|
+
1042 航空主题 (18)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Files & paths
|
|
331
|
+
|
|
332
|
+
| Path | Purpose | Mode |
|
|
333
|
+
|---|---|---|
|
|
334
|
+
| `~/.muc/config.json` | tenant config: APP_KEY, APP_ID, signing secrets, hosts/ports | 600 |
|
|
335
|
+
| `~/.muc/credentials.json` | accessToken, tokenPwd, profile | 600 |
|
|
336
|
+
| `~/.muc/device-id` | stable per-CLI deviceId (sha256 hex) | 600 |
|
|
337
|
+
|
|
338
|
+
Both files share the `~/.muc/` parent (mode 700). The CLI creates them on
|
|
339
|
+
first use; you should never need to edit them by hand.
|
|
340
|
+
|
|
341
|
+
## What it can't do (yet)
|
|
342
|
+
|
|
343
|
+
- **Richtext messages** (`subType=11`, mixed text + image + @-mention in one
|
|
344
|
+
bubble). The image segments share the file-channel upload path so the
|
|
345
|
+
bulk of the work is already there; just needs a body-builder + a
|
|
346
|
+
`send-rich` command.
|
|
347
|
+
- **Forwarded / merged messages** (`subType=26`).
|
|
348
|
+
- **SSO / scan-to-login.** REST username+password works; the SSO flow is
|
|
349
|
+
not wired up.
|
|
350
|
+
- **Live push streaming.** Use `muc history` for server-side message
|
|
351
|
+
retrieval; there is no long-lived `talk/msg` listener wired up.
|
|
352
|
+
- **Image thumbnails.** The upload step asks the server to make `tn1`/`tn5`
|
|
353
|
+
thumbs, but `muc download` only fetches the original `fileKey`.
|
|
354
|
+
- **End-to-end-encrypted file content.** Not active on the default tenant
|
|
355
|
+
and the CLI doesn't implement it.
|
|
356
|
+
|
|
357
|
+
## Troubleshooting
|
|
358
|
+
|
|
359
|
+
- `code: CONFIG_MISSING` (exit 6) — `~/.muc/config.json` is absent. Run
|
|
360
|
+
`muc init` to create it. The CLI cannot guess your tenant's signing
|
|
361
|
+
secrets.
|
|
362
|
+
- `code: CONFIG_INVALID` (exit 7) — the file exists but failed schema
|
|
363
|
+
validation (corruption, manual edit gone wrong, or a stale schema after
|
|
364
|
+
upgrade). Run `muc init` to repair it; `--reset --yes` to start clean.
|
|
365
|
+
- `login failed: code=…` — sign or DES is wrong. Double-check the signing
|
|
366
|
+
secrets and `appKey` / `appId` you supplied to `muc init` against the
|
|
367
|
+
values your MUC admin provided. `muc init --show` (secrets redacted) lets
|
|
368
|
+
you confirm what's currently saved; `muc init` (interactive) lets you
|
|
369
|
+
re-enter the values.
|
|
370
|
+
- `error: not logged in` (exit 3, code `AUTH_REQUIRED`) — the saved
|
|
371
|
+
credentials file is missing or unreadable. Check `~/.muc/credentials.json`
|
|
372
|
+
and re-run `muc login`.
|
|
373
|
+
- `code: AUTH_EXPIRED` (exit 3) — accessToken or tokenPwd was rejected by
|
|
374
|
+
the server. Re-run `muc login --token-pwd` (or full `muc login` if
|
|
375
|
+
`tokenPwd` is also stale).
|
|
376
|
+
- `EPROTO` / TLS errors — corporate proxy or VPN intercepting traffic.
|
|
377
|
+
`muc-cli` does not pin certificates, so adding the proxy CA to your Node
|
|
378
|
+
trust store should suffice (`NODE_EXTRA_CA_CERTS=/path/to/ca.pem`).
|
|
379
|
+
- A TCP send kicks your desktop off the IM panel — the CLI's `--os` value
|
|
380
|
+
collided with the desktop's slot. See [Coexistence](#coexistence) above:
|
|
381
|
+
default `--os iOS` only risks the Mobile slot (your iPhone), not the
|
|
382
|
+
desktop. If both desktop and phone are online, drop to REST-only
|
|
383
|
+
commands.
|
|
384
|
+
|
|
385
|
+
### Exit-code summary
|
|
386
|
+
|
|
387
|
+
| Exit | Code |
|
|
388
|
+
|---|---|
|
|
389
|
+
| 0 | success |
|
|
390
|
+
| 1 | INTERNAL |
|
|
391
|
+
| 2 | USAGE |
|
|
392
|
+
| 3 | AUTH_REQUIRED, AUTH_EXPIRED |
|
|
393
|
+
| 4 | NOT_FOUND |
|
|
394
|
+
| 5 | REMOTE_ERROR, NETWORK |
|
|
395
|
+
| 6 | CONFIG_MISSING |
|
|
396
|
+
| 7 | CONFIG_INVALID |
|
|
397
|
+
|
|
398
|
+
## Security
|
|
399
|
+
|
|
400
|
+
- Both files in `~/.muc/` are written with mode `600`. **Don't** check them
|
|
401
|
+
into git or copy them to shared machines.
|
|
402
|
+
- **Don't share `~/.muc/config.json`** — it contains your tenant's signing
|
|
403
|
+
secrets (`signSecretRest`, `signSecretSecure`, `pwdDesKey`, `appKey`).
|
|
404
|
+
Anyone with these can impersonate API calls against your MUC tenant.
|
|
405
|
+
- `MUC_PASSWORD` ends up in shell history; prefer `--password-stdin` or
|
|
406
|
+
`--token-pwd` for refreshes.
|
|
407
|
+
- **Prefer interactive `muc init` over env vars.** Environment variables
|
|
408
|
+
leak through shell history and `/proc/<pid>/environ` to other processes
|
|
409
|
+
on the same machine. Use the interactive prompt or one-shot flags pulled
|
|
410
|
+
from a secret manager rather than persistent `MUC_*` exports.
|
|
411
|
+
- TCP login over 8101 registers a device session in one of two slots per
|
|
412
|
+
user (PC or Mobile). A second login in the same slot kicks the first.
|
|
413
|
+
Default `--os iOS` keeps the CLI on the Mobile slot; only your iPhone is
|
|
414
|
+
ever at risk.
|
|
415
|
+
- `tcp-ping` is the only chat-channel command that cannot kick anything —
|
|
416
|
+
use it as a network smoke-test before trying TCP sends.
|