weacpx 0.6.1 → 0.7.0
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/bridge/bridge-main.js +7 -1
- package/dist/channels/types.d.ts +9 -0
- package/dist/cli.js +948 -257
- package/dist/commands/handlers/session-handler.d.ts +4 -2
- package/dist/commands/handlers/session-list-marker.d.ts +1 -0
- package/dist/commands/parse-command.d.ts +3 -0
- package/dist/plugin-api.d.ts +9 -0
- package/dist/plugin-api.js +157 -0
- package/dist/{weixin/messaging → runtime}/conversation-executor.d.ts +1 -1
- package/dist/runtime/core-home.d.ts +26 -0
- package/dist/runtime/turn-lane.d.ts +2 -0
- package/dist/sessions/active-turn-registry.d.ts +6 -0
- package/dist/sessions/session-service.d.ts +37 -2
- package/dist/state/types.d.ts +7 -0
- package/dist/weixin/agent/interface.d.ts +1 -0
- package/dist/weixin/api/config-cache.d.ts +18 -1
- package/dist/weixin/bot.d.ts +11 -0
- package/dist/weixin/messaging/completion-notice.d.ts +2 -0
- package/dist/weixin/messaging/foreground-gate.d.ts +3 -0
- package/dist/weixin/messaging/handle-weixin-message-turn.d.ts +4 -0
- package/dist/weixin/messaging/inbound.d.ts +7 -0
- package/dist/weixin/messaging/quota-manager.d.ts +15 -1
- package/dist/weixin/monitor/monitor.d.ts +8 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -48,6 +48,17 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
48
48
|
var __promiseAll = (args) => Promise.all(args);
|
|
49
49
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
50
50
|
|
|
51
|
+
// src/runtime/core-home.ts
|
|
52
|
+
import { join } from "node:path";
|
|
53
|
+
function coreHomeDir(home) {
|
|
54
|
+
return join(home, CORE_HOME_DIR_NAME);
|
|
55
|
+
}
|
|
56
|
+
function coreHomeDisplayPath(...segments) {
|
|
57
|
+
return ["~", CORE_HOME_DIR_NAME, ...segments].join("/");
|
|
58
|
+
}
|
|
59
|
+
var CORE_HOME_DIR_NAME = ".weacpx";
|
|
60
|
+
var init_core_home = () => {};
|
|
61
|
+
|
|
51
62
|
// node_modules/graceful-fs/polyfills.js
|
|
52
63
|
var require_polyfills = __commonJS((exports, module) => {
|
|
53
64
|
var constants = __require("constants");
|
|
@@ -2777,6 +2788,9 @@ class DaemonStatusStore {
|
|
|
2777
2788
|
if (error.code === "ENOENT") {
|
|
2778
2789
|
return null;
|
|
2779
2790
|
}
|
|
2791
|
+
if (error instanceof SyntaxError) {
|
|
2792
|
+
return null;
|
|
2793
|
+
}
|
|
2780
2794
|
throw error;
|
|
2781
2795
|
}
|
|
2782
2796
|
}
|
|
@@ -3151,7 +3165,7 @@ var init_create_daemon_controller = __esm(() => {
|
|
|
3151
3165
|
|
|
3152
3166
|
// src/orchestration/orchestration-ipc.ts
|
|
3153
3167
|
import { createHash } from "node:crypto";
|
|
3154
|
-
import { join } from "node:path";
|
|
3168
|
+
import { join as join2 } from "node:path";
|
|
3155
3169
|
function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
3156
3170
|
if (platform === "win32") {
|
|
3157
3171
|
const suffix = createHash("sha256").update(runtimeDir).digest("hex").slice(0, 12);
|
|
@@ -3162,7 +3176,7 @@ function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
|
3162
3176
|
}
|
|
3163
3177
|
return {
|
|
3164
3178
|
kind: "unix",
|
|
3165
|
-
path:
|
|
3179
|
+
path: join2(runtimeDir, "orchestration.sock")
|
|
3166
3180
|
};
|
|
3167
3181
|
}
|
|
3168
3182
|
function createOrchestrationEndpoint(path2, platform = process.platform) {
|
|
@@ -3182,25 +3196,26 @@ function encodeOrchestrationRpcResponse(response) {
|
|
|
3182
3196
|
var init_orchestration_ipc = () => {};
|
|
3183
3197
|
|
|
3184
3198
|
// src/daemon/daemon-files.ts
|
|
3185
|
-
import { dirname as dirname4, join as
|
|
3199
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
3186
3200
|
function resolveDaemonPaths(options) {
|
|
3187
|
-
const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) :
|
|
3201
|
+
const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : join3(coreHomeDir(options.home), "runtime"));
|
|
3188
3202
|
return {
|
|
3189
3203
|
runtimeDir,
|
|
3190
|
-
pidFile:
|
|
3191
|
-
statusFile:
|
|
3192
|
-
stdoutLog:
|
|
3193
|
-
stderrLog:
|
|
3194
|
-
appLog:
|
|
3204
|
+
pidFile: join3(runtimeDir, "daemon.pid"),
|
|
3205
|
+
statusFile: join3(runtimeDir, "status.json"),
|
|
3206
|
+
stdoutLog: join3(runtimeDir, "stdout.log"),
|
|
3207
|
+
stderrLog: join3(runtimeDir, "stderr.log"),
|
|
3208
|
+
appLog: join3(runtimeDir, "app.log")
|
|
3195
3209
|
};
|
|
3196
3210
|
}
|
|
3197
3211
|
function resolveRuntimeDirFromConfigPath(configPath) {
|
|
3198
|
-
return
|
|
3212
|
+
return join3(dirname4(configPath), "runtime");
|
|
3199
3213
|
}
|
|
3200
3214
|
function resolveDaemonOrchestrationSocketPath(runtimeDir, platform = process.platform) {
|
|
3201
3215
|
return resolveOrchestrationEndpoint(runtimeDir, platform).path;
|
|
3202
3216
|
}
|
|
3203
3217
|
var init_daemon_files = __esm(() => {
|
|
3218
|
+
init_core_home();
|
|
3204
3219
|
init_orchestration_ipc();
|
|
3205
3220
|
});
|
|
3206
3221
|
|
|
@@ -10524,9 +10539,56 @@ function normalizeId(input) {
|
|
|
10524
10539
|
var init_scheduled_service = () => {};
|
|
10525
10540
|
|
|
10526
10541
|
// src/plugins/plugin-home.ts
|
|
10527
|
-
import {
|
|
10542
|
+
import { readFileSync } from "node:fs";
|
|
10543
|
+
import { copyFile, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
10528
10544
|
import { homedir as homedir3 } from "node:os";
|
|
10529
|
-
import { join as
|
|
10545
|
+
import { dirname as dirname6, join as join5 } from "node:path";
|
|
10546
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10547
|
+
function resolveCoreRoot() {
|
|
10548
|
+
try {
|
|
10549
|
+
let dir = dirname6(fileURLToPath2(import.meta.url));
|
|
10550
|
+
for (let depth = 0;depth < 12; depth++) {
|
|
10551
|
+
try {
|
|
10552
|
+
const pkg = JSON.parse(readFileSync(join5(dir, "package.json"), "utf-8"));
|
|
10553
|
+
if (pkg.name && CORE_PACKAGE_NAMES.includes(pkg.name))
|
|
10554
|
+
return dir;
|
|
10555
|
+
} catch {}
|
|
10556
|
+
const parent = dirname6(dir);
|
|
10557
|
+
if (parent === dir)
|
|
10558
|
+
break;
|
|
10559
|
+
dir = parent;
|
|
10560
|
+
}
|
|
10561
|
+
return null;
|
|
10562
|
+
} catch {
|
|
10563
|
+
return null;
|
|
10564
|
+
}
|
|
10565
|
+
}
|
|
10566
|
+
async function ensureCoreResolution(pluginHome) {
|
|
10567
|
+
const root = resolveCoreRoot();
|
|
10568
|
+
if (!root)
|
|
10569
|
+
return;
|
|
10570
|
+
const srcJs = join5(root, "dist", "plugin-api.js");
|
|
10571
|
+
for (const name of CORE_PACKAGE_NAMES) {
|
|
10572
|
+
const targetDir = join5(pluginHome, "node_modules", name);
|
|
10573
|
+
const dstJs = join5(targetDir, "plugin-api.js");
|
|
10574
|
+
await mkdir6(targetDir, { recursive: true });
|
|
10575
|
+
try {
|
|
10576
|
+
await copyFile(srcJs, dstJs);
|
|
10577
|
+
} catch (error2) {
|
|
10578
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
10579
|
+
console.warn(`weacpx: skipped plugin-api resolution shim for "${name}" — could not copy ${srcJs} (${message}). ` + `Channel plugins importing "${name}/plugin-api" at runtime may fail to load.`);
|
|
10580
|
+
continue;
|
|
10581
|
+
}
|
|
10582
|
+
await writeFile4(join5(targetDir, "package.json"), JSON.stringify({
|
|
10583
|
+
name,
|
|
10584
|
+
type: "module",
|
|
10585
|
+
exports: {
|
|
10586
|
+
"./plugin-api": "./plugin-api.js"
|
|
10587
|
+
}
|
|
10588
|
+
}, null, 2) + `
|
|
10589
|
+
`);
|
|
10590
|
+
}
|
|
10591
|
+
}
|
|
10530
10592
|
function coerceMissing(value) {
|
|
10531
10593
|
if (value === undefined)
|
|
10532
10594
|
return;
|
|
@@ -10546,10 +10608,10 @@ function resolvePluginHome(input = {}) {
|
|
|
10546
10608
|
if (envOverride)
|
|
10547
10609
|
return envOverride;
|
|
10548
10610
|
const home = coerceMissing(input.home) ?? coerceMissing(process.env.HOME) ?? homedir3();
|
|
10549
|
-
return
|
|
10611
|
+
return join5(coreHomeDir(home), "plugins");
|
|
10550
10612
|
}
|
|
10551
10613
|
async function normalizePluginHomeManifest(pluginHome) {
|
|
10552
|
-
const manifestPath =
|
|
10614
|
+
const manifestPath = join5(pluginHome, "package.json");
|
|
10553
10615
|
let raw;
|
|
10554
10616
|
try {
|
|
10555
10617
|
raw = await readFile6(manifestPath, "utf8");
|
|
@@ -10571,13 +10633,18 @@ async function normalizePluginHomeManifest(pluginHome) {
|
|
|
10571
10633
|
}
|
|
10572
10634
|
async function ensurePluginHome(pluginHome) {
|
|
10573
10635
|
await mkdir6(pluginHome, { recursive: true, mode: 448 });
|
|
10574
|
-
await writeFile4(
|
|
10636
|
+
await writeFile4(join5(pluginHome, "package.json"), JSON.stringify({ private: true, type: "module" }, null, 2) + `
|
|
10575
10637
|
`, { flag: "wx" }).catch((error2) => {
|
|
10576
10638
|
if (error2.code !== "EEXIST")
|
|
10577
10639
|
throw error2;
|
|
10578
10640
|
});
|
|
10641
|
+
await ensureCoreResolution(pluginHome);
|
|
10579
10642
|
}
|
|
10580
|
-
var
|
|
10643
|
+
var CORE_PACKAGE_NAMES;
|
|
10644
|
+
var init_plugin_home = __esm(() => {
|
|
10645
|
+
init_core_home();
|
|
10646
|
+
CORE_PACKAGE_NAMES = ["weacpx", "xacpx"];
|
|
10647
|
+
});
|
|
10581
10648
|
|
|
10582
10649
|
// src/weixin/storage/ensure-dir.ts
|
|
10583
10650
|
import fs2 from "node:fs";
|
|
@@ -12820,11 +12887,13 @@ function resolveContextTokenFilePath(accountId) {
|
|
|
12820
12887
|
return path6.join(resolveStateDir(), "openclaw-weixin", "accounts", `${accountId}.context-tokens.json`);
|
|
12821
12888
|
}
|
|
12822
12889
|
function persistContextTokens(accountId) {
|
|
12890
|
+
pruneContextTokensForAccount(accountId);
|
|
12823
12891
|
const prefix = `${accountId}:`;
|
|
12824
12892
|
const tokens = {};
|
|
12825
|
-
for (const [k,
|
|
12826
|
-
if (k.startsWith(prefix))
|
|
12827
|
-
tokens[k.slice(prefix.length)] =
|
|
12893
|
+
for (const [k, entry] of contextTokenStore) {
|
|
12894
|
+
if (k.startsWith(prefix)) {
|
|
12895
|
+
tokens[k.slice(prefix.length)] = { token: entry.token, updatedAt: entry.updatedAt };
|
|
12896
|
+
}
|
|
12828
12897
|
}
|
|
12829
12898
|
const filePath = resolveContextTokenFilePath(accountId);
|
|
12830
12899
|
try {
|
|
@@ -12841,12 +12910,15 @@ function restoreContextTokens(accountId) {
|
|
|
12841
12910
|
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
12842
12911
|
const tokens = JSON.parse(raw);
|
|
12843
12912
|
let count = 0;
|
|
12844
|
-
for (const [userId,
|
|
12845
|
-
|
|
12846
|
-
|
|
12913
|
+
for (const [userId, value] of Object.entries(tokens)) {
|
|
12914
|
+
const entry = parsePersistedContextToken(value);
|
|
12915
|
+
if (entry) {
|
|
12916
|
+
contextTokenStore.set(contextTokenKey(accountId, userId), entry);
|
|
12847
12917
|
count++;
|
|
12848
12918
|
}
|
|
12849
12919
|
}
|
|
12920
|
+
pruneContextTokensForAccount(accountId);
|
|
12921
|
+
persistContextTokens(accountId);
|
|
12850
12922
|
logger.info(`restoreContextTokens: restored ${count} tokens for account=${accountId}`);
|
|
12851
12923
|
} catch (err) {
|
|
12852
12924
|
logger.warn(`restoreContextTokens: failed to read ${filePath}: ${String(err)}`);
|
|
@@ -12870,15 +12942,64 @@ function clearContextTokensForAccount(accountId) {
|
|
|
12870
12942
|
function setContextToken(accountId, userId, token) {
|
|
12871
12943
|
const k = contextTokenKey(accountId, userId);
|
|
12872
12944
|
logger.debug(`setContextToken: key=${k}`);
|
|
12873
|
-
contextTokenStore.set(k, token);
|
|
12945
|
+
contextTokenStore.set(k, { token, updatedAt: contextTokenRetention.now() });
|
|
12874
12946
|
persistContextTokens(accountId);
|
|
12875
12947
|
}
|
|
12876
12948
|
function getContextToken(accountId, userId) {
|
|
12877
12949
|
const k = contextTokenKey(accountId, normalizeWeixinUserIdFromChatKey(userId));
|
|
12878
|
-
|
|
12950
|
+
pruneContextTokensForAccount(accountId);
|
|
12951
|
+
const val = contextTokenStore.get(k)?.token;
|
|
12879
12952
|
logger.debug(`getContextToken: key=${k} found=${val !== undefined} storeSize=${contextTokenStore.size}`);
|
|
12880
12953
|
return val;
|
|
12881
12954
|
}
|
|
12955
|
+
function parsePersistedContextToken(value) {
|
|
12956
|
+
if (typeof value === "string" && value) {
|
|
12957
|
+
return { token: value, updatedAt: contextTokenRetention.now() };
|
|
12958
|
+
}
|
|
12959
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
12960
|
+
return null;
|
|
12961
|
+
}
|
|
12962
|
+
const record3 = value;
|
|
12963
|
+
if (typeof record3.token !== "string" || record3.token.length === 0) {
|
|
12964
|
+
return null;
|
|
12965
|
+
}
|
|
12966
|
+
const updatedAt = typeof record3.updatedAt === "number" && Number.isFinite(record3.updatedAt) ? record3.updatedAt : contextTokenRetention.now();
|
|
12967
|
+
return { token: record3.token, updatedAt };
|
|
12968
|
+
}
|
|
12969
|
+
function pruneContextTokensForAccount(accountId) {
|
|
12970
|
+
const prefix = `${accountId}:`;
|
|
12971
|
+
const now = contextTokenRetention.now();
|
|
12972
|
+
const entries = [...contextTokenStore.entries()].filter(([key]) => key.startsWith(prefix));
|
|
12973
|
+
for (const [key, entry] of entries) {
|
|
12974
|
+
if (now - entry.updatedAt > contextTokenRetention.tokenTtlMs) {
|
|
12975
|
+
contextTokenStore.delete(key);
|
|
12976
|
+
}
|
|
12977
|
+
}
|
|
12978
|
+
const freshEntries = [...contextTokenStore.entries()].filter(([key]) => key.startsWith(prefix)).sort((a, b) => a[1].updatedAt - b[1].updatedAt);
|
|
12979
|
+
const excess = freshEntries.length - contextTokenRetention.maxTokensPerAccount;
|
|
12980
|
+
for (let i = 0;i < excess; i++) {
|
|
12981
|
+
const key = freshEntries[i]?.[0];
|
|
12982
|
+
if (key)
|
|
12983
|
+
contextTokenStore.delete(key);
|
|
12984
|
+
}
|
|
12985
|
+
}
|
|
12986
|
+
function normalizeContextTokenRetention(options = {}) {
|
|
12987
|
+
return {
|
|
12988
|
+
maxTokensPerAccount: normalizePositiveInt(options.maxTokensPerAccount, DEFAULT_CONTEXT_TOKEN_MAX_PER_ACCOUNT),
|
|
12989
|
+
tokenTtlMs: normalizeNonNegativeMs(options.tokenTtlMs, DEFAULT_CONTEXT_TOKEN_TTL_MS),
|
|
12990
|
+
now: options.now ?? (() => Date.now())
|
|
12991
|
+
};
|
|
12992
|
+
}
|
|
12993
|
+
function normalizePositiveInt(value, fallback) {
|
|
12994
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1)
|
|
12995
|
+
return fallback;
|
|
12996
|
+
return Math.floor(value);
|
|
12997
|
+
}
|
|
12998
|
+
function normalizeNonNegativeMs(value, fallback) {
|
|
12999
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
13000
|
+
return fallback;
|
|
13001
|
+
return value;
|
|
13002
|
+
}
|
|
12882
13003
|
function normalizeWeixinUserIdFromChatKey(chatKey) {
|
|
12883
13004
|
const parts = chatKey.split(":");
|
|
12884
13005
|
if (parts[0] === "weixin" && parts[2]) {
|
|
@@ -12945,14 +13066,16 @@ function descriptorFromItem(item) {
|
|
|
12945
13066
|
return { item, kind: "audio" };
|
|
12946
13067
|
return;
|
|
12947
13068
|
}
|
|
12948
|
-
var contextTokenStore;
|
|
13069
|
+
var DEFAULT_CONTEXT_TOKEN_MAX_PER_ACCOUNT = 5000, DEFAULT_CONTEXT_TOKEN_TTL_MS, contextTokenStore, contextTokenRetention;
|
|
12949
13070
|
var init_inbound = __esm(() => {
|
|
12950
13071
|
init_logger();
|
|
12951
13072
|
init_random();
|
|
12952
13073
|
init_types2();
|
|
12953
13074
|
init_state_dir();
|
|
12954
13075
|
init_private_file();
|
|
13076
|
+
DEFAULT_CONTEXT_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
12955
13077
|
contextTokenStore = new Map;
|
|
13078
|
+
contextTokenRetention = normalizeContextTokenRetention();
|
|
12956
13079
|
});
|
|
12957
13080
|
|
|
12958
13081
|
// src/weixin/api/config-cache.ts
|
|
@@ -12960,18 +13083,27 @@ class WeixinConfigManager {
|
|
|
12960
13083
|
apiOpts;
|
|
12961
13084
|
log;
|
|
12962
13085
|
cache = new Map;
|
|
12963
|
-
|
|
13086
|
+
maxEntries;
|
|
13087
|
+
entryTtlMs;
|
|
13088
|
+
now;
|
|
13089
|
+
fetchConfig;
|
|
13090
|
+
constructor(apiOpts, log, options = {}) {
|
|
12964
13091
|
this.apiOpts = apiOpts;
|
|
12965
13092
|
this.log = log;
|
|
13093
|
+
this.maxEntries = normalizePositiveInt2(options.maxEntries, CONFIG_CACHE_DEFAULT_MAX_ENTRIES);
|
|
13094
|
+
this.entryTtlMs = normalizeNonNegativeMs2(options.entryTtlMs, CONFIG_CACHE_ENTRY_TTL_MS);
|
|
13095
|
+
this.now = options.now ?? (() => Date.now());
|
|
13096
|
+
this.fetchConfig = options.getConfig ?? getConfig;
|
|
12966
13097
|
}
|
|
12967
13098
|
async getForUser(userId, contextToken) {
|
|
12968
|
-
const now =
|
|
13099
|
+
const now = this.now();
|
|
13100
|
+
this.prune(now);
|
|
12969
13101
|
const entry = this.cache.get(userId);
|
|
12970
13102
|
const shouldFetch = !entry || now >= entry.nextFetchAt;
|
|
12971
13103
|
if (shouldFetch) {
|
|
12972
13104
|
let fetchOk = false;
|
|
12973
13105
|
try {
|
|
12974
|
-
const resp = await
|
|
13106
|
+
const resp = await this.fetchConfig({
|
|
12975
13107
|
baseUrl: this.apiOpts.baseUrl,
|
|
12976
13108
|
token: this.apiOpts.token,
|
|
12977
13109
|
ilinkUserId: userId,
|
|
@@ -12982,7 +13114,8 @@ class WeixinConfigManager {
|
|
|
12982
13114
|
config: { typingTicket: resp.typing_ticket ?? "" },
|
|
12983
13115
|
everSucceeded: true,
|
|
12984
13116
|
nextFetchAt: now + Math.random() * CONFIG_CACHE_TTL_MS,
|
|
12985
|
-
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS
|
|
13117
|
+
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
|
|
13118
|
+
lastTouchedAt: now
|
|
12986
13119
|
});
|
|
12987
13120
|
this.log(`[weixin] config ${entry?.everSucceeded ? "refreshed" : "cached"} for ${userId}`);
|
|
12988
13121
|
fetchOk = true;
|
|
@@ -12996,24 +13129,72 @@ class WeixinConfigManager {
|
|
|
12996
13129
|
if (entry) {
|
|
12997
13130
|
entry.nextFetchAt = now + nextDelay;
|
|
12998
13131
|
entry.retryDelayMs = nextDelay;
|
|
13132
|
+
entry.lastTouchedAt = now;
|
|
12999
13133
|
} else {
|
|
13000
13134
|
this.cache.set(userId, {
|
|
13001
13135
|
config: { typingTicket: "" },
|
|
13002
13136
|
everSucceeded: false,
|
|
13003
13137
|
nextFetchAt: now + CONFIG_CACHE_INITIAL_RETRY_MS,
|
|
13004
|
-
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS
|
|
13138
|
+
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
|
|
13139
|
+
lastTouchedAt: now
|
|
13005
13140
|
});
|
|
13006
13141
|
}
|
|
13007
13142
|
}
|
|
13143
|
+
} else {
|
|
13144
|
+
entry.lastTouchedAt = now;
|
|
13008
13145
|
}
|
|
13146
|
+
this.enforceMaxEntries();
|
|
13009
13147
|
return this.cache.get(userId)?.config ?? { typingTicket: "" };
|
|
13010
13148
|
}
|
|
13149
|
+
cacheSizeForTests() {
|
|
13150
|
+
this.prune(this.now());
|
|
13151
|
+
return this.cache.size;
|
|
13152
|
+
}
|
|
13153
|
+
hasCachedUserForTests(userId) {
|
|
13154
|
+
this.prune(this.now());
|
|
13155
|
+
return this.cache.has(userId);
|
|
13156
|
+
}
|
|
13157
|
+
prune(now) {
|
|
13158
|
+
const cutoff = now - this.entryTtlMs;
|
|
13159
|
+
for (const [key, entry] of this.cache) {
|
|
13160
|
+
if (entry.lastTouchedAt < cutoff) {
|
|
13161
|
+
this.cache.delete(key);
|
|
13162
|
+
}
|
|
13163
|
+
}
|
|
13164
|
+
this.enforceMaxEntries();
|
|
13165
|
+
}
|
|
13166
|
+
enforceMaxEntries() {
|
|
13167
|
+
while (this.cache.size > this.maxEntries) {
|
|
13168
|
+
let oldestKey;
|
|
13169
|
+
let oldestTouchedAt = Number.POSITIVE_INFINITY;
|
|
13170
|
+
for (const [key, entry] of this.cache) {
|
|
13171
|
+
if (entry.lastTouchedAt < oldestTouchedAt) {
|
|
13172
|
+
oldestTouchedAt = entry.lastTouchedAt;
|
|
13173
|
+
oldestKey = key;
|
|
13174
|
+
}
|
|
13175
|
+
}
|
|
13176
|
+
if (oldestKey === undefined)
|
|
13177
|
+
return;
|
|
13178
|
+
this.cache.delete(oldestKey);
|
|
13179
|
+
}
|
|
13180
|
+
}
|
|
13181
|
+
}
|
|
13182
|
+
function normalizePositiveInt2(value, fallback) {
|
|
13183
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1)
|
|
13184
|
+
return fallback;
|
|
13185
|
+
return Math.floor(value);
|
|
13186
|
+
}
|
|
13187
|
+
function normalizeNonNegativeMs2(value, fallback) {
|
|
13188
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
13189
|
+
return fallback;
|
|
13190
|
+
return value;
|
|
13011
13191
|
}
|
|
13012
|
-
var CONFIG_CACHE_TTL_MS, CONFIG_CACHE_INITIAL_RETRY_MS = 2000, CONFIG_CACHE_MAX_RETRY_MS;
|
|
13192
|
+
var CONFIG_CACHE_TTL_MS, CONFIG_CACHE_INITIAL_RETRY_MS = 2000, CONFIG_CACHE_MAX_RETRY_MS, CONFIG_CACHE_DEFAULT_MAX_ENTRIES = 5000, CONFIG_CACHE_ENTRY_TTL_MS;
|
|
13013
13193
|
var init_config_cache = __esm(() => {
|
|
13014
13194
|
init_api();
|
|
13015
13195
|
CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
13016
13196
|
CONFIG_CACHE_MAX_RETRY_MS = 60 * 60 * 1000;
|
|
13197
|
+
CONFIG_CACHE_ENTRY_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
13017
13198
|
});
|
|
13018
13199
|
|
|
13019
13200
|
// src/weixin/api/session-guard.ts
|
|
@@ -13032,24 +13213,24 @@ var init_session_guard = __esm(() => {
|
|
|
13032
13213
|
pauseUntilMap = new Map;
|
|
13033
13214
|
});
|
|
13034
13215
|
|
|
13035
|
-
// src/
|
|
13216
|
+
// src/runtime/conversation-executor.ts
|
|
13036
13217
|
function createConversationExecutor() {
|
|
13037
13218
|
const states = new Map;
|
|
13038
13219
|
const getState = (conversationId) => {
|
|
13039
13220
|
const existing = states.get(conversationId);
|
|
13040
13221
|
if (existing)
|
|
13041
13222
|
return existing;
|
|
13042
|
-
const created = { activeControls: 0 };
|
|
13223
|
+
const created = { normalTails: new Map, activeControls: 0 };
|
|
13043
13224
|
states.set(conversationId, created);
|
|
13044
13225
|
return created;
|
|
13045
13226
|
};
|
|
13046
13227
|
const cleanupState = (conversationId, state) => {
|
|
13047
|
-
if (
|
|
13228
|
+
if (state.normalTails.size === 0 && state.activeControls === 0) {
|
|
13048
13229
|
states.delete(conversationId);
|
|
13049
13230
|
}
|
|
13050
13231
|
};
|
|
13051
13232
|
return {
|
|
13052
|
-
run(conversationId, lane, task) {
|
|
13233
|
+
run(conversationId, lane, task, sessionKey) {
|
|
13053
13234
|
const state = getState(conversationId);
|
|
13054
13235
|
if (lane === "control") {
|
|
13055
13236
|
state.activeControls += 1;
|
|
@@ -13058,23 +13239,23 @@ function createConversationExecutor() {
|
|
|
13058
13239
|
cleanupState(conversationId, state);
|
|
13059
13240
|
});
|
|
13060
13241
|
}
|
|
13061
|
-
const
|
|
13062
|
-
const
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
state.normalTail = next;
|
|
13242
|
+
const key = sessionKey ?? DEFAULT_SESSION_KEY;
|
|
13243
|
+
const previous = state.normalTails.get(key) ?? Promise.resolve();
|
|
13244
|
+
const next = previous.then(() => task(), () => task());
|
|
13245
|
+
state.normalTails.set(key, next);
|
|
13066
13246
|
return next.finally(() => {
|
|
13067
|
-
if (state.
|
|
13068
|
-
state.
|
|
13247
|
+
if (state.normalTails.get(key) === next) {
|
|
13248
|
+
state.normalTails.delete(key);
|
|
13069
13249
|
}
|
|
13070
13250
|
cleanupState(conversationId, state);
|
|
13071
13251
|
});
|
|
13072
13252
|
}
|
|
13073
13253
|
};
|
|
13074
13254
|
}
|
|
13255
|
+
var DEFAULT_SESSION_KEY = "__chat__";
|
|
13075
13256
|
|
|
13076
13257
|
// src/channels/media-store.ts
|
|
13077
|
-
import { access as access2, mkdir as mkdir7, readdir, rm as
|
|
13258
|
+
import { access as access2, mkdir as mkdir7, readdir, rm as rm5, stat, writeFile as writeFile5 } from "node:fs/promises";
|
|
13078
13259
|
import path7 from "node:path";
|
|
13079
13260
|
|
|
13080
13261
|
class RuntimeMediaStore {
|
|
@@ -13193,7 +13374,7 @@ async function cleanupDir(dir, cutoffMs) {
|
|
|
13193
13374
|
if (entry.isDirectory()) {
|
|
13194
13375
|
const childEmpty = await cleanupDir(full, cutoffMs);
|
|
13195
13376
|
if (childEmpty) {
|
|
13196
|
-
await
|
|
13377
|
+
await rm5(full, { recursive: true, force: true });
|
|
13197
13378
|
} else {
|
|
13198
13379
|
empty = false;
|
|
13199
13380
|
}
|
|
@@ -13203,7 +13384,7 @@ async function cleanupDir(dir, cutoffMs) {
|
|
|
13203
13384
|
if (!s)
|
|
13204
13385
|
continue;
|
|
13205
13386
|
if (s.mtimeMs < cutoffMs) {
|
|
13206
|
-
await
|
|
13387
|
+
await rm5(full, { force: true });
|
|
13207
13388
|
} else {
|
|
13208
13389
|
empty = false;
|
|
13209
13390
|
}
|
|
@@ -13568,6 +13749,18 @@ var init_media_download = __esm(() => {
|
|
|
13568
13749
|
WEIXIN_MEDIA_MAX_BYTES = 100 * 1024 * 1024;
|
|
13569
13750
|
});
|
|
13570
13751
|
|
|
13752
|
+
// src/weixin/messaging/completion-notice.ts
|
|
13753
|
+
function buildBackgroundCompletionNotice(internalAlias, status) {
|
|
13754
|
+
const display = toDisplaySessionAlias(internalAlias);
|
|
13755
|
+
return status === "done" ? `✅ ${display} 已完成,/use ${display} 查看结果` : `⚠️ ${display} 失败,/use ${display} 查看详情`;
|
|
13756
|
+
}
|
|
13757
|
+
function shouldSendBackgroundNotice(reserve) {
|
|
13758
|
+
return reserve ? reserve() : true;
|
|
13759
|
+
}
|
|
13760
|
+
var init_completion_notice = __esm(() => {
|
|
13761
|
+
init_channel_scope();
|
|
13762
|
+
});
|
|
13763
|
+
|
|
13571
13764
|
// src/weixin/messaging/execute-chat-turn.ts
|
|
13572
13765
|
async function executeChatTurn(params) {
|
|
13573
13766
|
let usedReply = false;
|
|
@@ -13595,6 +13788,16 @@ function buildFinalHeadsUp(input) {
|
|
|
13595
13788
|
\uD83D\uDCC4 结果共 ${total} 段,已发 ${sentSoFar} 段。回复 /jx 续看后 ${remaining} 段。`;
|
|
13596
13789
|
}
|
|
13597
13790
|
|
|
13791
|
+
// src/weixin/messaging/foreground-gate.ts
|
|
13792
|
+
function shouldDeliverSegment(isForeground) {
|
|
13793
|
+
return isForeground ? isForeground() : true;
|
|
13794
|
+
}
|
|
13795
|
+
function resolveFinalDisposition(isForeground, canStore) {
|
|
13796
|
+
if (isForeground)
|
|
13797
|
+
return "send";
|
|
13798
|
+
return canStore ? "store" : "drop";
|
|
13799
|
+
}
|
|
13800
|
+
|
|
13598
13801
|
// src/weixin/messaging/markdown-filter.ts
|
|
13599
13802
|
class StreamingMarkdownFilter {
|
|
13600
13803
|
buf = "";
|
|
@@ -14491,8 +14694,8 @@ function normalizeMediaArray(media) {
|
|
|
14491
14694
|
}
|
|
14492
14695
|
|
|
14493
14696
|
// src/logging/rotating-file-writer.ts
|
|
14494
|
-
import { readdir as readdir2, rename, rm as
|
|
14495
|
-
import { basename, dirname as
|
|
14697
|
+
import { readdir as readdir2, rename, rm as rm6, stat as stat2 } from "node:fs/promises";
|
|
14698
|
+
import { basename, dirname as dirname7, join as join7 } from "node:path";
|
|
14496
14699
|
async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
14497
14700
|
let currentSize = 0;
|
|
14498
14701
|
try {
|
|
@@ -14509,10 +14712,10 @@ async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
|
14509
14712
|
return;
|
|
14510
14713
|
}
|
|
14511
14714
|
if (maxFiles <= 0) {
|
|
14512
|
-
await
|
|
14715
|
+
await rm6(filePath, { force: true });
|
|
14513
14716
|
return;
|
|
14514
14717
|
}
|
|
14515
|
-
await
|
|
14718
|
+
await rm6(`${filePath}.${maxFiles}`, { force: true });
|
|
14516
14719
|
for (let index = maxFiles - 1;index >= 1; index -= 1) {
|
|
14517
14720
|
const source = `${filePath}.${index}`;
|
|
14518
14721
|
try {
|
|
@@ -14526,7 +14729,7 @@ async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
|
14526
14729
|
await rename(filePath, `${filePath}.1`);
|
|
14527
14730
|
}
|
|
14528
14731
|
async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
14529
|
-
const parentDir =
|
|
14732
|
+
const parentDir = dirname7(filePath);
|
|
14530
14733
|
const prefix = `${basename(filePath)}.`;
|
|
14531
14734
|
const cutoff = now().getTime() - retentionDays * 24 * 60 * 60 * 1000;
|
|
14532
14735
|
let files = [];
|
|
@@ -14542,10 +14745,10 @@ async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
|
14542
14745
|
if (!file.startsWith(prefix) || !/^\d+$/.test(file.slice(prefix.length))) {
|
|
14543
14746
|
continue;
|
|
14544
14747
|
}
|
|
14545
|
-
const candidate =
|
|
14748
|
+
const candidate = join7(parentDir, file);
|
|
14546
14749
|
const details = await stat2(candidate);
|
|
14547
14750
|
if (details.mtime.getTime() < cutoff) {
|
|
14548
|
-
await
|
|
14751
|
+
await rm6(candidate, { force: true });
|
|
14549
14752
|
}
|
|
14550
14753
|
}
|
|
14551
14754
|
}
|
|
@@ -14556,7 +14759,7 @@ var init_rotating_file_writer = () => {};
|
|
|
14556
14759
|
|
|
14557
14760
|
// src/perf/perf-log-writer.ts
|
|
14558
14761
|
import { appendFile as fsAppendFile, mkdir as fsMkdir } from "node:fs/promises";
|
|
14559
|
-
import { dirname as
|
|
14762
|
+
import { dirname as dirname8 } from "node:path";
|
|
14560
14763
|
function createPerfLogWriter(options) {
|
|
14561
14764
|
const append = options.appendImpl ?? ((p, d) => fsAppendFile(p, d, "utf8"));
|
|
14562
14765
|
const mkdir8 = options.mkdirImpl ?? ((p, o) => fsMkdir(p, o).then(() => {
|
|
@@ -14606,7 +14809,7 @@ function createPerfLogWriter(options) {
|
|
|
14606
14809
|
return;
|
|
14607
14810
|
const data = batch.join("");
|
|
14608
14811
|
try {
|
|
14609
|
-
await mkdir8(
|
|
14812
|
+
await mkdir8(dirname8(options.filePath), { recursive: true });
|
|
14610
14813
|
await rotateIfNeeded(options.filePath, Buffer.byteLength(data), options.maxSizeBytes, options.maxFiles);
|
|
14611
14814
|
await append(options.filePath, data);
|
|
14612
14815
|
consecutiveFailures = 0;
|
|
@@ -14916,7 +15119,9 @@ function isSlashCommandText(textBody) {
|
|
|
14916
15119
|
}
|
|
14917
15120
|
function getWeixinMessageTurnLane(full) {
|
|
14918
15121
|
const textBody = extractTextBody(full.item_list).trim().toLowerCase();
|
|
14919
|
-
|
|
15122
|
+
const command = textBody.split(/\s+/)[0] ?? "";
|
|
15123
|
+
const isSwitch = command === "/use" || command === "/ss";
|
|
15124
|
+
return command === "/cancel" || command === "/stop" || command === "/jx" || isSwitch ? "control" : "normal";
|
|
14920
15125
|
}
|
|
14921
15126
|
function buildWeixinChatKey(accountId, userId) {
|
|
14922
15127
|
return `weixin:${accountId}:${userId}`;
|
|
@@ -15066,6 +15271,9 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
15066
15271
|
}
|
|
15067
15272
|
let midFirstSent = false;
|
|
15068
15273
|
const sendReplySegment = async (text) => {
|
|
15274
|
+
if (!shouldDeliverSegment(deps.isForeground)) {
|
|
15275
|
+
return false;
|
|
15276
|
+
}
|
|
15069
15277
|
const plainText = markdownToPlainText(text).trim();
|
|
15070
15278
|
if (plainText.length === 0) {
|
|
15071
15279
|
return false;
|
|
@@ -15097,7 +15305,8 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
15097
15305
|
channel: "weixin",
|
|
15098
15306
|
chatType: full.group_id ? "group" : "direct",
|
|
15099
15307
|
...full.from_user_id ? { senderId: full.from_user_id } : {},
|
|
15100
|
-
...full.group_id ? { groupId: full.group_id } : {}
|
|
15308
|
+
...full.group_id ? { groupId: full.group_id } : {},
|
|
15309
|
+
...deps.boundSessionAlias ? { boundSessionAlias: deps.boundSessionAlias } : {}
|
|
15101
15310
|
},
|
|
15102
15311
|
perfSpan
|
|
15103
15312
|
};
|
|
@@ -15115,80 +15324,96 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
15115
15324
|
if (turn.text) {
|
|
15116
15325
|
const finalText = markdownToPlainText(turn.text).trim();
|
|
15117
15326
|
if (finalText.length > 0) {
|
|
15118
|
-
const
|
|
15119
|
-
if (
|
|
15120
|
-
|
|
15121
|
-
if (
|
|
15122
|
-
|
|
15123
|
-
|
|
15124
|
-
|
|
15125
|
-
deps.
|
|
15126
|
-
}
|
|
15127
|
-
await sendMessageWeixin({
|
|
15128
|
-
to,
|
|
15129
|
-
text: rawChunks[0],
|
|
15130
|
-
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
15131
|
-
});
|
|
15132
|
-
finalChunksSent += 1;
|
|
15133
|
-
if (!finalFirstSent) {
|
|
15134
|
-
finalFirstSent = true;
|
|
15135
|
-
perfSpan.mark("reply.final_first_sent", { bytes: utf8ByteLength(rawChunks[0]), chunkIndex: 1 });
|
|
15136
|
-
}
|
|
15137
|
-
}
|
|
15327
|
+
const disposition = resolveFinalDisposition(shouldDeliverSegment(deps.isForeground), Boolean(deps.boundSessionAlias && deps.onBackgroundFinal));
|
|
15328
|
+
if (disposition === "store") {
|
|
15329
|
+
await deps.onBackgroundFinal(deps.boundSessionAlias, finalText, "done");
|
|
15330
|
+
if (shouldSendBackgroundNotice(deps.reserveFinal ? () => deps.reserveFinal(to) : undefined)) {
|
|
15331
|
+
await sendMessageWeixin({
|
|
15332
|
+
to,
|
|
15333
|
+
text: buildBackgroundCompletionNotice(deps.boundSessionAlias, "done"),
|
|
15334
|
+
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
15335
|
+
}).catch((e) => deps.errLog(`bg completion notice failed: ${String(e)}`));
|
|
15138
15336
|
} else {
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
${buildFinalHeadsUp({
|
|
15149
|
-
total,
|
|
15150
|
-
sentSoFar
|
|
15151
|
-
})}`;
|
|
15152
|
-
}
|
|
15153
|
-
let sent = 0;
|
|
15154
|
-
for (let i = 0;i < wave.length; i += 1) {
|
|
15337
|
+
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=bg_notice chatKey=${to}`);
|
|
15338
|
+
}
|
|
15339
|
+
} else if (disposition === "drop") {
|
|
15340
|
+
deps.errLog(`weixin.final.dropped reason=backgrounded_no_store kind=text chatKey=${to}`);
|
|
15341
|
+
} else {
|
|
15342
|
+
const rawChunks = chunkFinalText(finalText, MAX_FINAL_CHUNK_BYTES);
|
|
15343
|
+
if (rawChunks.length > 0) {
|
|
15344
|
+
const total = rawChunks.length;
|
|
15345
|
+
if (total === 1) {
|
|
15155
15346
|
const reserved = deps.reserveFinal ? deps.reserveFinal(to) : true;
|
|
15156
15347
|
if (!reserved) {
|
|
15157
15348
|
finalDropped = true;
|
|
15158
|
-
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=
|
|
15159
|
-
|
|
15160
|
-
}
|
|
15161
|
-
try {
|
|
15349
|
+
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=text chatKey=${to}`);
|
|
15350
|
+
} else {
|
|
15162
15351
|
await sendMessageWeixin({
|
|
15163
15352
|
to,
|
|
15164
|
-
text:
|
|
15353
|
+
text: rawChunks[0],
|
|
15165
15354
|
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
15166
15355
|
});
|
|
15167
|
-
sent += 1;
|
|
15168
15356
|
finalChunksSent += 1;
|
|
15169
15357
|
if (!finalFirstSent) {
|
|
15170
15358
|
finalFirstSent = true;
|
|
15171
|
-
perfSpan.mark("reply.final_first_sent", { bytes: utf8ByteLength(
|
|
15359
|
+
perfSpan.mark("reply.final_first_sent", { bytes: utf8ByteLength(rawChunks[0]), chunkIndex: 1 });
|
|
15172
15360
|
}
|
|
15173
|
-
} catch (sendErr) {
|
|
15174
|
-
finalDropped = true;
|
|
15175
|
-
deps.errLog(`weixin.final.dropped reason=send_failed kind=text_paginated chatKey=${to} chunk=${i + 1}/${total} err=${String(sendErr)}`);
|
|
15176
|
-
break;
|
|
15177
15361
|
}
|
|
15178
|
-
}
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
const
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15362
|
+
} else {
|
|
15363
|
+
const prefixed = rawChunks.map((body, i) => `(${i + 1}/${total}) ${body}`);
|
|
15364
|
+
const available = deps.finalRemaining ? deps.finalRemaining(to) : total;
|
|
15365
|
+
const waveSize = Math.max(Math.min(available, total), 0);
|
|
15366
|
+
const wave = prefixed.slice(0, waveSize);
|
|
15367
|
+
const rest = prefixed.slice(waveSize);
|
|
15368
|
+
if (wave.length > 0 && rest.length > 0) {
|
|
15369
|
+
const sentSoFar = wave.length;
|
|
15370
|
+
wave[wave.length - 1] = `${wave[wave.length - 1]}
|
|
15371
|
+
|
|
15372
|
+
${buildFinalHeadsUp({
|
|
15373
|
+
total,
|
|
15374
|
+
sentSoFar
|
|
15375
|
+
})}`;
|
|
15376
|
+
}
|
|
15377
|
+
let sent = 0;
|
|
15378
|
+
for (let i = 0;i < wave.length; i += 1) {
|
|
15379
|
+
const reserved = deps.reserveFinal ? deps.reserveFinal(to) : true;
|
|
15380
|
+
if (!reserved) {
|
|
15381
|
+
finalDropped = true;
|
|
15382
|
+
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=text_paginated chatKey=${to} chunk=${i + 1}/${total}`);
|
|
15383
|
+
break;
|
|
15384
|
+
}
|
|
15385
|
+
try {
|
|
15386
|
+
await sendMessageWeixin({
|
|
15387
|
+
to,
|
|
15388
|
+
text: wave[i],
|
|
15389
|
+
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
15390
|
+
});
|
|
15391
|
+
sent += 1;
|
|
15392
|
+
finalChunksSent += 1;
|
|
15393
|
+
if (!finalFirstSent) {
|
|
15394
|
+
finalFirstSent = true;
|
|
15395
|
+
perfSpan.mark("reply.final_first_sent", { bytes: utf8ByteLength(wave[i]), chunkIndex: i + 1 });
|
|
15396
|
+
}
|
|
15397
|
+
} catch (sendErr) {
|
|
15398
|
+
finalDropped = true;
|
|
15399
|
+
deps.errLog(`weixin.final.dropped reason=send_failed kind=text_paginated chatKey=${to} chunk=${i + 1}/${total} err=${String(sendErr)}`);
|
|
15400
|
+
break;
|
|
15401
|
+
}
|
|
15402
|
+
}
|
|
15403
|
+
const restToPark = prefixed.slice(sent);
|
|
15404
|
+
finalChunksPending = restToPark.length;
|
|
15405
|
+
if (restToPark.length > 0 && deps.enqueuePendingFinal) {
|
|
15406
|
+
const pending = restToPark.map((text, idx) => {
|
|
15407
|
+
const seq = sent + idx + 1;
|
|
15408
|
+
const entry = { text, seq, total };
|
|
15409
|
+
if (contextToken !== undefined)
|
|
15410
|
+
entry.contextToken = contextToken;
|
|
15411
|
+
if (deps.accountId !== undefined)
|
|
15412
|
+
entry.accountId = deps.accountId;
|
|
15413
|
+
return entry;
|
|
15414
|
+
});
|
|
15415
|
+
deps.enqueuePendingFinal(to, pending);
|
|
15416
|
+
}
|
|
15192
15417
|
}
|
|
15193
15418
|
}
|
|
15194
15419
|
}
|
|
@@ -15259,18 +15484,35 @@ ${buildFinalHeadsUp({
|
|
|
15259
15484
|
perfSpan.setOutcome("error", { reason: "turn_error" });
|
|
15260
15485
|
const errorText = err instanceof Error ? err.stack ?? err.message : JSON.stringify(err);
|
|
15261
15486
|
deps.errLog(`handleWeixinMessageTurn: agent or send failed: ${errorText}`);
|
|
15262
|
-
const
|
|
15263
|
-
|
|
15264
|
-
|
|
15487
|
+
const errMessage = `⚠️ 执行出错:${err instanceof Error ? err.message : JSON.stringify(err)}`;
|
|
15488
|
+
const errDisposition = resolveFinalDisposition(shouldDeliverSegment(deps.isForeground), Boolean(deps.boundSessionAlias && deps.onBackgroundFinal));
|
|
15489
|
+
if (errDisposition === "store") {
|
|
15490
|
+
await deps.onBackgroundFinal(deps.boundSessionAlias, errMessage, "error");
|
|
15491
|
+
if (shouldSendBackgroundNotice(deps.reserveFinal ? () => deps.reserveFinal(to) : undefined)) {
|
|
15492
|
+
await sendMessageWeixin({
|
|
15493
|
+
to,
|
|
15494
|
+
text: buildBackgroundCompletionNotice(deps.boundSessionAlias, "error"),
|
|
15495
|
+
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
15496
|
+
}).catch((e) => deps.errLog(`bg completion notice failed: ${String(e)}`));
|
|
15497
|
+
} else {
|
|
15498
|
+
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=bg_notice chatKey=${to}`);
|
|
15499
|
+
}
|
|
15500
|
+
} else if (errDisposition === "drop") {
|
|
15501
|
+
deps.errLog(`weixin.final.dropped reason=backgrounded_no_store kind=error_notice chatKey=${to}`);
|
|
15265
15502
|
} else {
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15271
|
-
|
|
15272
|
-
|
|
15273
|
-
|
|
15503
|
+
const reservedErr = deps.reserveFinal ? deps.reserveFinal(to) : true;
|
|
15504
|
+
if (!reservedErr) {
|
|
15505
|
+
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=error_notice chatKey=${to}`);
|
|
15506
|
+
} else {
|
|
15507
|
+
sendWeixinErrorNotice({
|
|
15508
|
+
to,
|
|
15509
|
+
contextToken,
|
|
15510
|
+
message: errMessage,
|
|
15511
|
+
baseUrl: deps.baseUrl,
|
|
15512
|
+
token: deps.token,
|
|
15513
|
+
errLog: deps.errLog
|
|
15514
|
+
});
|
|
15515
|
+
}
|
|
15274
15516
|
}
|
|
15275
15517
|
} finally {
|
|
15276
15518
|
stopTypingIndicator();
|
|
@@ -15288,6 +15530,7 @@ var init_handle_weixin_message_turn = __esm(() => {
|
|
|
15288
15530
|
init_outbound_media_safety();
|
|
15289
15531
|
init_media_download();
|
|
15290
15532
|
init_mime();
|
|
15533
|
+
init_completion_notice();
|
|
15291
15534
|
init_inbound();
|
|
15292
15535
|
init_error_notice();
|
|
15293
15536
|
init_send_media();
|
|
@@ -15489,7 +15732,15 @@ async function monitorWeixinProvider(opts) {
|
|
|
15489
15732
|
}
|
|
15490
15733
|
}
|
|
15491
15734
|
}
|
|
15492
|
-
|
|
15735
|
+
const chatKey = buildWeixinChatKey(accountId, fromUserId);
|
|
15736
|
+
const isSlash = inboundText.trim().startsWith("/");
|
|
15737
|
+
const boundAlias = isSlash ? undefined : opts.peekCurrentSessionAlias?.(chatKey);
|
|
15738
|
+
const sessionKey = boundAlias ?? "__chat__";
|
|
15739
|
+
const isForeground = boundAlias ? () => opts.peekCurrentSessionAlias?.(chatKey) === boundAlias : undefined;
|
|
15740
|
+
if (boundAlias) {
|
|
15741
|
+
opts.activeTurns?.markActive(chatKey, boundAlias);
|
|
15742
|
+
}
|
|
15743
|
+
const runPromise = conversationExecutor.run(full.from_user_id ?? "", getWeixinMessageTurnLane(full), () => handleWeixinMessageTurn(full, {
|
|
15493
15744
|
accountId,
|
|
15494
15745
|
agent,
|
|
15495
15746
|
baseUrl,
|
|
@@ -15507,9 +15758,25 @@ async function monitorWeixinProvider(opts) {
|
|
|
15507
15758
|
...opts.prependPendingFinal ? { prependPendingFinal: opts.prependPendingFinal } : {},
|
|
15508
15759
|
...opts.mediaStore ? { mediaStore: opts.mediaStore } : {},
|
|
15509
15760
|
...opts.allowedMediaRoots ? { allowedMediaRoots: opts.allowedMediaRoots } : {},
|
|
15510
|
-
...opts.perfTracer ? { perfTracer: opts.perfTracer } : {}
|
|
15511
|
-
|
|
15761
|
+
...opts.perfTracer ? { perfTracer: opts.perfTracer } : {},
|
|
15762
|
+
...boundAlias ? { boundSessionAlias: boundAlias } : {},
|
|
15763
|
+
...isForeground ? { isForeground } : {},
|
|
15764
|
+
...opts.setBackgroundResult ? {
|
|
15765
|
+
onBackgroundFinal: async (alias, text, status) => {
|
|
15766
|
+
await opts.setBackgroundResult(chatKey, alias, {
|
|
15767
|
+
text,
|
|
15768
|
+
status,
|
|
15769
|
+
finished_at: new Date().toISOString()
|
|
15770
|
+
});
|
|
15771
|
+
}
|
|
15772
|
+
} : {}
|
|
15773
|
+
}), sessionKey);
|
|
15774
|
+
runPromise.catch((err) => {
|
|
15512
15775
|
errLog(`[weixin] message turn failed: ${String(err)}`);
|
|
15776
|
+
}).finally(() => {
|
|
15777
|
+
if (boundAlias) {
|
|
15778
|
+
opts.activeTurns?.markInactive(chatKey, boundAlias);
|
|
15779
|
+
}
|
|
15513
15780
|
});
|
|
15514
15781
|
}
|
|
15515
15782
|
} catch (err) {
|
|
@@ -15693,7 +15960,10 @@ async function start(agent, opts) {
|
|
|
15693
15960
|
...opts?.enqueuePendingFinal ? { enqueuePendingFinal: opts.enqueuePendingFinal } : {},
|
|
15694
15961
|
...opts?.dropPendingFinal ? { dropPendingFinal: opts.dropPendingFinal } : {},
|
|
15695
15962
|
...opts?.mediaStore ? { mediaStore: opts.mediaStore } : {},
|
|
15696
|
-
...opts?.perfTracer ? { perfTracer: opts.perfTracer } : {}
|
|
15963
|
+
...opts?.perfTracer ? { perfTracer: opts.perfTracer } : {},
|
|
15964
|
+
...opts?.peekCurrentSessionAlias ? { peekCurrentSessionAlias: opts.peekCurrentSessionAlias } : {},
|
|
15965
|
+
...opts?.setBackgroundResult ? { setBackgroundResult: opts.setBackgroundResult } : {},
|
|
15966
|
+
...opts?.activeTurns ? { activeTurns: opts.activeTurns } : {}
|
|
15697
15967
|
});
|
|
15698
15968
|
}
|
|
15699
15969
|
var init_bot = __esm(() => {
|
|
@@ -16125,16 +16395,16 @@ var init_scheduled_turn = __esm(() => {
|
|
|
16125
16395
|
});
|
|
16126
16396
|
|
|
16127
16397
|
// src/weixin/monitor/consumer-lock.ts
|
|
16128
|
-
import { mkdir as mkdir8, open as open3, readFile as readFile7, rm as
|
|
16129
|
-
import { dirname as
|
|
16398
|
+
import { mkdir as mkdir8, open as open3, readFile as readFile7, rm as rm7 } from "node:fs/promises";
|
|
16399
|
+
import { dirname as dirname9, join as join8 } from "node:path";
|
|
16130
16400
|
import { homedir as homedir4 } from "node:os";
|
|
16131
16401
|
function createWeixinConsumerLock(options = {}) {
|
|
16132
|
-
const lockFilePath = options.lockFilePath ??
|
|
16402
|
+
const lockFilePath = options.lockFilePath ?? join8(coreHomeDir(homedir4()), "runtime", "weixin-consumer.lock.json");
|
|
16133
16403
|
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning4;
|
|
16134
16404
|
const onDiagnostic = options.onDiagnostic;
|
|
16135
16405
|
return {
|
|
16136
16406
|
async acquire(meta2) {
|
|
16137
|
-
await mkdir8(
|
|
16407
|
+
await mkdir8(dirname9(lockFilePath), { recursive: true });
|
|
16138
16408
|
while (true) {
|
|
16139
16409
|
try {
|
|
16140
16410
|
const handle = await open3(lockFilePath, "wx");
|
|
@@ -16165,7 +16435,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
16165
16435
|
});
|
|
16166
16436
|
const existing = await loadLockMetadata(lockFilePath);
|
|
16167
16437
|
if (!existing) {
|
|
16168
|
-
await
|
|
16438
|
+
await rm7(lockFilePath, { force: true });
|
|
16169
16439
|
await onDiagnostic?.("lock_invalid_removed", {
|
|
16170
16440
|
lockFilePath,
|
|
16171
16441
|
reason: "invalid_or_unreadable_metadata"
|
|
@@ -16173,7 +16443,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
16173
16443
|
continue;
|
|
16174
16444
|
}
|
|
16175
16445
|
if (!isProcessRunning(existing.pid)) {
|
|
16176
|
-
await
|
|
16446
|
+
await rm7(lockFilePath, { force: true });
|
|
16177
16447
|
await onDiagnostic?.("lock_stale_removed", {
|
|
16178
16448
|
lockFilePath,
|
|
16179
16449
|
stalePid: existing.pid,
|
|
@@ -16198,7 +16468,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
16198
16468
|
}
|
|
16199
16469
|
},
|
|
16200
16470
|
async release() {
|
|
16201
|
-
await
|
|
16471
|
+
await rm7(lockFilePath, { force: true });
|
|
16202
16472
|
await onDiagnostic?.("lock_released", {
|
|
16203
16473
|
lockFilePath
|
|
16204
16474
|
});
|
|
@@ -16227,6 +16497,7 @@ function defaultIsProcessRunning4(pid) {
|
|
|
16227
16497
|
}
|
|
16228
16498
|
var ActiveWeixinConsumerLockError;
|
|
16229
16499
|
var init_consumer_lock = __esm(() => {
|
|
16500
|
+
init_core_home();
|
|
16230
16501
|
ActiveWeixinConsumerLockError = class ActiveWeixinConsumerLockError extends Error {
|
|
16231
16502
|
existing;
|
|
16232
16503
|
lockFilePath;
|
|
@@ -16289,6 +16560,7 @@ class WeixinChannel {
|
|
|
16289
16560
|
console.log("[weacpx] 未检测到登录凭证,正在启动扫码登录...");
|
|
16290
16561
|
await this.login();
|
|
16291
16562
|
}
|
|
16563
|
+
const sessions = input.sessions;
|
|
16292
16564
|
await start(input.agent, {
|
|
16293
16565
|
abortSignal: input.abortSignal,
|
|
16294
16566
|
...this.mediaStore ? { mediaStore: this.mediaStore } : {},
|
|
@@ -16301,7 +16573,12 @@ class WeixinChannel {
|
|
|
16301
16573
|
prependPendingFinal: (chatKey, chunks) => input.quota.prependPendingFinal(chatKey, chunks),
|
|
16302
16574
|
enqueuePendingFinal: (chatKey, chunks) => input.quota.enqueuePendingFinal(chatKey, chunks),
|
|
16303
16575
|
dropPendingFinal: (chatKey) => input.quota.clearPendingFinal(chatKey),
|
|
16304
|
-
...input.perfTracer ? { perfTracer: input.perfTracer } : {}
|
|
16576
|
+
...input.perfTracer ? { perfTracer: input.perfTracer } : {},
|
|
16577
|
+
...sessions ? {
|
|
16578
|
+
peekCurrentSessionAlias: (chatKey) => sessions.peekCurrentSessionAlias(chatKey),
|
|
16579
|
+
setBackgroundResult: (chatKey, alias, result) => sessions.setBackgroundResult(chatKey, alias, result)
|
|
16580
|
+
} : {},
|
|
16581
|
+
...input.activeTurns ? { activeTurns: input.activeTurns } : {}
|
|
16305
16582
|
});
|
|
16306
16583
|
}
|
|
16307
16584
|
async notifyTaskCompletion(task) {
|
|
@@ -16756,9 +17033,9 @@ __export(exports_plugin_loader, {
|
|
|
16756
17033
|
});
|
|
16757
17034
|
import { createRequire as createRequire2 } from "node:module";
|
|
16758
17035
|
import { pathToFileURL } from "node:url";
|
|
16759
|
-
import { join as
|
|
17036
|
+
import { join as join9 } from "node:path";
|
|
16760
17037
|
async function importPluginFromHome(packageName, pluginHome) {
|
|
16761
|
-
const requireFromHome = createRequire2(
|
|
17038
|
+
const requireFromHome = createRequire2(join9(pluginHome, "package.json"));
|
|
16762
17039
|
const entry = requireFromHome.resolve(packageName);
|
|
16763
17040
|
return await import(pathToFileURL(entry).href);
|
|
16764
17041
|
}
|
|
@@ -16813,7 +17090,7 @@ var init_bootstrap = __esm(() => {
|
|
|
16813
17090
|
|
|
16814
17091
|
// src/logging/app-logger.ts
|
|
16815
17092
|
import { appendFile, chmod as chmod2, mkdir as mkdir9 } from "node:fs/promises";
|
|
16816
|
-
import { dirname as
|
|
17093
|
+
import { dirname as dirname11 } from "node:path";
|
|
16817
17094
|
function createNoopAppLogger() {
|
|
16818
17095
|
return {
|
|
16819
17096
|
debug: async () => {},
|
|
@@ -16854,7 +17131,7 @@ function createAppLogger(options) {
|
|
|
16854
17131
|
return;
|
|
16855
17132
|
}
|
|
16856
17133
|
const line = formatLogLine(now(), level, event, message, context);
|
|
16857
|
-
await mkdir9(
|
|
17134
|
+
await mkdir9(dirname11(options.filePath), { recursive: true });
|
|
16858
17135
|
if (!modeEnsured) {
|
|
16859
17136
|
modeEnsured = true;
|
|
16860
17137
|
await chmod2(options.filePath, 384).catch(() => {});
|
|
@@ -17108,8 +17385,10 @@ function parseCommand(input) {
|
|
|
17108
17385
|
}
|
|
17109
17386
|
if (command === "/status")
|
|
17110
17387
|
return { kind: "status" };
|
|
17111
|
-
if (command === "/cancel")
|
|
17112
|
-
|
|
17388
|
+
if (command === "/cancel") {
|
|
17389
|
+
const alias = parts[1];
|
|
17390
|
+
return alias ? { kind: "cancel", alias } : { kind: "cancel" };
|
|
17391
|
+
}
|
|
17113
17392
|
if (command === "/clear")
|
|
17114
17393
|
return { kind: "session.reset" };
|
|
17115
17394
|
if (command === "/mode" && parts.length === 1)
|
|
@@ -17243,6 +17522,9 @@ function parseCommand(input) {
|
|
|
17243
17522
|
if (command === "/config" && parts[1] === "set" && parts.length === 4) {
|
|
17244
17523
|
return { kind: "config.set", path: parts[2] ?? "", value: parts[3] ?? "" };
|
|
17245
17524
|
}
|
|
17525
|
+
if (command === "/use" && parts[1] === "-") {
|
|
17526
|
+
return { kind: "session.use.previous" };
|
|
17527
|
+
}
|
|
17246
17528
|
if (command === "/use" && parts[1]) {
|
|
17247
17529
|
return { kind: "session.use", alias: parts[1] };
|
|
17248
17530
|
}
|
|
@@ -18410,6 +18692,11 @@ async function buildCoordinatorPrompt(input) {
|
|
|
18410
18692
|
}
|
|
18411
18693
|
var init_build_coordinator_prompt = () => {};
|
|
18412
18694
|
|
|
18695
|
+
// src/commands/handlers/session-list-marker.ts
|
|
18696
|
+
function decorateUnread(label, hasUnread) {
|
|
18697
|
+
return hasUnread ? `● ${label}` : label;
|
|
18698
|
+
}
|
|
18699
|
+
|
|
18413
18700
|
// src/commands/handlers/session-handler.ts
|
|
18414
18701
|
async function handleSessions(context, chatKey) {
|
|
18415
18702
|
const sessions = await context.sessions.listSessions(chatKey);
|
|
@@ -18432,10 +18719,11 @@ async function handleSessions(context, chatKey) {
|
|
|
18432
18719
|
return { text: lines.join(`
|
|
18433
18720
|
`) };
|
|
18434
18721
|
}
|
|
18722
|
+
const unread = new Set(context.sessions.listBackgroundResultAliases(chatKey));
|
|
18435
18723
|
return {
|
|
18436
18724
|
text: [
|
|
18437
18725
|
"会话列表:",
|
|
18438
|
-
...sessions.map((session) => `- ${session.alias} (${session.agent} @ ${session.workspace})${session.isCurrent ? " [当前]" : ""}`)
|
|
18726
|
+
...sessions.map((session) => `- ${decorateUnread(session.alias, unread.has(session.internalAlias))} (${session.agent} @ ${session.workspace})${session.isCurrent ? " [当前]" : ""}`)
|
|
18439
18727
|
].join(`
|
|
18440
18728
|
`)
|
|
18441
18729
|
};
|
|
@@ -18512,13 +18800,55 @@ async function refreshSessionTransportAgentCommandBestEffort(context, alias, eve
|
|
|
18512
18800
|
});
|
|
18513
18801
|
}
|
|
18514
18802
|
}
|
|
18515
|
-
|
|
18516
|
-
|
|
18803
|
+
function renderSwitched(switched) {
|
|
18804
|
+
const base = `已切到 ${switched.alias} · ${switched.agent} · ${switched.workspace}`;
|
|
18805
|
+
return switched.previousAlias ? `${base}(上一个:${switched.previousAlias})` : base;
|
|
18806
|
+
}
|
|
18807
|
+
async function appendSwitchBackContext(context, chatKey, internalAlias, baseText) {
|
|
18808
|
+
const result = await context.sessions.takeBackgroundResult(chatKey, internalAlias);
|
|
18809
|
+
if (result) {
|
|
18810
|
+
return `${baseText}
|
|
18811
|
+
|
|
18812
|
+
${result.text}`;
|
|
18813
|
+
}
|
|
18814
|
+
if (context.activeTurns?.isActive(chatKey, internalAlias)) {
|
|
18815
|
+
return `${baseText}
|
|
18816
|
+
|
|
18817
|
+
⏳ ${toDisplaySessionAlias(internalAlias)} 仍在执行中…`;
|
|
18818
|
+
}
|
|
18819
|
+
return baseText;
|
|
18820
|
+
}
|
|
18821
|
+
async function handleSessionUse(context, chatKey, input) {
|
|
18822
|
+
const result = context.sessions.resolveFuzzyAlias(chatKey, input);
|
|
18823
|
+
if (result.kind === "none") {
|
|
18824
|
+
return { text: `没有匹配「${input}」的会话。发 /sessions 看看有哪些。` };
|
|
18825
|
+
}
|
|
18826
|
+
if (result.kind === "ambiguous") {
|
|
18827
|
+
const lines = result.candidates.map((candidate) => `• ${candidate.alias} · ${candidate.agent} · ${candidate.workspace}`);
|
|
18828
|
+
return { text: [`「${input}」匹配到多个会话,请指定:`, ...lines].join(`
|
|
18829
|
+
`) };
|
|
18830
|
+
}
|
|
18831
|
+
const switched = await context.sessions.useSession(chatKey, result.alias);
|
|
18517
18832
|
await context.logger.info("session.selected", "selected logical session", {
|
|
18518
|
-
alias,
|
|
18833
|
+
alias: switched.alias,
|
|
18834
|
+
chatKey
|
|
18835
|
+
});
|
|
18836
|
+
const internalAlias = context.sessions.peekCurrentSessionAlias(chatKey) ?? result.alias;
|
|
18837
|
+
const text = await appendSwitchBackContext(context, chatKey, internalAlias, renderSwitched(switched));
|
|
18838
|
+
return { text };
|
|
18839
|
+
}
|
|
18840
|
+
async function handleSessionUsePrevious(context, chatKey) {
|
|
18841
|
+
const switched = await context.sessions.usePreviousSession(chatKey);
|
|
18842
|
+
if (!switched) {
|
|
18843
|
+
return { text: "还没有上一个会话,发 /sessions 看看有哪些。" };
|
|
18844
|
+
}
|
|
18845
|
+
await context.logger.info("session.selected", "selected previous logical session", {
|
|
18846
|
+
alias: switched.alias,
|
|
18519
18847
|
chatKey
|
|
18520
18848
|
});
|
|
18521
|
-
|
|
18849
|
+
const internalAlias = context.sessions.peekCurrentSessionAlias(chatKey) ?? switched.alias;
|
|
18850
|
+
const text = await appendSwitchBackContext(context, chatKey, internalAlias, renderSwitched(switched));
|
|
18851
|
+
return { text };
|
|
18522
18852
|
}
|
|
18523
18853
|
async function handleModeShow(context, chatKey) {
|
|
18524
18854
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
@@ -18594,7 +18924,34 @@ async function handleStatus(context, chatKey) {
|
|
|
18594
18924
|
`)
|
|
18595
18925
|
};
|
|
18596
18926
|
}
|
|
18597
|
-
async function handleCancel(context, chatKey) {
|
|
18927
|
+
async function handleCancel(context, chatKey, alias) {
|
|
18928
|
+
if (alias !== undefined) {
|
|
18929
|
+
const result = context.sessions.resolveFuzzyAlias(chatKey, alias);
|
|
18930
|
+
if (result.kind === "none") {
|
|
18931
|
+
return { text: `没有匹配「${alias}」的会话。发 /sessions 看看有哪些。` };
|
|
18932
|
+
}
|
|
18933
|
+
if (result.kind === "ambiguous") {
|
|
18934
|
+
const lines = result.candidates.map((candidate) => `• ${candidate.alias} · ${candidate.agent} · ${candidate.workspace}`);
|
|
18935
|
+
return { text: [`「${alias}」匹配到多个会话,请指定:`, ...lines].join(`
|
|
18936
|
+
`) };
|
|
18937
|
+
}
|
|
18938
|
+
const internalAlias = await context.sessions.resolveAliasForChat(chatKey, result.alias);
|
|
18939
|
+
const target = await context.sessions.getSession(internalAlias);
|
|
18940
|
+
if (!target) {
|
|
18941
|
+
return { text: `没有匹配「${alias}」的会话。发 /sessions 看看有哪些。` };
|
|
18942
|
+
}
|
|
18943
|
+
try {
|
|
18944
|
+
const cancelResult = await context.interaction.cancelTransportSession(target);
|
|
18945
|
+
return { text: cancelResult.message || "cancelled" };
|
|
18946
|
+
} catch (error2) {
|
|
18947
|
+
const recovered = await context.recovery.tryRecoverMissingSession(target, error2);
|
|
18948
|
+
if (recovered) {
|
|
18949
|
+
const cancelResult = await context.interaction.cancelTransportSession(recovered);
|
|
18950
|
+
return { text: cancelResult.message || "cancelled" };
|
|
18951
|
+
}
|
|
18952
|
+
return context.recovery.renderTransportError(target, error2);
|
|
18953
|
+
}
|
|
18954
|
+
}
|
|
18598
18955
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
18599
18956
|
if (!session) {
|
|
18600
18957
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
@@ -18759,7 +19116,7 @@ async function handlePromptWithSession(context, session, chatKey, text, reply, r
|
|
|
18759
19116
|
}
|
|
18760
19117
|
}
|
|
18761
19118
|
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18762
|
-
const session = await context.sessions.getCurrentSession(chatKey);
|
|
19119
|
+
const session = metadata?.boundSessionAlias ? context.sessions.getResolvedSessionByInternalAlias(metadata.boundSessionAlias) : await context.sessions.getCurrentSession(chatKey);
|
|
18763
19120
|
if (!session) {
|
|
18764
19121
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
18765
19122
|
}
|
|
@@ -18860,6 +19217,8 @@ var init_session_handler = __esm(() => {
|
|
|
18860
19217
|
{ usage: "/session tail [N]", description: "补拉当前会话的历史输出(默认 50 行)" },
|
|
18861
19218
|
{ usage: "/session rm <alias>", description: "删除逻辑会话" },
|
|
18862
19219
|
{ usage: "/use <alias>", description: "切换当前会话" },
|
|
19220
|
+
{ usage: "/use <片段>", description: "按别名片段切换(精确>前缀>子串;多命中会列候选)" },
|
|
19221
|
+
{ usage: "/use -", description: "切回上一个会话(像 shell 的 cd -)" },
|
|
18863
19222
|
{ usage: "/session reset 或 /clear", description: "重置当前会话上下文" }
|
|
18864
19223
|
],
|
|
18865
19224
|
examples: [
|
|
@@ -18867,6 +19226,8 @@ var init_session_handler = __esm(() => {
|
|
|
18867
19226
|
"/ssn",
|
|
18868
19227
|
"/ssn 1",
|
|
18869
19228
|
"/use backend-fix",
|
|
19229
|
+
"/use back",
|
|
19230
|
+
"/use -",
|
|
18870
19231
|
"/session rm old-session",
|
|
18871
19232
|
"/session reset"
|
|
18872
19233
|
]
|
|
@@ -18932,12 +19293,14 @@ var init_session_handler = __esm(() => {
|
|
|
18932
19293
|
cancelHelp = {
|
|
18933
19294
|
topic: "cancel",
|
|
18934
19295
|
aliases: ["stop"],
|
|
18935
|
-
summary: "
|
|
19296
|
+
summary: "取消会话里正在执行的任务。",
|
|
18936
19297
|
commands: [
|
|
18937
|
-
{ usage: "/cancel", description: "
|
|
18938
|
-
{ usage: "/
|
|
19298
|
+
{ usage: "/cancel", description: "取消当前前台会话的任务" },
|
|
19299
|
+
{ usage: "/cancel <alias>", description: "取消指定(含后台)会话的任务" },
|
|
19300
|
+
{ usage: "/stop", description: "取消当前任务(/cancel 别名)" },
|
|
19301
|
+
{ usage: "/stop <alias>", description: "取消指定会话的任务(/cancel <alias> 别名)" }
|
|
18939
19302
|
],
|
|
18940
|
-
examples: ["/cancel"]
|
|
19303
|
+
examples: ["/cancel", "/cancel backend"]
|
|
18941
19304
|
};
|
|
18942
19305
|
});
|
|
18943
19306
|
|
|
@@ -20645,7 +21008,7 @@ import { spawn as spawn5 } from "node:child_process";
|
|
|
20645
21008
|
import { createWriteStream } from "node:fs";
|
|
20646
21009
|
import { mkdir as mkdir10 } from "node:fs/promises";
|
|
20647
21010
|
import { homedir as homedir6 } from "node:os";
|
|
20648
|
-
import { join as
|
|
21011
|
+
import { join as join13 } from "node:path";
|
|
20649
21012
|
async function autoInstallOptionalDep(pkg, parentPackages, options = {}) {
|
|
20650
21013
|
const runCli = options.runCli ?? defaultRunCli;
|
|
20651
21014
|
const openLog = options.openLog ?? defaultLogSink;
|
|
@@ -20760,10 +21123,10 @@ ${err.message}`, reason: "spawn" });
|
|
|
20760
21123
|
});
|
|
20761
21124
|
});
|
|
20762
21125
|
}, defaultLogSink = async () => {
|
|
20763
|
-
const dir =
|
|
21126
|
+
const dir = join13(coreHomeDir(homedir6()), "logs");
|
|
20764
21127
|
await mkdir10(dir, { recursive: true });
|
|
20765
21128
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "").replace(/-/g, "");
|
|
20766
|
-
const path14 =
|
|
21129
|
+
const path14 = join13(dir, `auto-install-${timestamp}.log`);
|
|
20767
21130
|
const stream = createWriteStream(path14, { flags: "a" });
|
|
20768
21131
|
return {
|
|
20769
21132
|
path: path14,
|
|
@@ -20776,6 +21139,7 @@ ${err.message}`, reason: "spawn" });
|
|
|
20776
21139
|
};
|
|
20777
21140
|
};
|
|
20778
21141
|
var init_auto_install_optional_dep = __esm(() => {
|
|
21142
|
+
init_core_home();
|
|
20779
21143
|
PRECISE_COMMANDS = {
|
|
20780
21144
|
npm: { cmd: "npm", args: (pkg) => ["install", pkg, "--no-save", "--no-audit", "--no-fund"] },
|
|
20781
21145
|
bun: { cmd: "bun", args: (pkg) => ["add", pkg] },
|
|
@@ -20789,7 +21153,7 @@ import { spawn as spawn6 } from "node:child_process";
|
|
|
20789
21153
|
import { createRequire as createRequire3 } from "node:module";
|
|
20790
21154
|
import { access as access3 } from "node:fs/promises";
|
|
20791
21155
|
import { homedir as homedir7 } from "node:os";
|
|
20792
|
-
import { dirname as
|
|
21156
|
+
import { dirname as dirname12, join as join14 } from "node:path";
|
|
20793
21157
|
function deriveParentPackageName(platformPackage) {
|
|
20794
21158
|
return platformPackage.replace(/-(?:linux|darwin|win32|windows|freebsd|openbsd|sunos|aix)(?:-(?:x64|arm64|ia32|arm|ppc64|s390x))?(?:-(?:baseline|musl|gnu|gnueabihf|musleabihf|msvc))?$/, "");
|
|
20795
21159
|
}
|
|
@@ -20802,7 +21166,7 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
20802
21166
|
const queryRoot = deps.queryPackageManagerRoot ?? defaultQueryPackageManagerRoot;
|
|
20803
21167
|
const parentName = deriveParentPackageName(platformPackage);
|
|
20804
21168
|
const rawCandidates = [];
|
|
20805
|
-
const bunGlobalRoot = env.BUN_INSTALL ?
|
|
21169
|
+
const bunGlobalRoot = env.BUN_INSTALL ? join14(env.BUN_INSTALL, "install", "global", "node_modules") : join14(home, ".bun", "install", "global", "node_modules");
|
|
20806
21170
|
const [npmRoot, pnpmRoot, yarnRoot] = await Promise.all([
|
|
20807
21171
|
queryRoot("npm"),
|
|
20808
21172
|
queryRoot("pnpm"),
|
|
@@ -20825,20 +21189,20 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
20825
21189
|
if (resolved)
|
|
20826
21190
|
rawCandidates.push({ path: resolved, manager: classify(resolved) });
|
|
20827
21191
|
}
|
|
20828
|
-
rawCandidates.push({ path:
|
|
21192
|
+
rawCandidates.push({ path: join14(bunGlobalRoot, parentName), manager: "bun" });
|
|
20829
21193
|
if (npmRoot)
|
|
20830
|
-
rawCandidates.push({ path:
|
|
21194
|
+
rawCandidates.push({ path: join14(npmRoot, parentName), manager: "npm" });
|
|
20831
21195
|
if (pnpmRoot)
|
|
20832
|
-
rawCandidates.push({ path:
|
|
21196
|
+
rawCandidates.push({ path: join14(pnpmRoot, parentName), manager: "pnpm" });
|
|
20833
21197
|
if (yarnRoot)
|
|
20834
|
-
rawCandidates.push({ path:
|
|
21198
|
+
rawCandidates.push({ path: join14(yarnRoot, parentName), manager: "yarn" });
|
|
20835
21199
|
const seen = new Set;
|
|
20836
21200
|
const verified = [];
|
|
20837
21201
|
for (const candidate of rawCandidates) {
|
|
20838
21202
|
if (seen.has(candidate.path))
|
|
20839
21203
|
continue;
|
|
20840
21204
|
seen.add(candidate.path);
|
|
20841
|
-
if (await fsExists(
|
|
21205
|
+
if (await fsExists(join14(candidate.path, "package.json"))) {
|
|
20842
21206
|
verified.push(candidate);
|
|
20843
21207
|
}
|
|
20844
21208
|
}
|
|
@@ -20862,7 +21226,7 @@ function defaultResolveFromCwd(name, cwd) {
|
|
|
20862
21226
|
const pkgJson = require2.resolve(`${name}/package.json`, {
|
|
20863
21227
|
paths: [cwd, ...require2.resolve.paths(name) ?? []]
|
|
20864
21228
|
});
|
|
20865
|
-
return
|
|
21229
|
+
return dirname12(pkgJson);
|
|
20866
21230
|
} catch {
|
|
20867
21231
|
return null;
|
|
20868
21232
|
}
|
|
@@ -20909,7 +21273,7 @@ async function defaultQueryPackageManagerRoot(tool) {
|
|
|
20909
21273
|
const trimmed = stdout2.trim().split(/\r?\n/).pop()?.trim() ?? "";
|
|
20910
21274
|
if (!trimmed)
|
|
20911
21275
|
return done(null);
|
|
20912
|
-
done(spec.postfix ?
|
|
21276
|
+
done(spec.postfix ? join14(trimmed, spec.postfix) : trimmed);
|
|
20913
21277
|
});
|
|
20914
21278
|
});
|
|
20915
21279
|
}
|
|
@@ -21013,7 +21377,8 @@ class CommandRouter {
|
|
|
21013
21377
|
__setDiscoverPathsForTest(fn) {
|
|
21014
21378
|
this.discoverPaths = fn;
|
|
21015
21379
|
}
|
|
21016
|
-
|
|
21380
|
+
activeTurns;
|
|
21381
|
+
constructor(sessions, transport, config2, configStore, logger2, resolveSessionAgentCommand = resolveSessionAgentCommandFromIndex, orchestration, quota, scheduled, scheduledDelivery, resolveNativeSessionListFormat, activeTurns) {
|
|
21017
21382
|
this.sessions = sessions;
|
|
21018
21383
|
this.transport = transport;
|
|
21019
21384
|
this.config = config2;
|
|
@@ -21025,6 +21390,7 @@ class CommandRouter {
|
|
|
21025
21390
|
this.scheduledDelivery = scheduledDelivery;
|
|
21026
21391
|
this.resolveNativeSessionListFormat = resolveNativeSessionListFormat;
|
|
21027
21392
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
21393
|
+
this.activeTurns = activeTurns;
|
|
21028
21394
|
}
|
|
21029
21395
|
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
21030
21396
|
const startedAt = Date.now();
|
|
@@ -21095,6 +21461,8 @@ class CommandRouter {
|
|
|
21095
21461
|
return await handleNativeSessionSelect(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.identifier, command.alias);
|
|
21096
21462
|
case "session.use":
|
|
21097
21463
|
return await handleSessionUse(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
|
|
21464
|
+
case "session.use.previous":
|
|
21465
|
+
return await handleSessionUsePrevious(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
21098
21466
|
case "mode.show":
|
|
21099
21467
|
return await handleModeShow(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
21100
21468
|
case "mode.set":
|
|
@@ -21108,7 +21476,7 @@ class CommandRouter {
|
|
|
21108
21476
|
case "status":
|
|
21109
21477
|
return await handleStatus(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
21110
21478
|
case "cancel":
|
|
21111
|
-
return await handleCancel(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
21479
|
+
return await handleCancel(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
|
|
21112
21480
|
case "session.reset":
|
|
21113
21481
|
return await handleSessionReset(this.createSessionHandlerContext(reply, perfSpan), chatKey);
|
|
21114
21482
|
case "session.tail":
|
|
@@ -21203,7 +21571,8 @@ class CommandRouter {
|
|
|
21203
21571
|
...this.createHandlerContext(),
|
|
21204
21572
|
lifecycle: this.createSessionLifecycleOps(reply, perfSpan),
|
|
21205
21573
|
interaction: this.createSessionInteractionOps(perfSpan),
|
|
21206
|
-
recovery: this.createSessionRenderRecoveryOps()
|
|
21574
|
+
recovery: this.createSessionRenderRecoveryOps(),
|
|
21575
|
+
...this.activeTurns ? { activeTurns: this.activeTurns } : {}
|
|
21207
21576
|
};
|
|
21208
21577
|
}
|
|
21209
21578
|
createSessionLifecycleOps(reply, perfSpan) {
|
|
@@ -21568,7 +21937,7 @@ var init_command_router = __esm(() => {
|
|
|
21568
21937
|
});
|
|
21569
21938
|
|
|
21570
21939
|
// src/config/resolve-acpx-command.ts
|
|
21571
|
-
import { readFileSync } from "node:fs";
|
|
21940
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
21572
21941
|
import { createRequire as createRequire4 } from "node:module";
|
|
21573
21942
|
import { posix, win32 } from "node:path";
|
|
21574
21943
|
function resolveAcpxCommand(options = {}) {
|
|
@@ -21584,7 +21953,7 @@ function resolveAcpxCommandMetadata(options = {}) {
|
|
|
21584
21953
|
}
|
|
21585
21954
|
const platform = options.platform ?? process.platform;
|
|
21586
21955
|
const resolvePackageJson = options.resolvePackageJson ?? ((id) => require3.resolve(id));
|
|
21587
|
-
const readPackageJson = options.readPackageJson ?? ((path14) => JSON.parse(
|
|
21956
|
+
const readPackageJson = options.readPackageJson ?? ((path14) => JSON.parse(readFileSync2(path14, "utf8")));
|
|
21588
21957
|
try {
|
|
21589
21958
|
const packageJsonPath = resolvePackageJson("acpx/package.json");
|
|
21590
21959
|
const pkg = readPackageJson(packageJsonPath);
|
|
@@ -21658,7 +22027,7 @@ var init_console_agent = __esm(() => {
|
|
|
21658
22027
|
});
|
|
21659
22028
|
|
|
21660
22029
|
// src/orchestration/orchestration-server.ts
|
|
21661
|
-
import { rm as
|
|
22030
|
+
import { rm as rm8 } from "node:fs/promises";
|
|
21662
22031
|
import { createConnection as createConnection2, createServer } from "node:net";
|
|
21663
22032
|
|
|
21664
22033
|
class OrchestrationServer {
|
|
@@ -21991,7 +22360,7 @@ class OrchestrationServer {
|
|
|
21991
22360
|
return;
|
|
21992
22361
|
}
|
|
21993
22362
|
const removeFile = this.deps.removeFile ?? (async (path14) => {
|
|
21994
|
-
await
|
|
22363
|
+
await rm8(path14, { force: true });
|
|
21995
22364
|
});
|
|
21996
22365
|
await removeFile(this.endpoint.path);
|
|
21997
22366
|
}
|
|
@@ -25940,6 +26309,20 @@ class SessionService {
|
|
|
25940
26309
|
}
|
|
25941
26310
|
return this.toResolvedSession(session);
|
|
25942
26311
|
}
|
|
26312
|
+
getResolvedSessionByInternalAlias(alias) {
|
|
26313
|
+
const session = this.state.sessions[alias];
|
|
26314
|
+
if (!session) {
|
|
26315
|
+
return null;
|
|
26316
|
+
}
|
|
26317
|
+
try {
|
|
26318
|
+
return this.toResolvedSession(session);
|
|
26319
|
+
} catch {
|
|
26320
|
+
return null;
|
|
26321
|
+
}
|
|
26322
|
+
}
|
|
26323
|
+
peekCurrentSessionAlias(chatKey) {
|
|
26324
|
+
return this.state.chat_contexts[chatKey]?.current_session;
|
|
26325
|
+
}
|
|
25943
26326
|
async getPreferredSessionForTransport(transportSession) {
|
|
25944
26327
|
const matches = Object.values(this.state.sessions).filter((session) => session.transport_session === transportSession).sort((left, right) => right.last_used_at.localeCompare(left.last_used_at));
|
|
25945
26328
|
const expectedAlias = transportSession.split(":").at(-1);
|
|
@@ -25964,17 +26347,122 @@ class SessionService {
|
|
|
25964
26347
|
return null;
|
|
25965
26348
|
}
|
|
25966
26349
|
async useSession(chatKey, alias) {
|
|
25967
|
-
await this.mutate(async () => {
|
|
26350
|
+
return await this.mutate(async () => {
|
|
25968
26351
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
25969
26352
|
const internalAlias = resolveSessionAliasForInput(channelId, alias, Object.keys(this.state.sessions));
|
|
25970
26353
|
const session = this.state.sessions[internalAlias];
|
|
25971
26354
|
if (!session) {
|
|
25972
26355
|
throw new Error(`session "${alias}" does not exist`);
|
|
25973
26356
|
}
|
|
26357
|
+
const prevCtx = this.state.chat_contexts[chatKey];
|
|
26358
|
+
const previousCurrent = prevCtx?.current_session;
|
|
26359
|
+
const carriedPrevious = previousCurrent && previousCurrent !== internalAlias ? previousCurrent : prevCtx?.previous_session;
|
|
25974
26360
|
session.last_used_at = new Date().toISOString();
|
|
25975
|
-
this.state.chat_contexts[chatKey] = {
|
|
26361
|
+
this.state.chat_contexts[chatKey] = {
|
|
26362
|
+
current_session: internalAlias,
|
|
26363
|
+
...carriedPrevious ? { previous_session: carriedPrevious } : {}
|
|
26364
|
+
};
|
|
26365
|
+
await this.persist();
|
|
26366
|
+
return {
|
|
26367
|
+
alias: toDisplaySessionAlias(session.alias),
|
|
26368
|
+
agent: session.agent,
|
|
26369
|
+
workspace: session.workspace,
|
|
26370
|
+
previousAlias: carriedPrevious ? toDisplaySessionAlias(carriedPrevious) : undefined
|
|
26371
|
+
};
|
|
26372
|
+
});
|
|
26373
|
+
}
|
|
26374
|
+
async usePreviousSession(chatKey) {
|
|
26375
|
+
return await this.mutate(async () => {
|
|
26376
|
+
const ctx = this.state.chat_contexts[chatKey];
|
|
26377
|
+
const prevInternal = ctx?.previous_session;
|
|
26378
|
+
if (!prevInternal) {
|
|
26379
|
+
return null;
|
|
26380
|
+
}
|
|
26381
|
+
const prevSession = this.state.sessions[prevInternal];
|
|
26382
|
+
if (!prevSession) {
|
|
26383
|
+
if (ctx) {
|
|
26384
|
+
delete ctx.previous_session;
|
|
26385
|
+
await this.persist();
|
|
26386
|
+
}
|
|
26387
|
+
return null;
|
|
26388
|
+
}
|
|
26389
|
+
const currentInternal = ctx?.current_session;
|
|
26390
|
+
prevSession.last_used_at = new Date().toISOString();
|
|
26391
|
+
this.state.chat_contexts[chatKey] = {
|
|
26392
|
+
current_session: prevInternal,
|
|
26393
|
+
...currentInternal && currentInternal !== prevInternal ? { previous_session: currentInternal } : {}
|
|
26394
|
+
};
|
|
25976
26395
|
await this.persist();
|
|
26396
|
+
return {
|
|
26397
|
+
alias: toDisplaySessionAlias(prevSession.alias),
|
|
26398
|
+
agent: prevSession.agent,
|
|
26399
|
+
workspace: prevSession.workspace,
|
|
26400
|
+
previousAlias: currentInternal && currentInternal !== prevInternal ? toDisplaySessionAlias(currentInternal) : undefined
|
|
26401
|
+
};
|
|
26402
|
+
});
|
|
26403
|
+
}
|
|
26404
|
+
async setBackgroundResult(chatKey, alias, result) {
|
|
26405
|
+
await this.mutate(async () => {
|
|
26406
|
+
const ctx = this.state.chat_contexts[chatKey] ?? { current_session: "" };
|
|
26407
|
+
const results = { ...ctx.background_results ?? {}, [alias]: result };
|
|
26408
|
+
this.state.chat_contexts[chatKey] = { ...ctx, background_results: results };
|
|
26409
|
+
await this.persist();
|
|
26410
|
+
});
|
|
26411
|
+
}
|
|
26412
|
+
async takeBackgroundResult(chatKey, alias) {
|
|
26413
|
+
return await this.mutate(async () => {
|
|
26414
|
+
const ctx = this.state.chat_contexts[chatKey];
|
|
26415
|
+
const found = ctx?.background_results?.[alias];
|
|
26416
|
+
if (!ctx || !found)
|
|
26417
|
+
return null;
|
|
26418
|
+
const remaining = { ...ctx.background_results };
|
|
26419
|
+
delete remaining[alias];
|
|
26420
|
+
if (Object.keys(remaining).length > 0) {
|
|
26421
|
+
this.state.chat_contexts[chatKey] = { ...ctx, background_results: remaining };
|
|
26422
|
+
} else {
|
|
26423
|
+
const { background_results: _omit, ...rest } = ctx;
|
|
26424
|
+
this.state.chat_contexts[chatKey] = rest;
|
|
26425
|
+
}
|
|
26426
|
+
await this.persist();
|
|
26427
|
+
return found;
|
|
26428
|
+
});
|
|
26429
|
+
}
|
|
26430
|
+
listBackgroundResultAliases(chatKey) {
|
|
26431
|
+
const results = this.state.chat_contexts[chatKey]?.background_results;
|
|
26432
|
+
return results ? Object.keys(results) : [];
|
|
26433
|
+
}
|
|
26434
|
+
resolveFuzzyAlias(chatKey, fragment) {
|
|
26435
|
+
const channelId = getChannelIdFromChatKey(chatKey);
|
|
26436
|
+
const frag = fragment.trim();
|
|
26437
|
+
const items = Object.values(this.state.sessions).filter((session) => isSessionAliasVisibleInChannel(session.alias, channelId)).map((session) => ({
|
|
26438
|
+
display: toDisplaySessionAlias(session.alias),
|
|
26439
|
+
agent: session.agent,
|
|
26440
|
+
workspace: session.workspace
|
|
26441
|
+
}));
|
|
26442
|
+
const toCandidate = (item) => ({
|
|
26443
|
+
alias: item.display,
|
|
26444
|
+
agent: item.agent,
|
|
26445
|
+
workspace: item.workspace
|
|
25977
26446
|
});
|
|
26447
|
+
const exact = items.find((item) => item.display === frag);
|
|
26448
|
+
if (exact) {
|
|
26449
|
+
return { kind: "match", alias: exact.display };
|
|
26450
|
+
}
|
|
26451
|
+
const prefix = items.filter((item) => item.display.startsWith(frag));
|
|
26452
|
+
if (prefix.length === 1) {
|
|
26453
|
+
return { kind: "match", alias: prefix[0].display };
|
|
26454
|
+
}
|
|
26455
|
+
if (prefix.length > 1) {
|
|
26456
|
+
return { kind: "ambiguous", candidates: prefix.map(toCandidate) };
|
|
26457
|
+
}
|
|
26458
|
+
const substring = items.filter((item) => item.display.includes(frag));
|
|
26459
|
+
if (substring.length === 1) {
|
|
26460
|
+
return { kind: "match", alias: substring[0].display };
|
|
26461
|
+
}
|
|
26462
|
+
if (substring.length > 1) {
|
|
26463
|
+
return { kind: "ambiguous", candidates: substring.map(toCandidate) };
|
|
26464
|
+
}
|
|
26465
|
+
return { kind: "none" };
|
|
25978
26466
|
}
|
|
25979
26467
|
async resolveAliasForChat(chatKey, displayAlias) {
|
|
25980
26468
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
@@ -26076,6 +26564,10 @@ class SessionService {
|
|
|
26076
26564
|
for (const [chatKey, ctx] of Object.entries(this.state.chat_contexts)) {
|
|
26077
26565
|
if (ctx.current_session === alias) {
|
|
26078
26566
|
delete this.state.chat_contexts[chatKey];
|
|
26567
|
+
continue;
|
|
26568
|
+
}
|
|
26569
|
+
if (ctx.previous_session === alias) {
|
|
26570
|
+
delete ctx.previous_session;
|
|
26079
26571
|
}
|
|
26080
26572
|
}
|
|
26081
26573
|
await this.persist();
|
|
@@ -26107,9 +26599,11 @@ class SessionService {
|
|
|
26107
26599
|
}
|
|
26108
26600
|
const createdAt = Date.parse(cached2.created_at);
|
|
26109
26601
|
if (Number.isNaN(createdAt)) {
|
|
26602
|
+
await this.deleteNativeSessionListIfCurrent(chatKey, cached2);
|
|
26110
26603
|
return null;
|
|
26111
26604
|
}
|
|
26112
26605
|
if (this.now() - createdAt > ttlMs) {
|
|
26606
|
+
await this.deleteNativeSessionListIfCurrent(chatKey, cached2);
|
|
26113
26607
|
return null;
|
|
26114
26608
|
}
|
|
26115
26609
|
return {
|
|
@@ -26125,6 +26619,15 @@ class SessionService {
|
|
|
26125
26619
|
...cached2.next_cursor !== undefined ? { nextCursor: cached2.next_cursor } : {}
|
|
26126
26620
|
};
|
|
26127
26621
|
}
|
|
26622
|
+
async deleteNativeSessionListIfCurrent(chatKey, cached2) {
|
|
26623
|
+
await this.mutate(async () => {
|
|
26624
|
+
if (this.state.native_session_lists[chatKey] !== cached2) {
|
|
26625
|
+
return;
|
|
26626
|
+
}
|
|
26627
|
+
delete this.state.native_session_lists[chatKey];
|
|
26628
|
+
await this.persist();
|
|
26629
|
+
});
|
|
26630
|
+
}
|
|
26128
26631
|
toResolvedSession(session) {
|
|
26129
26632
|
const agentConfig = this.config.agents[session.agent];
|
|
26130
26633
|
if (!agentConfig) {
|
|
@@ -26224,6 +26727,29 @@ var init_session_service = __esm(() => {
|
|
|
26224
26727
|
init_channel_scope();
|
|
26225
26728
|
});
|
|
26226
26729
|
|
|
26730
|
+
// src/sessions/active-turn-registry.ts
|
|
26731
|
+
function createActiveTurnRegistry() {
|
|
26732
|
+
const byChat = new Map;
|
|
26733
|
+
return {
|
|
26734
|
+
markActive(chatKey, alias) {
|
|
26735
|
+
const set2 = byChat.get(chatKey) ?? new Set;
|
|
26736
|
+
set2.add(alias);
|
|
26737
|
+
byChat.set(chatKey, set2);
|
|
26738
|
+
},
|
|
26739
|
+
markInactive(chatKey, alias) {
|
|
26740
|
+
const set2 = byChat.get(chatKey);
|
|
26741
|
+
if (!set2)
|
|
26742
|
+
return;
|
|
26743
|
+
set2.delete(alias);
|
|
26744
|
+
if (set2.size === 0)
|
|
26745
|
+
byChat.delete(chatKey);
|
|
26746
|
+
},
|
|
26747
|
+
isActive(chatKey, alias) {
|
|
26748
|
+
return byChat.get(chatKey)?.has(alias) ?? false;
|
|
26749
|
+
}
|
|
26750
|
+
};
|
|
26751
|
+
}
|
|
26752
|
+
|
|
26227
26753
|
// src/state/debounced-state-store.ts
|
|
26228
26754
|
class DebouncedStateStore {
|
|
26229
26755
|
deps;
|
|
@@ -26447,6 +26973,8 @@ async function runConsole(paths, deps) {
|
|
|
26447
26973
|
agent: runtime.agent,
|
|
26448
26974
|
abortSignal: shutdownController.signal,
|
|
26449
26975
|
quota: runtime.quota,
|
|
26976
|
+
sessions: runtime.sessions,
|
|
26977
|
+
activeTurns: runtime.activeTurns,
|
|
26450
26978
|
logger: runtime.logger,
|
|
26451
26979
|
perfTracer: runtime.perfTracer,
|
|
26452
26980
|
commandHints: listWeacpxCommandHints(),
|
|
@@ -26600,7 +27128,7 @@ function encodeBridgeSessionNoteEvent(event) {
|
|
|
26600
27128
|
|
|
26601
27129
|
// src/transport/acpx-bridge/acpx-bridge-client.ts
|
|
26602
27130
|
import { spawn as spawn7 } from "node:child_process";
|
|
26603
|
-
import { fileURLToPath as
|
|
27131
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
26604
27132
|
import { createInterface } from "node:readline";
|
|
26605
27133
|
|
|
26606
27134
|
class AcpxBridgeClient {
|
|
@@ -26725,7 +27253,7 @@ function buildBridgeSpawnSpec(options) {
|
|
|
26725
27253
|
};
|
|
26726
27254
|
}
|
|
26727
27255
|
async function spawnAcpxBridgeClient(options = {}) {
|
|
26728
|
-
const bridgeEntryPath = options.bridgeEntryPath ??
|
|
27256
|
+
const bridgeEntryPath = options.bridgeEntryPath ?? fileURLToPath4(new URL("../../bridge/bridge-main.ts", import.meta.url));
|
|
26729
27257
|
const spawnSpec = buildBridgeSpawnSpec({
|
|
26730
27258
|
execPath: process.execPath,
|
|
26731
27259
|
bridgeEntryPath
|
|
@@ -27177,7 +27705,7 @@ var init_spawn_command = __esm(() => {
|
|
|
27177
27705
|
});
|
|
27178
27706
|
|
|
27179
27707
|
// src/transport/prompt-media.ts
|
|
27180
|
-
import { mkdtemp, open as open4, rm as
|
|
27708
|
+
import { mkdtemp, open as open4, rm as rm9, writeFile as writeFile7 } from "node:fs/promises";
|
|
27181
27709
|
import { tmpdir as defaultTmpdir } from "node:os";
|
|
27182
27710
|
import path14 from "node:path";
|
|
27183
27711
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -27302,7 +27830,7 @@ var init_prompt_media = __esm(() => {
|
|
|
27302
27830
|
readImageFile: readImageFileBounded,
|
|
27303
27831
|
mkdtemp,
|
|
27304
27832
|
writeFile: writeFile7,
|
|
27305
|
-
rm:
|
|
27833
|
+
rm: rm9,
|
|
27306
27834
|
tmpdir: defaultTmpdir
|
|
27307
27835
|
};
|
|
27308
27836
|
});
|
|
@@ -27601,12 +28129,12 @@ var init_streaming_prompt = __esm(() => {
|
|
|
27601
28129
|
|
|
27602
28130
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
27603
28131
|
import { chmod as chmodFs } from "node:fs/promises";
|
|
27604
|
-
import { dirname as
|
|
28132
|
+
import { dirname as dirname13, join as join15 } from "node:path";
|
|
27605
28133
|
function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
27606
28134
|
if (platform === "win32") {
|
|
27607
28135
|
return null;
|
|
27608
28136
|
}
|
|
27609
|
-
return
|
|
28137
|
+
return join15(dirname13(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
27610
28138
|
}
|
|
27611
28139
|
async function ensureNodePtyHelperExecutable(helperPath, chmod3 = chmodFs) {
|
|
27612
28140
|
if (!helperPath) {
|
|
@@ -27628,7 +28156,7 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
27628
28156
|
import { spawn as spawn8 } from "node:child_process";
|
|
27629
28157
|
import { readFile as readFile12, unlink } from "node:fs/promises";
|
|
27630
28158
|
import { homedir as homedir8 } from "node:os";
|
|
27631
|
-
import { join as
|
|
28159
|
+
import { join as join16 } from "node:path";
|
|
27632
28160
|
function buildWeacpxMcpServerSpec(input) {
|
|
27633
28161
|
const { command, args } = splitCommandLine(input.weacpxCommand);
|
|
27634
28162
|
return {
|
|
@@ -27679,7 +28207,13 @@ class AcpxQueueOwnerLauncher {
|
|
|
27679
28207
|
const key = input.acpxRecordId;
|
|
27680
28208
|
const previous = this.launchLocks.get(key) ?? Promise.resolve();
|
|
27681
28209
|
const next = previous.then(() => this.doLaunch(input), () => this.doLaunch(input));
|
|
27682
|
-
|
|
28210
|
+
const tracked = next.catch(() => {});
|
|
28211
|
+
this.launchLocks.set(key, tracked);
|
|
28212
|
+
tracked.finally(() => {
|
|
28213
|
+
if (this.launchLocks.get(key) === tracked) {
|
|
28214
|
+
this.launchLocks.delete(key);
|
|
28215
|
+
}
|
|
28216
|
+
});
|
|
27683
28217
|
return next;
|
|
27684
28218
|
}
|
|
27685
28219
|
async doLaunch(input) {
|
|
@@ -27792,7 +28326,7 @@ async function terminateAcpxQueueOwner(sessionId) {
|
|
|
27792
28326
|
await unlink(lockPath).catch(() => {});
|
|
27793
28327
|
}
|
|
27794
28328
|
function queueLockFilePath(sessionId) {
|
|
27795
|
-
return
|
|
28329
|
+
return join16(homedir8(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
|
|
27796
28330
|
}
|
|
27797
28331
|
function shortHash(value, length) {
|
|
27798
28332
|
return createHash3("sha256").update(value).digest("hex").slice(0, length);
|
|
@@ -28637,23 +29171,33 @@ var init_channel_registry = __esm(() => {
|
|
|
28637
29171
|
});
|
|
28638
29172
|
|
|
28639
29173
|
// src/weixin/messaging/quota-manager.ts
|
|
28640
|
-
function freshState() {
|
|
28641
|
-
return { midUsed: 0, finalUsed: 0, pendingFinalChunks: [] };
|
|
29174
|
+
function freshState(now) {
|
|
29175
|
+
return { midUsed: 0, finalUsed: 0, pendingFinalChunks: [], lastTouchedAt: now };
|
|
28642
29176
|
}
|
|
28643
29177
|
|
|
28644
29178
|
class QuotaManager {
|
|
28645
29179
|
states = new Map;
|
|
28646
29180
|
observer;
|
|
28647
29181
|
normalizeKey;
|
|
28648
|
-
|
|
29182
|
+
maxStates;
|
|
29183
|
+
stateTtlMs;
|
|
29184
|
+
maxPendingFinalChunksPerChat;
|
|
29185
|
+
now;
|
|
29186
|
+
constructor(observer, normalizeKey, options = {}) {
|
|
28649
29187
|
this.observer = observer;
|
|
28650
29188
|
this.normalizeKey = normalizeKey ?? ((key) => key);
|
|
29189
|
+
this.maxStates = normalizePositiveInt3(options.maxStates, DEFAULT_MAX_STATES);
|
|
29190
|
+
this.stateTtlMs = normalizeNonNegativeMs3(options.stateTtlMs, DEFAULT_STATE_TTL_MS);
|
|
29191
|
+
this.maxPendingFinalChunksPerChat = normalizePositiveInt3(options.maxPendingFinalChunksPerChat, DEFAULT_MAX_PENDING_FINAL_CHUNKS_PER_CHAT);
|
|
29192
|
+
this.now = options.now ?? (() => Date.now());
|
|
28651
29193
|
}
|
|
28652
29194
|
onInbound(chatKey) {
|
|
28653
29195
|
const key = this.normalizeKey(chatKey);
|
|
29196
|
+
this.prune();
|
|
28654
29197
|
const existing = this.states.get(key);
|
|
28655
29198
|
const pending = existing?.pendingFinalChunks ?? [];
|
|
28656
|
-
this.states.set(key, { midUsed: 0, finalUsed: 0, pendingFinalChunks: pending });
|
|
29199
|
+
this.states.set(key, { midUsed: 0, finalUsed: 0, pendingFinalChunks: pending, lastTouchedAt: this.now() });
|
|
29200
|
+
this.enforceMaxStates();
|
|
28657
29201
|
this.observer?.onInbound?.(key);
|
|
28658
29202
|
}
|
|
28659
29203
|
reserveMidSegment(chatKey) {
|
|
@@ -28686,36 +29230,48 @@ class QuotaManager {
|
|
|
28686
29230
|
return;
|
|
28687
29231
|
const state = this.getOrCreate(this.normalizeKey(chatKey));
|
|
28688
29232
|
state.pendingFinalChunks.push(...chunks);
|
|
29233
|
+
this.trimPendingFinalChunks(state, "front");
|
|
28689
29234
|
}
|
|
28690
29235
|
prependPendingFinal(chatKey, chunks) {
|
|
28691
29236
|
if (chunks.length === 0)
|
|
28692
29237
|
return;
|
|
28693
29238
|
const state = this.getOrCreate(this.normalizeKey(chatKey));
|
|
28694
29239
|
state.pendingFinalChunks.unshift(...chunks);
|
|
29240
|
+
this.trimPendingFinalChunks(state, "back");
|
|
28695
29241
|
}
|
|
28696
29242
|
drainPendingFinalUpToBudget(chatKey, available) {
|
|
28697
29243
|
if (available <= 0)
|
|
28698
29244
|
return [];
|
|
28699
|
-
const
|
|
29245
|
+
const key = this.normalizeKey(chatKey);
|
|
29246
|
+
const state = this.getOrCreate(key);
|
|
28700
29247
|
if (state.pendingFinalChunks.length === 0)
|
|
28701
29248
|
return [];
|
|
28702
|
-
|
|
29249
|
+
const drained = state.pendingFinalChunks.splice(0, available);
|
|
29250
|
+
state.lastTouchedAt = this.now();
|
|
29251
|
+
this.deleteIfEmpty(key, state);
|
|
29252
|
+
return drained;
|
|
28703
29253
|
}
|
|
28704
29254
|
hasPendingFinal(chatKey) {
|
|
29255
|
+
this.prune();
|
|
28705
29256
|
return (this.states.get(this.normalizeKey(chatKey))?.pendingFinalChunks.length ?? 0) > 0;
|
|
28706
29257
|
}
|
|
28707
29258
|
countPendingFinal(chatKey) {
|
|
29259
|
+
this.prune();
|
|
28708
29260
|
return this.states.get(this.normalizeKey(chatKey))?.pendingFinalChunks.length ?? 0;
|
|
28709
29261
|
}
|
|
28710
29262
|
clearPendingFinal(chatKey) {
|
|
28711
|
-
const
|
|
29263
|
+
const key = this.normalizeKey(chatKey);
|
|
29264
|
+
const state = this.states.get(key);
|
|
28712
29265
|
if (!state)
|
|
28713
29266
|
return;
|
|
28714
29267
|
state.pendingFinalChunks = [];
|
|
29268
|
+
state.lastTouchedAt = this.now();
|
|
29269
|
+
this.deleteIfEmpty(key, state);
|
|
28715
29270
|
}
|
|
28716
29271
|
snapshot(chatKey) {
|
|
28717
29272
|
const key = this.normalizeKey(chatKey);
|
|
28718
|
-
|
|
29273
|
+
this.prune();
|
|
29274
|
+
const state = this.states.get(key) ?? freshState(this.now());
|
|
28719
29275
|
const midRemaining = MID_BUDGET - state.midUsed;
|
|
28720
29276
|
const finalRemaining = FINAL_BUDGET - state.finalUsed;
|
|
28721
29277
|
return {
|
|
@@ -28727,15 +29283,72 @@ class QuotaManager {
|
|
|
28727
29283
|
};
|
|
28728
29284
|
}
|
|
28729
29285
|
getOrCreate(key) {
|
|
29286
|
+
this.prune();
|
|
28730
29287
|
let state = this.states.get(key);
|
|
28731
29288
|
if (!state) {
|
|
28732
|
-
state = freshState();
|
|
29289
|
+
state = freshState(this.now());
|
|
28733
29290
|
this.states.set(key, state);
|
|
29291
|
+
this.enforceMaxStates();
|
|
29292
|
+
} else {
|
|
29293
|
+
state.lastTouchedAt = this.now();
|
|
28734
29294
|
}
|
|
28735
29295
|
return state;
|
|
28736
29296
|
}
|
|
29297
|
+
prune() {
|
|
29298
|
+
const cutoff = this.now() - this.stateTtlMs;
|
|
29299
|
+
for (const [key, state] of this.states) {
|
|
29300
|
+
if (state.lastTouchedAt < cutoff) {
|
|
29301
|
+
this.states.delete(key);
|
|
29302
|
+
}
|
|
29303
|
+
}
|
|
29304
|
+
this.enforceMaxStates();
|
|
29305
|
+
}
|
|
29306
|
+
enforceMaxStates() {
|
|
29307
|
+
while (this.states.size > this.maxStates) {
|
|
29308
|
+
let oldestKey;
|
|
29309
|
+
let oldestTouchedAt = Number.POSITIVE_INFINITY;
|
|
29310
|
+
for (const [key, state] of this.states) {
|
|
29311
|
+
if (state.lastTouchedAt < oldestTouchedAt) {
|
|
29312
|
+
oldestTouchedAt = state.lastTouchedAt;
|
|
29313
|
+
oldestKey = key;
|
|
29314
|
+
}
|
|
29315
|
+
}
|
|
29316
|
+
if (oldestKey === undefined)
|
|
29317
|
+
return;
|
|
29318
|
+
this.states.delete(oldestKey);
|
|
29319
|
+
}
|
|
29320
|
+
}
|
|
29321
|
+
trimPendingFinalChunks(state, side) {
|
|
29322
|
+
state.lastTouchedAt = this.now();
|
|
29323
|
+
const excess = state.pendingFinalChunks.length - this.maxPendingFinalChunksPerChat;
|
|
29324
|
+
if (excess <= 0)
|
|
29325
|
+
return;
|
|
29326
|
+
if (side === "front") {
|
|
29327
|
+
state.pendingFinalChunks.splice(0, excess);
|
|
29328
|
+
} else {
|
|
29329
|
+
state.pendingFinalChunks.splice(state.pendingFinalChunks.length - excess, excess);
|
|
29330
|
+
}
|
|
29331
|
+
}
|
|
29332
|
+
deleteIfEmpty(key, state) {
|
|
29333
|
+
if (state.midUsed === 0 && state.finalUsed === 0 && state.pendingFinalChunks.length === 0) {
|
|
29334
|
+
this.states.delete(key);
|
|
29335
|
+
}
|
|
29336
|
+
}
|
|
29337
|
+
}
|
|
29338
|
+
function normalizePositiveInt3(value, fallback) {
|
|
29339
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1)
|
|
29340
|
+
return fallback;
|
|
29341
|
+
return Math.floor(value);
|
|
29342
|
+
}
|
|
29343
|
+
function normalizeNonNegativeMs3(value, fallback) {
|
|
29344
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
29345
|
+
return fallback;
|
|
29346
|
+
return value;
|
|
28737
29347
|
}
|
|
28738
|
-
var MID_BUDGET = 6, FINAL_BUDGET = 4;
|
|
29348
|
+
var MID_BUDGET = 6, FINAL_BUDGET = 4, DEFAULT_MAX_STATES = 5000, DEFAULT_STATE_TTL_MS, DEFAULT_MAX_PENDING_FINAL_CHUNKS_PER_CHAT = 40;
|
|
29349
|
+
var init_quota_manager = __esm(() => {
|
|
29350
|
+
DEFAULT_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
29351
|
+
});
|
|
28739
29352
|
|
|
28740
29353
|
// src/main.ts
|
|
28741
29354
|
var exports_main = {};
|
|
@@ -28748,8 +29361,8 @@ __export(exports_main, {
|
|
|
28748
29361
|
});
|
|
28749
29362
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
28750
29363
|
import { homedir as homedir9 } from "node:os";
|
|
28751
|
-
import { dirname as
|
|
28752
|
-
import { fileURLToPath as
|
|
29364
|
+
import { dirname as dirname14, join as join17 } from "node:path";
|
|
29365
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
28753
29366
|
function startProgressHeartbeat(orchestration, config2, logger2, channel) {
|
|
28754
29367
|
const thresholdSeconds = config2.orchestration.progressHeartbeatSeconds;
|
|
28755
29368
|
if (thresholdSeconds <= 0) {
|
|
@@ -28829,6 +29442,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
28829
29442
|
}
|
|
28830
29443
|
});
|
|
28831
29444
|
const sessions = new SessionService(config2, debouncedStateStore, state, { stateMutex });
|
|
29445
|
+
const activeTurns = createActiveTurnRegistry();
|
|
28832
29446
|
const scheduledService = new ScheduledTaskService(state, debouncedStateStore, { stateMutex });
|
|
28833
29447
|
const pendingWorkerDispatches = new Set;
|
|
28834
29448
|
const transport = config2.transport.type === "acpx-bridge" ? await (deps.createBridgeTransport?.() ?? Promise.resolve(new AcpxBridgeTransport(await spawnAcpxBridgeClient({
|
|
@@ -29199,7 +29813,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
29199
29813
|
listScheduledTasksFromRoute: async (input) => await listScheduledTasksFromRoute(input, { state, scheduled: scheduledService }),
|
|
29200
29814
|
cancelScheduledTaskFromRoute: async (input) => await cancelScheduledTaskFromRoute(input, { state, scheduled: scheduledService })
|
|
29201
29815
|
});
|
|
29202
|
-
const router = new CommandRouter(sessions, transport, config2, configStore, logger2, undefined, orchestration, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined, deps.channel?.nativeSessionListFormat ? deps.channel.nativeSessionListFormat.bind(deps.channel) : undefined);
|
|
29816
|
+
const router = new CommandRouter(sessions, transport, config2, configStore, logger2, undefined, orchestration, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined, deps.channel?.nativeSessionListFormat ? deps.channel.nativeSessionListFormat.bind(deps.channel) : undefined, activeTurns);
|
|
29203
29817
|
const agent = new ConsoleAgent(router, logger2);
|
|
29204
29818
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
29205
29819
|
dispatchTask: buildScheduledDispatchTask({
|
|
@@ -29220,6 +29834,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
29220
29834
|
agent,
|
|
29221
29835
|
router,
|
|
29222
29836
|
sessions,
|
|
29837
|
+
activeTurns,
|
|
29223
29838
|
stateStore,
|
|
29224
29839
|
configStore,
|
|
29225
29840
|
logger: logger2,
|
|
@@ -29325,8 +29940,8 @@ async function main() {
|
|
|
29325
29940
|
}
|
|
29326
29941
|
}
|
|
29327
29942
|
async function prepareChannelMedia(configPath, config2) {
|
|
29328
|
-
const runtimeDir =
|
|
29329
|
-
const mediaRootDir =
|
|
29943
|
+
const runtimeDir = join17(dirname14(configPath), "runtime");
|
|
29944
|
+
const mediaRootDir = join17(runtimeDir, "media");
|
|
29330
29945
|
const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
|
|
29331
29946
|
await mediaStore.cleanupExpired().catch((error2) => {
|
|
29332
29947
|
console.error("[weacpx] media cleanup failed:", error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -29339,30 +29954,30 @@ function resolveRuntimePaths() {
|
|
|
29339
29954
|
if (!home) {
|
|
29340
29955
|
throw new Error("Unable to resolve the current user home directory");
|
|
29341
29956
|
}
|
|
29342
|
-
const configPath = process.env.WEACPX_CONFIG ??
|
|
29343
|
-
const runtimeDir =
|
|
29957
|
+
const configPath = process.env.WEACPX_CONFIG ?? join17(coreHomeDir(home), "config.json");
|
|
29958
|
+
const runtimeDir = join17(dirname14(configPath), "runtime");
|
|
29344
29959
|
return {
|
|
29345
29960
|
configPath,
|
|
29346
|
-
statePath: process.env.WEACPX_STATE ??
|
|
29347
|
-
perfLogPath:
|
|
29961
|
+
statePath: process.env.WEACPX_STATE ?? join17(coreHomeDir(home), "state.json"),
|
|
29962
|
+
perfLogPath: join17(runtimeDir, "perf.log"),
|
|
29348
29963
|
orchestrationSocketPath: process.env.WEACPX_ORCHESTRATION_SOCKET ?? resolveDaemonOrchestrationSocketPath(runtimeDir)
|
|
29349
29964
|
};
|
|
29350
29965
|
}
|
|
29351
29966
|
function resolveBridgeEntryPath() {
|
|
29352
29967
|
if (import.meta.url.includes("/dist/")) {
|
|
29353
|
-
return
|
|
29968
|
+
return fileURLToPath5(new URL("./bridge/bridge-main.js", import.meta.url));
|
|
29354
29969
|
}
|
|
29355
|
-
return
|
|
29970
|
+
return fileURLToPath5(new URL("./bridge/bridge-main.ts", import.meta.url));
|
|
29356
29971
|
}
|
|
29357
29972
|
function resolveAppLogPath(configPath) {
|
|
29358
|
-
const rootDir =
|
|
29359
|
-
const runtimeDir =
|
|
29360
|
-
return
|
|
29973
|
+
const rootDir = dirname14(configPath);
|
|
29974
|
+
const runtimeDir = join17(rootDir, "runtime");
|
|
29975
|
+
return join17(runtimeDir, "app.log");
|
|
29361
29976
|
}
|
|
29362
29977
|
function resolvePerfLogPath(configPath) {
|
|
29363
|
-
const rootDir =
|
|
29364
|
-
const runtimeDir =
|
|
29365
|
-
return
|
|
29978
|
+
const rootDir = dirname14(configPath);
|
|
29979
|
+
const runtimeDir = join17(rootDir, "runtime");
|
|
29980
|
+
return join17(runtimeDir, "perf.log");
|
|
29366
29981
|
}
|
|
29367
29982
|
function resolveOrchestrationSocketPathFromConfigPath(configPath) {
|
|
29368
29983
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
@@ -29372,6 +29987,7 @@ function shouldNotifyTaskCompletion(task) {
|
|
|
29372
29987
|
return Boolean(task.chatKey && task.replyContextToken && (task.status === "completed" || task.status === "failed"));
|
|
29373
29988
|
}
|
|
29374
29989
|
var init_main = __esm(async () => {
|
|
29990
|
+
init_core_home();
|
|
29375
29991
|
init_command_router();
|
|
29376
29992
|
init_config_store();
|
|
29377
29993
|
init_ensure_config();
|
|
@@ -29401,6 +30017,7 @@ var init_main = __esm(async () => {
|
|
|
29401
30017
|
init_media_store();
|
|
29402
30018
|
init_quota_errors();
|
|
29403
30019
|
init_inbound();
|
|
30020
|
+
init_quota_manager();
|
|
29404
30021
|
init_perf_tracer();
|
|
29405
30022
|
init_bootstrap();
|
|
29406
30023
|
if (false) {}
|
|
@@ -29592,7 +30209,7 @@ var init_config_check = __esm(async () => {
|
|
|
29592
30209
|
});
|
|
29593
30210
|
|
|
29594
30211
|
// src/doctor/checks/daemon-check.ts
|
|
29595
|
-
import { fileURLToPath as
|
|
30212
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
29596
30213
|
import { homedir as homedir10 } from "node:os";
|
|
29597
30214
|
async function checkDaemon(options = {}) {
|
|
29598
30215
|
const home = options.home ?? process.env.HOME ?? homedir10();
|
|
@@ -29686,7 +30303,7 @@ function defaultIsProcessRunning5(pid) {
|
|
|
29686
30303
|
}
|
|
29687
30304
|
}
|
|
29688
30305
|
function resolveCliEntryPath() {
|
|
29689
|
-
return process.argv[1] ??
|
|
30306
|
+
return process.argv[1] ?? fileURLToPath6(import.meta.url);
|
|
29690
30307
|
}
|
|
29691
30308
|
function formatError5(error2) {
|
|
29692
30309
|
return error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -29748,7 +30365,7 @@ async function checkOrchestrationHealth(options) {
|
|
|
29748
30365
|
// src/doctor/checks/runtime-check.ts
|
|
29749
30366
|
import { constants } from "node:fs";
|
|
29750
30367
|
import { access as access4, stat as stat3 } from "node:fs/promises";
|
|
29751
|
-
import { dirname as
|
|
30368
|
+
import { dirname as dirname15 } from "node:path";
|
|
29752
30369
|
import { homedir as homedir11 } from "node:os";
|
|
29753
30370
|
async function checkRuntime(options = {}) {
|
|
29754
30371
|
const home = options.home ?? process.env.HOME ?? homedir11();
|
|
@@ -29849,7 +30466,7 @@ async function checkFileCreatable(label, path15, probe, platform) {
|
|
|
29849
30466
|
detail: `${label}: ${path15} (unusable: ${formatError6(error2)})`
|
|
29850
30467
|
};
|
|
29851
30468
|
}
|
|
29852
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
30469
|
+
const parentCheck = await checkCreatableAncestorDirectory(dirname15(path15), probe, platform);
|
|
29853
30470
|
if (!parentCheck.ok) {
|
|
29854
30471
|
return {
|
|
29855
30472
|
ok: false,
|
|
@@ -29885,7 +30502,7 @@ async function checkCreatableAncestorDirectory(path15, probe, platform) {
|
|
|
29885
30502
|
blockingPath: path15
|
|
29886
30503
|
};
|
|
29887
30504
|
}
|
|
29888
|
-
const parent =
|
|
30505
|
+
const parent = dirname15(path15);
|
|
29889
30506
|
if (parent === path15) {
|
|
29890
30507
|
return {
|
|
29891
30508
|
ok: false,
|
|
@@ -30308,7 +30925,7 @@ var init_render_doctor = __esm(() => {
|
|
|
30308
30925
|
|
|
30309
30926
|
// src/doctor/doctor.ts
|
|
30310
30927
|
import { homedir as homedir12 } from "node:os";
|
|
30311
|
-
import { join as
|
|
30928
|
+
import { join as join18 } from "node:path";
|
|
30312
30929
|
async function runDoctor(options = {}, deps = {}) {
|
|
30313
30930
|
const home = deps.home ?? process.env.HOME ?? homedir12();
|
|
30314
30931
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
@@ -30363,8 +30980,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
|
|
|
30363
30980
|
return resolveRuntimePaths();
|
|
30364
30981
|
}
|
|
30365
30982
|
return {
|
|
30366
|
-
configPath:
|
|
30367
|
-
statePath:
|
|
30983
|
+
configPath: join18(coreHomeDir(home), "config.json"),
|
|
30984
|
+
statePath: join18(coreHomeDir(home), "state.json")
|
|
30368
30985
|
};
|
|
30369
30986
|
}
|
|
30370
30987
|
function depsUseExplicitRuntimeOverrides() {
|
|
@@ -30429,6 +31046,7 @@ function formatError9(error2) {
|
|
|
30429
31046
|
return error2 instanceof Error ? error2.message : String(error2);
|
|
30430
31047
|
}
|
|
30431
31048
|
var init_doctor = __esm(async () => {
|
|
31049
|
+
init_core_home();
|
|
30432
31050
|
init_load_config();
|
|
30433
31051
|
init_state_store();
|
|
30434
31052
|
init_daemon_check();
|
|
@@ -30462,6 +31080,7 @@ var init_doctor2 = __esm(async () => {
|
|
|
30462
31080
|
});
|
|
30463
31081
|
|
|
30464
31082
|
// src/cli.ts
|
|
31083
|
+
init_core_home();
|
|
30465
31084
|
init_config_store();
|
|
30466
31085
|
init_load_config();
|
|
30467
31086
|
init_ensure_config();
|
|
@@ -30470,8 +31089,8 @@ init_create_daemon_controller();
|
|
|
30470
31089
|
init_daemon_files();
|
|
30471
31090
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
30472
31091
|
import { homedir as homedir13 } from "node:os";
|
|
30473
|
-
import { dirname as
|
|
30474
|
-
import { fileURLToPath as
|
|
31092
|
+
import { dirname as dirname16, join as join19, sep } from "node:path";
|
|
31093
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
30475
31094
|
|
|
30476
31095
|
// src/daemon/daemon-runtime.ts
|
|
30477
31096
|
init_daemon_status();
|
|
@@ -42884,15 +43503,17 @@ class StdioServerTransport {
|
|
|
42884
43503
|
init_version();
|
|
42885
43504
|
|
|
42886
43505
|
// src/mcp/resolve-endpoint.ts
|
|
43506
|
+
init_core_home();
|
|
42887
43507
|
init_daemon_files();
|
|
42888
43508
|
init_orchestration_ipc();
|
|
42889
43509
|
import { homedir as homedir2 } from "node:os";
|
|
43510
|
+
import { join as join4 } from "node:path";
|
|
42890
43511
|
function resolveDefaultOrchestrationEndpoint(env = process.env, platform = process.platform) {
|
|
42891
43512
|
if (typeof env.WEACPX_ORCHESTRATION_SOCKET === "string" && env.WEACPX_ORCHESTRATION_SOCKET.trim().length > 0) {
|
|
42892
43513
|
return createOrchestrationEndpoint(env.WEACPX_ORCHESTRATION_SOCKET.trim(), platform);
|
|
42893
43514
|
}
|
|
42894
43515
|
const home = requireHome(env);
|
|
42895
|
-
const configPath = typeof env.WEACPX_CONFIG === "string" && env.WEACPX_CONFIG.trim().length > 0 ? env.WEACPX_CONFIG.trim() :
|
|
43516
|
+
const configPath = typeof env.WEACPX_CONFIG === "string" && env.WEACPX_CONFIG.trim().length > 0 ? env.WEACPX_CONFIG.trim() : join4(coreHomeDir(home), "config.json");
|
|
42896
43517
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
42897
43518
|
return resolveOrchestrationEndpoint(runtimeDir, platform);
|
|
42898
43519
|
}
|
|
@@ -44391,12 +45012,14 @@ function resolveTemplateChoice(answer, names) {
|
|
|
44391
45012
|
init_plugin_home();
|
|
44392
45013
|
import { spawn as spawn4 } from "node:child_process";
|
|
44393
45014
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
44394
|
-
import { dirname as
|
|
44395
|
-
import { fileURLToPath as
|
|
45015
|
+
import { dirname as dirname10, join as join10 } from "node:path";
|
|
45016
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
44396
45017
|
|
|
44397
45018
|
// src/plugins/package-manager.ts
|
|
44398
45019
|
init_plugin_home();
|
|
44399
45020
|
import { spawn as spawn3 } from "node:child_process";
|
|
45021
|
+
import { rm as rm4 } from "node:fs/promises";
|
|
45022
|
+
import { join as join6 } from "node:path";
|
|
44400
45023
|
async function defaultRunCommand(command, args, options) {
|
|
44401
45024
|
await new Promise((resolve, reject) => {
|
|
44402
45025
|
const child = spawn3(command, args, { cwd: options.cwd, stdio: "inherit" });
|
|
@@ -44437,6 +45060,9 @@ async function installPluginPackage(input) {
|
|
|
44437
45060
|
const runCommand = input.runCommand ?? defaultRunCommand;
|
|
44438
45061
|
const packageManager = input.packageManager ?? await detectPackageManager();
|
|
44439
45062
|
await normalizePluginHomeManifest(input.pluginHome);
|
|
45063
|
+
if (packageManager === "bun") {
|
|
45064
|
+
await rm4(join6(input.pluginHome, "bun.lock"), { force: true }).catch(() => {});
|
|
45065
|
+
}
|
|
44440
45066
|
const spec = input.version ? `${input.packageName}@${input.version}` : input.packageName;
|
|
44441
45067
|
if (packageManager === "bun") {
|
|
44442
45068
|
await runCommand("bun", ["add", spec], { cwd: input.pluginHome });
|
|
@@ -44460,6 +45086,7 @@ async function removePluginPackage(input) {
|
|
|
44460
45086
|
// src/cli-update.ts
|
|
44461
45087
|
init_plugin_loader();
|
|
44462
45088
|
init_validate_plugin();
|
|
45089
|
+
var SUCCESSOR = { from: "weacpx", package: "xacpx", minVersion: "0.8.0" };
|
|
44463
45090
|
async function handleUpdateCli(args, deps) {
|
|
44464
45091
|
let all = false;
|
|
44465
45092
|
const explicitTargets = [];
|
|
@@ -44476,14 +45103,20 @@ async function handleUpdateCli(args, deps) {
|
|
|
44476
45103
|
const config2 = await deps.loadConfig();
|
|
44477
45104
|
const packageName = deps.packageName ?? await readPackageName();
|
|
44478
45105
|
const latestOf = deps.getLatestVersion ?? getLatestNpmVersion;
|
|
44479
|
-
const
|
|
44480
|
-
|
|
44481
|
-
|
|
44482
|
-
|
|
44483
|
-
|
|
44484
|
-
|
|
44485
|
-
|
|
44486
|
-
|
|
45106
|
+
const successor = await resolveSuccessor(packageName, latestOf);
|
|
45107
|
+
const selfTarget = successor ? {
|
|
45108
|
+
kind: "self",
|
|
45109
|
+
name: packageName,
|
|
45110
|
+
currentVersion: deps.readCurrentVersion(),
|
|
45111
|
+
latestVersion: successor.version,
|
|
45112
|
+
successorPackage: successor.package
|
|
45113
|
+
} : {
|
|
45114
|
+
kind: "self",
|
|
45115
|
+
name: packageName,
|
|
45116
|
+
currentVersion: deps.readCurrentVersion(),
|
|
45117
|
+
latestVersion: await latestOf(packageName)
|
|
45118
|
+
};
|
|
45119
|
+
const targets = [selfTarget];
|
|
44487
45120
|
for (const plugin of config2.plugins ?? []) {
|
|
44488
45121
|
targets.push({
|
|
44489
45122
|
kind: "plugin",
|
|
@@ -44503,7 +45136,7 @@ async function handleUpdateCli(args, deps) {
|
|
|
44503
45136
|
deps.print(`以下项目无法检查最新版本,已取消更新:${unavailable.map((target) => target.name).join(", ")}`);
|
|
44504
45137
|
return 1;
|
|
44505
45138
|
}
|
|
44506
|
-
const candidates = targets.filter((target) => target.latestVersion && (target.kind !== "plugin" || target.pinned) && target.currentVersion !== target.latestVersion);
|
|
45139
|
+
const candidates = targets.filter((target) => target.latestVersion && (target.kind !== "plugin" || target.pinned) && (target.successorPackage ? true : target.currentVersion !== target.latestVersion));
|
|
44507
45140
|
const selected = await selectTargets(targets, candidates, { all, explicitTarget: explicitTargets[0], deps });
|
|
44508
45141
|
if (!selected.ok) {
|
|
44509
45142
|
deps.print(selected.message);
|
|
@@ -44514,6 +45147,8 @@ async function handleUpdateCli(args, deps) {
|
|
|
44514
45147
|
return 0;
|
|
44515
45148
|
}
|
|
44516
45149
|
const selfUpdater = deps.updateSelf ?? defaultUpdateSelf;
|
|
45150
|
+
const selfMigrator = deps.migrateSelf ?? defaultMigrateSelf;
|
|
45151
|
+
const stopDaemon = deps.stopDaemon ?? (async () => {});
|
|
44517
45152
|
const pluginHome = deps.pluginHome ?? resolvePluginHome();
|
|
44518
45153
|
const pluginUpdater = deps.updatePlugin ?? (async (input) => {
|
|
44519
45154
|
await ensurePluginHome(pluginHome);
|
|
@@ -44524,17 +45159,25 @@ async function handleUpdateCli(args, deps) {
|
|
|
44524
45159
|
for (const target of selected.targets) {
|
|
44525
45160
|
try {
|
|
44526
45161
|
if (target.kind === "self") {
|
|
45162
|
+
const successorPackage = target.successorPackage;
|
|
44527
45163
|
if (!all && !explicitTargets[0]) {
|
|
44528
45164
|
if (!deps.isInteractive()) {
|
|
44529
|
-
deps.print("更新 weacpx 本体需要确认;非交互模式请使用 `weacpx update --all` 或 `weacpx update weacpx`。");
|
|
45165
|
+
deps.print(successorPackage ? `weacpx 已更名为 ${successorPackage};非交互模式请使用 \`weacpx update --all\` 或 \`weacpx update weacpx\` 确认迁移。` : "更新 weacpx 本体需要确认;非交互模式请使用 `weacpx update --all` 或 `weacpx update weacpx`。");
|
|
44530
45166
|
return 1;
|
|
44531
45167
|
}
|
|
44532
|
-
const
|
|
45168
|
+
const question = successorPackage ? `weacpx 已更名为 ${successorPackage},确认迁移到 ${successorPackage}?[y/N] ` : "确认更新 weacpx 本体?[y/N] ";
|
|
45169
|
+
const answer = (await deps.promptText(question)).trim().toLowerCase();
|
|
44533
45170
|
if (answer !== "y" && answer !== "yes") {
|
|
44534
|
-
deps.print("已取消更新 weacpx 本体。");
|
|
45171
|
+
deps.print(successorPackage ? `已取消迁移到 ${successorPackage}。` : "已取消更新 weacpx 本体。");
|
|
44535
45172
|
continue;
|
|
44536
45173
|
}
|
|
44537
45174
|
}
|
|
45175
|
+
if (successorPackage) {
|
|
45176
|
+
await stopDaemon();
|
|
45177
|
+
await selfMigrator({ from: target.name, to: successorPackage, toVersion: target.latestVersion ?? undefined });
|
|
45178
|
+
deps.print(`weacpx 已更名为 ${successorPackage},已迁移至 ${successorPackage} ${target.latestVersion ?? "latest"}。今后请使用 \`${successorPackage}\` 命令;若此前在后台运行,请用 \`${successorPackage} start\` 重新启动。`);
|
|
45179
|
+
continue;
|
|
45180
|
+
}
|
|
44538
45181
|
await selfUpdater(target.name);
|
|
44539
45182
|
deps.print(`weacpx 已更新:${target.latestVersion ?? "latest"}`);
|
|
44540
45183
|
continue;
|
|
@@ -44574,19 +45217,21 @@ async function handleUpdateCli(args, deps) {
|
|
|
44574
45217
|
function formatTarget(target) {
|
|
44575
45218
|
const current = target.currentVersion ?? "未锁定";
|
|
44576
45219
|
const latest = target.latestVersion ?? "无法检查";
|
|
44577
|
-
|
|
44578
|
-
|
|
45220
|
+
if (target.kind === "self") {
|
|
45221
|
+
return target.successorPackage ? `weacpx → ${target.successorPackage} (${current} -> ${latest},改名)` : `weacpx (${current} -> ${latest})`;
|
|
45222
|
+
}
|
|
45223
|
+
return `插件 ${target.name} (${current} -> ${latest})`;
|
|
44579
45224
|
}
|
|
44580
45225
|
async function selectTargets(targets, candidates, input) {
|
|
44581
45226
|
if (input.explicitTarget) {
|
|
44582
|
-
const target = targets.find((entry) => entry.name === input.explicitTarget || input.explicitTarget === "weacpx"
|
|
45227
|
+
const target = targets.find((entry) => entry.name === input.explicitTarget || entry.kind === "self" && (input.explicitTarget === "weacpx" || input.explicitTarget === entry.successorPackage));
|
|
44583
45228
|
if (!target)
|
|
44584
45229
|
return { ok: false, message: `没有找到更新项:${input.explicitTarget}`, exitCode: 1 };
|
|
44585
45230
|
if (!target.latestVersion)
|
|
44586
45231
|
return { ok: false, message: `${target.name} 无法检查最新版本,已跳过。`, exitCode: 1 };
|
|
44587
45232
|
if (target.kind === "plugin" && !target.pinned)
|
|
44588
45233
|
return { ok: false, message: `${target.name} 未记录当前版本;请先使用 \`weacpx plugin update ${target.name}\` 或显式选择版本。`, exitCode: 1 };
|
|
44589
|
-
if (target.currentVersion === target.latestVersion)
|
|
45234
|
+
if (!target.successorPackage && target.currentVersion === target.latestVersion)
|
|
44590
45235
|
return { ok: true, targets: [] };
|
|
44591
45236
|
return { ok: true, targets: [target] };
|
|
44592
45237
|
}
|
|
@@ -44611,7 +45256,7 @@ async function selectTargets(targets, candidates, input) {
|
|
|
44611
45256
|
return { ok: false, message: `${target.name} 无法检查最新版本,已跳过。`, exitCode: 1 };
|
|
44612
45257
|
if (target.kind === "plugin" && !target.pinned)
|
|
44613
45258
|
return { ok: false, message: `${target.name} 未记录当前版本;请先使用 \`weacpx plugin update ${target.name}\` 或显式选择版本。`, exitCode: 1 };
|
|
44614
|
-
if (target.currentVersion === target.latestVersion)
|
|
45259
|
+
if (!target.successorPackage && target.currentVersion === target.latestVersion)
|
|
44615
45260
|
continue;
|
|
44616
45261
|
if (!selected.includes(target))
|
|
44617
45262
|
selected.push(target);
|
|
@@ -44640,6 +45285,45 @@ async function defaultUpdateSelf(packageName) {
|
|
|
44640
45285
|
}
|
|
44641
45286
|
await runInherit("npm", ["install", "-g", packageName]);
|
|
44642
45287
|
}
|
|
45288
|
+
async function defaultMigrateSelf(input) {
|
|
45289
|
+
const manager = process.env.WEACPX_PACKAGE_MANAGER?.trim().toLowerCase() === "bun" ? "bun" : "npm";
|
|
45290
|
+
const spec = input.toVersion ? `${input.to}@${input.toVersion}` : `${input.to}@latest`;
|
|
45291
|
+
if (manager === "bun") {
|
|
45292
|
+
await runInherit("bun", ["add", "-g", spec]);
|
|
45293
|
+
await runInherit("bun", ["remove", "-g", input.from]);
|
|
45294
|
+
return;
|
|
45295
|
+
}
|
|
45296
|
+
await runInherit("npm", ["install", "-g", spec]);
|
|
45297
|
+
await runInherit("npm", ["uninstall", "-g", input.from]);
|
|
45298
|
+
}
|
|
45299
|
+
async function resolveSuccessor(currentPackage, latestOf) {
|
|
45300
|
+
if (currentPackage !== SUCCESSOR.from)
|
|
45301
|
+
return null;
|
|
45302
|
+
const version2 = await latestOf(SUCCESSOR.package);
|
|
45303
|
+
if (!version2 || !meetsMinVersion(version2, SUCCESSOR.minVersion))
|
|
45304
|
+
return null;
|
|
45305
|
+
return { package: SUCCESSOR.package, version: version2 };
|
|
45306
|
+
}
|
|
45307
|
+
function meetsMinVersion(candidate, min) {
|
|
45308
|
+
return compareSemver2(candidate, min) >= 0;
|
|
45309
|
+
}
|
|
45310
|
+
function compareSemver2(a, b) {
|
|
45311
|
+
const parse5 = (value) => {
|
|
45312
|
+
const match = /^\s*v?(\d+)\.(\d+)\.(\d+)(-[^\s]*)?/.exec(value);
|
|
45313
|
+
if (!match)
|
|
45314
|
+
return { nums: [0, 0, 0], prerelease: false };
|
|
45315
|
+
return { nums: [Number(match[1]), Number(match[2]), Number(match[3])], prerelease: Boolean(match[4]) };
|
|
45316
|
+
};
|
|
45317
|
+
const left = parse5(a);
|
|
45318
|
+
const right = parse5(b);
|
|
45319
|
+
for (let i = 0;i < 3; i += 1) {
|
|
45320
|
+
if (left.nums[i] !== right.nums[i])
|
|
45321
|
+
return left.nums[i] < right.nums[i] ? -1 : 1;
|
|
45322
|
+
}
|
|
45323
|
+
if (left.prerelease === right.prerelease)
|
|
45324
|
+
return 0;
|
|
45325
|
+
return left.prerelease ? -1 : 1;
|
|
45326
|
+
}
|
|
44643
45327
|
async function runCapture(command, args) {
|
|
44644
45328
|
return await new Promise((resolve, reject) => {
|
|
44645
45329
|
const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -44671,8 +45355,8 @@ async function runInherit(command, args) {
|
|
|
44671
45355
|
}
|
|
44672
45356
|
async function readPackageName() {
|
|
44673
45357
|
try {
|
|
44674
|
-
const here =
|
|
44675
|
-
for (const candidate of [
|
|
45358
|
+
const here = dirname10(fileURLToPath3(import.meta.url));
|
|
45359
|
+
for (const candidate of [join10(here, "..", "package.json"), join10(here, "..", "..", "package.json")]) {
|
|
44676
45360
|
try {
|
|
44677
45361
|
const parsed = JSON.parse(await readFile8(candidate, "utf8"));
|
|
44678
45362
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
@@ -44691,6 +45375,7 @@ async function validatePluginDefault(packageName, pluginHome) {
|
|
|
44691
45375
|
init_version();
|
|
44692
45376
|
|
|
44693
45377
|
// src/channels/cli/channel-cli.ts
|
|
45378
|
+
init_core_home();
|
|
44694
45379
|
init_registry();
|
|
44695
45380
|
init_create_channel();
|
|
44696
45381
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -44985,7 +45670,7 @@ async function runRestart(deps) {
|
|
|
44985
45670
|
} catch (error2) {
|
|
44986
45671
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
44987
45672
|
deps.print(`配置已保存,但重启失败:${message}`);
|
|
44988
|
-
deps.print("
|
|
45673
|
+
deps.print(`请查看日志:${coreHomeDisplayPath("runtime", "stderr.log")}`);
|
|
44989
45674
|
deps.print("也可以稍后执行:weacpx start");
|
|
44990
45675
|
return 1;
|
|
44991
45676
|
}
|
|
@@ -45296,9 +45981,10 @@ async function setChannelAccountEnabled(type, accountId, enabled, rawArgs, deps)
|
|
|
45296
45981
|
}
|
|
45297
45982
|
|
|
45298
45983
|
// src/plugins/plugin-cli.ts
|
|
45984
|
+
init_core_home();
|
|
45299
45985
|
init_plugin_home();
|
|
45300
45986
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
45301
|
-
import { isAbsolute, join as
|
|
45987
|
+
import { isAbsolute, join as join12, resolve } from "node:path";
|
|
45302
45988
|
init_plugin_loader();
|
|
45303
45989
|
init_validate_plugin();
|
|
45304
45990
|
|
|
@@ -45308,13 +45994,13 @@ init_plugin_loader();
|
|
|
45308
45994
|
init_validate_plugin();
|
|
45309
45995
|
init_known_plugins();
|
|
45310
45996
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
45311
|
-
import { join as
|
|
45997
|
+
import { join as join11 } from "node:path";
|
|
45312
45998
|
function suggestedPluginPackageForChannel(type) {
|
|
45313
45999
|
return findKnownPluginByChannel(type)?.packageName ?? `<npm-package-that-provides-${type}>`;
|
|
45314
46000
|
}
|
|
45315
46001
|
async function readDependencyEntries(pluginHome) {
|
|
45316
46002
|
try {
|
|
45317
|
-
const raw = await readFile9(
|
|
46003
|
+
const raw = await readFile9(join11(pluginHome, "package.json"), "utf8");
|
|
45318
46004
|
const parsed = JSON.parse(raw);
|
|
45319
46005
|
const out = {};
|
|
45320
46006
|
for (const [name, value] of Object.entries(parsed.dependencies ?? {})) {
|
|
@@ -45416,7 +46102,7 @@ function looksLikePath(spec) {
|
|
|
45416
46102
|
}
|
|
45417
46103
|
async function readDependencyEntries2(pluginHome) {
|
|
45418
46104
|
try {
|
|
45419
|
-
const raw = await readFile10(
|
|
46105
|
+
const raw = await readFile10(join12(pluginHome, "package.json"), "utf8");
|
|
45420
46106
|
const parsed = JSON.parse(raw);
|
|
45421
46107
|
const out = {};
|
|
45422
46108
|
for (const [name, value] of Object.entries(parsed.dependencies ?? {})) {
|
|
@@ -45442,7 +46128,7 @@ async function resolveLocalPluginName(installSpec, pluginHome, namesBeforeInstal
|
|
|
45442
46128
|
return name;
|
|
45443
46129
|
}
|
|
45444
46130
|
try {
|
|
45445
|
-
const raw = await readFile10(
|
|
46131
|
+
const raw = await readFile10(join12(installSpec, "package.json"), "utf8");
|
|
45446
46132
|
const parsed = JSON.parse(raw);
|
|
45447
46133
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
45448
46134
|
return parsed.name.trim();
|
|
@@ -45864,7 +46550,7 @@ async function runRestart2(deps) {
|
|
|
45864
46550
|
return await deps.restartDaemon();
|
|
45865
46551
|
} catch (error2) {
|
|
45866
46552
|
deps.print(`配置已保存,但重启失败:${describeError(error2)}`);
|
|
45867
|
-
deps.print("
|
|
46553
|
+
deps.print(`请查看日志:${coreHomeDisplayPath("runtime", "stderr.log")}`);
|
|
45868
46554
|
deps.print("也可以稍后执行:weacpx start");
|
|
45869
46555
|
return 1;
|
|
45870
46556
|
}
|
|
@@ -46097,7 +46783,12 @@ async function runCli(args, deps = {}) {
|
|
|
46097
46783
|
print,
|
|
46098
46784
|
isInteractive: deps.isInteractive,
|
|
46099
46785
|
promptText: deps.promptText,
|
|
46100
|
-
overrides:
|
|
46786
|
+
overrides: {
|
|
46787
|
+
stopDaemon: async () => {
|
|
46788
|
+
await (deps.controller ?? createDefaultController(deps)).stop();
|
|
46789
|
+
},
|
|
46790
|
+
...deps.updateCliDeps
|
|
46791
|
+
}
|
|
46101
46792
|
})))(args.slice(1));
|
|
46102
46793
|
if (result === null) {
|
|
46103
46794
|
for (const line of HELP_LINES)
|
|
@@ -46543,7 +47234,7 @@ async function createCliScheduledTaskService() {
|
|
|
46543
47234
|
return new ScheduledTaskService(state, stateStore);
|
|
46544
47235
|
}
|
|
46545
47236
|
function resolveConfigPathForCurrentEnv() {
|
|
46546
|
-
return process.env.WEACPX_CONFIG ??
|
|
47237
|
+
return process.env.WEACPX_CONFIG ?? join19(coreHomeDir(requireHome2()), "config.json");
|
|
46547
47238
|
}
|
|
46548
47239
|
function resolveDaemonPathsForCurrentConfig() {
|
|
46549
47240
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
@@ -46910,7 +47601,7 @@ function safeDaemonLogPaths() {
|
|
|
46910
47601
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
46911
47602
|
const paths = resolveDaemonPathsForCurrentConfig();
|
|
46912
47603
|
return {
|
|
46913
|
-
appLog:
|
|
47604
|
+
appLog: join19(dirname16(configPath), "runtime", "app.log"),
|
|
46914
47605
|
stderrLog: paths.stderrLog
|
|
46915
47606
|
};
|
|
46916
47607
|
} catch {
|
|
@@ -46921,7 +47612,7 @@ function resolveCliEntryPath2() {
|
|
|
46921
47612
|
if (process.argv[1]) {
|
|
46922
47613
|
return process.argv[1];
|
|
46923
47614
|
}
|
|
46924
|
-
return
|
|
47615
|
+
return fileURLToPath7(import.meta.url);
|
|
46925
47616
|
}
|
|
46926
47617
|
function parseDoctorArgs(args) {
|
|
46927
47618
|
const options = {};
|