@modelzen/feishu-codex-bridge 0.6.7-test.1 → 0.6.7-test.4
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/cli.js +273 -27
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5139,7 +5139,7 @@ function openUrl(url) {
|
|
|
5139
5139
|
args = [url];
|
|
5140
5140
|
}
|
|
5141
5141
|
try {
|
|
5142
|
-
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
5142
|
+
const child = spawn(cmd, args, { stdio: "ignore", detached: true, windowsHide: true });
|
|
5143
5143
|
child.on("error", () => {
|
|
5144
5144
|
});
|
|
5145
5145
|
child.unref();
|
|
@@ -5716,6 +5716,8 @@ var DM = {
|
|
|
5716
5716
|
coffeeSettings: "dm.coffee.settings",
|
|
5717
5717
|
doctor: "dm.doctor",
|
|
5718
5718
|
reconnect: "dm.reconnect",
|
|
5719
|
+
restart: "dm.restart",
|
|
5720
|
+
restartDo: "dm.restart.do",
|
|
5719
5721
|
update: "dm.update",
|
|
5720
5722
|
updateDo: "dm.update.do",
|
|
5721
5723
|
// 📊 Codex 用量(限额 + 个人资料统计 + 热力图);share 打开内容选择卡,
|
|
@@ -5793,7 +5795,7 @@ function buildDmMenuCard(opts = {}) {
|
|
|
5793
5795
|
actionsFixed([
|
|
5794
5796
|
button("\u{1F4CA} \u7528\u91CF", { a: DM.usage }),
|
|
5795
5797
|
button("\u{1FA7A} \u8BCA\u65AD", { a: DM.doctor }),
|
|
5796
|
-
button("\u{
|
|
5798
|
+
button("\u{1F501} \u91CD\u542F", { a: DM.restart }),
|
|
5797
5799
|
button("\u2B06\uFE0F \u66F4\u65B0", { a: DM.update })
|
|
5798
5800
|
], MENU_BTN_W_BOT),
|
|
5799
5801
|
// 🌐 网页控制台:刻意低调——小号按钮靠左 + 右侧灰字说明;仅在 daemon 本机控制台
|
|
@@ -5910,6 +5912,35 @@ function buildReconnectCard(conn) {
|
|
|
5910
5912
|
{ header: { title: "\u{1F504} \u957F\u8FDE\u63A5", template } }
|
|
5911
5913
|
);
|
|
5912
5914
|
}
|
|
5915
|
+
function buildRestartConfirmCard(conn) {
|
|
5916
|
+
return card(
|
|
5917
|
+
[
|
|
5918
|
+
md(`\u957F\u8FDE\u63A5\u72B6\u6001\uFF1A**${conn}**`),
|
|
5919
|
+
note("\u91CD\u542F\u4F1A**\u65AD\u5F00\u5F53\u524D\u6240\u6709\u4F1A\u8BDD**\u5E76\u91CD\u65B0\u62C9\u8D77\u540E\u53F0\u670D\u52A1\uFF08\u7EA6\u6570\u79D2\uFF0C\u5176\u95F4\u673A\u5668\u4EBA\u77ED\u6682\u79BB\u7EBF\uFF09\uFF0C\u5B8C\u6210\u540E\u81EA\u52A8\u6062\u590D\u3002\u4EC5\u5728\u957F\u671F\u65AD\u8FDE\u6216\u5F02\u5E38\u65F6\u624D\u9700\u8981\u3002"),
|
|
5920
|
+
actions([
|
|
5921
|
+
button("\u{1F501} \u786E\u8BA4\u91CD\u542F", { a: DM.restartDo }, "danger"),
|
|
5922
|
+
button("\u53D6\u6D88", { a: DM.menu })
|
|
5923
|
+
])
|
|
5924
|
+
],
|
|
5925
|
+
{ header: { title: "\u{1F501} \u91CD\u542F\u540E\u53F0\u670D\u52A1", template: "orange" } }
|
|
5926
|
+
);
|
|
5927
|
+
}
|
|
5928
|
+
function buildRestartingCard(mode = "restarting") {
|
|
5929
|
+
if (mode === "foreground") {
|
|
5930
|
+
return card(
|
|
5931
|
+
[
|
|
5932
|
+
md("\u2139\uFE0F \u5F53\u524D\u4E3A**\u524D\u53F0\u8FD0\u884C**\uFF08\u975E\u540E\u53F0\u5B88\u62A4\u670D\u52A1\uFF09\u3002"),
|
|
5933
|
+
note("\u6B64\u6309\u94AE\u53EA\u91CD\u542F\u7531 `feishu-codex-bridge start` \u5B89\u88C5\u7684\u540E\u53F0\u670D\u52A1\u3002\u524D\u53F0\u8FD0\u884C\u8BF7\u5728\u5176\u7EC8\u7AEF\u91CC Ctrl+C \u540E\u91CD\u8DD1 `feishu-codex-bridge run`\u3002"),
|
|
5934
|
+
backToMenu()
|
|
5935
|
+
],
|
|
5936
|
+
{ header: { title: "\u{1F501} \u91CD\u542F\u540E\u53F0\u670D\u52A1", template: "orange" } }
|
|
5937
|
+
);
|
|
5938
|
+
}
|
|
5939
|
+
return card(
|
|
5940
|
+
[md("\u{1F501} \u6B63\u5728\u91CD\u542F\u540E\u53F0\u670D\u52A1\u2026"), note("\u673A\u5668\u4EBA\u5C06\u77ED\u6682\u79BB\u7EBF\u6570\u79D2\u540E\u81EA\u52A8\u6062\u590D\uFF1B\u672C\u5361\u4E0D\u518D\u66F4\u65B0\u3002")],
|
|
5941
|
+
{ header: { title: "\u{1F501} \u91CD\u542F\u540E\u53F0\u670D\u52A1", template: "orange" } }
|
|
5942
|
+
);
|
|
5943
|
+
}
|
|
5913
5944
|
function connLabel(state) {
|
|
5914
5945
|
switch (state) {
|
|
5915
5946
|
case "connected":
|
|
@@ -8509,7 +8540,8 @@ function withCodexHooksFeature(text) {
|
|
|
8509
8540
|
}
|
|
8510
8541
|
|
|
8511
8542
|
// src/service/update.ts
|
|
8512
|
-
|
|
8543
|
+
init_paths();
|
|
8544
|
+
import { closeSync, existsSync as existsSync9, openSync as openSync2, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync2, writeSync } from "fs";
|
|
8513
8545
|
import { dirname as dirname10, join as join15, resolve as resolve6 } from "path";
|
|
8514
8546
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
8515
8547
|
|
|
@@ -8800,7 +8832,17 @@ function startNow(opts = {}) {
|
|
|
8800
8832
|
stdio: ["ignore", out, err],
|
|
8801
8833
|
env: mergeProcessEnv(process.env, overrides)
|
|
8802
8834
|
});
|
|
8803
|
-
if (child.pid)
|
|
8835
|
+
if (child.pid) {
|
|
8836
|
+
const pidPath = join13(dir, "service.pid");
|
|
8837
|
+
let cur = null;
|
|
8838
|
+
try {
|
|
8839
|
+
const n = Number.parseInt(readFileSync3(pidPath, "utf8").trim(), 10);
|
|
8840
|
+
cur = Number.isFinite(n) ? n : null;
|
|
8841
|
+
} catch {
|
|
8842
|
+
cur = null;
|
|
8843
|
+
}
|
|
8844
|
+
if (cur === null || !pidAlive(cur)) writeFileSync(pidPath, String(child.pid), "utf8");
|
|
8845
|
+
}
|
|
8804
8846
|
child.unref();
|
|
8805
8847
|
}
|
|
8806
8848
|
async function installWinStartup() {
|
|
@@ -8811,16 +8853,16 @@ async function installWinStartup() {
|
|
|
8811
8853
|
const reg = spawnSync2(
|
|
8812
8854
|
"reg",
|
|
8813
8855
|
["add", RUN_KEY_PATH, "/v", RUN_KEY_NAME, "/t", "REG_SZ", "/d", `wscript.exe "${launcherVbsPath()}"`, "/f"],
|
|
8814
|
-
{ encoding: "utf8" }
|
|
8856
|
+
{ encoding: "utf8", windowsHide: true }
|
|
8815
8857
|
);
|
|
8816
8858
|
if (reg.status !== 0) {
|
|
8817
8859
|
throw new Error(`\u5199\u5165\u767B\u5F55\u81EA\u542F\u6CE8\u518C\u8868\u9879\u5931\u8D25\uFF08exit ${reg.status ?? "unknown"}\uFF09\uFF1A${(reg.stderr || reg.stdout || "").trim()}`);
|
|
8818
8860
|
}
|
|
8819
|
-
startNow();
|
|
8861
|
+
if (!winStartupRunning()) startNow();
|
|
8820
8862
|
return statusWinStartup();
|
|
8821
8863
|
}
|
|
8822
8864
|
async function uninstallWinStartup() {
|
|
8823
|
-
spawnSync2("reg", ["delete", RUN_KEY_PATH, "/v", RUN_KEY_NAME, "/f"], { stdio: "ignore" });
|
|
8865
|
+
spawnSync2("reg", ["delete", RUN_KEY_PATH, "/v", RUN_KEY_NAME, "/f"], { stdio: "ignore", windowsHide: true });
|
|
8824
8866
|
killService();
|
|
8825
8867
|
rmSync(servicePidFile(), { force: true });
|
|
8826
8868
|
}
|
|
@@ -8885,7 +8927,11 @@ function clearRelaunchClaim(reqPath) {
|
|
|
8885
8927
|
function buildRelauncherPowershell(reqPath, binPath = resolveCliBinPath(), nodePath = process.execPath) {
|
|
8886
8928
|
const inner = `"${nodePath}" "${binPath}" __win-relaunch "${reqPath}"`;
|
|
8887
8929
|
const lit = `'${inner.replace(/'/g, "''")}'`;
|
|
8888
|
-
return
|
|
8930
|
+
return (
|
|
8931
|
+
// Silence the progress stream: with stderr redirected PowerShell serializes
|
|
8932
|
+
// progress records as noisy #< CLIXML blobs into our captured output.
|
|
8933
|
+
`$ProgressPreference='SilentlyContinue'; $c=${lit}; try { $r = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine=$c } -ErrorAction Stop; Write-Output ('cim rv=' + $r.ReturnValue + ' pid=' + $r.ProcessId) } catch { Write-Output ('cim-error: ' + $_.Exception.Message); schtasks /create /tn ${RELAUNCH_TASK_NAME} /tr $c /sc ONCE /st 00:00 /f | Out-Null; $cr=$LASTEXITCODE; schtasks /run /tn ${RELAUNCH_TASK_NAME} | Out-Null; Write-Output ('schtasks create=' + $cr + ' run=' + $LASTEXITCODE) }`
|
|
8934
|
+
);
|
|
8889
8935
|
}
|
|
8890
8936
|
function encodePowershellCommand(script) {
|
|
8891
8937
|
return Buffer.from(script, "utf16le").toString("base64");
|
|
@@ -8898,15 +8944,24 @@ function powershellPath() {
|
|
|
8898
8944
|
function spawnTreeFreeRelauncher(reqPath) {
|
|
8899
8945
|
const ps = powershellPath();
|
|
8900
8946
|
if (ps) {
|
|
8901
|
-
const
|
|
8902
|
-
const child = spawn3(
|
|
8947
|
+
const r = spawnSync2(
|
|
8903
8948
|
ps,
|
|
8904
8949
|
["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-EncodedCommand", encodePowershellCommand(buildRelauncherPowershell(reqPath))],
|
|
8905
|
-
{
|
|
8950
|
+
{ encoding: "utf8", windowsHide: true, timeout: 2e4 }
|
|
8906
8951
|
);
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8952
|
+
const out = [
|
|
8953
|
+
`exit=${r.status ?? "?"}`,
|
|
8954
|
+
r.error ? `err=${r.error.message}` : "",
|
|
8955
|
+
(r.stdout || "").replace(/\s+/g, " ").trim(),
|
|
8956
|
+
(r.stderr || "").replace(/\s+/g, " ").trim()
|
|
8957
|
+
].filter(Boolean).join(" ");
|
|
8958
|
+
appendServiceErr("relaunch", `powershell submit: ${out.slice(0, 900)}`);
|
|
8959
|
+
const stdout = r.stdout || "";
|
|
8960
|
+
if (!r.error && r.status === 0 && (/cim rv=0\b/.test(stdout) || /schtasks .*run=0\b/.test(stdout))) {
|
|
8961
|
+
return true;
|
|
8962
|
+
}
|
|
8963
|
+
appendServiceErr("relaunch", "powershell did not confirm a launch; trying node-side schtasks fallback");
|
|
8964
|
+
return spawnSchtasksRelauncher(reqPath);
|
|
8910
8965
|
}
|
|
8911
8966
|
appendServiceErr("relaunch", "powershell.exe not found; trying schtasks fallback");
|
|
8912
8967
|
return spawnSchtasksRelauncher(reqPath);
|
|
@@ -8974,7 +9029,12 @@ async function runWinRelaunch(deps = {}) {
|
|
|
8974
9029
|
}
|
|
8975
9030
|
await sleep(300);
|
|
8976
9031
|
start({ appDir: appDir2, envPath: req.envPath, userProfile: req.userProfile });
|
|
8977
|
-
|
|
9032
|
+
let newPid = "?";
|
|
9033
|
+
try {
|
|
9034
|
+
newPid = readFileSync3(join13(appDir2, "service.pid"), "utf8").trim() || "?";
|
|
9035
|
+
} catch {
|
|
9036
|
+
}
|
|
9037
|
+
logLine(`relaunched; new daemon pid=${newPid} (appDir=${appDir2})`);
|
|
8978
9038
|
} finally {
|
|
8979
9039
|
clearClaim(reqPath);
|
|
8980
9040
|
if (process.platform === "win32") {
|
|
@@ -9021,7 +9081,8 @@ function clearServicePid() {
|
|
|
9021
9081
|
}
|
|
9022
9082
|
function isInstalled() {
|
|
9023
9083
|
const r = spawnSync2("reg", ["query", RUN_KEY_PATH, "/v", RUN_KEY_NAME], {
|
|
9024
|
-
stdio: ["ignore", "ignore", "ignore"]
|
|
9084
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
9085
|
+
windowsHide: true
|
|
9025
9086
|
});
|
|
9026
9087
|
return r.status === 0;
|
|
9027
9088
|
}
|
|
@@ -9287,6 +9348,84 @@ async function installLatest(opts = {}) {
|
|
|
9287
9348
|
});
|
|
9288
9349
|
});
|
|
9289
9350
|
}
|
|
9351
|
+
var UPDATE_LOCK_STALE_MS = 30 * 60 * 1e3;
|
|
9352
|
+
function updateLockFile() {
|
|
9353
|
+
return join15(paths.appDir, "update.lock");
|
|
9354
|
+
}
|
|
9355
|
+
function livePid(pid) {
|
|
9356
|
+
try {
|
|
9357
|
+
process.kill(pid, 0);
|
|
9358
|
+
return true;
|
|
9359
|
+
} catch (err) {
|
|
9360
|
+
return err.code === "EPERM";
|
|
9361
|
+
}
|
|
9362
|
+
}
|
|
9363
|
+
function isUpdateLockStale(file) {
|
|
9364
|
+
try {
|
|
9365
|
+
const rec = JSON.parse(readFileSync4(file, "utf8"));
|
|
9366
|
+
if (typeof rec.pid !== "number") return true;
|
|
9367
|
+
if (!livePid(rec.pid)) return true;
|
|
9368
|
+
return typeof rec.at === "number" && Date.now() - rec.at > UPDATE_LOCK_STALE_MS;
|
|
9369
|
+
} catch {
|
|
9370
|
+
return true;
|
|
9371
|
+
}
|
|
9372
|
+
}
|
|
9373
|
+
function acquireUpdateLock() {
|
|
9374
|
+
const file = updateLockFile();
|
|
9375
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
9376
|
+
try {
|
|
9377
|
+
const fd = openSync2(file, "wx");
|
|
9378
|
+
writeSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
|
|
9379
|
+
closeSync(fd);
|
|
9380
|
+
return releaseUpdateLock(file);
|
|
9381
|
+
} catch (err) {
|
|
9382
|
+
const code = err.code;
|
|
9383
|
+
if (code !== "EEXIST") return null;
|
|
9384
|
+
if (!isUpdateLockStale(file)) return null;
|
|
9385
|
+
try {
|
|
9386
|
+
rmSync2(file, { force: true });
|
|
9387
|
+
} catch {
|
|
9388
|
+
}
|
|
9389
|
+
}
|
|
9390
|
+
}
|
|
9391
|
+
return null;
|
|
9392
|
+
}
|
|
9393
|
+
function releaseUpdateLock(file) {
|
|
9394
|
+
return () => {
|
|
9395
|
+
try {
|
|
9396
|
+
const rec = JSON.parse(readFileSync4(file, "utf8"));
|
|
9397
|
+
if (rec.pid !== process.pid) return;
|
|
9398
|
+
} catch {
|
|
9399
|
+
}
|
|
9400
|
+
try {
|
|
9401
|
+
rmSync2(file, { force: true });
|
|
9402
|
+
} catch {
|
|
9403
|
+
}
|
|
9404
|
+
};
|
|
9405
|
+
}
|
|
9406
|
+
function updateStatusFile() {
|
|
9407
|
+
return join15(paths.appDir, "update-status.json");
|
|
9408
|
+
}
|
|
9409
|
+
function writeUpdateStatus(status) {
|
|
9410
|
+
try {
|
|
9411
|
+
writeFileSync2(updateStatusFile(), JSON.stringify(status), "utf8");
|
|
9412
|
+
} catch {
|
|
9413
|
+
}
|
|
9414
|
+
}
|
|
9415
|
+
function readUpdateStatus() {
|
|
9416
|
+
try {
|
|
9417
|
+
const s = JSON.parse(readFileSync4(updateStatusFile(), "utf8"));
|
|
9418
|
+
return s && typeof s.phase === "string" && typeof s.at === "number" ? s : null;
|
|
9419
|
+
} catch {
|
|
9420
|
+
return null;
|
|
9421
|
+
}
|
|
9422
|
+
}
|
|
9423
|
+
function clearUpdateStatus() {
|
|
9424
|
+
try {
|
|
9425
|
+
rmSync2(updateStatusFile(), { force: true });
|
|
9426
|
+
} catch {
|
|
9427
|
+
}
|
|
9428
|
+
}
|
|
9290
9429
|
function daemonRunning() {
|
|
9291
9430
|
return isServiceRunning();
|
|
9292
9431
|
}
|
|
@@ -9876,7 +10015,7 @@ function buildUsageShareCard(data, opts = {}) {
|
|
|
9876
10015
|
// src/web/discovery.ts
|
|
9877
10016
|
init_paths();
|
|
9878
10017
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
9879
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, unlinkSync, writeFileSync as
|
|
10018
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
9880
10019
|
import { dirname as dirname11 } from "path";
|
|
9881
10020
|
function publishWebConsole(rec, file = paths.webConsoleFile) {
|
|
9882
10021
|
mkdirSync2(dirname11(file), { recursive: true });
|
|
@@ -9884,7 +10023,7 @@ function publishWebConsole(rec, file = paths.webConsoleFile) {
|
|
|
9884
10023
|
unlinkSync(file);
|
|
9885
10024
|
} catch {
|
|
9886
10025
|
}
|
|
9887
|
-
|
|
10026
|
+
writeFileSync3(file, `${JSON.stringify(rec, null, 2)}
|
|
9888
10027
|
`, { mode: 384 });
|
|
9889
10028
|
}
|
|
9890
10029
|
function readWebConsole(file = paths.webConsoleFile) {
|
|
@@ -9924,7 +10063,7 @@ function stableWebConsoleToken(file = paths.webTokenFile) {
|
|
|
9924
10063
|
unlinkSync(file);
|
|
9925
10064
|
} catch {
|
|
9926
10065
|
}
|
|
9927
|
-
|
|
10066
|
+
writeFileSync3(file, `${token}
|
|
9928
10067
|
`, { mode: 384 });
|
|
9929
10068
|
} catch {
|
|
9930
10069
|
}
|
|
@@ -12629,6 +12768,24 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
12629
12768
|
await sendManagedCard(channel, evt.chatId, buildReconnectCard(conn), evt.messageId).catch(
|
|
12630
12769
|
(err) => log.fail("console", err, { cmd: "reconnect" })
|
|
12631
12770
|
);
|
|
12771
|
+
}).on(DM.restart, async ({ evt }) => {
|
|
12772
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12773
|
+
const conn = channel.getConnectionStatus?.()?.state ?? "unknown";
|
|
12774
|
+
await sendManagedCard(channel, evt.chatId, buildRestartConfirmCard(conn), evt.messageId).catch(
|
|
12775
|
+
(err) => log.fail("console", err, { cmd: "restart" })
|
|
12776
|
+
);
|
|
12777
|
+
}).on(DM.restartDo, ({ evt }) => {
|
|
12778
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12779
|
+
void (async () => {
|
|
12780
|
+
await new Promise((r) => setTimeout(r, CARD_SETTLE_MS));
|
|
12781
|
+
if (!daemonRunning()) {
|
|
12782
|
+
await updateManagedCard(channel, evt.messageId, buildRestartingCard("foreground")).catch(() => void 0);
|
|
12783
|
+
return;
|
|
12784
|
+
}
|
|
12785
|
+
await updateManagedCard(channel, evt.messageId, buildRestartingCard("restarting")).catch(() => void 0);
|
|
12786
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
12787
|
+
await restartDaemon().catch((e) => log.fail("console", e, { phase: "restart" }));
|
|
12788
|
+
})();
|
|
12632
12789
|
}).on(DM.update, ({ evt }) => {
|
|
12633
12790
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12634
12791
|
void (async () => {
|
|
@@ -12651,10 +12808,24 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
12651
12808
|
void (async () => {
|
|
12652
12809
|
await new Promise((r) => setTimeout(r, CARD_SETTLE_MS));
|
|
12653
12810
|
const from = currentVersion();
|
|
12811
|
+
const release2 = acquireUpdateLock();
|
|
12812
|
+
if (!release2) {
|
|
12813
|
+
await updateManagedCard(
|
|
12814
|
+
channel,
|
|
12815
|
+
evt.messageId,
|
|
12816
|
+
buildUpdateCard({ phase: "error", from, message: "\u5DF2\u6709\u4E00\u4E2A\u66F4\u65B0\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5\u3002" })
|
|
12817
|
+
).catch(() => void 0);
|
|
12818
|
+
return;
|
|
12819
|
+
}
|
|
12654
12820
|
await updateManagedCard(channel, evt.messageId, buildUpdateCard({ phase: "updating", from })).catch(
|
|
12655
12821
|
() => void 0
|
|
12656
12822
|
);
|
|
12657
|
-
|
|
12823
|
+
let res;
|
|
12824
|
+
try {
|
|
12825
|
+
res = await installLatest();
|
|
12826
|
+
} finally {
|
|
12827
|
+
release2();
|
|
12828
|
+
}
|
|
12658
12829
|
if (!res.ok) {
|
|
12659
12830
|
log.info("console", "update-failed", { from });
|
|
12660
12831
|
await updateManagedCard(
|
|
@@ -13959,6 +14130,9 @@ ${facts}`;
|
|
|
13959
14130
|
case DM.reconnect:
|
|
13960
14131
|
await sendDm(buildReconnectCard(channel.getConnectionStatus?.()?.state ?? "unknown"));
|
|
13961
14132
|
break;
|
|
14133
|
+
case DM.restart:
|
|
14134
|
+
await sendDm(buildRestartConfirmCard(channel.getConnectionStatus?.()?.state ?? "unknown"));
|
|
14135
|
+
break;
|
|
13962
14136
|
case DM.usage: {
|
|
13963
14137
|
const { messageId } = await sendDm(buildUsageCard({ phase: "loading" }));
|
|
13964
14138
|
let state;
|
|
@@ -14673,8 +14847,12 @@ function createAdminService(deps = {}) {
|
|
|
14673
14847
|
dev: false
|
|
14674
14848
|
}));
|
|
14675
14849
|
},
|
|
14850
|
+
updateStatus() {
|
|
14851
|
+
return readUpdateStatus();
|
|
14852
|
+
},
|
|
14676
14853
|
async applyUpdate() {
|
|
14677
14854
|
if (!deps.applyUpdate) throw new NotWiredYetError("\u2B06\uFE0F \u5347\u7EA7");
|
|
14855
|
+
clearUpdateStatus();
|
|
14678
14856
|
deps.applyUpdate();
|
|
14679
14857
|
},
|
|
14680
14858
|
async hostDoctor() {
|
|
@@ -14902,14 +15080,31 @@ async function doUpdate() {
|
|
|
14902
15080
|
log.info("daemon-control", "update-skipped-dev", {});
|
|
14903
15081
|
return;
|
|
14904
15082
|
}
|
|
14905
|
-
const
|
|
15083
|
+
const release2 = acquireUpdateLock();
|
|
15084
|
+
if (!release2) {
|
|
15085
|
+
log.info("daemon-control", "update-skipped-in-progress", {});
|
|
15086
|
+
appendServiceErr("daemon-control", "update already in progress (lock held by another trigger); skipped");
|
|
15087
|
+
return;
|
|
15088
|
+
}
|
|
15089
|
+
const from = currentVersion();
|
|
15090
|
+
writeUpdateStatus({ phase: "installing", from, at: Date.now() });
|
|
15091
|
+
let res;
|
|
15092
|
+
try {
|
|
15093
|
+
res = await installLatest();
|
|
15094
|
+
} finally {
|
|
15095
|
+
release2();
|
|
15096
|
+
}
|
|
14906
15097
|
log.info("daemon-control", "update-installed", { ok: res.ok });
|
|
14907
15098
|
appendServiceErr("daemon-control", `update-installed ok=${res.ok}`);
|
|
14908
15099
|
if (!res.ok) {
|
|
14909
15100
|
appendServiceErr("daemon-control", "install failed \u2014 keeping old version, not restarting");
|
|
15101
|
+
writeUpdateStatus({ phase: "error", ok: false, from, message: `\u5B89\u88C5\u5931\u8D25\uFF1A${res.message}`.slice(0, 300), at: Date.now() });
|
|
14910
15102
|
return;
|
|
14911
15103
|
}
|
|
15104
|
+
const to = currentVersion();
|
|
15105
|
+
writeUpdateStatus({ phase: "restarting", ok: true, from, to, at: Date.now() });
|
|
14912
15106
|
await doRestart("update");
|
|
15107
|
+
writeUpdateStatus({ phase: "done", ok: true, from, to, at: Date.now() });
|
|
14913
15108
|
}
|
|
14914
15109
|
async function doRestart(phase) {
|
|
14915
15110
|
if (!isServiceRunning()) {
|
|
@@ -15671,6 +15866,7 @@ ${UI_PURE_JS}
|
|
|
15671
15866
|
var lastUpdate = null; // /api/update/check \u7ED3\u679C\u7F13\u5B58\uFF1A\u603B\u89C8 5s \u91CD\u6E32\u4E0D\u91CD\u67E5\uFF08\u6BCF\u6B21\u67E5\u90FD spawn npm view\uFF09
|
|
15672
15867
|
var updateCheckedAt = 0; // \u4E0A\u6B21\u68C0\u67E5\u65F6\u95F4\u6233\uFF1B\u62FF\u5230\u7ED3\u679C\u8D85 30min\u3001\u8F6F\u5931\u8D25\u8D85 ~2min \u624D\u968F\u91CD\u6E32\u81EA\u52A8\u590D\u67E5
|
|
15673
15868
|
var updateChecking = false;// in-flight \u95F8\uFF1Anpm view \u53EF\u80FD\u6570\u79D2~20s\uFF0C\u671F\u95F4 5s \u91CD\u6E32\u4E0D\u53E0\u53D1\u7B2C\u4E8C\u4E2A\u8BF7\u6C42
|
|
15869
|
+
var updateInFlight = false; // \u300C\u5347\u7EA7\u5E76\u91CD\u542F\u300Din-flight\uFF1A\u7981\u7528\u6309\u94AE\u9632\u91CD\u590D\u70B9\u51FB\uFF08\u5E76\u53D1\u89E6\u53D1\u4F1A\u7ED5\u8FC7\u670D\u52A1\u7AEF\u66F4\u65B0\u9501\u7684\u7A84\u7A97\uFF09\uFF0C\u5E76\u9A71\u52A8\u8F6E\u8BE2\u7ED3\u679C
|
|
15674
15870
|
var drawerProject = null; // \u62BD\u5C49\u91CC\u6253\u5F00\u7684\u9879\u76EE\u540D
|
|
15675
15871
|
var diagBotId = null; // diag \u5C5E\u4E8E\u54EA\u4E2A bot\uFF08\u9632\u4E32\u53F0\uFF09
|
|
15676
15872
|
var bkDetailId = null; // \u540E\u7AEF Agent \u8BE6\u60C5\u89C6\u56FE\u5F53\u524D\u5C55\u5F00\u7684\u540E\u7AEF id\uFF08null=\u5217\u8868\uFF09
|
|
@@ -16580,10 +16776,13 @@ ${UI_PURE_JS}
|
|
|
16580
16776
|
if (u.hasUpdate && u.latest) {
|
|
16581
16777
|
box.appendChild(el('div', null, '\u{1F195} \u6709\u65B0\u7248 v' + u.latest + '\uFF08\u5F53\u524D v' + u.current + '\uFF09'));
|
|
16582
16778
|
var row = el('div', 'statline');
|
|
16583
|
-
|
|
16584
|
-
|
|
16779
|
+
// updateInFlight \u671F\u95F4\u663E\u793A\u7981\u7528\u7684\u300C\u66F4\u65B0\u4E2D\u2026\u300D\u2014\u2014\u5373\u4F7F\u5361\u7247\u6BCF 5s \u91CD\u6E32\uFF0C\u6309\u94AE\u4E5F\u4FDD\u6301\u7981\u7528\uFF0C
|
|
16780
|
+
// \u6321\u4F4F\u91CD\u590D\u63D0\u4EA4\uFF08\u91CD\u590D\u89E6\u53D1\u662F\u5E76\u53D1\u7ED5\u8FC7\u670D\u52A1\u7AEF\u66F4\u65B0\u9501\u7684\u5165\u53E3\uFF09\u3002
|
|
16781
|
+
var up = el('button', 'btn primary', updateInFlight ? '\u23F3 \u66F4\u65B0\u4E2D\u2026' : ('\u2B06\uFE0F \u66F4\u65B0\u5E76\u91CD\u542F \xB7 v' + u.latest));
|
|
16782
|
+
if (updateInFlight) { up.disabled = true; }
|
|
16783
|
+
else { up.onclick = function () { askUpdate(u.latest); }; }
|
|
16585
16784
|
row.appendChild(up);
|
|
16586
|
-
row.appendChild(el('span', 'note', '\u9ED8\u8BA4\u53EA\u68C0\u6D4B\u4E0D\u81EA\u52A8\u5347\u7EA7\uFF1B\u70B9\u4E0A\u65B9\u6309\u94AE\u88C5\u6700\u65B0\u7248\u5E76\u81EA\u52A8\u91CD\u542F\u3002'));
|
|
16785
|
+
row.appendChild(el('span', 'note', updateInFlight ? '\u5B89\u88C5\u4E2D\uFF0C\u5B8C\u6210\u540E\u4F1A\u81EA\u52A8\u91CD\u542F\u2026' : '\u9ED8\u8BA4\u53EA\u68C0\u6D4B\u4E0D\u81EA\u52A8\u5347\u7EA7\uFF1B\u70B9\u4E0A\u65B9\u6309\u94AE\u88C5\u6700\u65B0\u7248\u5E76\u81EA\u52A8\u91CD\u542F\u3002'));
|
|
16587
16786
|
box.appendChild(row);
|
|
16588
16787
|
} else {
|
|
16589
16788
|
box.appendChild(el('div', 'note', '\u2705 \u5DF2\u662F\u6700\u65B0\u7248 v' + u.current + (u.latest ? '' : '\uFF08\u6700\u65B0\u7248\u67E5\u8BE2\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\uFF09')));
|
|
@@ -16633,9 +16832,52 @@ ${UI_PURE_JS}
|
|
|
16633
16832
|
'\u91CD\u542F\u671F\u95F4\u6240\u6709\u7FA4\u77ED\u6682\u65E0\u54CD\u5E94\uFF1B\u6B63\u5728\u8FDB\u884C\u7684\u4F1A\u8BDD\u4F1A\u88AB\u4F18\u96C5\u5173\u95ED\u3002',
|
|
16634
16833
|
],
|
|
16635
16834
|
confirmLabel: '\u786E\u8BA4\u66F4\u65B0',
|
|
16636
|
-
onConfirm: function () {
|
|
16835
|
+
onConfirm: function () { postUpdate(latest); },
|
|
16637
16836
|
});
|
|
16638
16837
|
}
|
|
16838
|
+
// \u5347\u7EA7\uFF1APOST \u540E\u8F6E\u8BE2 /api/update/status\uFF0C\u5931\u8D25\u4F1A\u660E\u786E\u62A5\u51FA\u6765\uFF08\u5426\u5219 detached helper \u7684\u5931\u8D25\u5BF9
|
|
16839
|
+
// \u7F51\u9875\u7AEF\u5B8C\u5168\u9759\u9ED8\u3001\u7528\u6237\u53EA\u770B\u5230\u300C\u5DF2\u53D1\u8D77\u300D\uFF09\uFF1B\u6210\u529F\u4F1A\u91CD\u542F daemon \u2192 \u8FDE\u63A5\u77ED\u6682\u65AD\u5F00\uFF0C\u7531\u7248\u672C\u53D8\u66F4\u6D41
|
|
16840
|
+
// \u63A5\u7BA1\u663E\u793A\u300C\u5DF2\u662F\u6700\u65B0\u7248\u300D\u3002updateInFlight \u517C\u4F5C\u6309\u94AE in-flight \u95F8\uFF0C\u6321\u4F4F\u91CD\u590D\u70B9\u51FB\u3002
|
|
16841
|
+
function postUpdate(latest) {
|
|
16842
|
+
if (updateInFlight) { toast('\u23F3 \u5347\u7EA7\u5DF2\u5728\u8FDB\u884C\u4E2D\u2026'); return; }
|
|
16843
|
+
updateInFlight = true;
|
|
16844
|
+
var box = $('updateBody'); if (box && lastUpdate) renderUpdate(box, lastUpdate); // \u7ACB\u523B\u628A\u6309\u94AE\u5207\u6210\u7981\u7528\u7684\u300C\u66F4\u65B0\u4E2D\u2026\u300D
|
|
16845
|
+
fetch('/api/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
|
|
16846
|
+
.then(function (r) { return r.json().then(function (j) { return { status: r.status, body: j }; }); })
|
|
16847
|
+
.then(function (resp) {
|
|
16848
|
+
if (resp.status !== 202) {
|
|
16849
|
+
updateInFlight = false;
|
|
16850
|
+
toast((resp.status === 501 ? '\u23F3 ' : '\u274C ') + (resp.body.message || ('HTTP ' + resp.status)));
|
|
16851
|
+
var b = $('updateBody'); if (b) loadUpdate(b);
|
|
16852
|
+
return;
|
|
16853
|
+
}
|
|
16854
|
+
toast('\u2705 ' + (resp.body.message || '\u5347\u7EA7\u5DF2\u53D1\u8D77'));
|
|
16855
|
+
pollUpdateStatus(0, latest);
|
|
16856
|
+
})
|
|
16857
|
+
.catch(function () { updateInFlight = false; toast('\u274C \u8BF7\u6C42\u5931\u8D25'); });
|
|
16858
|
+
}
|
|
16859
|
+
function pollUpdateStatus(tries, latest) {
|
|
16860
|
+
if (tries > 60) { updateInFlight = false; toast('\u23F3 \u5B89\u88C5\u4ECD\u5728\u540E\u53F0\u8FDB\u884C\uFF0C\u7A0D\u540E\u5237\u65B0\u9875\u9762\u67E5\u770B'); return; } // ~90s \u515C\u5E95\uFF1A\u522B\u65E0\u9650\u8F6E\u8BE2
|
|
16861
|
+
fetch('/api/update/status').then(function (r) { return r.json(); })
|
|
16862
|
+
.then(function (s) {
|
|
16863
|
+
if (!s || s.phase === 'installing' || s.phase === 'restarting') {
|
|
16864
|
+
setTimeout(function () { pollUpdateStatus(tries + 1, latest); }, 1500);
|
|
16865
|
+
return;
|
|
16866
|
+
}
|
|
16867
|
+
updateInFlight = false;
|
|
16868
|
+
if (s.phase === 'error') {
|
|
16869
|
+
toast('\u274C \u5347\u7EA7\u5931\u8D25\uFF1A' + (s.message || '\u672A\u77E5\u9519\u8BEF'));
|
|
16870
|
+
var box = $('updateBody'); if (box) loadUpdate(box); // \u6062\u590D\u53EF\u70B9\u7684\u66F4\u65B0\u5361\u4F9B\u91CD\u8BD5
|
|
16871
|
+
} else if (s.phase === 'done') {
|
|
16872
|
+
toast('\u2705 \u5DF2\u66F4\u65B0\u5230 v' + (s.to || latest || '') + '\uFF0C\u6B63\u5728\u91CD\u542F\u2026');
|
|
16873
|
+
lastUpdate = null; updateCheckedAt = 0; // \u4F5C\u5E9F\u7F13\u5B58\uFF1A\u91CD\u542F\u540E\u7248\u672C\u53D8\u66F4\u6D41\u4F1A\u7FFB\u6210\u300C\u5DF2\u662F\u6700\u65B0\u7248\u300D
|
|
16874
|
+
}
|
|
16875
|
+
})
|
|
16876
|
+
.catch(function () {
|
|
16877
|
+
// \u62FF\u4E0D\u5230\u72B6\u6001\uFF1A\u591A\u534A daemon \u6B63\u5728\u91CD\u542F\uFF08\u6210\u529F\u8DEF\u5F84\uFF09\u2192 \u4EA4\u7ED9\u91CD\u8FDE/\u7248\u672C\u53D8\u66F4\u6D41\u6536\u5C3E\u3002
|
|
16878
|
+
updateInFlight = false;
|
|
16879
|
+
});
|
|
16880
|
+
}
|
|
16639
16881
|
// 202 = \u5DF2\u53D1\u8D77\uFF08detached helper \u63A5\u7BA1\uFF09\uFF1B501 = \u53EA\u8BFB\u9884\u89C8\uFF08\u65E0 daemon\uFF09\u3002
|
|
16640
16882
|
function postAction(path, label) {
|
|
16641
16883
|
fetch(path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
|
|
@@ -17762,6 +18004,10 @@ function createWebServer(opts) {
|
|
|
17762
18004
|
sendJson(res, 200, await opts.service.checkUpdate());
|
|
17763
18005
|
return;
|
|
17764
18006
|
}
|
|
18007
|
+
if (req.method === "GET" && pathName === "/api/update/status") {
|
|
18008
|
+
sendJson(res, 200, opts.service.updateStatus());
|
|
18009
|
+
return;
|
|
18010
|
+
}
|
|
17765
18011
|
if (req.method === "POST" && pathName === "/api/update") {
|
|
17766
18012
|
try {
|
|
17767
18013
|
await opts.service.applyUpdate();
|
|
@@ -18475,7 +18721,7 @@ async function runSupervisor(bots) {
|
|
|
18475
18721
|
|
|
18476
18722
|
// src/core/single-instance.ts
|
|
18477
18723
|
init_paths();
|
|
18478
|
-
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as
|
|
18724
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
18479
18725
|
import { dirname as dirname15 } from "path";
|
|
18480
18726
|
var CLAIM_ATTEMPTS = 5;
|
|
18481
18727
|
var PID_REUSE_SLACK_MS = 15e3;
|
|
@@ -18526,7 +18772,7 @@ function acquireSingleInstanceLock(appId, file = paths.processesFile) {
|
|
|
18526
18772
|
const record = { pid: process.pid, appId, startedAt: Date.now() };
|
|
18527
18773
|
for (let attempt = 0; attempt < CLAIM_ATTEMPTS; attempt++) {
|
|
18528
18774
|
try {
|
|
18529
|
-
|
|
18775
|
+
writeFileSync4(file, `${JSON.stringify(record)}
|
|
18530
18776
|
`, { flag: "wx" });
|
|
18531
18777
|
const release2 = () => {
|
|
18532
18778
|
try {
|
package/package.json
CHANGED