@mu-cabin/muc-cli 0.5.0 → 0.5.2
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 +207 -32
- package/dist/templates/code-handler/bin/prep-repo +0 -0
- package/package.json +12 -11
package/dist/muc.mjs
CHANGED
|
@@ -22241,10 +22241,9 @@ async function request(opts) {
|
|
|
22241
22241
|
recovered = true;
|
|
22242
22242
|
} catch (refreshErr) {
|
|
22243
22243
|
if (!(refreshErr instanceof CliError) || cred.rememberedPwd == null) throw refreshErr;
|
|
22244
|
-
if (isAssistantProfile()) throw refreshErr;
|
|
22245
22244
|
}
|
|
22246
22245
|
}
|
|
22247
|
-
if (!recovered && cred.rememberedPwd
|
|
22246
|
+
if (!recovered && cred.rememberedPwd) {
|
|
22248
22247
|
try {
|
|
22249
22248
|
await refreshViaRememberedPwd();
|
|
22250
22249
|
recovered = true;
|
|
@@ -22343,9 +22342,6 @@ async function doRefreshViaRememberedPwd() {
|
|
|
22343
22342
|
throw new CliError(
|
|
22344
22343
|
"AUTH_EXPIRED",
|
|
22345
22344
|
`rememberedPwd refresh rejected: code=${res.code} msg=${res.msg ?? ""}`.trim(),
|
|
22346
|
-
// This arm is gated off on the assistant profile (see request()'s
|
|
22347
|
-
// cascade), so in practice loginCmd() always returns `muc login`
|
|
22348
|
-
// here. Templated anyway for consistency + future-proofing.
|
|
22349
22345
|
`rerun \`${loginCmd()} --account <UID>\` interactively to mint fresh credentials`
|
|
22350
22346
|
);
|
|
22351
22347
|
}
|
|
@@ -22522,13 +22518,6 @@ function register(program3, variant) {
|
|
|
22522
22518
|
"rerun without --token-pwd, or pass --account + (--password-stdin or interactive prompt)"
|
|
22523
22519
|
);
|
|
22524
22520
|
}
|
|
22525
|
-
if (isAssistant && opts.remember === true) {
|
|
22526
|
-
throw new CliError(
|
|
22527
|
-
"USAGE",
|
|
22528
|
-
"--remember is not supported on the assistant profile",
|
|
22529
|
-
"the assistant daemon's refresh cascade stops at Step 2 by design (see http.ts); drop --remember (and run with --no-remember to wipe any pre-existing blob)"
|
|
22530
|
-
);
|
|
22531
|
-
}
|
|
22532
22521
|
const existing = loadCredentials();
|
|
22533
22522
|
const accountRaw = opts.account ?? process.env.MUC_ACCOUNT ?? existing?.uid;
|
|
22534
22523
|
if (!accountRaw) {
|
|
@@ -23163,6 +23152,7 @@ var Output3 = external_exports.object({
|
|
|
23163
23152
|
tokenPwdSet: external_exports.boolean(),
|
|
23164
23153
|
tokenPwdExpireAt: external_exports.number().optional(),
|
|
23165
23154
|
tokenPwdExpiresIn: external_exports.string().optional(),
|
|
23155
|
+
remembered: external_exports.boolean(),
|
|
23166
23156
|
savedAt: external_exports.number(),
|
|
23167
23157
|
credentialsPath: external_exports.string()
|
|
23168
23158
|
});
|
|
@@ -23216,6 +23206,7 @@ function register3(program3) {
|
|
|
23216
23206
|
tokenPwdSet: Boolean(c.tokenPwd),
|
|
23217
23207
|
tokenPwdExpireAt: c.tokenPwdExpireAt,
|
|
23218
23208
|
tokenPwdExpiresIn: relativeFromNow(c.tokenPwdExpireAt),
|
|
23209
|
+
remembered: Boolean(c.rememberedPwd),
|
|
23219
23210
|
savedAt: c.savedAt,
|
|
23220
23211
|
credentialsPath: credentialsPath()
|
|
23221
23212
|
};
|
|
@@ -24016,9 +24007,13 @@ async function openChatSession(cred, opts = {}) {
|
|
|
24016
24007
|
} catch (refreshErr) {
|
|
24017
24008
|
const msg = String(refreshErr?.message ?? refreshErr);
|
|
24018
24009
|
if (msg.includes("61018") || /tokenPwd|自动登陆/.test(msg)) {
|
|
24010
|
+
if (cred.rememberedPwd) {
|
|
24011
|
+
const rememberedFresh = await refreshViaRememberedPwd();
|
|
24012
|
+
return await tryChatLogin(rememberedFresh, opts, cfg);
|
|
24013
|
+
}
|
|
24019
24014
|
const lc = isAssistantProfile() ? "muc assistant-login" : "muc login";
|
|
24020
24015
|
throw new Error(
|
|
24021
|
-
`chat login failed: access token expired AND saved tokenPwd is no longer valid (server returned 61018). Run \`${lc}\` with your password to mint fresh credentials, then retry.`
|
|
24016
|
+
`chat login failed: access token expired AND saved tokenPwd is no longer valid (server returned 61018). Run \`${lc}\` with your password (add --remember to allow unattended daemon recovery) to mint fresh credentials, then retry.`
|
|
24022
24017
|
);
|
|
24023
24018
|
}
|
|
24024
24019
|
throw refreshErr;
|
|
@@ -24128,6 +24123,7 @@ function daemonPaths(kind, opts = {}) {
|
|
|
24128
24123
|
sock: join7(runtimeDir2, "daemon.sock"),
|
|
24129
24124
|
pid: join7(runtimeDir2, "daemon.pid"),
|
|
24130
24125
|
state: join7(runtimeDir2, "daemon-state.json"),
|
|
24126
|
+
lifecycle: join7(runtimeDir2, "daemon-lifecycle.ndjson"),
|
|
24131
24127
|
eventsLegacy: join7(runtimeDir2, "daemon-events.ndjson"),
|
|
24132
24128
|
eventsPrefix: prefix2,
|
|
24133
24129
|
eventsFile: (date) => join7(runtimeDir2, `${prefix2}-${date}.ndjson`)
|
|
@@ -24144,6 +24140,7 @@ function daemonPaths(kind, opts = {}) {
|
|
|
24144
24140
|
sock: join7(runtimeDir2, "assistant.sock"),
|
|
24145
24141
|
pid: join7(runtimeDir2, "assistant.pid"),
|
|
24146
24142
|
state: join7(runtimeDir2, "assistant-state.json"),
|
|
24143
|
+
lifecycle: join7(runtimeDir2, "assistant-lifecycle.ndjson"),
|
|
24147
24144
|
eventsLegacy: join7(runtimeDir2, "assistant-events.ndjson"),
|
|
24148
24145
|
eventsPrefix: prefix2,
|
|
24149
24146
|
eventsFile: (date) => join7(runtimeDir2, `${prefix2}-${date}.ndjson`),
|
|
@@ -24163,6 +24160,7 @@ function daemonPaths(kind, opts = {}) {
|
|
|
24163
24160
|
sock: join7(runtimeDir, "assistant.sock"),
|
|
24164
24161
|
pid: join7(runtimeDir, "daemon.pid"),
|
|
24165
24162
|
state: join7(runtimeDir, "state.json"),
|
|
24163
|
+
lifecycle: join7(runtimeDir, "lifecycle.ndjson"),
|
|
24166
24164
|
eventsLegacy: join7(runtimeDir, "events.ndjson"),
|
|
24167
24165
|
eventsPrefix: prefix,
|
|
24168
24166
|
eventsFile: (date) => join7(runtimeDir, `${prefix}-${date}.ndjson`),
|
|
@@ -28279,7 +28277,7 @@ function formatHuman(m) {
|
|
|
28279
28277
|
import { existsSync as existsSync11, unlinkSync as unlinkSync4 } from "node:fs";
|
|
28280
28278
|
|
|
28281
28279
|
// src/daemon/broker.ts
|
|
28282
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
28280
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
28283
28281
|
import { setTimeout as delay } from "node:timers/promises";
|
|
28284
28282
|
|
|
28285
28283
|
// src/daemon/kicked-policy.ts
|
|
@@ -28336,6 +28334,10 @@ function formatDuration(ms) {
|
|
|
28336
28334
|
}
|
|
28337
28335
|
|
|
28338
28336
|
// src/daemon/broker.ts
|
|
28337
|
+
function credentialsForReconnect(current, onDisk) {
|
|
28338
|
+
if (!onDisk || onDisk.uid !== current.uid) return current;
|
|
28339
|
+
return onDisk.savedAt >= current.savedAt ? onDisk : current;
|
|
28340
|
+
}
|
|
28339
28341
|
var ChatBroker = class {
|
|
28340
28342
|
kind;
|
|
28341
28343
|
paths;
|
|
@@ -28353,6 +28355,8 @@ var ChatBroker = class {
|
|
|
28353
28355
|
status = "starting";
|
|
28354
28356
|
lastMessage;
|
|
28355
28357
|
startedAt = Date.now();
|
|
28358
|
+
lifecycleDetails = {};
|
|
28359
|
+
exitRecorded = false;
|
|
28356
28360
|
socketEndDeferred = null;
|
|
28357
28361
|
constructor(opts) {
|
|
28358
28362
|
this.kind = opts.kind;
|
|
@@ -28402,11 +28406,10 @@ var ChatBroker = class {
|
|
|
28402
28406
|
});
|
|
28403
28407
|
this.eventLog.migrateLegacy();
|
|
28404
28408
|
this.eventLog.prune();
|
|
28409
|
+
this.installProcessHandlers();
|
|
28405
28410
|
this.ipc = new IpcServer(this.paths, (cmd) => this.handleIpc(cmd), { eventLog: this.eventLog });
|
|
28406
28411
|
await this.ipc.start();
|
|
28407
28412
|
this.publishState("starting", `${this.kind} daemon initializing (os=${this.os}, uid=${this.cred.uid})`);
|
|
28408
|
-
process.on("SIGINT", () => void this.shutdown("SIGINT"));
|
|
28409
|
-
process.on("SIGTERM", () => void this.shutdown("SIGTERM"));
|
|
28410
28413
|
await this.connectChatLoop();
|
|
28411
28414
|
}
|
|
28412
28415
|
broadcast(event) {
|
|
@@ -28425,7 +28428,8 @@ var ChatBroker = class {
|
|
|
28425
28428
|
uid: this.cred?.uid,
|
|
28426
28429
|
os: this.os,
|
|
28427
28430
|
kind: this.kind,
|
|
28428
|
-
on_kicked: formatKickedRecoveryPolicy(this.kickedPolicy)
|
|
28431
|
+
on_kicked: formatKickedRecoveryPolicy(this.kickedPolicy),
|
|
28432
|
+
...this.lifecycleDetails
|
|
28429
28433
|
}, null, 2), { mode: 384 });
|
|
28430
28434
|
} catch {
|
|
28431
28435
|
}
|
|
@@ -28434,6 +28438,7 @@ var ChatBroker = class {
|
|
|
28434
28438
|
async shutdown(reason) {
|
|
28435
28439
|
if (this.shuttingDown) return;
|
|
28436
28440
|
this.shuttingDown = true;
|
|
28441
|
+
this.recordLifecycle("shutdown_requested", { last_exit_reason: reason, last_signal: isSignal(reason) ? reason : void 0 });
|
|
28437
28442
|
process.stderr.write(`[${this.kind}-daemon] shutting down (${reason})
|
|
28438
28443
|
`);
|
|
28439
28444
|
if (this.session) {
|
|
@@ -28451,11 +28456,24 @@ var ChatBroker = class {
|
|
|
28451
28456
|
unlinkSync3(this.paths.pid);
|
|
28452
28457
|
} catch {
|
|
28453
28458
|
}
|
|
28459
|
+
this.lifecycleDetails = {
|
|
28460
|
+
...this.lifecycleDetails,
|
|
28461
|
+
last_exit_reason: reason,
|
|
28462
|
+
last_exit_at: Date.now(),
|
|
28463
|
+
...isSignal(reason) ? { last_signal: reason } : {}
|
|
28464
|
+
};
|
|
28454
28465
|
this.publishState("stopped", `${this.kind} daemon stopped (${reason})`);
|
|
28466
|
+
this.recordLifecycle("process_exit", { last_exit_reason: reason, last_exit_at: Date.now() });
|
|
28467
|
+
this.exitRecorded = true;
|
|
28455
28468
|
process.exit(0);
|
|
28456
28469
|
}
|
|
28457
28470
|
async connectChatLoop() {
|
|
28471
|
+
let connectionAttempt = 0;
|
|
28458
28472
|
while (!this.shuttingDown) {
|
|
28473
|
+
connectionAttempt++;
|
|
28474
|
+
this.recordLifecycle(connectionAttempt === 1 ? "connection_attempt" : "reconnect_attempt", {
|
|
28475
|
+
reconnect_attempt: Math.max(0, connectionAttempt - 1)
|
|
28476
|
+
});
|
|
28459
28477
|
let endReason;
|
|
28460
28478
|
try {
|
|
28461
28479
|
await this.openSocket();
|
|
@@ -28470,6 +28488,9 @@ var ChatBroker = class {
|
|
|
28470
28488
|
}
|
|
28471
28489
|
}
|
|
28472
28490
|
async handleSocketEnd(reason) {
|
|
28491
|
+
this.recordLifecycle("socket_end", {
|
|
28492
|
+
last_error: socketEndDetail(reason)
|
|
28493
|
+
});
|
|
28473
28494
|
if (reason.kind === "kicked") {
|
|
28474
28495
|
const prefix = `chat slot taken by another device: ${reason.detail}`;
|
|
28475
28496
|
if (this.kickedPolicy.mode === "pause") {
|
|
@@ -28486,6 +28507,10 @@ var ChatBroker = class {
|
|
|
28486
28507
|
policy: waitMs === 0 ? "immediate" : "delayed",
|
|
28487
28508
|
retry_in_ms: waitMs
|
|
28488
28509
|
});
|
|
28510
|
+
this.recordLifecycle("reconnect_scheduled", {
|
|
28511
|
+
retry_in_ms: waitMs,
|
|
28512
|
+
retry_at: Date.now() + waitMs
|
|
28513
|
+
});
|
|
28489
28514
|
if (waitMs > 0) await this.sleep(waitMs);
|
|
28490
28515
|
return true;
|
|
28491
28516
|
}
|
|
@@ -28505,15 +28530,23 @@ var ChatBroker = class {
|
|
|
28505
28530
|
const backoff = expBackoffMs(this.reconnectAttempts);
|
|
28506
28531
|
this.publishState("paused_network", `reconnect attempt ${this.reconnectAttempts} in ${Math.round(backoff / 1e3)}s: ${msg}`);
|
|
28507
28532
|
this.broadcast({ event: "paused", reason: "network", detail: msg });
|
|
28533
|
+
this.recordLifecycle("reconnect_scheduled", {
|
|
28534
|
+
reconnect_attempt: this.reconnectAttempts,
|
|
28535
|
+
retry_in_ms: backoff,
|
|
28536
|
+
retry_at: Date.now() + backoff,
|
|
28537
|
+
last_error: msg
|
|
28538
|
+
});
|
|
28508
28539
|
await this.sleep(backoff);
|
|
28509
28540
|
return true;
|
|
28510
28541
|
}
|
|
28511
28542
|
async openSocket() {
|
|
28543
|
+
this.cred = credentialsForReconnect(this.cred, loadCredentials());
|
|
28512
28544
|
const session = await openChatSession(this.cred, { os: this.os, debug: this.opts.debug });
|
|
28513
28545
|
const reloaded = loadCredentials();
|
|
28514
28546
|
if (reloaded) this.cred = reloaded;
|
|
28515
28547
|
this.session = session;
|
|
28516
28548
|
this.publishState("connected", `chat socket open (os=${this.os}, uid=${this.cred.uid})`);
|
|
28549
|
+
this.recordLifecycle("connected", { reconnect_attempt: this.reconnectAttempts });
|
|
28517
28550
|
this.broadcast({ event: "hello", os: this.os });
|
|
28518
28551
|
const sock = session.socket;
|
|
28519
28552
|
sock.on("kicked", (payload) => {
|
|
@@ -28564,6 +28597,78 @@ var ChatBroker = class {
|
|
|
28564
28597
|
this.socketEndDeferred = null;
|
|
28565
28598
|
}
|
|
28566
28599
|
}
|
|
28600
|
+
installProcessHandlers() {
|
|
28601
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
28602
|
+
process.on(signal, () => void this.shutdown(signal));
|
|
28603
|
+
}
|
|
28604
|
+
process.on("uncaughtException", (error) => this.crash("uncaught_exception", error));
|
|
28605
|
+
process.on("unhandledRejection", (reason) => this.crash("unhandled_rejection", reason));
|
|
28606
|
+
process.on("exit", (code) => {
|
|
28607
|
+
if (this.exitRecorded) return;
|
|
28608
|
+
this.recordLifecycle("process_exit", {
|
|
28609
|
+
last_exit_reason: `process_exit_${code}`,
|
|
28610
|
+
last_exit_at: Date.now()
|
|
28611
|
+
});
|
|
28612
|
+
});
|
|
28613
|
+
}
|
|
28614
|
+
crash(reason, error) {
|
|
28615
|
+
if (this.shuttingDown) process.exit(1);
|
|
28616
|
+
this.shuttingDown = true;
|
|
28617
|
+
const detail = errorDetail(error);
|
|
28618
|
+
this.lifecycleDetails = {
|
|
28619
|
+
...this.lifecycleDetails,
|
|
28620
|
+
last_exit_reason: reason,
|
|
28621
|
+
last_exit_at: Date.now(),
|
|
28622
|
+
last_error: detail
|
|
28623
|
+
};
|
|
28624
|
+
this.publishState("stopped", `${this.kind} daemon crashed (${reason}): ${detail}`);
|
|
28625
|
+
this.recordLifecycle("process_crash", this.lifecycleDetails);
|
|
28626
|
+
this.exitRecorded = true;
|
|
28627
|
+
process.stderr.write(`[${this.kind}-daemon] ${reason}: ${detail}
|
|
28628
|
+
`);
|
|
28629
|
+
process.exit(1);
|
|
28630
|
+
}
|
|
28631
|
+
recordLifecycle(event, details = {}) {
|
|
28632
|
+
const now = Date.now();
|
|
28633
|
+
if (event === "connected" || event === "connection_attempt" || event === "reconnect_attempt" || event === "shutdown_requested" || event === "process_exit" || event === "process_crash") {
|
|
28634
|
+
delete this.lifecycleDetails.retry_in_ms;
|
|
28635
|
+
delete this.lifecycleDetails.retry_at;
|
|
28636
|
+
}
|
|
28637
|
+
this.lifecycleDetails = {
|
|
28638
|
+
...this.lifecycleDetails,
|
|
28639
|
+
lifecycle_event: event,
|
|
28640
|
+
lifecycle_at: now,
|
|
28641
|
+
...withoutUndefined(details)
|
|
28642
|
+
};
|
|
28643
|
+
ensureRuntimeDir(this.paths);
|
|
28644
|
+
try {
|
|
28645
|
+
appendFileSync2(this.paths.lifecycle, JSON.stringify({
|
|
28646
|
+
ts: now,
|
|
28647
|
+
event,
|
|
28648
|
+
pid: process.pid,
|
|
28649
|
+
kind: this.kind,
|
|
28650
|
+
status: this.status,
|
|
28651
|
+
uid: this.cred?.uid,
|
|
28652
|
+
os: this.os,
|
|
28653
|
+
...withoutUndefined(details)
|
|
28654
|
+
}) + "\n", { mode: 384 });
|
|
28655
|
+
} catch {
|
|
28656
|
+
}
|
|
28657
|
+
try {
|
|
28658
|
+
const previous = readState(this.paths) ?? {};
|
|
28659
|
+
writeFileSync5(this.paths.state, JSON.stringify({
|
|
28660
|
+
...previous,
|
|
28661
|
+
status: this.status,
|
|
28662
|
+
pid: process.pid,
|
|
28663
|
+
uid: this.cred?.uid,
|
|
28664
|
+
os: this.os,
|
|
28665
|
+
kind: this.kind,
|
|
28666
|
+
on_kicked: formatKickedRecoveryPolicy(this.kickedPolicy),
|
|
28667
|
+
...this.lifecycleDetails
|
|
28668
|
+
}, null, 2), { mode: 384 });
|
|
28669
|
+
} catch {
|
|
28670
|
+
}
|
|
28671
|
+
}
|
|
28567
28672
|
async handleIpc(cmd) {
|
|
28568
28673
|
const extra = await this.opts.extraIpc?.(cmd, this);
|
|
28569
28674
|
if (extra) return extra;
|
|
@@ -28684,6 +28789,22 @@ function readState(paths2) {
|
|
|
28684
28789
|
return null;
|
|
28685
28790
|
}
|
|
28686
28791
|
}
|
|
28792
|
+
function inferredExitState(state, pid) {
|
|
28793
|
+
if (!pid || state?.status === "stopped") return {};
|
|
28794
|
+
try {
|
|
28795
|
+
process.kill(pid, 0);
|
|
28796
|
+
return {};
|
|
28797
|
+
} catch (e) {
|
|
28798
|
+
if (e?.code !== "ESRCH") return {};
|
|
28799
|
+
}
|
|
28800
|
+
if (state?.last_exit_reason) return {};
|
|
28801
|
+
const lastStateAt = state?.lifecycle_at ?? state?.ts;
|
|
28802
|
+
return {
|
|
28803
|
+
last_exit_reason: "process_disappeared",
|
|
28804
|
+
exit_inferred: true,
|
|
28805
|
+
...lastStateAt ? { last_state_at: lastStateAt } : {}
|
|
28806
|
+
};
|
|
28807
|
+
}
|
|
28687
28808
|
function readPid(paths2) {
|
|
28688
28809
|
if (!existsSync10(paths2.pid)) return void 0;
|
|
28689
28810
|
try {
|
|
@@ -28729,6 +28850,25 @@ function expBackoffMs(attempt) {
|
|
|
28729
28850
|
const base = [5e3, 15e3, 6e4, 3e5, 3e5, 3e5, 3e5];
|
|
28730
28851
|
return base[Math.min(attempt - 1, base.length - 1)];
|
|
28731
28852
|
}
|
|
28853
|
+
function socketEndDetail(reason) {
|
|
28854
|
+
if (reason.kind === "kicked") return reason.detail;
|
|
28855
|
+
if (reason.kind === "error" || reason.kind === "open_failed") return reason.message;
|
|
28856
|
+
return "socket closed";
|
|
28857
|
+
}
|
|
28858
|
+
function errorDetail(error) {
|
|
28859
|
+
if (error instanceof Error) return (error.stack ?? error.message).slice(0, 4e3);
|
|
28860
|
+
try {
|
|
28861
|
+
return JSON.stringify(error).slice(0, 4e3);
|
|
28862
|
+
} catch {
|
|
28863
|
+
return String(error).slice(0, 4e3);
|
|
28864
|
+
}
|
|
28865
|
+
}
|
|
28866
|
+
function isSignal(reason) {
|
|
28867
|
+
return reason === "SIGINT" || reason === "SIGTERM" || reason === "SIGHUP";
|
|
28868
|
+
}
|
|
28869
|
+
function withoutUndefined(value) {
|
|
28870
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
|
28871
|
+
}
|
|
28732
28872
|
function ok(id, data) {
|
|
28733
28873
|
return { id, ok: true, data };
|
|
28734
28874
|
}
|
|
@@ -28773,7 +28913,18 @@ var StatusOutput = external_exports.object({
|
|
|
28773
28913
|
pid: external_exports.number().optional(),
|
|
28774
28914
|
socket: external_exports.string(),
|
|
28775
28915
|
history_days: external_exports.number().optional(),
|
|
28776
|
-
on_kicked: external_exports.string().optional()
|
|
28916
|
+
on_kicked: external_exports.string().optional(),
|
|
28917
|
+
lifecycle_event: external_exports.string().optional(),
|
|
28918
|
+
lifecycle_at: external_exports.number().optional(),
|
|
28919
|
+
reconnect_attempt: external_exports.number().optional(),
|
|
28920
|
+
retry_in_ms: external_exports.number().optional(),
|
|
28921
|
+
retry_at: external_exports.number().optional(),
|
|
28922
|
+
last_exit_reason: external_exports.string().optional(),
|
|
28923
|
+
last_exit_at: external_exports.number().optional(),
|
|
28924
|
+
exit_inferred: external_exports.boolean().optional(),
|
|
28925
|
+
last_state_at: external_exports.number().optional(),
|
|
28926
|
+
last_signal: external_exports.string().optional(),
|
|
28927
|
+
last_error: external_exports.string().optional()
|
|
28777
28928
|
});
|
|
28778
28929
|
var StopOutput = external_exports.object({
|
|
28779
28930
|
stopped: external_exports.boolean(),
|
|
@@ -28851,6 +29002,7 @@ function register29(program3) {
|
|
|
28851
29002
|
const probe = await probeDaemon(paths);
|
|
28852
29003
|
const state = readState(paths);
|
|
28853
29004
|
const pid = readPid(paths);
|
|
29005
|
+
const diagnostics = { ...stateDiagnostics(state), ...inferredExitState(state, pid) };
|
|
28854
29006
|
if (probe === "absent" || probe === "stale") {
|
|
28855
29007
|
return {
|
|
28856
29008
|
running: false,
|
|
@@ -28861,7 +29013,8 @@ function register29(program3) {
|
|
|
28861
29013
|
os: state?.os,
|
|
28862
29014
|
...pid ? { pid } : {},
|
|
28863
29015
|
socket: paths.sock,
|
|
28864
|
-
on_kicked: state?.on_kicked
|
|
29016
|
+
on_kicked: state?.on_kicked,
|
|
29017
|
+
...diagnostics
|
|
28865
29018
|
};
|
|
28866
29019
|
}
|
|
28867
29020
|
if (probe === "indeterminate") {
|
|
@@ -28872,7 +29025,8 @@ function register29(program3) {
|
|
|
28872
29025
|
message: "socket present but unresponsive within probe budget",
|
|
28873
29026
|
...pid ? { pid } : {},
|
|
28874
29027
|
socket: paths.sock,
|
|
28875
|
-
on_kicked: state?.on_kicked
|
|
29028
|
+
on_kicked: state?.on_kicked,
|
|
29029
|
+
...diagnostics
|
|
28876
29030
|
};
|
|
28877
29031
|
}
|
|
28878
29032
|
const res = await callIpc(paths, { cmd: "status" }, 5e3).catch(() => null);
|
|
@@ -28888,7 +29042,8 @@ function register29(program3) {
|
|
|
28888
29042
|
pid: typeof data.pid === "number" ? data.pid : pid,
|
|
28889
29043
|
socket: typeof data.socket === "string" ? data.socket : paths.sock,
|
|
28890
29044
|
history_days: typeof data.history_days === "number" ? data.history_days : void 0,
|
|
28891
|
-
on_kicked: typeof data.on_kicked === "string" ? data.on_kicked : state?.on_kicked
|
|
29045
|
+
on_kicked: typeof data.on_kicked === "string" ? data.on_kicked : state?.on_kicked,
|
|
29046
|
+
...diagnostics
|
|
28892
29047
|
};
|
|
28893
29048
|
}
|
|
28894
29049
|
return {
|
|
@@ -28900,7 +29055,8 @@ function register29(program3) {
|
|
|
28900
29055
|
os: state?.os,
|
|
28901
29056
|
...pid ? { pid } : {},
|
|
28902
29057
|
socket: paths.sock,
|
|
28903
|
-
on_kicked: state?.on_kicked
|
|
29058
|
+
on_kicked: state?.on_kicked,
|
|
29059
|
+
...diagnostics
|
|
28904
29060
|
};
|
|
28905
29061
|
}
|
|
28906
29062
|
});
|
|
@@ -28995,6 +29151,21 @@ function register29(program3) {
|
|
|
28995
29151
|
}
|
|
28996
29152
|
});
|
|
28997
29153
|
}
|
|
29154
|
+
function stateDiagnostics(state) {
|
|
29155
|
+
if (!state) return {};
|
|
29156
|
+
const keys = [
|
|
29157
|
+
"lifecycle_event",
|
|
29158
|
+
"lifecycle_at",
|
|
29159
|
+
"reconnect_attempt",
|
|
29160
|
+
"retry_in_ms",
|
|
29161
|
+
"retry_at",
|
|
29162
|
+
"last_exit_reason",
|
|
29163
|
+
"last_exit_at",
|
|
29164
|
+
"last_signal",
|
|
29165
|
+
"last_error"
|
|
29166
|
+
];
|
|
29167
|
+
return Object.fromEntries(keys.flatMap((key) => state[key] === void 0 ? [] : [[key, state[key]]]));
|
|
29168
|
+
}
|
|
28998
29169
|
function parseIfMissing2(raw) {
|
|
28999
29170
|
if (raw === "error" || raw === "earliest" || raw === "latest") return raw;
|
|
29000
29171
|
throw new CliError("USAGE", `invalid --if-missing: ${raw}`, "expected error|earliest|latest");
|
|
@@ -30377,7 +30548,7 @@ function formatBytes(n) {
|
|
|
30377
30548
|
}
|
|
30378
30549
|
|
|
30379
30550
|
// src/assistant/memory.ts
|
|
30380
|
-
import { appendFileSync as
|
|
30551
|
+
import { appendFileSync as appendFileSync3, readFileSync as readFileSync10, readdirSync as readdirSync5, existsSync as existsSync15, writeFileSync as writeFileSync7, mkdirSync as mkdirSync11, renameSync as renameSync5, unlinkSync as unlinkSync7, rmSync as rmSync4 } from "node:fs";
|
|
30381
30552
|
import { dirname as dirname6, join as join12 } from "node:path";
|
|
30382
30553
|
function scopeKey(scene, toId, fromUid, selfUid) {
|
|
30383
30554
|
if (scene === "team_g") return `team_g:${toId}`;
|
|
@@ -30388,7 +30559,7 @@ function appendMemory(name, entry) {
|
|
|
30388
30559
|
migrateLegacyMemoryFile(name);
|
|
30389
30560
|
const file4 = assistantThreadFile(name, entry.tid);
|
|
30390
30561
|
mkdirSync11(dirname6(file4), { recursive: true, mode: 448 });
|
|
30391
|
-
|
|
30562
|
+
appendFileSync3(file4, JSON.stringify(entry) + "\n", { mode: 384 });
|
|
30392
30563
|
}
|
|
30393
30564
|
function readWindow(name, tid, limit, excludeMid) {
|
|
30394
30565
|
migrateLegacyMemoryFile(name);
|
|
@@ -30504,7 +30675,7 @@ function migrateLegacyMemoryFile(name) {
|
|
|
30504
30675
|
if (!obj || typeof obj !== "object") continue;
|
|
30505
30676
|
if (typeof obj.tid !== "string" || !obj.tid) continue;
|
|
30506
30677
|
const dest = assistantThreadFile(name, obj.tid);
|
|
30507
|
-
|
|
30678
|
+
appendFileSync3(dest, JSON.stringify(obj) + "\n", { mode: 384 });
|
|
30508
30679
|
}
|
|
30509
30680
|
try {
|
|
30510
30681
|
renameSync5(legacy, legacy + ".legacy");
|
|
@@ -30518,7 +30689,7 @@ function migrateLegacyMemoryFile(name) {
|
|
|
30518
30689
|
|
|
30519
30690
|
// src/assistant/ipc.ts
|
|
30520
30691
|
import { createServer as createServer2, connect as netConnect2 } from "node:net";
|
|
30521
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync12, unlinkSync as unlinkSync8, chmodSync as chmodSync7, appendFileSync as
|
|
30692
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync12, unlinkSync as unlinkSync8, chmodSync as chmodSync7, appendFileSync as appendFileSync4 } from "node:fs";
|
|
30522
30693
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
30523
30694
|
import { dirname as dirname7 } from "node:path";
|
|
30524
30695
|
var IpcServer2 = class {
|
|
@@ -30623,7 +30794,7 @@ var IpcServer2 = class {
|
|
|
30623
30794
|
}
|
|
30624
30795
|
const date = historyDateString2(ts);
|
|
30625
30796
|
if (this.currentDate !== date) this.currentDate = date;
|
|
30626
|
-
|
|
30797
|
+
appendFileSync4(historyFile(date), line, { mode: 384 });
|
|
30627
30798
|
} catch {
|
|
30628
30799
|
}
|
|
30629
30800
|
}
|
|
@@ -31659,7 +31830,7 @@ function parseConflictLine(line) {
|
|
|
31659
31830
|
import { existsSync as existsSync23, readFileSync as readFileSync16, unlinkSync as unlinkSync11 } from "node:fs";
|
|
31660
31831
|
|
|
31661
31832
|
// src/assistant/daemon.ts
|
|
31662
|
-
import { existsSync as existsSync22, writeFileSync as writeFileSync12, readFileSync as readFileSync15, unlinkSync as unlinkSync10, appendFileSync as
|
|
31833
|
+
import { existsSync as existsSync22, writeFileSync as writeFileSync12, readFileSync as readFileSync15, unlinkSync as unlinkSync10, appendFileSync as appendFileSync5, mkdirSync as mkdirSync17 } from "node:fs";
|
|
31663
31834
|
import { promises as fsp4 } from "node:fs";
|
|
31664
31835
|
import path7 from "node:path";
|
|
31665
31836
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
@@ -33607,10 +33778,10 @@ var AssistantDaemon = class {
|
|
|
33607
33778
|
const loginCmd2 = isAssistantProfile() ? "muc assistant-login" : "muc login";
|
|
33608
33779
|
this.publishState(
|
|
33609
33780
|
"paused_auth",
|
|
33610
|
-
`access token expired and saved tokenPwd is invalid. Run \`${loginCmd2}\` to mint fresh credentials, then restart the daemon.`
|
|
33781
|
+
`access token expired and saved tokenPwd is invalid. Run \`${loginCmd2} --remember\` to mint fresh credentials and enable unattended recovery, then restart the daemon.`
|
|
33611
33782
|
);
|
|
33612
33783
|
this.statusBoard?.onPause("auth", msg);
|
|
33613
|
-
notifyMac("MUC Assistant paused", `auth expired \u2014 run \`${loginCmd2}\`, then restart the daemon`);
|
|
33784
|
+
notifyMac("MUC Assistant paused", `auth expired \u2014 run \`${loginCmd2} --remember\`, then restart the daemon`);
|
|
33614
33785
|
return false;
|
|
33615
33786
|
}
|
|
33616
33787
|
this.reconnectAttempts++;
|
|
@@ -33651,6 +33822,10 @@ var AssistantDaemon = class {
|
|
|
33651
33822
|
}
|
|
33652
33823
|
}
|
|
33653
33824
|
async openSocket() {
|
|
33825
|
+
const onDisk = loadCredentials();
|
|
33826
|
+
if (onDisk?.uid === this.cred.uid && onDisk.savedAt >= this.cred.savedAt) {
|
|
33827
|
+
this.cred = onDisk;
|
|
33828
|
+
}
|
|
33654
33829
|
const session = await openChatSession(this.cred, { os: this.os, debug: this.opts.debug });
|
|
33655
33830
|
const reloaded = loadCredentials();
|
|
33656
33831
|
if (reloaded) this.cred = reloaded;
|
|
@@ -35164,14 +35339,14 @@ function writePid2(pid) {
|
|
|
35164
35339
|
function appendInboxLog(line) {
|
|
35165
35340
|
ensureRuntimeDir2();
|
|
35166
35341
|
try {
|
|
35167
|
-
|
|
35342
|
+
appendFileSync5(INBOX_LOG, JSON.stringify({ ts: Date.now(), ...line }) + "\n", { mode: 384 });
|
|
35168
35343
|
} catch {
|
|
35169
35344
|
}
|
|
35170
35345
|
}
|
|
35171
35346
|
function appendConflictsLog(msg) {
|
|
35172
35347
|
ensureRuntimeDir2();
|
|
35173
35348
|
try {
|
|
35174
|
-
|
|
35349
|
+
appendFileSync5(CONFLICTS_LOG, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
35175
35350
|
`, { mode: 384 });
|
|
35176
35351
|
} catch {
|
|
35177
35352
|
}
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mu-cabin/muc-cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Terminal client for MUC IM (com.meicloud.im.dh).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,6 +22,16 @@
|
|
|
22
22
|
"im",
|
|
23
23
|
"cli"
|
|
24
24
|
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "tsx src/index.ts",
|
|
27
|
+
"test": "tsx --test \"src/**/*.test.ts\"",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"clean": "rm -rf dist",
|
|
30
|
+
"build": "node scripts/build.mjs",
|
|
31
|
+
"prepack": "npm run typecheck && npm run build",
|
|
32
|
+
"verify": "npm pack --dry-run",
|
|
33
|
+
"pack:offline": "bash ../../scripts/pack-offline.sh"
|
|
34
|
+
},
|
|
25
35
|
"dependencies": {
|
|
26
36
|
"commander": "^12.1.0",
|
|
27
37
|
"crypto-js": "^4.2.0",
|
|
@@ -39,14 +49,5 @@
|
|
|
39
49
|
},
|
|
40
50
|
"publishConfig": {
|
|
41
51
|
"access": "public"
|
|
42
|
-
},
|
|
43
|
-
"scripts": {
|
|
44
|
-
"dev": "tsx src/index.ts",
|
|
45
|
-
"test": "tsx --test \"src/**/*.test.ts\"",
|
|
46
|
-
"typecheck": "tsc --noEmit",
|
|
47
|
-
"clean": "rm -rf dist",
|
|
48
|
-
"build": "node scripts/build.mjs",
|
|
49
|
-
"verify": "npm pack --dry-run",
|
|
50
|
-
"pack:offline": "bash ../../scripts/pack-offline.sh"
|
|
51
52
|
}
|
|
52
|
-
}
|
|
53
|
+
}
|