@mu-cabin/muc-cli 0.6.1 → 0.6.3
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/README.md +34 -0
- package/REFERENCE.md +5 -1
- package/dist/muc.mjs +43 -32
- package/package.json +3 -3
- package/skills/muc/SKILL.md +9 -0
- package/skills/muc/references/mutations.md +6 -2
package/README.md
CHANGED
|
@@ -4,6 +4,38 @@
|
|
|
4
4
|
|
|
5
5
|
0.6.0 迁入新版命令契约。要求 **Node.js ≥ 22.16**,使用内置 SQLite,无需独立 Supervisor、Launcher、Keychain、TUI 或客户端。运行时依赖已打包进单个 `dist/muc.mjs`。
|
|
6
6
|
|
|
7
|
+
## 0.6.2 附件下载修复
|
|
8
|
+
|
|
9
|
+
图片和文件可下载到操作系统允许写入的目录,包括 macOS `/tmp`、共享目录和目录符号链接。路径无效或目标文件已存在时返回明确错误与修复提示,不再误报后台不支持操作。下载仍不覆盖已有文件。
|
|
10
|
+
|
|
11
|
+
## 0.6.3 Windows 兼容修复
|
|
12
|
+
|
|
13
|
+
修复 Windows 已有后台锁文件被 Unix 权限检查误判,导致 `login` 等命令无法启动后台的问题;单实例互斥继续由 Windows 命名管道保证。JSON stdin 支持 UTF-8 BOM 和 CRLF。源码清理和离线打包不再需要 Bash 或 `rm`。
|
|
14
|
+
|
|
15
|
+
### Windows 安装与运行
|
|
16
|
+
|
|
17
|
+
安装 Node.js 22.16 或更新版本,在 PowerShell 中运行(`npm.cmd` / `muc.cmd` 可避开 `.ps1` 执行策略限制):
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
node --version
|
|
21
|
+
npm.cmd install -g @mu-cabin/muc-cli@0.6.3
|
|
22
|
+
muc.cmd --version
|
|
23
|
+
muc.cmd login --account YOUR_ACCOUNT --profile-id default --remember
|
|
24
|
+
muc.cmd whoami --profile-id default
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
默认数据目录为 `%USERPROFILE%\.muc`。首次使用仍需管理员通过受保护 stdin 完成 `init`;升级已有配置无需重新初始化。更新前运行 `muc.cmd daemon stop`,安装后下次登录或查询会启动新版后台;使用自定义配置根时,停止和启动都指定同一 `--config-root`。
|
|
28
|
+
|
|
29
|
+
PowerShell 不支持 Bash 的 `<<'JSON'`。JSON 可用单引号字符串通过管道传入;Windows PowerShell 5.1 先设置 UTF-8,防止中文被转换为问号:
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
33
|
+
'{"limit":20}' | muc.cmd exec chats.list --profile-id default
|
|
34
|
+
'{"includeSelf":false}' | muc.cmd --output jsonl exec events.watch --profile-id default
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
CMD 可使用 `echo {"limit":20} | muc.cmd exec chats.list --profile-id default`。复杂 JSON 使用 UTF-8 文件重定向:`muc.cmd exec chats.list --profile-id default < request.json`。JSON 中的 Windows 路径使用 `C:/Users/NAME/Downloads/file.png` 或双反斜杠,含空格的命令行路径用双引号包围。密码仍只在用户终端的隐藏提示或受保护 stdin 输入。
|
|
38
|
+
|
|
7
39
|
## 配置和登录
|
|
8
40
|
|
|
9
41
|
默认数据目录为 `~/.muc`;`MUC_CONFIG_DIR` 或全局 `--config-root` 可指定隔离目录。首次部署用 `muc init` 从受保护 stdin 接收管理员提供的租户和端点配置。
|
|
@@ -57,3 +89,5 @@ pnpm --filter @mu-cabin/muc-cli build
|
|
|
57
89
|
```
|
|
58
90
|
|
|
59
91
|
仓库内的 `AGENTS.md` 与 `docs/development.md` 记录开发约定和验收证据;它们不属于安装包。Skill 源码随包发布,全局安装副本需另行安装。
|
|
92
|
+
|
|
93
|
+
`pnpm --filter @mu-cabin/muc-cli clean` 和 `pack:offline` 支持 Windows、macOS、Linux。MUC 的运行时依赖已内嵌,`pack:offline` 直接生成普通 npm tarball `mu-cabin-muc-cli-<version>.tgz`,目标机器可用 `npm install -g --offline <tarball>` 安装,无需另行捆绑依赖。
|
package/REFERENCE.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# MUC 命令契约
|
|
2
2
|
|
|
3
|
-
本文件描述 0.6.
|
|
3
|
+
本文件描述 0.6.3 的命令契约。旧的 `send/history/daemon-*` 命令已移除。
|
|
4
4
|
|
|
5
5
|
## 命令结构
|
|
6
6
|
|
|
7
7
|
全局参数放在命令之前:`muc [--config-root <绝对路径>] [--output json|jsonl|text] <命令>`。默认 JSON。配置根也可由 `MUC_CONFIG_DIR` 提供。
|
|
8
8
|
|
|
9
|
+
Windows 默认配置根为 `%USERPROFILE%\.muc`。PowerShell 若阻止 npm 生成的 `muc.ps1`,使用 `muc.cmd`;无须修改执行策略。业务 JSON 输入使用 UTF-8,允许开头的 UTF-8 BOM 与 CRLF。PowerShell 5.1 管道应先设置 `$OutputEncoding = [System.Text.UTF8Encoding]::new($false)`;不要直接复制 Bash 的 `<<'JSON'` 语法。示例见 README 的 Windows 章节。
|
|
10
|
+
|
|
9
11
|
| 命令 | 用途 |
|
|
10
12
|
| --- | --- |
|
|
11
13
|
| `init` | 从受保护 stdin 配置租户与初始 profile |
|
|
@@ -46,3 +48,5 @@
|
|
|
46
48
|
认证错误退出码为 3,禁止访问为 8,配置缺失为 6、配置无效为 7,输入错误为 2;提交结果不明为 75,并带 `automaticReplay:false`。错误的 `hint` 提供可执行的恢复路径。
|
|
47
49
|
|
|
48
50
|
普通文件发送默认不带尾注(包括带说明的文件消息);明确 `--tail` 才添加。文件上传须完成服务端扫描并取得未来有效期,才会进入消息发送;扫描失败不会降级发送过期文件。
|
|
51
|
+
|
|
52
|
+
附件下载按操作系统权限处理输出目录,支持 `/tmp`、共享目录和目录符号链接;仍不覆盖已有文件。`OUTPUT_PATH_INVALID` 表示路径无效或不可用,`OUTPUT_EXISTS` 表示目标文件已存在。省略 `outputPath` 使用账号私有下载目录。
|
package/dist/muc.mjs
CHANGED
|
@@ -11279,7 +11279,7 @@ function toErrorEnvelope(error) {
|
|
|
11279
11279
|
ok: false,
|
|
11280
11280
|
error: {
|
|
11281
11281
|
code: publicErrorCode(error),
|
|
11282
|
-
message: error.message,
|
|
11282
|
+
message: outputPathErrorMessage(publicErrorCode(error)) ?? error.message,
|
|
11283
11283
|
...errorHint(publicErrorCode(error)) ? { hint: errorHint(publicErrorCode(error)) } : {},
|
|
11284
11284
|
...details === void 0 ? {} : { details },
|
|
11285
11285
|
...error instanceof CommitOutcomeUnknownError ? { automaticReplay: false } : {}
|
|
@@ -11304,16 +11304,23 @@ function safeErrorDetails(value) {
|
|
|
11304
11304
|
}
|
|
11305
11305
|
return Object.keys(safe).length === 0 ? void 0 : Object.freeze(safe);
|
|
11306
11306
|
}
|
|
11307
|
-
var REMOTE_CODES = /* @__PURE__ */ new Set(["AUTH_REQUIRED", "AUTH_EXPIRED", "AUTH_FAILED", "FORBIDDEN", "CONFIG_MISSING", "CONFIG_INVALID", "PROFILE_NOT_FOUND", "PROFILE_EXISTS", "PROFILE_CHANGED", "PROFILE_ACCOUNT_MISMATCH", "RUNTIME_CONFLICT", "RUNTIME_NOT_READY", "INVALID_INPUT", "INVALID_PARAMS", "INVALID_CURSOR", "NOT_FOUND", "UNAVAILABLE", "UNAUTHORIZED", "DEADLINE_EXCEEDED", "IDEMPOTENCY_CONFLICT", "METHOD_NOT_FOUND", "INTERNAL_ERROR"]);
|
|
11307
|
+
var REMOTE_CODES = /* @__PURE__ */ new Set(["OUTPUT_PATH_INVALID", "OUTPUT_EXISTS", "AUTH_REQUIRED", "AUTH_EXPIRED", "AUTH_FAILED", "FORBIDDEN", "CONFIG_MISSING", "CONFIG_INVALID", "PROFILE_NOT_FOUND", "PROFILE_EXISTS", "PROFILE_CHANGED", "PROFILE_ACCOUNT_MISMATCH", "RUNTIME_CONFLICT", "RUNTIME_NOT_READY", "INVALID_INPUT", "INVALID_PARAMS", "INVALID_CURSOR", "NOT_FOUND", "UNAVAILABLE", "UNAUTHORIZED", "DEADLINE_EXCEEDED", "IDEMPOTENCY_CONFLICT", "METHOD_NOT_FOUND", "INTERNAL_ERROR"]);
|
|
11308
11308
|
function publicErrorCode(error) {
|
|
11309
11309
|
return error instanceof RemoteRpcError && REMOTE_CODES.has(String(error.remoteCode)) ? String(error.remoteCode) : error.code;
|
|
11310
11310
|
}
|
|
11311
11311
|
function errorHint(code) {
|
|
11312
|
+
if (code === "OUTPUT_PATH_INVALID") return "Choose an existing writable directory, or omit outputPath to use private download storage.";
|
|
11313
|
+
if (code === "OUTPUT_EXISTS") return "Choose a new output filename; downloads never overwrite existing files.";
|
|
11312
11314
|
if (code === "CONFIG_MISSING") return "Run muc init with administrator-provided configuration on protected stdin.";
|
|
11313
11315
|
if (["AUTH_REQUIRED", "AUTH_EXPIRED", "AUTH_FAILED"].includes(code)) return "Run muc login for this profile in your own terminal; do not paste credentials into chat.";
|
|
11314
11316
|
if (code === "CONFIG_INVALID") return "Inspect the configuration privately; do not copy credentials into diagnostics.";
|
|
11315
11317
|
return void 0;
|
|
11316
11318
|
}
|
|
11319
|
+
function outputPathErrorMessage(code) {
|
|
11320
|
+
if (code === "OUTPUT_PATH_INVALID") return "The attachment output path is invalid or unavailable.";
|
|
11321
|
+
if (code === "OUTPUT_EXISTS") return "The attachment output file already exists.";
|
|
11322
|
+
return void 0;
|
|
11323
|
+
}
|
|
11317
11324
|
|
|
11318
11325
|
// src/cli/io.ts
|
|
11319
11326
|
var MAX_STDIN_BYTES = 1024 * 1024;
|
|
@@ -11439,7 +11446,7 @@ async function readJsonObject(io) {
|
|
|
11439
11446
|
}
|
|
11440
11447
|
let parsed;
|
|
11441
11448
|
try {
|
|
11442
|
-
parsed = JSON.parse(text2);
|
|
11449
|
+
parsed = JSON.parse(text2.replace(/^\uFEFF/u, ""));
|
|
11443
11450
|
} catch {
|
|
11444
11451
|
throw new CliInputError("Operation input must be one valid JSON object.");
|
|
11445
11452
|
}
|
|
@@ -11691,7 +11698,10 @@ async function ensureSafeLockFile(path) {
|
|
|
11691
11698
|
throw error;
|
|
11692
11699
|
}
|
|
11693
11700
|
const entry = await lstat2(path);
|
|
11694
|
-
if (!entry.isFile() || entry.isSymbolicLink() ||
|
|
11701
|
+
if (!entry.isFile() || entry.isSymbolicLink() || // Windows exposes synthetic mode bits; chmod cannot clear group/other
|
|
11702
|
+
// access there. Keep the file/type checks and let the named pipe own
|
|
11703
|
+
// mutual exclusion, as with the other private Windows state files.
|
|
11704
|
+
process.platform !== "win32" && (entry.mode & 63) !== 0) {
|
|
11695
11705
|
throw new SingleInstanceError();
|
|
11696
11706
|
}
|
|
11697
11707
|
if (typeof process.getuid === "function" && entry.uid !== process.getuid()) {
|
|
@@ -12295,8 +12305,8 @@ async function assertLegacyStopped(root) {
|
|
|
12295
12305
|
for (const file of ["runtime/daemon.pid", "runtime/assistant/assistant.pid"]) {
|
|
12296
12306
|
const path = join6(root, file);
|
|
12297
12307
|
try {
|
|
12298
|
-
const
|
|
12299
|
-
if (!
|
|
12308
|
+
const stat3 = await lstat4(path);
|
|
12309
|
+
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > 128) throw new Error("unsafe");
|
|
12300
12310
|
const pid = Number((await readFile3(path, "utf8")).trim());
|
|
12301
12311
|
if (!Number.isSafeInteger(pid) || pid < 1) throw new Error("invalid");
|
|
12302
12312
|
try {
|
|
@@ -12316,7 +12326,7 @@ async function assertLegacyStopped(root) {
|
|
|
12316
12326
|
// package.json
|
|
12317
12327
|
var package_default = {
|
|
12318
12328
|
name: "@mu-cabin/muc-cli",
|
|
12319
|
-
version: "0.6.
|
|
12329
|
+
version: "0.6.3",
|
|
12320
12330
|
description: "Standalone agent-first MUC CLI with an embedded background daemon",
|
|
12321
12331
|
license: "MIT",
|
|
12322
12332
|
type: "module",
|
|
@@ -12343,11 +12353,11 @@ var package_default = {
|
|
|
12343
12353
|
dev: "tsx src/index.ts",
|
|
12344
12354
|
test: "pnpm build && vitest run",
|
|
12345
12355
|
typecheck: "tsc --noEmit",
|
|
12346
|
-
clean:
|
|
12356
|
+
clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
|
|
12347
12357
|
build: "node scripts/build.mjs",
|
|
12348
12358
|
prepack: "pnpm typecheck && pnpm test",
|
|
12349
12359
|
verify: "npm pack --dry-run",
|
|
12350
|
-
"pack:offline": "
|
|
12360
|
+
"pack:offline": "npm pack"
|
|
12351
12361
|
},
|
|
12352
12362
|
devDependencies: {
|
|
12353
12363
|
"@types/node": "^22.19.15",
|
|
@@ -26783,15 +26793,15 @@ async function readImageDimensions(path) {
|
|
|
26783
26793
|
async function readVoiceDurationMs(path) {
|
|
26784
26794
|
const handle = await open6(path, fsConstants2.O_RDONLY | (fsConstants2.O_NOFOLLOW ?? 0));
|
|
26785
26795
|
try {
|
|
26786
|
-
const
|
|
26796
|
+
const stat3 = await handle.stat();
|
|
26787
26797
|
const header = Buffer.alloc(64);
|
|
26788
26798
|
const { bytesRead } = await handle.read(header, 0, header.byteLength, 0);
|
|
26789
26799
|
const bytes = header.subarray(0, bytesRead);
|
|
26790
26800
|
if (bytes.subarray(0, 6).toString("ascii") === "#!AMR\n") {
|
|
26791
|
-
return readAmrDuration(handle,
|
|
26801
|
+
return readAmrDuration(handle, stat3.size, 6, AMR_NB_FRAME_BYTES);
|
|
26792
26802
|
}
|
|
26793
26803
|
if (bytes.subarray(0, 9).toString("ascii") === "#!AMR-WB\n") {
|
|
26794
|
-
return readAmrDuration(handle,
|
|
26804
|
+
return readAmrDuration(handle, stat3.size, 9, AMR_WB_FRAME_BYTES);
|
|
26795
26805
|
}
|
|
26796
26806
|
return wavDuration(bytes);
|
|
26797
26807
|
} finally {
|
|
@@ -26979,7 +26989,7 @@ import { createHash as createHash16, randomUUID as randomUUID15 } from "node:cry
|
|
|
26979
26989
|
import { constants as fsConstants3 } from "node:fs";
|
|
26980
26990
|
import { open as open8, unlink as unlink7 } from "node:fs/promises";
|
|
26981
26991
|
import { createConnection as createConnection4 } from "node:net";
|
|
26982
|
-
import { basename as basename2, isAbsolute as
|
|
26992
|
+
import { basename as basename2, isAbsolute as isAbsolute6, join as join13, resolve as resolve10 } from "node:path";
|
|
26983
26993
|
|
|
26984
26994
|
// src/core/native/application/file-downloader.ts
|
|
26985
26995
|
import { createHash as createHash15, randomUUID as randomUUID14 } from "node:crypto";
|
|
@@ -26988,9 +26998,10 @@ import {
|
|
|
26988
26998
|
link,
|
|
26989
26999
|
lstat as lstat9,
|
|
26990
27000
|
open as open7,
|
|
27001
|
+
stat as stat2,
|
|
26991
27002
|
unlink as unlink6
|
|
26992
27003
|
} from "node:fs/promises";
|
|
26993
|
-
import { dirname as dirname5,
|
|
27004
|
+
import { dirname as dirname5, join as join12, resolve as resolve9 } from "node:path";
|
|
26994
27005
|
var DEFAULT_DOWNLOAD_CHUNK_BYTES = 512 * 1024;
|
|
26995
27006
|
var LARGE_DOWNLOAD_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
26996
27007
|
var LARGE_DOWNLOAD_THRESHOLD_BYTES = 50 * 1024 * 1024;
|
|
@@ -27062,23 +27073,19 @@ var NativeFileDownloader = class {
|
|
|
27062
27073
|
return join12(directory, safeName);
|
|
27063
27074
|
}
|
|
27064
27075
|
if (outputPath.includes("\0")) {
|
|
27065
|
-
throw new NativeOperationGatewayError("
|
|
27076
|
+
throw new NativeOperationGatewayError("OUTPUT_PATH_INVALID", "Output path is invalid.");
|
|
27066
27077
|
}
|
|
27067
27078
|
const requested = resolve9(outputPath);
|
|
27068
|
-
const entry = await
|
|
27079
|
+
const entry = await stat2(requested).catch((error) => {
|
|
27069
27080
|
if (error.code === "ENOENT") return void 0;
|
|
27070
|
-
throw
|
|
27081
|
+
throw new NativeOperationGatewayError("OUTPUT_PATH_INVALID", "Output path is unavailable.");
|
|
27071
27082
|
});
|
|
27072
|
-
if (entry?.isSymbolicLink()) {
|
|
27073
|
-
throw new NativeOperationGatewayError("UNSUPPORTED", "Output path may not be a symlink.");
|
|
27074
|
-
}
|
|
27075
27083
|
const destination = entry?.isDirectory() ? join12(requested, safeName) : requested;
|
|
27076
|
-
const parent = await
|
|
27077
|
-
|
|
27078
|
-
|
|
27079
|
-
|
|
27080
|
-
|
|
27081
|
-
throw new NativeOperationGatewayError("UNSUPPORTED", "Output path is invalid.");
|
|
27084
|
+
const parent = await stat2(dirname5(destination)).catch(() => {
|
|
27085
|
+
throw new NativeOperationGatewayError("OUTPUT_PATH_INVALID", "Output directory is unavailable.");
|
|
27086
|
+
});
|
|
27087
|
+
if (!parent.isDirectory()) {
|
|
27088
|
+
throw new NativeOperationGatewayError("OUTPUT_PATH_INVALID", "Output parent is not a directory.");
|
|
27082
27089
|
}
|
|
27083
27090
|
return destination;
|
|
27084
27091
|
}
|
|
@@ -27182,7 +27189,7 @@ var NativeFileDownloader = class {
|
|
|
27182
27189
|
await link(temporary, destination);
|
|
27183
27190
|
} catch (error) {
|
|
27184
27191
|
if (error.code === "EEXIST") {
|
|
27185
|
-
throw new NativeOperationGatewayError("
|
|
27192
|
+
throw new NativeOperationGatewayError("OUTPUT_EXISTS", "Output file already exists.");
|
|
27186
27193
|
}
|
|
27187
27194
|
throw error;
|
|
27188
27195
|
}
|
|
@@ -27283,7 +27290,7 @@ function requireFileSuccess(value, phase) {
|
|
|
27283
27290
|
async function assertDestinationAbsent(path) {
|
|
27284
27291
|
try {
|
|
27285
27292
|
await lstat9(path);
|
|
27286
|
-
throw new NativeOperationGatewayError("
|
|
27293
|
+
throw new NativeOperationGatewayError("OUTPUT_EXISTS", "Output file already exists.");
|
|
27287
27294
|
} catch (error) {
|
|
27288
27295
|
if (error.code !== "ENOENT") throw error;
|
|
27289
27296
|
}
|
|
@@ -27464,7 +27471,7 @@ var MucNativeOperationGateway = class {
|
|
|
27464
27471
|
});
|
|
27465
27472
|
}
|
|
27466
27473
|
async downloadCustomEmoji(profileId2, emojiId, outputPath, options = {}) {
|
|
27467
|
-
if (!
|
|
27474
|
+
if (!isAbsolute6(outputPath) || outputPath.includes("\0")) {
|
|
27468
27475
|
throw new NativeOperationGatewayError("REMOTE_REJECTED", "Custom emoji output path is invalid.");
|
|
27469
27476
|
}
|
|
27470
27477
|
const request = async () => {
|
|
@@ -28752,7 +28759,7 @@ function customEmojiFromRecord(value) {
|
|
|
28752
28759
|
};
|
|
28753
28760
|
}
|
|
28754
28761
|
async function readLocalImageBytes(input, maxBytes, label, signal) {
|
|
28755
|
-
if (!
|
|
28762
|
+
if (!isAbsolute6(input.sourcePath) || input.sourcePath.includes("\0")) {
|
|
28756
28763
|
throw new NativeOperationGatewayError("REMOTE_REJECTED", `${label} source path is invalid.`);
|
|
28757
28764
|
}
|
|
28758
28765
|
signal?.throwIfAborted();
|
|
@@ -29217,6 +29224,8 @@ var SAFE_MESSAGES = Object.freeze({
|
|
|
29217
29224
|
INVALID_PARAMS: "Request parameters are invalid.",
|
|
29218
29225
|
METHOD_NOT_FOUND: "RPC method is not supported.",
|
|
29219
29226
|
NOT_FOUND: "The requested resource was not found.",
|
|
29227
|
+
OUTPUT_PATH_INVALID: "The attachment output path is invalid or unavailable.",
|
|
29228
|
+
OUTPUT_EXISTS: "The attachment output file already exists.",
|
|
29220
29229
|
OPERATION_UNSUPPORTED: "The requested operation or version is unsupported.",
|
|
29221
29230
|
PROFILE_NOT_FOUND: "The requested profile was not found.",
|
|
29222
29231
|
PROFILE_CHANGED: "The profile changed while the request was in flight.",
|
|
@@ -29881,6 +29890,8 @@ function mapNativeError(error) {
|
|
|
29881
29890
|
RUNTIME_NOT_READY: "RUNTIME_NOT_READY",
|
|
29882
29891
|
NOT_FOUND: "NOT_FOUND",
|
|
29883
29892
|
UNSUPPORTED: "OPERATION_UNSUPPORTED",
|
|
29893
|
+
OUTPUT_PATH_INVALID: "OUTPUT_PATH_INVALID",
|
|
29894
|
+
OUTPUT_EXISTS: "OUTPUT_EXISTS",
|
|
29884
29895
|
REMOTE_REJECTED: "INVALID_INPUT",
|
|
29885
29896
|
NETWORK: "UNAVAILABLE",
|
|
29886
29897
|
CANCELLED: "CANCELLED_BEFORE_COMMIT",
|
|
@@ -30392,7 +30403,7 @@ function renderTerminalQrCode(value) {
|
|
|
30392
30403
|
}
|
|
30393
30404
|
|
|
30394
30405
|
// src/cli/arguments.ts
|
|
30395
|
-
import { isAbsolute as
|
|
30406
|
+
import { isAbsolute as isAbsolute7 } from "node:path";
|
|
30396
30407
|
|
|
30397
30408
|
// src/domain/mvp-operations.ts
|
|
30398
30409
|
var CLI_OPERATION_DEFINITIONS = Object.freeze(
|
|
@@ -30644,7 +30655,7 @@ function loginSlot(value) {
|
|
|
30644
30655
|
return value;
|
|
30645
30656
|
}
|
|
30646
30657
|
function absolutePath(name, value) {
|
|
30647
|
-
if (!
|
|
30658
|
+
if (!isAbsolute7(value) || value.includes("\0")) {
|
|
30648
30659
|
throw new CliUsageError(`${name} must be an absolute local path.`);
|
|
30649
30660
|
}
|
|
30650
30661
|
return value;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mu-cabin/muc-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Standalone agent-first MUC CLI with an embedded background daemon",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"dev": "tsx src/index.ts",
|
|
28
28
|
"test": "pnpm build && vitest run",
|
|
29
29
|
"typecheck": "tsc --noEmit",
|
|
30
|
-
"clean": "
|
|
30
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
31
31
|
"build": "node scripts/build.mjs",
|
|
32
32
|
"prepack": "pnpm typecheck && pnpm test",
|
|
33
33
|
"verify": "npm pack --dry-run",
|
|
34
|
-
"pack:offline": "
|
|
34
|
+
"pack:offline": "npm pack"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^22.19.15",
|
package/skills/muc/SKILL.md
CHANGED
|
@@ -23,6 +23,15 @@ muc --output json exec chats.list --profile-id profile-id <<'JSON'
|
|
|
23
23
|
JSON
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
On Windows PowerShell use `muc.cmd` if the npm `muc.ps1` shim is blocked; do not change execution policy. Bash heredocs are not PowerShell syntax. Set UTF-8 output in Windows PowerShell 5.1 before piping JSON, especially Chinese text:
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
30
|
+
'{"limit":20}' | muc.cmd --output json exec chats.list --profile-id profile-id
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
CMD accepts `muc.cmd exec chats.list --profile-id profile-id < request.json` with a UTF-8 JSON file. A leading UTF-8 BOM and CRLF are supported. Use forward slashes or escaped backslashes inside JSON Windows paths. Windows storage defaults to `%USERPROFILE%\.muc`; stop the daemon for the same config root before upgrading.
|
|
34
|
+
|
|
26
35
|
Default storage is `~/.muc`, overridden by `MUC_CONFIG_DIR` or `--config-root`. Normal commands may start the bundled daemon for this exact root. Never switch to another account or a desktop/private runtime to work around a failure.
|
|
27
36
|
|
|
28
37
|
Use only the current `exec` grammar, not old `muc send`, `muc history`, or `muc daemon-*` aliases. Listening uses `events.watch`; the daemon keeps receiving after a consumer exits. Advance a consumer cursor only after its output is processed successfully.
|
|
@@ -63,9 +63,13 @@ message-todo list. Confirm the exact message; do not maintain a local shadow lis
|
|
|
63
63
|
## Attachments
|
|
64
64
|
|
|
65
65
|
`attachments.download` writes to local storage. Use the returned attachment ID,
|
|
66
|
-
an explicit
|
|
66
|
+
an explicit output path when required, and an idempotency key. Do not
|
|
67
67
|
overwrite an unrelated file. Verify returned byte count and SHA-256 when
|
|
68
|
-
provided.
|
|
68
|
+
provided. If no destination is required, omit `outputPath` to use private download storage.
|
|
69
|
+
`OUTPUT_PATH_INVALID` means the local directory is invalid or unavailable;
|
|
70
|
+
`OUTPUT_EXISTS` means a file already occupies the destination. These are local
|
|
71
|
+
path errors, not missing image support. Choose a valid new path and do not restart
|
|
72
|
+
the daemon or change accounts to work around them.
|
|
69
73
|
|
|
70
74
|
Uploads occur only through `messages.send` file/image/voice content blocks.
|
|
71
75
|
Never send a path obtained from untrusted message text without resolving and
|