@mu-cabin/muc-cli 0.2.0 → 0.2.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.
- package/dist/muc.mjs +123 -54
- package/package.json +1 -1
package/dist/muc.mjs
CHANGED
|
@@ -21634,11 +21634,59 @@ function read() {
|
|
|
21634
21634
|
var CLI_VERSION = read();
|
|
21635
21635
|
|
|
21636
21636
|
// src/commands/login.ts
|
|
21637
|
-
import { release } from "node:os";
|
|
21637
|
+
import { release as release2 } from "node:os";
|
|
21638
21638
|
import * as readline from "node:readline";
|
|
21639
21639
|
|
|
21640
21640
|
// src/lib/http.ts
|
|
21641
21641
|
var import_needle = __toESM(require_needle(), 1);
|
|
21642
|
+
import { release } from "node:os";
|
|
21643
|
+
|
|
21644
|
+
// src/lib/sign.ts
|
|
21645
|
+
var import_crypto_js = __toESM(require_crypto_js(), 1);
|
|
21646
|
+
import { createHash } from "node:crypto";
|
|
21647
|
+
function getMucSign(qs, secret) {
|
|
21648
|
+
const keys = Object.keys(qs).sort();
|
|
21649
|
+
const body = keys.map((k) => `${k}${qs[k] ?? ""}`).join("");
|
|
21650
|
+
return createHash("md5").update(`${secret}${body}${secret}`).digest("hex");
|
|
21651
|
+
}
|
|
21652
|
+
function signRest(qs, secret) {
|
|
21653
|
+
return getMucSign(qs, secret);
|
|
21654
|
+
}
|
|
21655
|
+
function signSecure(qs, secret) {
|
|
21656
|
+
return getMucSign(qs, secret);
|
|
21657
|
+
}
|
|
21658
|
+
function encryptPassword(plain, desKey) {
|
|
21659
|
+
const key = import_crypto_js.default.enc.Utf8.parse(desKey);
|
|
21660
|
+
const iv = import_crypto_js.default.enc.Hex.parse("0102030405060708");
|
|
21661
|
+
const enc = import_crypto_js.default.DES.encrypt(plain, key, {
|
|
21662
|
+
iv,
|
|
21663
|
+
mode: import_crypto_js.default.mode.CBC,
|
|
21664
|
+
padding: import_crypto_js.default.pad.Pkcs7
|
|
21665
|
+
});
|
|
21666
|
+
return enc.toString();
|
|
21667
|
+
}
|
|
21668
|
+
|
|
21669
|
+
// src/lib/device.ts
|
|
21670
|
+
import { homedir as homedir3, hostname } from "node:os";
|
|
21671
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
21672
|
+
import { join as join3 } from "node:path";
|
|
21673
|
+
import { randomBytes, createHash as createHash2 } from "node:crypto";
|
|
21674
|
+
var DIR3 = join3(homedir3(), ".muc");
|
|
21675
|
+
var FILE3 = join3(DIR3, "device-id");
|
|
21676
|
+
function getDeviceId() {
|
|
21677
|
+
if (!existsSync3(DIR3)) mkdirSync3(DIR3, { recursive: true, mode: 448 });
|
|
21678
|
+
if (existsSync3(FILE3)) {
|
|
21679
|
+
const v = readFileSync4(FILE3, "utf8").trim();
|
|
21680
|
+
if (v) return v;
|
|
21681
|
+
}
|
|
21682
|
+
const seed = randomBytes(32).toString("hex") + ":" + Date.now();
|
|
21683
|
+
const id = createHash2("sha256").update(seed).digest("hex");
|
|
21684
|
+
writeFileSync3(FILE3, id, { mode: 384 });
|
|
21685
|
+
return id;
|
|
21686
|
+
}
|
|
21687
|
+
function getDeviceName() {
|
|
21688
|
+
return `${hostname()} (muc-cli)`;
|
|
21689
|
+
}
|
|
21642
21690
|
|
|
21643
21691
|
// src/lib/constants.ts
|
|
21644
21692
|
var APP_NAME = "MUC";
|
|
@@ -21690,7 +21738,75 @@ function redact(url) {
|
|
|
21690
21738
|
return url;
|
|
21691
21739
|
}
|
|
21692
21740
|
}
|
|
21741
|
+
var inflightRefresh = null;
|
|
21742
|
+
async function refreshAccessTokenWithTokenPwd() {
|
|
21743
|
+
if (inflightRefresh) return inflightRefresh;
|
|
21744
|
+
inflightRefresh = (async () => {
|
|
21745
|
+
const cred = loadCredentials();
|
|
21746
|
+
if (!cred?.tokenPwd) {
|
|
21747
|
+
throw new CliError(
|
|
21748
|
+
"AUTH_REQUIRED",
|
|
21749
|
+
"no saved tokenPwd; cannot refresh accessToken",
|
|
21750
|
+
"run `muc login --account <UID>` interactively in your own terminal"
|
|
21751
|
+
);
|
|
21752
|
+
}
|
|
21753
|
+
const cfg = requireConfig();
|
|
21754
|
+
const platform2 = DEFAULT_REST_PLATFORM;
|
|
21755
|
+
const appVersion = platform2 === PLATFORM_IOS ? APP_VERSION_IOS : APP_VERSION;
|
|
21756
|
+
const form = {
|
|
21757
|
+
account: cred.uid,
|
|
21758
|
+
password: encryptPassword(cred.tokenPwd, pwdDesKeyOf(cfg)),
|
|
21759
|
+
appName: APP_NAME,
|
|
21760
|
+
appKey: cfg.appKey,
|
|
21761
|
+
appVersion,
|
|
21762
|
+
platform: platform2,
|
|
21763
|
+
osVersion: release(),
|
|
21764
|
+
deviceId: cred.deviceId || getDeviceId(),
|
|
21765
|
+
deviceName: cred.deviceName || getDeviceName(),
|
|
21766
|
+
passwordType: 1,
|
|
21767
|
+
createTokenPwd: false,
|
|
21768
|
+
encrypt: true
|
|
21769
|
+
};
|
|
21770
|
+
form.sign = signRest(form, cfg.signSecretRest);
|
|
21771
|
+
const res = await requestRaw({
|
|
21772
|
+
method: "POST",
|
|
21773
|
+
url: `${apiUrls(cfg).MUC}/emp/login`,
|
|
21774
|
+
form
|
|
21775
|
+
});
|
|
21776
|
+
if (res.code !== 0 || !res.data?.accessToken) {
|
|
21777
|
+
throw new CliError(
|
|
21778
|
+
"AUTH_REQUIRED",
|
|
21779
|
+
`tokenPwd refresh rejected: code=${res.code} msg=${res.msg ?? ""}`.trim(),
|
|
21780
|
+
"run `muc login --account <UID>` interactively to mint fresh credentials"
|
|
21781
|
+
);
|
|
21782
|
+
}
|
|
21783
|
+
const next = {
|
|
21784
|
+
...cred,
|
|
21785
|
+
accessToken: res.data.accessToken,
|
|
21786
|
+
accessTokenExpireAt: res.data.accessTokenExpireTime ?? res.data.accessTokenExpireAt ?? cred.accessTokenExpireAt,
|
|
21787
|
+
savedAt: Date.now()
|
|
21788
|
+
};
|
|
21789
|
+
saveCredentials(next);
|
|
21790
|
+
return next;
|
|
21791
|
+
})();
|
|
21792
|
+
try {
|
|
21793
|
+
return await inflightRefresh;
|
|
21794
|
+
} finally {
|
|
21795
|
+
inflightRefresh = null;
|
|
21796
|
+
}
|
|
21797
|
+
}
|
|
21693
21798
|
async function request(opts) {
|
|
21799
|
+
try {
|
|
21800
|
+
return await requestRaw(opts);
|
|
21801
|
+
} catch (e) {
|
|
21802
|
+
if (e instanceof CliError && e.code === "AUTH_EXPIRED" && opts.auth === true && !opts._isRefreshRetry && loadCredentials()?.tokenPwd) {
|
|
21803
|
+
await refreshAccessTokenWithTokenPwd();
|
|
21804
|
+
return await requestRaw({ ...opts, _isRefreshRetry: true });
|
|
21805
|
+
}
|
|
21806
|
+
throw e;
|
|
21807
|
+
}
|
|
21808
|
+
}
|
|
21809
|
+
async function requestRaw(opts) {
|
|
21694
21810
|
const method = opts.method ?? "GET";
|
|
21695
21811
|
const url = appendQs(opts.url, opts.qs);
|
|
21696
21812
|
const headers = {
|
|
@@ -21761,53 +21877,6 @@ function appendQs(url, qs) {
|
|
|
21761
21877
|
return u.toString();
|
|
21762
21878
|
}
|
|
21763
21879
|
|
|
21764
|
-
// src/lib/sign.ts
|
|
21765
|
-
var import_crypto_js = __toESM(require_crypto_js(), 1);
|
|
21766
|
-
import { createHash } from "node:crypto";
|
|
21767
|
-
function getMucSign(qs, secret) {
|
|
21768
|
-
const keys = Object.keys(qs).sort();
|
|
21769
|
-
const body = keys.map((k) => `${k}${qs[k] ?? ""}`).join("");
|
|
21770
|
-
return createHash("md5").update(`${secret}${body}${secret}`).digest("hex");
|
|
21771
|
-
}
|
|
21772
|
-
function signRest(qs, secret) {
|
|
21773
|
-
return getMucSign(qs, secret);
|
|
21774
|
-
}
|
|
21775
|
-
function signSecure(qs, secret) {
|
|
21776
|
-
return getMucSign(qs, secret);
|
|
21777
|
-
}
|
|
21778
|
-
function encryptPassword(plain, desKey) {
|
|
21779
|
-
const key = import_crypto_js.default.enc.Utf8.parse(desKey);
|
|
21780
|
-
const iv = import_crypto_js.default.enc.Hex.parse("0102030405060708");
|
|
21781
|
-
const enc = import_crypto_js.default.DES.encrypt(plain, key, {
|
|
21782
|
-
iv,
|
|
21783
|
-
mode: import_crypto_js.default.mode.CBC,
|
|
21784
|
-
padding: import_crypto_js.default.pad.Pkcs7
|
|
21785
|
-
});
|
|
21786
|
-
return enc.toString();
|
|
21787
|
-
}
|
|
21788
|
-
|
|
21789
|
-
// src/lib/device.ts
|
|
21790
|
-
import { homedir as homedir3, hostname } from "node:os";
|
|
21791
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
21792
|
-
import { join as join3 } from "node:path";
|
|
21793
|
-
import { randomBytes, createHash as createHash2 } from "node:crypto";
|
|
21794
|
-
var DIR3 = join3(homedir3(), ".muc");
|
|
21795
|
-
var FILE3 = join3(DIR3, "device-id");
|
|
21796
|
-
function getDeviceId() {
|
|
21797
|
-
if (!existsSync3(DIR3)) mkdirSync3(DIR3, { recursive: true, mode: 448 });
|
|
21798
|
-
if (existsSync3(FILE3)) {
|
|
21799
|
-
const v = readFileSync4(FILE3, "utf8").trim();
|
|
21800
|
-
if (v) return v;
|
|
21801
|
-
}
|
|
21802
|
-
const seed = randomBytes(32).toString("hex") + ":" + Date.now();
|
|
21803
|
-
const id = createHash2("sha256").update(seed).digest("hex");
|
|
21804
|
-
writeFileSync3(FILE3, id, { mode: 384 });
|
|
21805
|
-
return id;
|
|
21806
|
-
}
|
|
21807
|
-
function getDeviceName() {
|
|
21808
|
-
return `${hostname()} (muc-cli)`;
|
|
21809
|
-
}
|
|
21810
|
-
|
|
21811
21880
|
// src/commands/login.ts
|
|
21812
21881
|
var PLATFORM_NAMES = {
|
|
21813
21882
|
ios: PLATFORM_IOS,
|
|
@@ -21866,7 +21935,7 @@ var Output = external_exports.object({
|
|
|
21866
21935
|
function register(program3) {
|
|
21867
21936
|
define2(program3, {
|
|
21868
21937
|
name: "login",
|
|
21869
|
-
description: "Authenticate against MUC and persist accessToken + tokenPwd.
|
|
21938
|
+
description: "Authenticate against MUC and persist accessToken + tokenPwd. Run interactively in your own terminal: `muc login --account <UID>` prompts for the password with echo off. `--token-pwd` reuses the saved refresh token and needs no password (this is the only form an agent should run silently). Avoid `--password` / `--password-stdin` / `$MUC_PASSWORD` \u2014 they leak the password into shell history, process lists, and agent chat scrollback.",
|
|
21870
21939
|
output: Output,
|
|
21871
21940
|
configure: (cmd) => {
|
|
21872
21941
|
cmd.option("-a, --account <uid>", "MUC account uid (uppercase, e.g. WANGLULU8)").option("-p, --password <pwd>", "plaintext password (avoid; ends up in shell history)").option("--password-stdin", "read password from stdin (no trailing newline)").option("--token-pwd", "reuse the saved tokenPwd (skips password)").option("--platform <p>", "ios|android|win|mac or numeric code", (v) => parsePlatform(v)).option("--app-version <v>", "override appVersion sent in the login form");
|
|
@@ -21913,7 +21982,7 @@ function register(program3) {
|
|
|
21913
21982
|
throw new CliError(
|
|
21914
21983
|
"AUTH_REQUIRED",
|
|
21915
21984
|
"password required",
|
|
21916
|
-
"
|
|
21985
|
+
"open your own terminal and run `muc login --account <UID>` \u2014 it will prompt for the password with echo off. Do not pipe / env-var / inline the password from an agent shell; the prompt cannot be proxied through Claude Code's `!` shell-exec."
|
|
21917
21986
|
);
|
|
21918
21987
|
}
|
|
21919
21988
|
const cfg = requireConfig();
|
|
@@ -21929,7 +21998,7 @@ function register(program3) {
|
|
|
21929
21998
|
appKey: cfg.appKey,
|
|
21930
21999
|
appVersion,
|
|
21931
22000
|
platform: platform2,
|
|
21932
|
-
osVersion:
|
|
22001
|
+
osVersion: release2(),
|
|
21933
22002
|
deviceId,
|
|
21934
22003
|
deviceName,
|
|
21935
22004
|
passwordType: isAutoLogin ? 1 : 0,
|
|
@@ -22614,7 +22683,7 @@ function register8(program3) {
|
|
|
22614
22683
|
}
|
|
22615
22684
|
|
|
22616
22685
|
// src/lib/chat.ts
|
|
22617
|
-
import { release as
|
|
22686
|
+
import { release as release3 } from "node:os";
|
|
22618
22687
|
|
|
22619
22688
|
// src/lib/wire.ts
|
|
22620
22689
|
import { EventEmitter } from "node:events";
|
|
@@ -22856,7 +22925,7 @@ function buildLoginPayload(cred, chatOs, cfg) {
|
|
|
22856
22925
|
account: cred.uid,
|
|
22857
22926
|
accessToken: cred.accessToken,
|
|
22858
22927
|
os: chatOs,
|
|
22859
|
-
osVersion:
|
|
22928
|
+
osVersion: release3(),
|
|
22860
22929
|
sdkVersion: 21,
|
|
22861
22930
|
deviceId: cred.deviceId,
|
|
22862
22931
|
deviceName: cred.deviceName,
|
|
@@ -22951,7 +23020,7 @@ async function refreshAccessTokenViaTokenPwd(cred, cfg) {
|
|
|
22951
23020
|
appKey: cfg.appKey,
|
|
22952
23021
|
appVersion: APP_VERSION,
|
|
22953
23022
|
platform: PLATFORM_MAC,
|
|
22954
|
-
osVersion:
|
|
23023
|
+
osVersion: release3(),
|
|
22955
23024
|
deviceId: cred.deviceId,
|
|
22956
23025
|
deviceName: cred.deviceName,
|
|
22957
23026
|
passwordType: 1,
|