@mu-cabin/muc-cli 0.3.0 → 0.3.1

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.
Files changed (3) hide show
  1. package/README.md +16 -4
  2. package/dist/muc.mjs +120 -68
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -223,9 +223,15 @@ muc history --team 9009876543210000 --before 2026-04-20 --json | jq .
223
223
  | `muc wait-react --mid <MID> [--from-uid UID] [--sticker-id ID] [--timeout 300]` | Block until someone reacts with an emoticon to `--mid`, then exit. Useful as a "human-in-the-loop approval" gate after `muc send`. Exits with `TIMEOUT` (exit 5) if no matching reaction arrives within `--timeout` seconds. |
224
224
  | `muc listen [--from-uid UID] [--team TEAM] [--peer UID] [--contains TEXT] [--regex …] [--at-me] [--max N]` | Long-lived TCP tap that emits one **NDJSON line per matching inbound message** (in `--json` mode each line is the standard envelope; in human mode it's a one-line summary). Supports repeatable filters (OR semantics within each kind, AND across kinds), `--at-me` to restrict to group messages where another user @-mentions you (implies excluding self), `--include-self` to include your own sends, `--include-reactions` to also surface type=3 sub=300/301 frames, `--max N` to exit after N matches. Stop with `^C`. |
225
225
 
226
- `--os` defaults to `iOS`. Valid values: `Windows`, `OS X`, `Mac`, `iOS`,
227
- `Android`. **`--os` does not provide coexistence — read the section below
228
- before using any chat-channel command if you have a real device logged in.**
226
+ `--os` resolves as **per-call `--os` → `defaultChatOs` in `~/.muc/config.json`
227
+ → built-in `iOS`**. Valid values: `Windows`, `OS X`, `Mac`, `iOS`, `Android`.
228
+ Set the per-machine default once with `muc init --default-os <os>` (or the
229
+ `MUC_DEFAULT_OS` env var, or the interactive prompt during `muc init`). Useful
230
+ if your real device is an iPhone — flip the CLI default to `windows` so
231
+ chat-channel commands park on the PC slot instead of kicking your phone.
232
+ **`--os` / `defaultChatOs` does not provide coexistence — read the section
233
+ below before using any chat-channel command if you have a real device
234
+ logged in.**
229
235
 
230
236
  Sending to anyone other than yourself requires the explicit `--yes-not-self`
231
237
  flag — a guard against accidentally messaging real humans during testing.
@@ -286,7 +292,9 @@ in as `iOS` (Mobile slot).
286
292
 
287
293
  The CLI defaults to `--os iOS` so it coexists with a Mac desktop. If your
288
294
  iPhone is also signed in, both slots are taken and any TCP command will
289
- kick one of them — fall back to REST-only commands.
295
+ kick one of them — fall back to REST-only commands, or run
296
+ `muc init --default-os windows` to flip the per-machine default to the PC
297
+ slot (which kicks the desktop instead of the phone).
290
298
 
291
299
  `tcp-ping` only does TCP connect + NEGOTIATE (no `auth/login`), so it can't
292
300
  kick anything. Run it first to confirm the wire protocol is happy on your
@@ -361,6 +369,10 @@ first use; you should never need to edit them by hand.
361
369
 
362
370
  ## Changelog
363
371
 
372
+ #### `0.3.1`
373
+ `2026.04.29`
374
+ - Add `defaultChatOs` to `~/.muc/config.json`: per-machine fallback for the chat-channel `--os` slot. Configure via `muc init --default-os <windows|osx|mac|ios|android>`, the `MUC_DEFAULT_OS` env var, or the interactive prompt during `muc init`. Resolution order: per-call `--os` → `defaultChatOs` → built-in `iOS`. Use this when your real device is on the Mobile slot (e.g. iPhone) so chat-channel commands park on PC instead of kicking your phone.
375
+
364
376
  #### `0.3.0`
365
377
  `2026.04.29`
366
378
  - Add `muc react` / `muc unreact` / `muc reactions` to add, revoke, and list emoticon reactions on existing messages
package/dist/muc.mjs CHANGED
@@ -21288,6 +21288,7 @@ import { join } from "node:path";
21288
21288
  var DIR = join(homedir(), ".muc");
21289
21289
  var FILE = join(DIR, "config.json");
21290
21290
  var CONFIG_SCHEMA_VERSION = 1;
21291
+ var CHAT_OS_VALUES = ["Windows", "OS X", "Mac", "iOS", "Android"];
21291
21292
  var MucConfigSchema = external_exports.object({
21292
21293
  appKey: external_exports.string().regex(/^[0-9a-f]{8}$/, "appKey must be 8 lowercase hex chars"),
21293
21294
  appId: external_exports.string().regex(/^[0-9a-f]{24}$/, "appId must be 24 lowercase hex chars"),
@@ -21307,6 +21308,10 @@ var MucConfigSchema = external_exports.object({
21307
21308
  // any other → that literal string (e.g. "\n— bot")
21308
21309
  // Per-call `--tail` / `--no-tail` flags on `muc send` override this.
21309
21310
  messageTail: external_exports.string().max(200).optional(),
21311
+ // Default chat-channel `os` slot for TCP commands when --os is not passed.
21312
+ // PC ({Windows, OS X, Mac}) vs Mobile ({iOS, Android}); same slot as the
21313
+ // running desktop will kick it. Built-in fallback is 'iOS' (Mobile).
21314
+ defaultChatOs: external_exports.enum(CHAT_OS_VALUES).optional(),
21310
21315
  savedAt: external_exports.number().int().nonnegative(),
21311
21316
  schemaVersion: external_exports.literal(CONFIG_SCHEMA_VERSION)
21312
21317
  });
@@ -21394,6 +21399,47 @@ function saveCredentials(c) {
21394
21399
  }
21395
21400
  var credentialsPath = FILE2;
21396
21401
 
21402
+ // src/lib/constants.ts
21403
+ var APP_NAME = "MUC";
21404
+ var APP_VERSION = "3.3.1-250925001";
21405
+ var APP_VERSION_IOS = "3.3.0301";
21406
+ var PLATFORM_IOS = 1;
21407
+ var PLATFORM_ANDROID = 2;
21408
+ var PLATFORM_WIN = 4;
21409
+ var PLATFORM_MAC = 5;
21410
+ var SECURE_SYS_KEY = "mx_app_key";
21411
+ var PWD_DES_IV = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]);
21412
+ var DEFAULT_CHAT_OS = "iOS";
21413
+ var DEFAULT_REST_PLATFORM = PLATFORM_IOS;
21414
+ function defaultMessageTail(userName) {
21415
+ const name = userName.trim() || "unknown";
21416
+ return `
21417
+ \u2014\u2014 \u6765\u81EA${name}\u7684muc-cli`;
21418
+ }
21419
+ var DEFAULTS = {
21420
+ // Hosts/ports for the 东航 deployment. Other tenants override at init.
21421
+ restHost: "https://muc.ceair.com/",
21422
+ ssoHost: "https://store.ceair.com/",
21423
+ imHost: "muc.ceair.com",
21424
+ imPort: 8101,
21425
+ fileHost: "muc.ceair.com",
21426
+ filePort: 6101
21427
+ };
21428
+ function apiUrls(cfg) {
21429
+ const host = cfg.restHost.endsWith("/") ? cfg.restHost : `${cfg.restHost}/`;
21430
+ return {
21431
+ SYS: `${host}sysconfig/v5/app`,
21432
+ MUC: `${host}muc/v5/app`,
21433
+ MAM: `${host}mam/v5/app`,
21434
+ MAS: `${host}mas/v5/app`,
21435
+ MPM: `${host}mpm/v5/app`,
21436
+ IMM: `${host}imm/v5/app`,
21437
+ MRM: `${host}mrm/v5/app`,
21438
+ CONTACT: `${host}contacts/v5/app`,
21439
+ SECURE: `${host}contacts/secure/v5/app`
21440
+ };
21441
+ }
21442
+
21397
21443
  // src/lib/cli.ts
21398
21444
  var SCHEMA_VERSION = 1;
21399
21445
  var EXIT_CODES = {
@@ -21573,6 +21619,11 @@ function parseChatOs(raw) {
21573
21619
  "expected windows|osx|mac|ios|android"
21574
21620
  );
21575
21621
  }
21622
+ function resolveChatOs(optsOs, cfg) {
21623
+ if (optsOs !== void 0) return optsOs;
21624
+ if (cfg.defaultChatOs !== void 0) return cfg.defaultChatOs;
21625
+ return DEFAULT_CHAT_OS;
21626
+ }
21576
21627
  function parseMoment(label) {
21577
21628
  return (raw) => {
21578
21629
  if (/^\d+$/.test(raw)) {
@@ -21717,47 +21768,6 @@ function getDeviceName() {
21717
21768
  return `${hostname()} (muc-cli)`;
21718
21769
  }
21719
21770
 
21720
- // src/lib/constants.ts
21721
- var APP_NAME = "MUC";
21722
- var APP_VERSION = "3.3.1-250925001";
21723
- var APP_VERSION_IOS = "3.3.0301";
21724
- var PLATFORM_IOS = 1;
21725
- var PLATFORM_ANDROID = 2;
21726
- var PLATFORM_WIN = 4;
21727
- var PLATFORM_MAC = 5;
21728
- var SECURE_SYS_KEY = "mx_app_key";
21729
- var PWD_DES_IV = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]);
21730
- var DEFAULT_CHAT_OS = "iOS";
21731
- var DEFAULT_REST_PLATFORM = PLATFORM_IOS;
21732
- function defaultMessageTail(userName) {
21733
- const name = userName.trim() || "unknown";
21734
- return `
21735
- \u2014\u2014 \u6765\u81EA${name}\u7684muc-cli`;
21736
- }
21737
- var DEFAULTS = {
21738
- // Hosts/ports for the 东航 deployment. Other tenants override at init.
21739
- restHost: "https://muc.ceair.com/",
21740
- ssoHost: "https://store.ceair.com/",
21741
- imHost: "muc.ceair.com",
21742
- imPort: 8101,
21743
- fileHost: "muc.ceair.com",
21744
- filePort: 6101
21745
- };
21746
- function apiUrls(cfg) {
21747
- const host = cfg.restHost.endsWith("/") ? cfg.restHost : `${cfg.restHost}/`;
21748
- return {
21749
- SYS: `${host}sysconfig/v5/app`,
21750
- MUC: `${host}muc/v5/app`,
21751
- MAM: `${host}mam/v5/app`,
21752
- MAS: `${host}mas/v5/app`,
21753
- MPM: `${host}mpm/v5/app`,
21754
- IMM: `${host}imm/v5/app`,
21755
- MRM: `${host}mrm/v5/app`,
21756
- CONTACT: `${host}contacts/v5/app`,
21757
- SECURE: `${host}contacts/secure/v5/app`
21758
- };
21759
- }
21760
-
21761
21771
  // src/lib/http.ts
21762
21772
  var UA = `MUC/${APP_VERSION} (CLI; muc-cli/${CLI_VERSION})`;
21763
21773
  var SENSITIVE_QS = /* @__PURE__ */ new Set(["accessToken", "tokenPwd", "sign"]);
@@ -22136,6 +22146,8 @@ var FingerprintShape = external_exports.object({
22136
22146
  filePort: external_exports.number(),
22137
22147
  messageTail: external_exports.string(),
22138
22148
  // summary: "default" / "none" / "custom (N chars)"
22149
+ defaultChatOs: external_exports.string(),
22150
+ // summary: explicit value or "default (iOS)"
22139
22151
  savedAt: external_exports.number(),
22140
22152
  schemaVersion: external_exports.literal(CONFIG_SCHEMA_VERSION)
22141
22153
  });
@@ -22161,6 +22173,10 @@ function summarizeTail(t) {
22161
22173
  if (t === "") return "none";
22162
22174
  return `custom (${t.length} chars)`;
22163
22175
  }
22176
+ function summarizeChatOs(v) {
22177
+ if (v === void 0) return `default (${DEFAULT_CHAT_OS})`;
22178
+ return v;
22179
+ }
22164
22180
  function redactConfig(c) {
22165
22181
  return {
22166
22182
  appKey: fingerprint2(c.appKey),
@@ -22175,6 +22191,7 @@ function redactConfig(c) {
22175
22191
  fileHost: c.fileHost,
22176
22192
  filePort: c.filePort,
22177
22193
  messageTail: summarizeTail(c.messageTail),
22194
+ defaultChatOs: summarizeChatOs(c.defaultChatOs),
22178
22195
  savedAt: c.savedAt,
22179
22196
  schemaVersion: c.schemaVersion
22180
22197
  };
@@ -22320,7 +22337,7 @@ function register2(program3) {
22320
22337
  description: "Bootstrap or update ~/.muc/config.json (tenant config: APP_KEY, APP_ID, signing secrets, hosts/ports). Interactive prompts by default; non-interactive via flags or MUC_* env vars. WARNING: env vars are visible to other processes via /proc/<pid>/environ and shell history \u2014 prefer interactive or flag-based for production.",
22321
22338
  output: Output2,
22322
22339
  configure: (cmd) => {
22323
- cmd.option("--check", "verify config presence and validity (exit 0 ok / 6 missing / 7 invalid); does not modify").option("--show", "print the saved config (secrets are always redacted in both human and JSON output; cat ~/.muc/config.json for raw values)").option("--reset", "delete ~/.muc/config.json (requires --yes)").option("--yes", "confirm a destructive action like --reset").option("--no-interactive", "fail rather than prompt for missing fields").option("--app-key <hex>", "tenant APP_KEY (8 lowercase hex)").option("--app-id <hex>", "tenant APP_ID (24 lowercase hex)").option("--sign-secret-rest <s>", "REST signing secret").option("--sign-secret-secure <s>", "secure-API signing secret").option("--pwd-des-key <s>", "password DES key (defaults to APP_KEY)").option("--rest-host <url>", "REST host URL").option("--sso-host <url>", "SSO host URL (optional)").option("--im-host <h>", "IM TCP host").option("--im-port <n>", "IM TCP port").option("--file-host <h>", "file-channel TCP host").option("--file-port <n>", "file-channel TCP port").option("--message-tail <text>", "custom signature appended to outbound `muc send` text (max 200 chars)").option("--no-message-tail", "explicitly disable the message tail (no signature appended)");
22340
+ cmd.option("--check", "verify config presence and validity (exit 0 ok / 6 missing / 7 invalid); does not modify").option("--show", "print the saved config (secrets are always redacted in both human and JSON output; cat ~/.muc/config.json for raw values)").option("--reset", "delete ~/.muc/config.json (requires --yes)").option("--yes", "confirm a destructive action like --reset").option("--no-interactive", "fail rather than prompt for missing fields").option("--app-key <hex>", "tenant APP_KEY (8 lowercase hex)").option("--app-id <hex>", "tenant APP_ID (24 lowercase hex)").option("--sign-secret-rest <s>", "REST signing secret").option("--sign-secret-secure <s>", "secure-API signing secret").option("--pwd-des-key <s>", "password DES key (defaults to APP_KEY)").option("--rest-host <url>", "REST host URL").option("--sso-host <url>", "SSO host URL (optional)").option("--im-host <h>", "IM TCP host").option("--im-port <n>", "IM TCP port").option("--file-host <h>", "file-channel TCP host").option("--file-port <n>", "file-channel TCP port").option("--message-tail <text>", "custom signature appended to outbound `muc send` text (max 200 chars)").option("--no-message-tail", "explicitly disable the message tail (no signature appended)").option("--default-os <os>", "default chat-channel os slot for TCP commands when --os is not passed (windows|osx|mac|ios|android); built-in fallback is iOS");
22324
22341
  },
22325
22342
  run: async (cmd) => {
22326
22343
  const opts = cmd.opts();
@@ -22430,6 +22447,26 @@ function register2(program3) {
22430
22447
  } else if (existing && "messageTail" in existing) {
22431
22448
  messageTail = existing.messageTail;
22432
22449
  }
22450
+ let defaultChatOs;
22451
+ let dcoRaw;
22452
+ const dcoFlag = opts.defaultOs;
22453
+ const dcoEnv = process.env.MUC_DEFAULT_OS;
22454
+ if (typeof dcoFlag === "string" && dcoFlag.length > 0) {
22455
+ dcoRaw = dcoFlag;
22456
+ } else if (dcoEnv !== void 0 && dcoEnv.length > 0) {
22457
+ dcoRaw = dcoEnv;
22458
+ } else if (existing?.defaultChatOs !== void 0) {
22459
+ defaultChatOs = existing.defaultChatOs;
22460
+ } else if (interactive) {
22461
+ const answer = await readLine(
22462
+ "Default chat OS slot (windows|osx|mac|ios|android)",
22463
+ DEFAULT_CHAT_OS.toLowerCase()
22464
+ );
22465
+ if (answer.length > 0) dcoRaw = answer;
22466
+ }
22467
+ if (dcoRaw !== void 0) {
22468
+ defaultChatOs = parseChatOs(dcoRaw);
22469
+ }
22433
22470
  const draft = {
22434
22471
  appKey: String(partial.appKey ?? ""),
22435
22472
  appId: String(partial.appId ?? ""),
@@ -22443,6 +22480,7 @@ function register2(program3) {
22443
22480
  fileHost: String(partial.fileHost ?? ""),
22444
22481
  filePort: Number(partial.filePort ?? 0),
22445
22482
  messageTail,
22483
+ defaultChatOs,
22446
22484
  savedAt: Date.now(),
22447
22485
  schemaVersion: CONFIG_SCHEMA_VERSION
22448
22486
  };
@@ -23220,13 +23258,14 @@ function register9(program3) {
23220
23258
  description: 'Search groups the current user belongs to (TCP chat channel sid="team", cid="search_own_teams"). Pass --enrich for names + member counts. Will kick a session in the same online-slot.',
23221
23259
  output: Output9,
23222
23260
  configure: (cmd) => {
23223
- cmd.argument("<keyword>", "team name fragment").option("--enrich", "follow up with team/get_team per id").option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
23261
+ cmd.argument("<keyword>", "team name fragment").option("--enrich", "follow up with team/get_team per id").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
23224
23262
  },
23225
23263
  run: async (cmd) => {
23226
23264
  const [kw] = cmd.args;
23227
- const { enrich, os, debug } = cmd.opts();
23265
+ const { enrich, os: optsOs, debug } = cmd.opts();
23228
23266
  const cfg = requireConfig();
23229
23267
  const cred = requireAuth(loadCredentials());
23268
+ const os = resolveChatOs(optsOs, cfg);
23230
23269
  const session = await openChatSession(cred, { os, debug });
23231
23270
  const { socket } = session;
23232
23271
  try {
@@ -23412,11 +23451,13 @@ function register13(program3) {
23412
23451
  description: "After chat-login, run sync/syncDone, sync/syncSession, sync/syncWithdraws and return the raw responses.",
23413
23452
  output: Output13,
23414
23453
  configure: (cmd) => {
23415
- cmd.option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
23454
+ cmd.option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
23416
23455
  },
23417
23456
  run: async (cmd) => {
23418
- const { os, debug } = cmd.opts();
23457
+ const { os: optsOs, debug } = cmd.opts();
23458
+ const cfg = requireConfig();
23419
23459
  const cred = requireAuth(loadCredentials());
23460
+ const os = resolveChatOs(optsOs, cfg);
23420
23461
  const session = await openChatSession(cred, { os, debug });
23421
23462
  const { socket } = session;
23422
23463
  try {
@@ -23670,7 +23711,7 @@ function register15(program3) {
23670
23711
  description: "Send a plain-text chat message via the TCP chat channel. VISIBLE TO THE RECIPIENT \u2014 non-self targets require --yes-not-self.",
23671
23712
  output: Output15,
23672
23713
  configure: (cmd) => {
23673
- cmd.argument("<text...>", "message text (variadic; words rejoined with spaces)").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--tail <text>", "override message tail for this call only").option("--no-tail", "do not append a message tail for this call").option("--debug", "forward wire-level debug events to stderr");
23714
+ cmd.argument("<text...>", "message text (variadic; words rejoined with spaces)").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--tail <text>", "override message tail for this call only").option("--no-tail", "do not append a message tail for this call").option("--debug", "forward wire-level debug events to stderr");
23674
23715
  },
23675
23716
  run: async (cmd) => {
23676
23717
  const text = cmd.args.join(" ").trim();
@@ -23678,6 +23719,7 @@ function register15(program3) {
23678
23719
  const opts = cmd.opts();
23679
23720
  const cfg = requireConfig();
23680
23721
  const cred = requireAuth(loadCredentials());
23722
+ const os = resolveChatOs(opts.os, cfg);
23681
23723
  requireSingleTarget(opts, "send");
23682
23724
  const conflict = detectFlagConflict(process.argv.slice(2), "tail");
23683
23725
  if (conflict.sawValueForm && conflict.sawNegatedForm) {
@@ -23716,7 +23758,7 @@ function register15(program3) {
23716
23758
  subType: 1,
23717
23759
  body: finalText,
23718
23760
  fName: cred.name,
23719
- platform: opts.os === "Mac" || opts.os === "OS X" ? "Mac" : "Windows",
23761
+ platform: os === "Mac" || os === "OS X" ? "Mac" : "Windows",
23720
23762
  appKey: cfg.appKey
23721
23763
  });
23722
23764
  if (opts.dryRun) {
@@ -23728,7 +23770,7 @@ function register15(program3) {
23728
23770
  frame: { sid: "talk", cid: "sendMsg", data: { msg } }
23729
23771
  };
23730
23772
  }
23731
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
23773
+ const session = await openChatSession(cred, { os, debug: opts.debug });
23732
23774
  const { socket } = session;
23733
23775
  let res;
23734
23776
  try {
@@ -24468,7 +24510,7 @@ function register16(program3) {
24468
24510
  description: "Upload a local file (TCP 6101) and broadcast as MESSAGE_CHAT_FILE (subType=7). VISIBLE \u2014 non-self targets require --yes-not-self.",
24469
24511
  output: Output16,
24470
24512
  configure: (cmd) => {
24471
- cmd.argument("<file>", "absolute or relative path to a local file").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "resolve target + sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel os slot for the talk/sendMsg", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
24513
+ cmd.argument("<file>", "absolute or relative path to a local file").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "resolve target + sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel os slot for the talk/sendMsg (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
24472
24514
  },
24473
24515
  run: async (cmd) => {
24474
24516
  const filePath = path2.resolve(String(cmd.args[0] ?? ""));
@@ -24476,6 +24518,7 @@ function register16(program3) {
24476
24518
  const opts = cmd.opts();
24477
24519
  const cfg = requireConfig();
24478
24520
  const cred = requireAuth(loadCredentials());
24521
+ const os = resolveChatOs(opts.os, cfg);
24479
24522
  requireSingleTarget(opts, "send-file");
24480
24523
  let scene;
24481
24524
  let toId;
@@ -24526,10 +24569,10 @@ function register16(program3) {
24526
24569
  expiredDay: up.expiredDay
24527
24570
  }),
24528
24571
  fName: cred.name,
24529
- platform: opts.os === "Mac" || opts.os === "OS X" ? "Mac" : "Windows",
24572
+ platform: os === "Mac" || os === "OS X" ? "Mac" : "Windows",
24530
24573
  appKey: cfg.appKey
24531
24574
  });
24532
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
24575
+ const session = await openChatSession(cred, { os, debug: opts.debug });
24533
24576
  let res;
24534
24577
  try {
24535
24578
  res = await session.socket.send("talk", "sendMsg", { msg });
@@ -24642,13 +24685,14 @@ function register17(program3) {
24642
24685
  description: "Upload an image (TCP 6101) and broadcast as MESSAGE_CHAT_IMAGE (subType=8). VISIBLE \u2014 non-self targets require --yes-not-self.",
24643
24686
  output: Output17,
24644
24687
  configure: (cmd) => {
24645
- cmd.argument("<file>", "PNG/JPEG/GIF/BMP image path").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--width <px>", "override probed image width", parsePosInt("--width")).option("--height <px>", "override probed image height", parsePosInt("--height")).option("--dry-run", "sanity-check the local file (probe + stat) but do not upload or send").option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
24688
+ cmd.argument("<file>", "PNG/JPEG/GIF/BMP image path").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--width <px>", "override probed image width", parsePosInt("--width")).option("--height <px>", "override probed image height", parsePosInt("--height")).option("--dry-run", "sanity-check the local file (probe + stat) but do not upload or send").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
24646
24689
  },
24647
24690
  run: async (cmd) => {
24648
24691
  const filePath = path3.resolve(String(cmd.args[0] ?? ""));
24649
24692
  const opts = cmd.opts();
24650
24693
  const cfg = requireConfig();
24651
24694
  const cred = requireAuth(loadCredentials());
24695
+ const os = resolveChatOs(opts.os, cfg);
24652
24696
  requireSingleTarget(opts, "send-image");
24653
24697
  let scene;
24654
24698
  let toId;
@@ -24714,10 +24758,10 @@ function register17(program3) {
24714
24758
  height
24715
24759
  }),
24716
24760
  fName: cred.name,
24717
- platform: opts.os === "Mac" || opts.os === "OS X" ? "Mac" : "Windows",
24761
+ platform: os === "Mac" || os === "OS X" ? "Mac" : "Windows",
24718
24762
  appKey: cfg.appKey
24719
24763
  });
24720
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
24764
+ const session = await openChatSession(cred, { os, debug: opts.debug });
24721
24765
  let res;
24722
24766
  try {
24723
24767
  res = await session.socket.send("talk", "sendMsg", { msg });
@@ -24796,13 +24840,14 @@ function register18(program3) {
24796
24840
  description: "Upload an audio clip (TCP 6101) and broadcast as MESSAGE_CHAT_AUDIO (subType=9). VISIBLE \u2014 non-self targets require --yes-not-self.",
24797
24841
  output: Output18,
24798
24842
  configure: (cmd) => {
24799
- cmd.argument("<file>", "audio file path (typically .amr)").requiredOption("--duration <ms>", "clip duration in milliseconds", parsePosInt("--duration")).option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--dry-run", "sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
24843
+ cmd.argument("<file>", "audio file path (typically .amr)").requiredOption("--duration <ms>", "clip duration in milliseconds", parsePosInt("--duration")).option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--dry-run", "sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
24800
24844
  },
24801
24845
  run: async (cmd) => {
24802
24846
  const filePath = path4.resolve(String(cmd.args[0] ?? ""));
24803
24847
  const opts = cmd.opts();
24804
24848
  const cfg = requireConfig();
24805
24849
  const cred = requireAuth(loadCredentials());
24850
+ const os = resolveChatOs(opts.os, cfg);
24806
24851
  requireSingleTarget(opts, "send-voice");
24807
24852
  let scene;
24808
24853
  let toId;
@@ -24861,10 +24906,10 @@ function register18(program3) {
24861
24906
  duration: opts.duration
24862
24907
  }),
24863
24908
  fName: cred.name,
24864
- platform: opts.os === "Mac" || opts.os === "OS X" ? "Mac" : "Windows",
24909
+ platform: os === "Mac" || os === "OS X" ? "Mac" : "Windows",
24865
24910
  appKey: cfg.appKey
24866
24911
  });
24867
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
24912
+ const session = await openChatSession(cred, { os, debug: opts.debug });
24868
24913
  let res;
24869
24914
  try {
24870
24915
  res = await session.socket.send("talk", "sendMsg", { msg });
@@ -25033,12 +25078,13 @@ function register20(program3) {
25033
25078
  description: "Withdraw a previously-sent message (P2P_MSG_CANCEL). VISIBLE TO THE RECIPIENT \u2014 non-self targets require --yes-not-self.",
25034
25079
  output: Output20,
25035
25080
  configure: (cmd) => {
25036
- cmd.requiredOption("--mid <mid>", "message id to withdraw").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "withdraw in your self-chat (default if no target)").option("--yes-not-self", "required to confirm withdrawing in a non-self conversation").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
25081
+ cmd.requiredOption("--mid <mid>", "message id to withdraw").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "withdraw in your self-chat (default if no target)").option("--yes-not-self", "required to confirm withdrawing in a non-self conversation").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
25037
25082
  },
25038
25083
  run: async (cmd) => {
25039
25084
  const opts = cmd.opts();
25040
25085
  const cfg = requireConfig();
25041
25086
  const cred = requireAuth(loadCredentials());
25087
+ const os = resolveChatOs(opts.os, cfg);
25042
25088
  requireSingleTarget(opts, "withdraw");
25043
25089
  let scene;
25044
25090
  let toId;
@@ -25068,7 +25114,7 @@ function register20(program3) {
25068
25114
  subType: 2,
25069
25115
  body: { mids: [opts.mid] },
25070
25116
  fName: cred.name,
25071
- platform: opts.os === "Mac" || opts.os === "OS X" ? "Mac" : "Windows",
25117
+ platform: os === "Mac" || os === "OS X" ? "Mac" : "Windows",
25072
25118
  appKey: cfg.appKey
25073
25119
  });
25074
25120
  if (opts.dryRun) {
@@ -25080,7 +25126,7 @@ function register20(program3) {
25080
25126
  frame: { sid: "talk", cid: "sendMsg", data: { msg } }
25081
25127
  };
25082
25128
  }
25083
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
25129
+ const session = await openChatSession(cred, { os, debug: opts.debug });
25084
25130
  const { socket } = session;
25085
25131
  let res;
25086
25132
  try {
@@ -25123,11 +25169,13 @@ function register21(program3) {
25123
25169
  description: "Send user/delSession to drop the local session record on all of the user's devices.",
25124
25170
  output: Output21,
25125
25171
  configure: (cmd) => {
25126
- cmd.option("--peer <uid>", "session with a peer uid").option("--team <id>", "session with a team id").option("--to-self", "self-chat session (default if no target)").option("--yes-not-self", "required to confirm clearing a non-self session").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
25172
+ cmd.option("--peer <uid>", "session with a peer uid").option("--team <id>", "session with a team id").option("--to-self", "self-chat session (default if no target)").option("--yes-not-self", "required to confirm clearing a non-self session").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
25127
25173
  },
25128
25174
  run: async (cmd) => {
25129
25175
  const opts = cmd.opts();
25176
+ const cfg = requireConfig();
25130
25177
  const cred = requireAuth(loadCredentials());
25178
+ const os = resolveChatOs(opts.os, cfg);
25131
25179
  requireSingleTarget(opts, "clear-session");
25132
25180
  let scene;
25133
25181
  let toId;
@@ -25154,7 +25202,7 @@ function register21(program3) {
25154
25202
  if (opts.dryRun) {
25155
25203
  return { scene, toId, sId, dryRun: true, frame };
25156
25204
  }
25157
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
25205
+ const session = await openChatSession(cred, { os, debug: opts.debug });
25158
25206
  const { socket } = session;
25159
25207
  let res;
25160
25208
  try {
@@ -25442,13 +25490,15 @@ function register25(program3) {
25442
25490
  description: "Block until someone adds an emoticon reaction (subType=300) to --mid. Consumes a TCP slot \u2014 defaults to --os iOS (Mobile slot).",
25443
25491
  output: Output25,
25444
25492
  configure: (cmd) => {
25445
- cmd.requiredOption("--mid <mid>", "message id to wait for a reaction on").option("--from-uid <uid>", "only consider reactions from this UID (case-insensitive match against fId of the type=3 notification)").option("--sticker-id <id>", "only consider reactions whose stickerId matches").option("--timeout <seconds>", "give up after this many seconds (default 300)", parsePosInt("--timeout"), 300).option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
25493
+ cmd.requiredOption("--mid <mid>", "message id to wait for a reaction on").option("--from-uid <uid>", "only consider reactions from this UID (case-insensitive match against fId of the type=3 notification)").option("--sticker-id <id>", "only consider reactions whose stickerId matches").option("--timeout <seconds>", "give up after this many seconds (default 300)", parsePosInt("--timeout"), 300).option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
25446
25494
  },
25447
25495
  run: async (cmd) => {
25448
25496
  const opts = cmd.opts();
25497
+ const cfg = requireConfig();
25449
25498
  const cred = requireAuth(loadCredentials());
25499
+ const os = resolveChatOs(opts.os, cfg);
25450
25500
  const wantedFrom = opts.fromUid?.toUpperCase();
25451
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
25501
+ const session = await openChatSession(cred, { os, debug: opts.debug });
25452
25502
  const { socket } = session;
25453
25503
  try {
25454
25504
  return await new Promise((resolve2, reject) => {
@@ -25588,7 +25638,7 @@ function register26(program3) {
25588
25638
  description: "Tail incoming chat messages, filtered by sender / team / peer / keyword / regex / @-mention. Emit one line per match \u2014 NDJSON envelope under `--json`, human summary otherwise. Consumes a TCP slot.",
25589
25639
  streamSchema: STREAM_SCHEMA,
25590
25640
  configure: (cmd) => {
25591
- cmd.option("--from-uid <uid>", "only emit messages from this UID (repeat for OR; matched against fId, case-insensitive)", collect, []).option("--team <id>", "only emit messages in this team (repeat for OR)", collect, []).option("--peer <uid>", "only emit p2p messages with this peer UID (repeat for OR)", collect, []).option("--contains <text>", "only emit messages whose text contains this substring, case-insensitive (repeat for OR)", collect, []).option("--regex <pattern>", "only emit messages whose text matches this RE2-flavoured regex (repeat for OR)", collect, []).option("--type <n>", "only emit messages of this `type` field (default 1; repeat for OR)", collectInt("--type"), []).option("--subtype <n>", "only emit messages of this `subType` field (repeat for OR; default = any)", collectInt("--subtype"), []).option("--max <n>", "exit after emitting this many matching lines", parsePosInt("--max")).option("--at-me", "only emit group messages that @-mention you (mirrors the desktop SDK isAtMe: scene=team_g, fId\u2260self, atIds includes self uid). Implies excluding self.").option("--include-self", "do not filter out messages whose fId is your own UID").option("--include-reactions", "also emit type=3 sub=300/301 (reaction add/remove) frames").option("--include-offline", "also emit messages flushed via cid=offMsg on connect (default: only post-connect msg)").option("--os <os>", "chat-channel os slot", parseChatOs, DEFAULT_CHAT_OS).option("--debug", "forward wire-level debug events to stderr");
25641
+ cmd.option("--from-uid <uid>", "only emit messages from this UID (repeat for OR; matched against fId, case-insensitive)", collect, []).option("--team <id>", "only emit messages in this team (repeat for OR)", collect, []).option("--peer <uid>", "only emit p2p messages with this peer UID (repeat for OR)", collect, []).option("--contains <text>", "only emit messages whose text contains this substring, case-insensitive (repeat for OR)", collect, []).option("--regex <pattern>", "only emit messages whose text matches this RE2-flavoured regex (repeat for OR)", collect, []).option("--type <n>", "only emit messages of this `type` field (default 1; repeat for OR)", collectInt("--type"), []).option("--subtype <n>", "only emit messages of this `subType` field (repeat for OR; default = any)", collectInt("--subtype"), []).option("--max <n>", "exit after emitting this many matching lines", parsePosInt("--max")).option("--at-me", "only emit group messages that @-mention you (mirrors the desktop SDK isAtMe: scene=team_g, fId\u2260self, atIds includes self uid). Implies excluding self.").option("--include-self", "do not filter out messages whose fId is your own UID").option("--include-reactions", "also emit type=3 sub=300/301 (reaction add/remove) frames").option("--include-offline", "also emit messages flushed via cid=offMsg on connect (default: only post-connect msg)").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--debug", "forward wire-level debug events to stderr");
25592
25642
  },
25593
25643
  run: async (cmd) => {
25594
25644
  const opts = cmd.opts();
@@ -25615,8 +25665,10 @@ function register26(program3) {
25615
25665
  }
25616
25666
  const includeSelf = !!opts.includeSelf;
25617
25667
  const selfUidUpper = cred.uid.toUpperCase();
25618
- const selfAppKey = requireConfig().appKey;
25619
- const session = await openChatSession(cred, { os: opts.os, debug: opts.debug });
25668
+ const cfg = requireConfig();
25669
+ const selfAppKey = cfg.appKey;
25670
+ const os = resolveChatOs(opts.os, cfg);
25671
+ const session = await openChatSession(cred, { os, debug: opts.debug });
25620
25672
  const { socket } = session;
25621
25673
  const json = isJsonMode();
25622
25674
  let matched = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mu-cabin/muc-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Terminal client for MUC IM (com.meicloud.im.dh).",
5
5
  "license": "MIT",
6
6
  "type": "module",