weacpx 0.6.0 → 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 +66 -24
- package/dist/channels/types.d.ts +14 -0
- package/dist/cli.js +1268 -407
- package/dist/commands/command-hints.d.ts +11 -0
- package/dist/commands/command-list.d.ts +3 -0
- package/dist/commands/config-clone.d.ts +2 -0
- package/dist/commands/handlers/agent-handler.d.ts +6 -0
- package/dist/commands/handlers/config-handler.d.ts +5 -0
- package/dist/commands/handlers/later-handler.d.ts +21 -0
- package/dist/commands/handlers/orchestration-handler.d.ts +16 -0
- package/dist/commands/handlers/permission-handler.d.ts +9 -0
- package/dist/commands/handlers/session-handler.d.ts +39 -0
- package/dist/commands/handlers/session-list-marker.d.ts +1 -0
- package/dist/commands/handlers/workspace-handler.d.ts +8 -0
- package/dist/commands/help/help-registry.d.ts +4 -0
- package/dist/commands/help/help-types.d.ts +12 -0
- package/dist/commands/parse-command.d.ts +178 -0
- package/dist/commands/router-types.d.ts +144 -0
- package/dist/commands/workspace-name.d.ts +4 -0
- package/dist/commands/workspace-path.d.ts +4 -0
- package/dist/config/agent-templates.d.ts +4 -0
- package/dist/config/config-store.d.ts +13 -0
- package/dist/config/load-config.d.ts +10 -0
- package/dist/config/resolve-agent-command.d.ts +2 -0
- package/dist/formatting/render-text.d.ts +23 -0
- package/dist/orchestration/async-mutex.d.ts +4 -0
- package/dist/orchestration/build-coordinator-prompt.d.ts +66 -0
- package/dist/orchestration/orchestration-service.d.ts +471 -0
- package/dist/orchestration/progress-line-parser.d.ts +19 -0
- package/dist/orchestration/render-delegate-group-result.d.ts +6 -0
- package/dist/orchestration/render-delegate-question-package.d.ts +21 -0
- package/dist/orchestration/render-delegate-result.d.ts +2 -0
- package/dist/orchestration/task-watch-timeouts.d.ts +5 -0
- package/dist/plugin-api.d.ts +10 -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/scheduled/parse-later-time.d.ts +11 -0
- package/dist/scheduled/scheduled-render.d.ts +7 -0
- package/dist/scheduled/scheduled-service.d.ts +41 -0
- package/dist/scheduled/scheduled-types.d.ts +29 -0
- package/dist/sessions/active-turn-registry.d.ts +6 -0
- package/dist/sessions/session-service.d.ts +118 -0
- package/dist/state/state-store.d.ts +8 -0
- package/dist/state/types.d.ts +51 -0
- package/dist/transport/tool-event-mode.d.ts +14 -0
- package/dist/transport/types.d.ts +129 -0
- package/dist/util/path.d.ts +4 -0
- package/dist/util/sanitize.d.ts +10 -0
- package/dist/util/text.d.ts +3 -0
- package/dist/version.d.ts +2 -0
- package/dist/weixin/agent/interface.d.ts +1 -0
- package/dist/weixin/api/config-cache.d.ts +18 -1
- package/dist/weixin/auth/accounts.d.ts +0 -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");
|
|
@@ -2088,36 +2099,56 @@ var init_private_file = __esm(() => {
|
|
|
2088
2099
|
import_write_file_atomic = __toESM(require_lib(), 1);
|
|
2089
2100
|
});
|
|
2090
2101
|
|
|
2091
|
-
// src/
|
|
2092
|
-
import { access } from "node:fs/promises";
|
|
2093
|
-
import { homedir } from "node:os";
|
|
2102
|
+
// src/util/path.ts
|
|
2094
2103
|
import path from "node:path";
|
|
2095
|
-
|
|
2104
|
+
import { homedir } from "node:os";
|
|
2105
|
+
function normalizePath(input) {
|
|
2096
2106
|
const expanded = expandHome(input);
|
|
2097
2107
|
if (isWindowsLikePath(expanded)) {
|
|
2098
2108
|
return path.win32.normalize(expanded).replace(/\\/g, "/");
|
|
2099
2109
|
}
|
|
2100
2110
|
return path.posix.normalize(expanded.replace(/\\/g, "/"));
|
|
2101
2111
|
}
|
|
2102
|
-
function
|
|
2103
|
-
const normalized =
|
|
2112
|
+
function basenameForPath(input) {
|
|
2113
|
+
const normalized = normalizePath(input);
|
|
2104
2114
|
if (ROOT_PATH_RE.test(normalized)) {
|
|
2105
2115
|
return normalized;
|
|
2106
2116
|
}
|
|
2107
2117
|
const base = path.posix.basename(normalized);
|
|
2108
2118
|
return base || normalized;
|
|
2109
2119
|
}
|
|
2110
|
-
function
|
|
2111
|
-
const normalizedLeft =
|
|
2112
|
-
const normalizedRight =
|
|
2120
|
+
function isSamePath(left, right) {
|
|
2121
|
+
const normalizedLeft = normalizePath(left);
|
|
2122
|
+
const normalizedRight = normalizePath(right);
|
|
2113
2123
|
if (isWindowsLikePath(normalizedLeft) || isWindowsLikePath(normalizedRight)) {
|
|
2114
2124
|
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
2115
2125
|
}
|
|
2116
2126
|
return normalizedLeft === normalizedRight;
|
|
2117
2127
|
}
|
|
2128
|
+
function isWindowsLikePath(input) {
|
|
2129
|
+
return WINDOWS_DRIVE_PATH_RE.test(input) || WINDOWS_UNC_PATH_RE.test(input);
|
|
2130
|
+
}
|
|
2118
2131
|
function expandHome(input) {
|
|
2119
2132
|
return input.startsWith("~") ? homedir() + input.slice(1) : input;
|
|
2120
2133
|
}
|
|
2134
|
+
var WINDOWS_DRIVE_PATH_RE, WINDOWS_UNC_PATH_RE, ROOT_PATH_RE;
|
|
2135
|
+
var init_path = __esm(() => {
|
|
2136
|
+
WINDOWS_DRIVE_PATH_RE = /^[a-zA-Z]:[\\/]/;
|
|
2137
|
+
WINDOWS_UNC_PATH_RE = /^\\\\/;
|
|
2138
|
+
ROOT_PATH_RE = /^(\/|[a-zA-Z]:\/?)$/;
|
|
2139
|
+
});
|
|
2140
|
+
|
|
2141
|
+
// src/commands/workspace-path.ts
|
|
2142
|
+
import { access } from "node:fs/promises";
|
|
2143
|
+
function normalizeWorkspacePath(input) {
|
|
2144
|
+
return normalizePath(input);
|
|
2145
|
+
}
|
|
2146
|
+
function basenameForWorkspacePath(input) {
|
|
2147
|
+
return basenameForPath(input);
|
|
2148
|
+
}
|
|
2149
|
+
function sameWorkspacePath(left, right) {
|
|
2150
|
+
return isSamePath(left, right);
|
|
2151
|
+
}
|
|
2121
2152
|
async function pathExists(filePath) {
|
|
2122
2153
|
try {
|
|
2123
2154
|
await access(filePath);
|
|
@@ -2126,14 +2157,8 @@ async function pathExists(filePath) {
|
|
|
2126
2157
|
return false;
|
|
2127
2158
|
}
|
|
2128
2159
|
}
|
|
2129
|
-
function isWindowsLikePath(input) {
|
|
2130
|
-
return WINDOWS_DRIVE_PATH_RE.test(input) || WINDOWS_UNC_PATH_RE.test(input);
|
|
2131
|
-
}
|
|
2132
|
-
var WINDOWS_DRIVE_PATH_RE, WINDOWS_UNC_PATH_RE, ROOT_PATH_RE;
|
|
2133
2160
|
var init_workspace_path = __esm(() => {
|
|
2134
|
-
|
|
2135
|
-
WINDOWS_UNC_PATH_RE = /^\\\\/;
|
|
2136
|
-
ROOT_PATH_RE = /^(\/|[a-zA-Z]:\/?)$/;
|
|
2161
|
+
init_path();
|
|
2137
2162
|
});
|
|
2138
2163
|
|
|
2139
2164
|
// src/config/resolve-agent-command.ts
|
|
@@ -2763,6 +2788,9 @@ class DaemonStatusStore {
|
|
|
2763
2788
|
if (error.code === "ENOENT") {
|
|
2764
2789
|
return null;
|
|
2765
2790
|
}
|
|
2791
|
+
if (error instanceof SyntaxError) {
|
|
2792
|
+
return null;
|
|
2793
|
+
}
|
|
2766
2794
|
throw error;
|
|
2767
2795
|
}
|
|
2768
2796
|
}
|
|
@@ -3137,7 +3165,7 @@ var init_create_daemon_controller = __esm(() => {
|
|
|
3137
3165
|
|
|
3138
3166
|
// src/orchestration/orchestration-ipc.ts
|
|
3139
3167
|
import { createHash } from "node:crypto";
|
|
3140
|
-
import { join } from "node:path";
|
|
3168
|
+
import { join as join2 } from "node:path";
|
|
3141
3169
|
function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
3142
3170
|
if (platform === "win32") {
|
|
3143
3171
|
const suffix = createHash("sha256").update(runtimeDir).digest("hex").slice(0, 12);
|
|
@@ -3148,7 +3176,7 @@ function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
|
3148
3176
|
}
|
|
3149
3177
|
return {
|
|
3150
3178
|
kind: "unix",
|
|
3151
|
-
path:
|
|
3179
|
+
path: join2(runtimeDir, "orchestration.sock")
|
|
3152
3180
|
};
|
|
3153
3181
|
}
|
|
3154
3182
|
function createOrchestrationEndpoint(path2, platform = process.platform) {
|
|
@@ -3168,25 +3196,26 @@ function encodeOrchestrationRpcResponse(response) {
|
|
|
3168
3196
|
var init_orchestration_ipc = () => {};
|
|
3169
3197
|
|
|
3170
3198
|
// src/daemon/daemon-files.ts
|
|
3171
|
-
import { dirname as dirname4, join as
|
|
3199
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
3172
3200
|
function resolveDaemonPaths(options) {
|
|
3173
|
-
const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) :
|
|
3201
|
+
const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : join3(coreHomeDir(options.home), "runtime"));
|
|
3174
3202
|
return {
|
|
3175
3203
|
runtimeDir,
|
|
3176
|
-
pidFile:
|
|
3177
|
-
statusFile:
|
|
3178
|
-
stdoutLog:
|
|
3179
|
-
stderrLog:
|
|
3180
|
-
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")
|
|
3181
3209
|
};
|
|
3182
3210
|
}
|
|
3183
3211
|
function resolveRuntimeDirFromConfigPath(configPath) {
|
|
3184
|
-
return
|
|
3212
|
+
return join3(dirname4(configPath), "runtime");
|
|
3185
3213
|
}
|
|
3186
3214
|
function resolveDaemonOrchestrationSocketPath(runtimeDir, platform = process.platform) {
|
|
3187
3215
|
return resolveOrchestrationEndpoint(runtimeDir, platform).path;
|
|
3188
3216
|
}
|
|
3189
3217
|
var init_daemon_files = __esm(() => {
|
|
3218
|
+
init_core_home();
|
|
3190
3219
|
init_orchestration_ipc();
|
|
3191
3220
|
});
|
|
3192
3221
|
|
|
@@ -9688,8 +9717,10 @@ function readVersion(moduleUrl = import.meta.url) {
|
|
|
9688
9717
|
}
|
|
9689
9718
|
return "unknown";
|
|
9690
9719
|
}
|
|
9691
|
-
var PACKAGE_NAME = "weacpx";
|
|
9692
|
-
var init_version = () => {
|
|
9720
|
+
var PACKAGE_NAME = "weacpx", WEACPX_CORE_VERSION;
|
|
9721
|
+
var init_version = __esm(() => {
|
|
9722
|
+
WEACPX_CORE_VERSION = readVersion();
|
|
9723
|
+
});
|
|
9693
9724
|
|
|
9694
9725
|
// src/orchestration/task-watch-timeouts.ts
|
|
9695
9726
|
var DEFAULT_TASK_WATCH_TIMEOUT_MS = 60000, MAX_TASK_WATCH_TIMEOUT_MS, DEFAULT_TASK_WATCH_POLL_INTERVAL_MS = 1000, MAX_TASK_WATCH_POLL_INTERVAL_MS = 1e4, TASK_WATCH_RPC_TIMEOUT_PADDING_MS = 5000;
|
|
@@ -9713,10 +9744,60 @@ var init_quota_errors = __esm(() => {
|
|
|
9713
9744
|
};
|
|
9714
9745
|
});
|
|
9715
9746
|
|
|
9747
|
+
// src/util/sanitize.ts
|
|
9748
|
+
function sanitizeString(input, options = {}) {
|
|
9749
|
+
const {
|
|
9750
|
+
replacement = "-",
|
|
9751
|
+
collapse = false,
|
|
9752
|
+
trim = false,
|
|
9753
|
+
lowercase: lowercase2 = false,
|
|
9754
|
+
fallback
|
|
9755
|
+
} = options;
|
|
9756
|
+
let result = lowercase2 ? input.toLowerCase() : input;
|
|
9757
|
+
if (options.allow) {
|
|
9758
|
+
const pattern = new RegExp(`[^${options.allow.source.slice(1, -1)}]+`, "g");
|
|
9759
|
+
result = result.replace(pattern, replacement);
|
|
9760
|
+
} else if (options.deny) {
|
|
9761
|
+
result = result.replace(options.deny, replacement);
|
|
9762
|
+
}
|
|
9763
|
+
if (collapse) {
|
|
9764
|
+
const escaped = escapeRegExp(replacement);
|
|
9765
|
+
result = result.replace(new RegExp(`${escaped}+`, "g"), replacement);
|
|
9766
|
+
}
|
|
9767
|
+
if (trim) {
|
|
9768
|
+
const escaped = escapeRegExp(replacement);
|
|
9769
|
+
result = result.replace(new RegExp(`^${escaped}+|${escaped}+$`, "g"), "");
|
|
9770
|
+
}
|
|
9771
|
+
if (fallback !== undefined && result.length === 0) {
|
|
9772
|
+
return fallback;
|
|
9773
|
+
}
|
|
9774
|
+
return result;
|
|
9775
|
+
}
|
|
9776
|
+
function escapeRegExp(s) {
|
|
9777
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9778
|
+
}
|
|
9779
|
+
|
|
9780
|
+
// src/util/text.ts
|
|
9781
|
+
function truncateText(text, maxLength, ellipsis = "…") {
|
|
9782
|
+
if (text.length <= maxLength)
|
|
9783
|
+
return text;
|
|
9784
|
+
return text.slice(0, maxLength - ellipsis.length) + ellipsis;
|
|
9785
|
+
}
|
|
9786
|
+
function escapeForDoubleQuotes(input) {
|
|
9787
|
+
return input.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
9788
|
+
}
|
|
9789
|
+
function quoteIfNeeded(input) {
|
|
9790
|
+
return `"${escapeForDoubleQuotes(input)}"`;
|
|
9791
|
+
}
|
|
9792
|
+
|
|
9716
9793
|
// src/commands/workspace-name.ts
|
|
9717
9794
|
function sanitizeWorkspaceName(input, fallback = "workspace") {
|
|
9718
|
-
|
|
9719
|
-
|
|
9795
|
+
return sanitizeString(input.trim(), {
|
|
9796
|
+
allow: /[a-zA-Z0-9._-]/,
|
|
9797
|
+
replacement: "-",
|
|
9798
|
+
trim: true,
|
|
9799
|
+
fallback
|
|
9800
|
+
});
|
|
9720
9801
|
}
|
|
9721
9802
|
function allocateWorkspaceName(base, existing) {
|
|
9722
9803
|
if (!Object.prototype.hasOwnProperty.call(existing, base))
|
|
@@ -9732,13 +9813,11 @@ function isWorkspaceNameValid(input) {
|
|
|
9732
9813
|
function quoteWorkspaceNameIfNeeded(input) {
|
|
9733
9814
|
if (isWorkspaceNameValid(input))
|
|
9734
9815
|
return input;
|
|
9735
|
-
return
|
|
9816
|
+
return quoteIfNeeded(input);
|
|
9736
9817
|
}
|
|
9737
|
-
var VALID_WORKSPACE_NAME_RE
|
|
9818
|
+
var VALID_WORKSPACE_NAME_RE;
|
|
9738
9819
|
var init_workspace_name = __esm(() => {
|
|
9739
9820
|
VALID_WORKSPACE_NAME_RE = /^[a-zA-Z0-9._-]+$/;
|
|
9740
|
-
UNSAFE_RUN_RE = /[^a-zA-Z0-9._-]+/g;
|
|
9741
|
-
TRIM_DASHES_RE = /^-+|-+$/g;
|
|
9742
9821
|
});
|
|
9743
9822
|
|
|
9744
9823
|
// src/orchestration/orchestration-types.ts
|
|
@@ -10288,7 +10367,7 @@ function renderLaterList(tasks, displaySession) {
|
|
|
10288
10367
|
`).trimEnd();
|
|
10289
10368
|
}
|
|
10290
10369
|
function preview(text) {
|
|
10291
|
-
return text
|
|
10370
|
+
return truncateText(text, LATER_MESSAGE_PREVIEW_CHARS);
|
|
10292
10371
|
}
|
|
10293
10372
|
function formatLocalDateTime(date4) {
|
|
10294
10373
|
const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
|
@@ -10434,7 +10513,10 @@ class ScheduledTaskService {
|
|
|
10434
10513
|
}
|
|
10435
10514
|
nextId() {
|
|
10436
10515
|
for (let attempt = 0;attempt < 20; attempt += 1) {
|
|
10437
|
-
const id = normalizeId(this.generateId())
|
|
10516
|
+
const id = sanitizeString(normalizeId(this.generateId()), {
|
|
10517
|
+
allow: /[0-9a-z]/,
|
|
10518
|
+
replacement: ""
|
|
10519
|
+
}).slice(0, 6);
|
|
10438
10520
|
if (id.length >= 4 && !this.state.scheduled_tasks[id])
|
|
10439
10521
|
return id;
|
|
10440
10522
|
}
|
|
@@ -10448,14 +10530,65 @@ class ScheduledTaskService {
|
|
|
10448
10530
|
}
|
|
10449
10531
|
}
|
|
10450
10532
|
function normalizeId(input) {
|
|
10451
|
-
return input.trim()
|
|
10533
|
+
return sanitizeString(input.trim(), {
|
|
10534
|
+
deny: /^#/,
|
|
10535
|
+
replacement: "",
|
|
10536
|
+
lowercase: true
|
|
10537
|
+
});
|
|
10452
10538
|
}
|
|
10453
10539
|
var init_scheduled_service = () => {};
|
|
10454
10540
|
|
|
10455
10541
|
// src/plugins/plugin-home.ts
|
|
10456
|
-
import {
|
|
10542
|
+
import { readFileSync } from "node:fs";
|
|
10543
|
+
import { copyFile, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
10457
10544
|
import { homedir as homedir3 } from "node:os";
|
|
10458
|
-
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
|
+
}
|
|
10459
10592
|
function coerceMissing(value) {
|
|
10460
10593
|
if (value === undefined)
|
|
10461
10594
|
return;
|
|
@@ -10475,17 +10608,43 @@ function resolvePluginHome(input = {}) {
|
|
|
10475
10608
|
if (envOverride)
|
|
10476
10609
|
return envOverride;
|
|
10477
10610
|
const home = coerceMissing(input.home) ?? coerceMissing(process.env.HOME) ?? homedir3();
|
|
10478
|
-
return
|
|
10611
|
+
return join5(coreHomeDir(home), "plugins");
|
|
10612
|
+
}
|
|
10613
|
+
async function normalizePluginHomeManifest(pluginHome) {
|
|
10614
|
+
const manifestPath = join5(pluginHome, "package.json");
|
|
10615
|
+
let raw;
|
|
10616
|
+
try {
|
|
10617
|
+
raw = await readFile6(manifestPath, "utf8");
|
|
10618
|
+
} catch {
|
|
10619
|
+
return false;
|
|
10620
|
+
}
|
|
10621
|
+
let parsed;
|
|
10622
|
+
try {
|
|
10623
|
+
parsed = JSON.parse(raw);
|
|
10624
|
+
} catch {
|
|
10625
|
+
return false;
|
|
10626
|
+
}
|
|
10627
|
+
const normalized = JSON.stringify(parsed, null, 2) + `
|
|
10628
|
+
`;
|
|
10629
|
+
if (normalized === raw)
|
|
10630
|
+
return false;
|
|
10631
|
+
await writeFile4(manifestPath, normalized, { mode: 384 });
|
|
10632
|
+
return true;
|
|
10479
10633
|
}
|
|
10480
10634
|
async function ensurePluginHome(pluginHome) {
|
|
10481
10635
|
await mkdir6(pluginHome, { recursive: true, mode: 448 });
|
|
10482
|
-
await writeFile4(
|
|
10636
|
+
await writeFile4(join5(pluginHome, "package.json"), JSON.stringify({ private: true, type: "module" }, null, 2) + `
|
|
10483
10637
|
`, { flag: "wx" }).catch((error2) => {
|
|
10484
10638
|
if (error2.code !== "EEXIST")
|
|
10485
10639
|
throw error2;
|
|
10486
10640
|
});
|
|
10641
|
+
await ensureCoreResolution(pluginHome);
|
|
10487
10642
|
}
|
|
10488
|
-
var
|
|
10643
|
+
var CORE_PACKAGE_NAMES;
|
|
10644
|
+
var init_plugin_home = __esm(() => {
|
|
10645
|
+
init_core_home();
|
|
10646
|
+
CORE_PACKAGE_NAMES = ["weacpx", "xacpx"];
|
|
10647
|
+
});
|
|
10489
10648
|
|
|
10490
10649
|
// src/weixin/storage/ensure-dir.ts
|
|
10491
10650
|
import fs2 from "node:fs";
|
|
@@ -10515,7 +10674,11 @@ var init_state_dir = () => {};
|
|
|
10515
10674
|
import fs3 from "node:fs";
|
|
10516
10675
|
import path4 from "node:path";
|
|
10517
10676
|
function normalizeAccountId(raw) {
|
|
10518
|
-
return raw.trim()
|
|
10677
|
+
return sanitizeString(raw.trim(), {
|
|
10678
|
+
deny: /[@.]/g,
|
|
10679
|
+
replacement: "-",
|
|
10680
|
+
lowercase: true
|
|
10681
|
+
});
|
|
10519
10682
|
}
|
|
10520
10683
|
function deriveRawAccountId(normalizedId) {
|
|
10521
10684
|
if (normalizedId.endsWith("-im-bot")) {
|
|
@@ -12724,11 +12887,13 @@ function resolveContextTokenFilePath(accountId) {
|
|
|
12724
12887
|
return path6.join(resolveStateDir(), "openclaw-weixin", "accounts", `${accountId}.context-tokens.json`);
|
|
12725
12888
|
}
|
|
12726
12889
|
function persistContextTokens(accountId) {
|
|
12890
|
+
pruneContextTokensForAccount(accountId);
|
|
12727
12891
|
const prefix = `${accountId}:`;
|
|
12728
12892
|
const tokens = {};
|
|
12729
|
-
for (const [k,
|
|
12730
|
-
if (k.startsWith(prefix))
|
|
12731
|
-
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
|
+
}
|
|
12732
12897
|
}
|
|
12733
12898
|
const filePath = resolveContextTokenFilePath(accountId);
|
|
12734
12899
|
try {
|
|
@@ -12745,12 +12910,15 @@ function restoreContextTokens(accountId) {
|
|
|
12745
12910
|
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
12746
12911
|
const tokens = JSON.parse(raw);
|
|
12747
12912
|
let count = 0;
|
|
12748
|
-
for (const [userId,
|
|
12749
|
-
|
|
12750
|
-
|
|
12913
|
+
for (const [userId, value] of Object.entries(tokens)) {
|
|
12914
|
+
const entry = parsePersistedContextToken(value);
|
|
12915
|
+
if (entry) {
|
|
12916
|
+
contextTokenStore.set(contextTokenKey(accountId, userId), entry);
|
|
12751
12917
|
count++;
|
|
12752
12918
|
}
|
|
12753
12919
|
}
|
|
12920
|
+
pruneContextTokensForAccount(accountId);
|
|
12921
|
+
persistContextTokens(accountId);
|
|
12754
12922
|
logger.info(`restoreContextTokens: restored ${count} tokens for account=${accountId}`);
|
|
12755
12923
|
} catch (err) {
|
|
12756
12924
|
logger.warn(`restoreContextTokens: failed to read ${filePath}: ${String(err)}`);
|
|
@@ -12774,15 +12942,64 @@ function clearContextTokensForAccount(accountId) {
|
|
|
12774
12942
|
function setContextToken(accountId, userId, token) {
|
|
12775
12943
|
const k = contextTokenKey(accountId, userId);
|
|
12776
12944
|
logger.debug(`setContextToken: key=${k}`);
|
|
12777
|
-
contextTokenStore.set(k, token);
|
|
12945
|
+
contextTokenStore.set(k, { token, updatedAt: contextTokenRetention.now() });
|
|
12778
12946
|
persistContextTokens(accountId);
|
|
12779
12947
|
}
|
|
12780
12948
|
function getContextToken(accountId, userId) {
|
|
12781
12949
|
const k = contextTokenKey(accountId, normalizeWeixinUserIdFromChatKey(userId));
|
|
12782
|
-
|
|
12950
|
+
pruneContextTokensForAccount(accountId);
|
|
12951
|
+
const val = contextTokenStore.get(k)?.token;
|
|
12783
12952
|
logger.debug(`getContextToken: key=${k} found=${val !== undefined} storeSize=${contextTokenStore.size}`);
|
|
12784
12953
|
return val;
|
|
12785
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
|
+
}
|
|
12786
13003
|
function normalizeWeixinUserIdFromChatKey(chatKey) {
|
|
12787
13004
|
const parts = chatKey.split(":");
|
|
12788
13005
|
if (parts[0] === "weixin" && parts[2]) {
|
|
@@ -12849,14 +13066,16 @@ function descriptorFromItem(item) {
|
|
|
12849
13066
|
return { item, kind: "audio" };
|
|
12850
13067
|
return;
|
|
12851
13068
|
}
|
|
12852
|
-
var contextTokenStore;
|
|
13069
|
+
var DEFAULT_CONTEXT_TOKEN_MAX_PER_ACCOUNT = 5000, DEFAULT_CONTEXT_TOKEN_TTL_MS, contextTokenStore, contextTokenRetention;
|
|
12853
13070
|
var init_inbound = __esm(() => {
|
|
12854
13071
|
init_logger();
|
|
12855
13072
|
init_random();
|
|
12856
13073
|
init_types2();
|
|
12857
13074
|
init_state_dir();
|
|
12858
13075
|
init_private_file();
|
|
13076
|
+
DEFAULT_CONTEXT_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
12859
13077
|
contextTokenStore = new Map;
|
|
13078
|
+
contextTokenRetention = normalizeContextTokenRetention();
|
|
12860
13079
|
});
|
|
12861
13080
|
|
|
12862
13081
|
// src/weixin/api/config-cache.ts
|
|
@@ -12864,18 +13083,27 @@ class WeixinConfigManager {
|
|
|
12864
13083
|
apiOpts;
|
|
12865
13084
|
log;
|
|
12866
13085
|
cache = new Map;
|
|
12867
|
-
|
|
13086
|
+
maxEntries;
|
|
13087
|
+
entryTtlMs;
|
|
13088
|
+
now;
|
|
13089
|
+
fetchConfig;
|
|
13090
|
+
constructor(apiOpts, log, options = {}) {
|
|
12868
13091
|
this.apiOpts = apiOpts;
|
|
12869
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;
|
|
12870
13097
|
}
|
|
12871
13098
|
async getForUser(userId, contextToken) {
|
|
12872
|
-
const now =
|
|
13099
|
+
const now = this.now();
|
|
13100
|
+
this.prune(now);
|
|
12873
13101
|
const entry = this.cache.get(userId);
|
|
12874
13102
|
const shouldFetch = !entry || now >= entry.nextFetchAt;
|
|
12875
13103
|
if (shouldFetch) {
|
|
12876
13104
|
let fetchOk = false;
|
|
12877
13105
|
try {
|
|
12878
|
-
const resp = await
|
|
13106
|
+
const resp = await this.fetchConfig({
|
|
12879
13107
|
baseUrl: this.apiOpts.baseUrl,
|
|
12880
13108
|
token: this.apiOpts.token,
|
|
12881
13109
|
ilinkUserId: userId,
|
|
@@ -12886,7 +13114,8 @@ class WeixinConfigManager {
|
|
|
12886
13114
|
config: { typingTicket: resp.typing_ticket ?? "" },
|
|
12887
13115
|
everSucceeded: true,
|
|
12888
13116
|
nextFetchAt: now + Math.random() * CONFIG_CACHE_TTL_MS,
|
|
12889
|
-
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS
|
|
13117
|
+
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
|
|
13118
|
+
lastTouchedAt: now
|
|
12890
13119
|
});
|
|
12891
13120
|
this.log(`[weixin] config ${entry?.everSucceeded ? "refreshed" : "cached"} for ${userId}`);
|
|
12892
13121
|
fetchOk = true;
|
|
@@ -12900,24 +13129,72 @@ class WeixinConfigManager {
|
|
|
12900
13129
|
if (entry) {
|
|
12901
13130
|
entry.nextFetchAt = now + nextDelay;
|
|
12902
13131
|
entry.retryDelayMs = nextDelay;
|
|
13132
|
+
entry.lastTouchedAt = now;
|
|
12903
13133
|
} else {
|
|
12904
13134
|
this.cache.set(userId, {
|
|
12905
13135
|
config: { typingTicket: "" },
|
|
12906
13136
|
everSucceeded: false,
|
|
12907
13137
|
nextFetchAt: now + CONFIG_CACHE_INITIAL_RETRY_MS,
|
|
12908
|
-
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS
|
|
13138
|
+
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
|
|
13139
|
+
lastTouchedAt: now
|
|
12909
13140
|
});
|
|
12910
13141
|
}
|
|
12911
13142
|
}
|
|
13143
|
+
} else {
|
|
13144
|
+
entry.lastTouchedAt = now;
|
|
12912
13145
|
}
|
|
13146
|
+
this.enforceMaxEntries();
|
|
12913
13147
|
return this.cache.get(userId)?.config ?? { typingTicket: "" };
|
|
12914
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);
|
|
12915
13186
|
}
|
|
12916
|
-
|
|
13187
|
+
function normalizeNonNegativeMs2(value, fallback) {
|
|
13188
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
13189
|
+
return fallback;
|
|
13190
|
+
return value;
|
|
13191
|
+
}
|
|
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;
|
|
12917
13193
|
var init_config_cache = __esm(() => {
|
|
12918
13194
|
init_api();
|
|
12919
13195
|
CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
12920
13196
|
CONFIG_CACHE_MAX_RETRY_MS = 60 * 60 * 1000;
|
|
13197
|
+
CONFIG_CACHE_ENTRY_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
12921
13198
|
});
|
|
12922
13199
|
|
|
12923
13200
|
// src/weixin/api/session-guard.ts
|
|
@@ -12936,24 +13213,24 @@ var init_session_guard = __esm(() => {
|
|
|
12936
13213
|
pauseUntilMap = new Map;
|
|
12937
13214
|
});
|
|
12938
13215
|
|
|
12939
|
-
// src/
|
|
13216
|
+
// src/runtime/conversation-executor.ts
|
|
12940
13217
|
function createConversationExecutor() {
|
|
12941
13218
|
const states = new Map;
|
|
12942
13219
|
const getState = (conversationId) => {
|
|
12943
13220
|
const existing = states.get(conversationId);
|
|
12944
13221
|
if (existing)
|
|
12945
13222
|
return existing;
|
|
12946
|
-
const created = { activeControls: 0 };
|
|
13223
|
+
const created = { normalTails: new Map, activeControls: 0 };
|
|
12947
13224
|
states.set(conversationId, created);
|
|
12948
13225
|
return created;
|
|
12949
13226
|
};
|
|
12950
13227
|
const cleanupState = (conversationId, state) => {
|
|
12951
|
-
if (
|
|
13228
|
+
if (state.normalTails.size === 0 && state.activeControls === 0) {
|
|
12952
13229
|
states.delete(conversationId);
|
|
12953
13230
|
}
|
|
12954
13231
|
};
|
|
12955
13232
|
return {
|
|
12956
|
-
run(conversationId, lane, task) {
|
|
13233
|
+
run(conversationId, lane, task, sessionKey) {
|
|
12957
13234
|
const state = getState(conversationId);
|
|
12958
13235
|
if (lane === "control") {
|
|
12959
13236
|
state.activeControls += 1;
|
|
@@ -12962,23 +13239,23 @@ function createConversationExecutor() {
|
|
|
12962
13239
|
cleanupState(conversationId, state);
|
|
12963
13240
|
});
|
|
12964
13241
|
}
|
|
12965
|
-
const
|
|
12966
|
-
const
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
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);
|
|
12970
13246
|
return next.finally(() => {
|
|
12971
|
-
if (state.
|
|
12972
|
-
state.
|
|
13247
|
+
if (state.normalTails.get(key) === next) {
|
|
13248
|
+
state.normalTails.delete(key);
|
|
12973
13249
|
}
|
|
12974
13250
|
cleanupState(conversationId, state);
|
|
12975
13251
|
});
|
|
12976
13252
|
}
|
|
12977
13253
|
};
|
|
12978
13254
|
}
|
|
13255
|
+
var DEFAULT_SESSION_KEY = "__chat__";
|
|
12979
13256
|
|
|
12980
13257
|
// src/channels/media-store.ts
|
|
12981
|
-
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";
|
|
12982
13259
|
import path7 from "node:path";
|
|
12983
13260
|
|
|
12984
13261
|
class RuntimeMediaStore {
|
|
@@ -13024,16 +13301,24 @@ class RuntimeMediaStore {
|
|
|
13024
13301
|
}
|
|
13025
13302
|
function sanitizeMediaFileName(fileName, mimeType) {
|
|
13026
13303
|
const base = path7.basename(fileName.trim() || "attachment");
|
|
13027
|
-
const
|
|
13028
|
-
|
|
13304
|
+
const safe = sanitizeString(base, {
|
|
13305
|
+
deny: /[\\/:*?"<>|\s]+/g,
|
|
13306
|
+
replacement: "-",
|
|
13307
|
+
trim: true,
|
|
13308
|
+
fallback: "attachment"
|
|
13309
|
+
});
|
|
13029
13310
|
const ext = path7.extname(safe);
|
|
13030
13311
|
if (ext)
|
|
13031
13312
|
return safe;
|
|
13032
13313
|
return `${safe}${extensionFromMime(mimeType)}`;
|
|
13033
13314
|
}
|
|
13034
13315
|
function safePathSegment(value) {
|
|
13035
|
-
|
|
13036
|
-
|
|
13316
|
+
return sanitizeString(value, {
|
|
13317
|
+
allow: /[A-Za-z0-9._-]/,
|
|
13318
|
+
replacement: "_",
|
|
13319
|
+
trim: true,
|
|
13320
|
+
fallback: "unknown"
|
|
13321
|
+
});
|
|
13037
13322
|
}
|
|
13038
13323
|
async function uniqueFileName(dir, baseName) {
|
|
13039
13324
|
const ext = path7.extname(baseName);
|
|
@@ -13089,7 +13374,7 @@ async function cleanupDir(dir, cutoffMs) {
|
|
|
13089
13374
|
if (entry.isDirectory()) {
|
|
13090
13375
|
const childEmpty = await cleanupDir(full, cutoffMs);
|
|
13091
13376
|
if (childEmpty) {
|
|
13092
|
-
await
|
|
13377
|
+
await rm5(full, { recursive: true, force: true });
|
|
13093
13378
|
} else {
|
|
13094
13379
|
empty = false;
|
|
13095
13380
|
}
|
|
@@ -13099,7 +13384,7 @@ async function cleanupDir(dir, cutoffMs) {
|
|
|
13099
13384
|
if (!s)
|
|
13100
13385
|
continue;
|
|
13101
13386
|
if (s.mtimeMs < cutoffMs) {
|
|
13102
|
-
await
|
|
13387
|
+
await rm5(full, { force: true });
|
|
13103
13388
|
} else {
|
|
13104
13389
|
empty = false;
|
|
13105
13390
|
}
|
|
@@ -13464,6 +13749,18 @@ var init_media_download = __esm(() => {
|
|
|
13464
13749
|
WEIXIN_MEDIA_MAX_BYTES = 100 * 1024 * 1024;
|
|
13465
13750
|
});
|
|
13466
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
|
+
|
|
13467
13764
|
// src/weixin/messaging/execute-chat-turn.ts
|
|
13468
13765
|
async function executeChatTurn(params) {
|
|
13469
13766
|
let usedReply = false;
|
|
@@ -13491,6 +13788,16 @@ function buildFinalHeadsUp(input) {
|
|
|
13491
13788
|
\uD83D\uDCC4 结果共 ${total} 段,已发 ${sentSoFar} 段。回复 /jx 续看后 ${remaining} 段。`;
|
|
13492
13789
|
}
|
|
13493
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
|
+
|
|
13494
13801
|
// src/weixin/messaging/markdown-filter.ts
|
|
13495
13802
|
class StreamingMarkdownFilter {
|
|
13496
13803
|
buf = "";
|
|
@@ -14387,8 +14694,8 @@ function normalizeMediaArray(media) {
|
|
|
14387
14694
|
}
|
|
14388
14695
|
|
|
14389
14696
|
// src/logging/rotating-file-writer.ts
|
|
14390
|
-
import { readdir as readdir2, rename, rm as
|
|
14391
|
-
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";
|
|
14392
14699
|
async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
14393
14700
|
let currentSize = 0;
|
|
14394
14701
|
try {
|
|
@@ -14405,10 +14712,10 @@ async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
|
14405
14712
|
return;
|
|
14406
14713
|
}
|
|
14407
14714
|
if (maxFiles <= 0) {
|
|
14408
|
-
await
|
|
14715
|
+
await rm6(filePath, { force: true });
|
|
14409
14716
|
return;
|
|
14410
14717
|
}
|
|
14411
|
-
await
|
|
14718
|
+
await rm6(`${filePath}.${maxFiles}`, { force: true });
|
|
14412
14719
|
for (let index = maxFiles - 1;index >= 1; index -= 1) {
|
|
14413
14720
|
const source = `${filePath}.${index}`;
|
|
14414
14721
|
try {
|
|
@@ -14422,7 +14729,7 @@ async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
|
14422
14729
|
await rename(filePath, `${filePath}.1`);
|
|
14423
14730
|
}
|
|
14424
14731
|
async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
14425
|
-
const parentDir =
|
|
14732
|
+
const parentDir = dirname7(filePath);
|
|
14426
14733
|
const prefix = `${basename(filePath)}.`;
|
|
14427
14734
|
const cutoff = now().getTime() - retentionDays * 24 * 60 * 60 * 1000;
|
|
14428
14735
|
let files = [];
|
|
@@ -14438,10 +14745,10 @@ async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
|
14438
14745
|
if (!file.startsWith(prefix) || !/^\d+$/.test(file.slice(prefix.length))) {
|
|
14439
14746
|
continue;
|
|
14440
14747
|
}
|
|
14441
|
-
const candidate =
|
|
14748
|
+
const candidate = join7(parentDir, file);
|
|
14442
14749
|
const details = await stat2(candidate);
|
|
14443
14750
|
if (details.mtime.getTime() < cutoff) {
|
|
14444
|
-
await
|
|
14751
|
+
await rm6(candidate, { force: true });
|
|
14445
14752
|
}
|
|
14446
14753
|
}
|
|
14447
14754
|
}
|
|
@@ -14452,7 +14759,7 @@ var init_rotating_file_writer = () => {};
|
|
|
14452
14759
|
|
|
14453
14760
|
// src/perf/perf-log-writer.ts
|
|
14454
14761
|
import { appendFile as fsAppendFile, mkdir as fsMkdir } from "node:fs/promises";
|
|
14455
|
-
import { dirname as
|
|
14762
|
+
import { dirname as dirname8 } from "node:path";
|
|
14456
14763
|
function createPerfLogWriter(options) {
|
|
14457
14764
|
const append = options.appendImpl ?? ((p, d) => fsAppendFile(p, d, "utf8"));
|
|
14458
14765
|
const mkdir8 = options.mkdirImpl ?? ((p, o) => fsMkdir(p, o).then(() => {
|
|
@@ -14502,7 +14809,7 @@ function createPerfLogWriter(options) {
|
|
|
14502
14809
|
return;
|
|
14503
14810
|
const data = batch.join("");
|
|
14504
14811
|
try {
|
|
14505
|
-
await mkdir8(
|
|
14812
|
+
await mkdir8(dirname8(options.filePath), { recursive: true });
|
|
14506
14813
|
await rotateIfNeeded(options.filePath, Buffer.byteLength(data), options.maxSizeBytes, options.maxFiles);
|
|
14507
14814
|
await append(options.filePath, data);
|
|
14508
14815
|
consecutiveFailures = 0;
|
|
@@ -14812,7 +15119,9 @@ function isSlashCommandText(textBody) {
|
|
|
14812
15119
|
}
|
|
14813
15120
|
function getWeixinMessageTurnLane(full) {
|
|
14814
15121
|
const textBody = extractTextBody(full.item_list).trim().toLowerCase();
|
|
14815
|
-
|
|
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";
|
|
14816
15125
|
}
|
|
14817
15126
|
function buildWeixinChatKey(accountId, userId) {
|
|
14818
15127
|
return `weixin:${accountId}:${userId}`;
|
|
@@ -14962,6 +15271,9 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
14962
15271
|
}
|
|
14963
15272
|
let midFirstSent = false;
|
|
14964
15273
|
const sendReplySegment = async (text) => {
|
|
15274
|
+
if (!shouldDeliverSegment(deps.isForeground)) {
|
|
15275
|
+
return false;
|
|
15276
|
+
}
|
|
14965
15277
|
const plainText = markdownToPlainText(text).trim();
|
|
14966
15278
|
if (plainText.length === 0) {
|
|
14967
15279
|
return false;
|
|
@@ -14993,7 +15305,8 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
14993
15305
|
channel: "weixin",
|
|
14994
15306
|
chatType: full.group_id ? "group" : "direct",
|
|
14995
15307
|
...full.from_user_id ? { senderId: full.from_user_id } : {},
|
|
14996
|
-
...full.group_id ? { groupId: full.group_id } : {}
|
|
15308
|
+
...full.group_id ? { groupId: full.group_id } : {},
|
|
15309
|
+
...deps.boundSessionAlias ? { boundSessionAlias: deps.boundSessionAlias } : {}
|
|
14997
15310
|
},
|
|
14998
15311
|
perfSpan
|
|
14999
15312
|
};
|
|
@@ -15011,80 +15324,96 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
15011
15324
|
if (turn.text) {
|
|
15012
15325
|
const finalText = markdownToPlainText(turn.text).trim();
|
|
15013
15326
|
if (finalText.length > 0) {
|
|
15014
|
-
const
|
|
15015
|
-
if (
|
|
15016
|
-
|
|
15017
|
-
if (
|
|
15018
|
-
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
deps.
|
|
15022
|
-
}
|
|
15023
|
-
await sendMessageWeixin({
|
|
15024
|
-
to,
|
|
15025
|
-
text: rawChunks[0],
|
|
15026
|
-
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
15027
|
-
});
|
|
15028
|
-
finalChunksSent += 1;
|
|
15029
|
-
if (!finalFirstSent) {
|
|
15030
|
-
finalFirstSent = true;
|
|
15031
|
-
perfSpan.mark("reply.final_first_sent", { bytes: utf8ByteLength(rawChunks[0]), chunkIndex: 1 });
|
|
15032
|
-
}
|
|
15033
|
-
}
|
|
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)}`));
|
|
15034
15336
|
} else {
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15043
|
-
|
|
15044
|
-
${buildFinalHeadsUp({
|
|
15045
|
-
total,
|
|
15046
|
-
sentSoFar
|
|
15047
|
-
})}`;
|
|
15048
|
-
}
|
|
15049
|
-
let sent = 0;
|
|
15050
|
-
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) {
|
|
15051
15346
|
const reserved = deps.reserveFinal ? deps.reserveFinal(to) : true;
|
|
15052
15347
|
if (!reserved) {
|
|
15053
15348
|
finalDropped = true;
|
|
15054
|
-
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=
|
|
15055
|
-
|
|
15056
|
-
}
|
|
15057
|
-
try {
|
|
15349
|
+
deps.errLog(`weixin.final.dropped reason=quota_exhausted kind=text chatKey=${to}`);
|
|
15350
|
+
} else {
|
|
15058
15351
|
await sendMessageWeixin({
|
|
15059
15352
|
to,
|
|
15060
|
-
text:
|
|
15353
|
+
text: rawChunks[0],
|
|
15061
15354
|
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
15062
15355
|
});
|
|
15063
|
-
sent += 1;
|
|
15064
15356
|
finalChunksSent += 1;
|
|
15065
15357
|
if (!finalFirstSent) {
|
|
15066
15358
|
finalFirstSent = true;
|
|
15067
|
-
perfSpan.mark("reply.final_first_sent", { bytes: utf8ByteLength(
|
|
15359
|
+
perfSpan.mark("reply.final_first_sent", { bytes: utf8ByteLength(rawChunks[0]), chunkIndex: 1 });
|
|
15068
15360
|
}
|
|
15069
|
-
} catch (sendErr) {
|
|
15070
|
-
finalDropped = true;
|
|
15071
|
-
deps.errLog(`weixin.final.dropped reason=send_failed kind=text_paginated chatKey=${to} chunk=${i + 1}/${total} err=${String(sendErr)}`);
|
|
15072
|
-
break;
|
|
15073
15361
|
}
|
|
15074
|
-
}
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
|
|
15078
|
-
const
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
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
|
+
}
|
|
15088
15417
|
}
|
|
15089
15418
|
}
|
|
15090
15419
|
}
|
|
@@ -15155,18 +15484,35 @@ ${buildFinalHeadsUp({
|
|
|
15155
15484
|
perfSpan.setOutcome("error", { reason: "turn_error" });
|
|
15156
15485
|
const errorText = err instanceof Error ? err.stack ?? err.message : JSON.stringify(err);
|
|
15157
15486
|
deps.errLog(`handleWeixinMessageTurn: agent or send failed: ${errorText}`);
|
|
15158
|
-
const
|
|
15159
|
-
|
|
15160
|
-
|
|
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}`);
|
|
15161
15502
|
} else {
|
|
15162
|
-
|
|
15163
|
-
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
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
|
+
}
|
|
15170
15516
|
}
|
|
15171
15517
|
} finally {
|
|
15172
15518
|
stopTypingIndicator();
|
|
@@ -15184,6 +15530,7 @@ var init_handle_weixin_message_turn = __esm(() => {
|
|
|
15184
15530
|
init_outbound_media_safety();
|
|
15185
15531
|
init_media_download();
|
|
15186
15532
|
init_mime();
|
|
15533
|
+
init_completion_notice();
|
|
15187
15534
|
init_inbound();
|
|
15188
15535
|
init_error_notice();
|
|
15189
15536
|
init_send_media();
|
|
@@ -15385,7 +15732,15 @@ async function monitorWeixinProvider(opts) {
|
|
|
15385
15732
|
}
|
|
15386
15733
|
}
|
|
15387
15734
|
}
|
|
15388
|
-
|
|
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, {
|
|
15389
15744
|
accountId,
|
|
15390
15745
|
agent,
|
|
15391
15746
|
baseUrl,
|
|
@@ -15403,9 +15758,25 @@ async function monitorWeixinProvider(opts) {
|
|
|
15403
15758
|
...opts.prependPendingFinal ? { prependPendingFinal: opts.prependPendingFinal } : {},
|
|
15404
15759
|
...opts.mediaStore ? { mediaStore: opts.mediaStore } : {},
|
|
15405
15760
|
...opts.allowedMediaRoots ? { allowedMediaRoots: opts.allowedMediaRoots } : {},
|
|
15406
|
-
...opts.perfTracer ? { perfTracer: opts.perfTracer } : {}
|
|
15407
|
-
|
|
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) => {
|
|
15408
15775
|
errLog(`[weixin] message turn failed: ${String(err)}`);
|
|
15776
|
+
}).finally(() => {
|
|
15777
|
+
if (boundAlias) {
|
|
15778
|
+
opts.activeTurns?.markInactive(chatKey, boundAlias);
|
|
15779
|
+
}
|
|
15409
15780
|
});
|
|
15410
15781
|
}
|
|
15411
15782
|
} catch (err) {
|
|
@@ -15589,7 +15960,10 @@ async function start(agent, opts) {
|
|
|
15589
15960
|
...opts?.enqueuePendingFinal ? { enqueuePendingFinal: opts.enqueuePendingFinal } : {},
|
|
15590
15961
|
...opts?.dropPendingFinal ? { dropPendingFinal: opts.dropPendingFinal } : {},
|
|
15591
15962
|
...opts?.mediaStore ? { mediaStore: opts.mediaStore } : {},
|
|
15592
|
-
...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 } : {}
|
|
15593
15967
|
});
|
|
15594
15968
|
}
|
|
15595
15969
|
var init_bot = __esm(() => {
|
|
@@ -16021,16 +16395,16 @@ var init_scheduled_turn = __esm(() => {
|
|
|
16021
16395
|
});
|
|
16022
16396
|
|
|
16023
16397
|
// src/weixin/monitor/consumer-lock.ts
|
|
16024
|
-
import { mkdir as mkdir8, open as open3, readFile as
|
|
16025
|
-
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";
|
|
16026
16400
|
import { homedir as homedir4 } from "node:os";
|
|
16027
16401
|
function createWeixinConsumerLock(options = {}) {
|
|
16028
|
-
const lockFilePath = options.lockFilePath ??
|
|
16402
|
+
const lockFilePath = options.lockFilePath ?? join8(coreHomeDir(homedir4()), "runtime", "weixin-consumer.lock.json");
|
|
16029
16403
|
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning4;
|
|
16030
16404
|
const onDiagnostic = options.onDiagnostic;
|
|
16031
16405
|
return {
|
|
16032
16406
|
async acquire(meta2) {
|
|
16033
|
-
await mkdir8(
|
|
16407
|
+
await mkdir8(dirname9(lockFilePath), { recursive: true });
|
|
16034
16408
|
while (true) {
|
|
16035
16409
|
try {
|
|
16036
16410
|
const handle = await open3(lockFilePath, "wx");
|
|
@@ -16061,7 +16435,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
16061
16435
|
});
|
|
16062
16436
|
const existing = await loadLockMetadata(lockFilePath);
|
|
16063
16437
|
if (!existing) {
|
|
16064
|
-
await
|
|
16438
|
+
await rm7(lockFilePath, { force: true });
|
|
16065
16439
|
await onDiagnostic?.("lock_invalid_removed", {
|
|
16066
16440
|
lockFilePath,
|
|
16067
16441
|
reason: "invalid_or_unreadable_metadata"
|
|
@@ -16069,7 +16443,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
16069
16443
|
continue;
|
|
16070
16444
|
}
|
|
16071
16445
|
if (!isProcessRunning(existing.pid)) {
|
|
16072
|
-
await
|
|
16446
|
+
await rm7(lockFilePath, { force: true });
|
|
16073
16447
|
await onDiagnostic?.("lock_stale_removed", {
|
|
16074
16448
|
lockFilePath,
|
|
16075
16449
|
stalePid: existing.pid,
|
|
@@ -16094,7 +16468,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
16094
16468
|
}
|
|
16095
16469
|
},
|
|
16096
16470
|
async release() {
|
|
16097
|
-
await
|
|
16471
|
+
await rm7(lockFilePath, { force: true });
|
|
16098
16472
|
await onDiagnostic?.("lock_released", {
|
|
16099
16473
|
lockFilePath
|
|
16100
16474
|
});
|
|
@@ -16103,7 +16477,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
16103
16477
|
}
|
|
16104
16478
|
async function loadLockMetadata(path14) {
|
|
16105
16479
|
try {
|
|
16106
|
-
const raw = await
|
|
16480
|
+
const raw = await readFile7(path14, "utf8");
|
|
16107
16481
|
const parsed = JSON.parse(raw);
|
|
16108
16482
|
if (!parsed || typeof parsed.pid !== "number" || !parsed.mode || !parsed.configPath || !parsed.statePath) {
|
|
16109
16483
|
return null;
|
|
@@ -16123,6 +16497,7 @@ function defaultIsProcessRunning4(pid) {
|
|
|
16123
16497
|
}
|
|
16124
16498
|
var ActiveWeixinConsumerLockError;
|
|
16125
16499
|
var init_consumer_lock = __esm(() => {
|
|
16500
|
+
init_core_home();
|
|
16126
16501
|
ActiveWeixinConsumerLockError = class ActiveWeixinConsumerLockError extends Error {
|
|
16127
16502
|
existing;
|
|
16128
16503
|
lockFilePath;
|
|
@@ -16185,6 +16560,7 @@ class WeixinChannel {
|
|
|
16185
16560
|
console.log("[weacpx] 未检测到登录凭证,正在启动扫码登录...");
|
|
16186
16561
|
await this.login();
|
|
16187
16562
|
}
|
|
16563
|
+
const sessions = input.sessions;
|
|
16188
16564
|
await start(input.agent, {
|
|
16189
16565
|
abortSignal: input.abortSignal,
|
|
16190
16566
|
...this.mediaStore ? { mediaStore: this.mediaStore } : {},
|
|
@@ -16197,7 +16573,12 @@ class WeixinChannel {
|
|
|
16197
16573
|
prependPendingFinal: (chatKey, chunks) => input.quota.prependPendingFinal(chatKey, chunks),
|
|
16198
16574
|
enqueuePendingFinal: (chatKey, chunks) => input.quota.enqueuePendingFinal(chatKey, chunks),
|
|
16199
16575
|
dropPendingFinal: (chatKey) => input.quota.clearPendingFinal(chatKey),
|
|
16200
|
-
...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 } : {}
|
|
16201
16582
|
});
|
|
16202
16583
|
}
|
|
16203
16584
|
async notifyTaskCompletion(task) {
|
|
@@ -16652,9 +17033,9 @@ __export(exports_plugin_loader, {
|
|
|
16652
17033
|
});
|
|
16653
17034
|
import { createRequire as createRequire2 } from "node:module";
|
|
16654
17035
|
import { pathToFileURL } from "node:url";
|
|
16655
|
-
import { join as
|
|
17036
|
+
import { join as join9 } from "node:path";
|
|
16656
17037
|
async function importPluginFromHome(packageName, pluginHome) {
|
|
16657
|
-
const requireFromHome = createRequire2(
|
|
17038
|
+
const requireFromHome = createRequire2(join9(pluginHome, "package.json"));
|
|
16658
17039
|
const entry = requireFromHome.resolve(packageName);
|
|
16659
17040
|
return await import(pathToFileURL(entry).href);
|
|
16660
17041
|
}
|
|
@@ -16709,7 +17090,7 @@ var init_bootstrap = __esm(() => {
|
|
|
16709
17090
|
|
|
16710
17091
|
// src/logging/app-logger.ts
|
|
16711
17092
|
import { appendFile, chmod as chmod2, mkdir as mkdir9 } from "node:fs/promises";
|
|
16712
|
-
import { dirname as
|
|
17093
|
+
import { dirname as dirname11 } from "node:path";
|
|
16713
17094
|
function createNoopAppLogger() {
|
|
16714
17095
|
return {
|
|
16715
17096
|
debug: async () => {},
|
|
@@ -16750,7 +17131,7 @@ function createAppLogger(options) {
|
|
|
16750
17131
|
return;
|
|
16751
17132
|
}
|
|
16752
17133
|
const line = formatLogLine(now(), level, event, message, context);
|
|
16753
|
-
await mkdir9(
|
|
17134
|
+
await mkdir9(dirname11(options.filePath), { recursive: true });
|
|
16754
17135
|
if (!modeEnsured) {
|
|
16755
17136
|
modeEnsured = true;
|
|
16756
17137
|
await chmod2(options.filePath, 384).catch(() => {});
|
|
@@ -16785,7 +17166,7 @@ var init_app_logger = __esm(() => {
|
|
|
16785
17166
|
});
|
|
16786
17167
|
|
|
16787
17168
|
// src/transport/acpx-session-index.ts
|
|
16788
|
-
import { readFile as
|
|
17169
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
16789
17170
|
import { homedir as homedir5 } from "node:os";
|
|
16790
17171
|
import { resolve as resolve2 } from "node:path";
|
|
16791
17172
|
async function resolveSessionAgentCommandFromIndex(session) {
|
|
@@ -16794,7 +17175,7 @@ async function resolveSessionAgentCommandFromIndex(session) {
|
|
|
16794
17175
|
return;
|
|
16795
17176
|
}
|
|
16796
17177
|
try {
|
|
16797
|
-
const raw = await
|
|
17178
|
+
const raw = await readFile11(resolve2(home, ".acpx", "sessions", "index.json"), "utf8");
|
|
16798
17179
|
const parsed = JSON.parse(raw);
|
|
16799
17180
|
const targetCwd = resolve2(session.cwd);
|
|
16800
17181
|
const match = parsed.entries?.find((entry) => entry.name === session.transportSession && entry.cwd === targetCwd && typeof entry.agentCommand === "string" && entry.agentCommand.trim().length > 0);
|
|
@@ -17004,8 +17385,10 @@ function parseCommand(input) {
|
|
|
17004
17385
|
}
|
|
17005
17386
|
if (command === "/status")
|
|
17006
17387
|
return { kind: "status" };
|
|
17007
|
-
if (command === "/cancel")
|
|
17008
|
-
|
|
17388
|
+
if (command === "/cancel") {
|
|
17389
|
+
const alias = parts[1];
|
|
17390
|
+
return alias ? { kind: "cancel", alias } : { kind: "cancel" };
|
|
17391
|
+
}
|
|
17009
17392
|
if (command === "/clear")
|
|
17010
17393
|
return { kind: "session.reset" };
|
|
17011
17394
|
if (command === "/mode" && parts.length === 1)
|
|
@@ -17139,6 +17522,9 @@ function parseCommand(input) {
|
|
|
17139
17522
|
if (command === "/config" && parts[1] === "set" && parts.length === 4) {
|
|
17140
17523
|
return { kind: "config.set", path: parts[2] ?? "", value: parts[3] ?? "" };
|
|
17141
17524
|
}
|
|
17525
|
+
if (command === "/use" && parts[1] === "-") {
|
|
17526
|
+
return { kind: "session.use.previous" };
|
|
17527
|
+
}
|
|
17142
17528
|
if (command === "/use" && parts[1]) {
|
|
17143
17529
|
return { kind: "session.use", alias: parts[1] };
|
|
17144
17530
|
}
|
|
@@ -18295,7 +18681,7 @@ async function buildCoordinatorPrompt(input) {
|
|
|
18295
18681
|
|
|
18296
18682
|
`) : input.userText ?? "";
|
|
18297
18683
|
if (input.maxPromptLength && promptText.length > input.maxPromptLength) {
|
|
18298
|
-
promptText = promptText
|
|
18684
|
+
promptText = truncateText(promptText, input.maxPromptLength, "...");
|
|
18299
18685
|
}
|
|
18300
18686
|
return {
|
|
18301
18687
|
promptText,
|
|
@@ -18306,6 +18692,11 @@ async function buildCoordinatorPrompt(input) {
|
|
|
18306
18692
|
}
|
|
18307
18693
|
var init_build_coordinator_prompt = () => {};
|
|
18308
18694
|
|
|
18695
|
+
// src/commands/handlers/session-list-marker.ts
|
|
18696
|
+
function decorateUnread(label, hasUnread) {
|
|
18697
|
+
return hasUnread ? `● ${label}` : label;
|
|
18698
|
+
}
|
|
18699
|
+
|
|
18309
18700
|
// src/commands/handlers/session-handler.ts
|
|
18310
18701
|
async function handleSessions(context, chatKey) {
|
|
18311
18702
|
const sessions = await context.sessions.listSessions(chatKey);
|
|
@@ -18328,10 +18719,11 @@ async function handleSessions(context, chatKey) {
|
|
|
18328
18719
|
return { text: lines.join(`
|
|
18329
18720
|
`) };
|
|
18330
18721
|
}
|
|
18722
|
+
const unread = new Set(context.sessions.listBackgroundResultAliases(chatKey));
|
|
18331
18723
|
return {
|
|
18332
18724
|
text: [
|
|
18333
18725
|
"会话列表:",
|
|
18334
|
-
...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 ? " [当前]" : ""}`)
|
|
18335
18727
|
].join(`
|
|
18336
18728
|
`)
|
|
18337
18729
|
};
|
|
@@ -18408,13 +18800,55 @@ async function refreshSessionTransportAgentCommandBestEffort(context, alias, eve
|
|
|
18408
18800
|
});
|
|
18409
18801
|
}
|
|
18410
18802
|
}
|
|
18411
|
-
|
|
18412
|
-
|
|
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);
|
|
18413
18832
|
await context.logger.info("session.selected", "selected logical session", {
|
|
18414
|
-
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,
|
|
18415
18847
|
chatKey
|
|
18416
18848
|
});
|
|
18417
|
-
|
|
18849
|
+
const internalAlias = context.sessions.peekCurrentSessionAlias(chatKey) ?? switched.alias;
|
|
18850
|
+
const text = await appendSwitchBackContext(context, chatKey, internalAlias, renderSwitched(switched));
|
|
18851
|
+
return { text };
|
|
18418
18852
|
}
|
|
18419
18853
|
async function handleModeShow(context, chatKey) {
|
|
18420
18854
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
@@ -18490,7 +18924,34 @@ async function handleStatus(context, chatKey) {
|
|
|
18490
18924
|
`)
|
|
18491
18925
|
};
|
|
18492
18926
|
}
|
|
18493
|
-
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
|
+
}
|
|
18494
18955
|
const session = await context.sessions.getCurrentSession(chatKey);
|
|
18495
18956
|
if (!session) {
|
|
18496
18957
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
@@ -18655,7 +19116,7 @@ async function handlePromptWithSession(context, session, chatKey, text, reply, r
|
|
|
18655
19116
|
}
|
|
18656
19117
|
}
|
|
18657
19118
|
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata) {
|
|
18658
|
-
const session = await context.sessions.getCurrentSession(chatKey);
|
|
19119
|
+
const session = metadata?.boundSessionAlias ? context.sessions.getResolvedSessionByInternalAlias(metadata.boundSessionAlias) : await context.sessions.getCurrentSession(chatKey);
|
|
18659
19120
|
if (!session) {
|
|
18660
19121
|
return { text: NO_CURRENT_SESSION_TEXT };
|
|
18661
19122
|
}
|
|
@@ -18756,6 +19217,8 @@ var init_session_handler = __esm(() => {
|
|
|
18756
19217
|
{ usage: "/session tail [N]", description: "补拉当前会话的历史输出(默认 50 行)" },
|
|
18757
19218
|
{ usage: "/session rm <alias>", description: "删除逻辑会话" },
|
|
18758
19219
|
{ usage: "/use <alias>", description: "切换当前会话" },
|
|
19220
|
+
{ usage: "/use <片段>", description: "按别名片段切换(精确>前缀>子串;多命中会列候选)" },
|
|
19221
|
+
{ usage: "/use -", description: "切回上一个会话(像 shell 的 cd -)" },
|
|
18759
19222
|
{ usage: "/session reset 或 /clear", description: "重置当前会话上下文" }
|
|
18760
19223
|
],
|
|
18761
19224
|
examples: [
|
|
@@ -18763,6 +19226,8 @@ var init_session_handler = __esm(() => {
|
|
|
18763
19226
|
"/ssn",
|
|
18764
19227
|
"/ssn 1",
|
|
18765
19228
|
"/use backend-fix",
|
|
19229
|
+
"/use back",
|
|
19230
|
+
"/use -",
|
|
18766
19231
|
"/session rm old-session",
|
|
18767
19232
|
"/session reset"
|
|
18768
19233
|
]
|
|
@@ -18828,12 +19293,14 @@ var init_session_handler = __esm(() => {
|
|
|
18828
19293
|
cancelHelp = {
|
|
18829
19294
|
topic: "cancel",
|
|
18830
19295
|
aliases: ["stop"],
|
|
18831
|
-
summary: "
|
|
19296
|
+
summary: "取消会话里正在执行的任务。",
|
|
18832
19297
|
commands: [
|
|
18833
|
-
{ usage: "/cancel", description: "
|
|
18834
|
-
{ usage: "/
|
|
19298
|
+
{ usage: "/cancel", description: "取消当前前台会话的任务" },
|
|
19299
|
+
{ usage: "/cancel <alias>", description: "取消指定(含后台)会话的任务" },
|
|
19300
|
+
{ usage: "/stop", description: "取消当前任务(/cancel 别名)" },
|
|
19301
|
+
{ usage: "/stop <alias>", description: "取消指定会话的任务(/cancel <alias> 别名)" }
|
|
18835
19302
|
],
|
|
18836
|
-
examples: ["/cancel"]
|
|
19303
|
+
examples: ["/cancel", "/cancel backend"]
|
|
18837
19304
|
};
|
|
18838
19305
|
});
|
|
18839
19306
|
|
|
@@ -19788,6 +20255,27 @@ function handleHelp(topic) {
|
|
|
19788
20255
|
}
|
|
19789
20256
|
return { text: renderHelpTopic(entry) };
|
|
19790
20257
|
}
|
|
20258
|
+
function handleInvalidCommand(recognizedCommand) {
|
|
20259
|
+
const topicName = recognizedCommand.replace(/^\//, "");
|
|
20260
|
+
const entry = getHelpTopic(topicName);
|
|
20261
|
+
if (entry) {
|
|
20262
|
+
return { text: `命令格式不正确,请参考下面的用法:
|
|
20263
|
+
|
|
20264
|
+
${renderHelpTopic(entry)}` };
|
|
20265
|
+
}
|
|
20266
|
+
return {
|
|
20267
|
+
text: [
|
|
20268
|
+
"无法识别的命令格式。",
|
|
20269
|
+
"",
|
|
20270
|
+
"正确的会话创建格式:",
|
|
20271
|
+
"/session new <别名> --agent <Agent名> --ws <工作区名>",
|
|
20272
|
+
"",
|
|
20273
|
+
"例如:",
|
|
20274
|
+
"/session new demo --agent claude --ws weacpx"
|
|
20275
|
+
].join(`
|
|
20276
|
+
`)
|
|
20277
|
+
};
|
|
20278
|
+
}
|
|
19791
20279
|
function renderHelpIndex() {
|
|
19792
20280
|
const topics = listHelpTopics();
|
|
19793
20281
|
return [
|
|
@@ -20520,7 +21008,7 @@ import { spawn as spawn5 } from "node:child_process";
|
|
|
20520
21008
|
import { createWriteStream } from "node:fs";
|
|
20521
21009
|
import { mkdir as mkdir10 } from "node:fs/promises";
|
|
20522
21010
|
import { homedir as homedir6 } from "node:os";
|
|
20523
|
-
import { join as
|
|
21011
|
+
import { join as join13 } from "node:path";
|
|
20524
21012
|
async function autoInstallOptionalDep(pkg, parentPackages, options = {}) {
|
|
20525
21013
|
const runCli = options.runCli ?? defaultRunCli;
|
|
20526
21014
|
const openLog = options.openLog ?? defaultLogSink;
|
|
@@ -20635,10 +21123,10 @@ ${err.message}`, reason: "spawn" });
|
|
|
20635
21123
|
});
|
|
20636
21124
|
});
|
|
20637
21125
|
}, defaultLogSink = async () => {
|
|
20638
|
-
const dir =
|
|
21126
|
+
const dir = join13(coreHomeDir(homedir6()), "logs");
|
|
20639
21127
|
await mkdir10(dir, { recursive: true });
|
|
20640
21128
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "").replace(/-/g, "");
|
|
20641
|
-
const path14 =
|
|
21129
|
+
const path14 = join13(dir, `auto-install-${timestamp}.log`);
|
|
20642
21130
|
const stream = createWriteStream(path14, { flags: "a" });
|
|
20643
21131
|
return {
|
|
20644
21132
|
path: path14,
|
|
@@ -20651,6 +21139,7 @@ ${err.message}`, reason: "spawn" });
|
|
|
20651
21139
|
};
|
|
20652
21140
|
};
|
|
20653
21141
|
var init_auto_install_optional_dep = __esm(() => {
|
|
21142
|
+
init_core_home();
|
|
20654
21143
|
PRECISE_COMMANDS = {
|
|
20655
21144
|
npm: { cmd: "npm", args: (pkg) => ["install", pkg, "--no-save", "--no-audit", "--no-fund"] },
|
|
20656
21145
|
bun: { cmd: "bun", args: (pkg) => ["add", pkg] },
|
|
@@ -20664,7 +21153,7 @@ import { spawn as spawn6 } from "node:child_process";
|
|
|
20664
21153
|
import { createRequire as createRequire3 } from "node:module";
|
|
20665
21154
|
import { access as access3 } from "node:fs/promises";
|
|
20666
21155
|
import { homedir as homedir7 } from "node:os";
|
|
20667
|
-
import { dirname as
|
|
21156
|
+
import { dirname as dirname12, join as join14 } from "node:path";
|
|
20668
21157
|
function deriveParentPackageName(platformPackage) {
|
|
20669
21158
|
return platformPackage.replace(/-(?:linux|darwin|win32|windows|freebsd|openbsd|sunos|aix)(?:-(?:x64|arm64|ia32|arm|ppc64|s390x))?(?:-(?:baseline|musl|gnu|gnueabihf|musleabihf|msvc))?$/, "");
|
|
20670
21159
|
}
|
|
@@ -20677,7 +21166,7 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
20677
21166
|
const queryRoot = deps.queryPackageManagerRoot ?? defaultQueryPackageManagerRoot;
|
|
20678
21167
|
const parentName = deriveParentPackageName(platformPackage);
|
|
20679
21168
|
const rawCandidates = [];
|
|
20680
|
-
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");
|
|
20681
21170
|
const [npmRoot, pnpmRoot, yarnRoot] = await Promise.all([
|
|
20682
21171
|
queryRoot("npm"),
|
|
20683
21172
|
queryRoot("pnpm"),
|
|
@@ -20700,20 +21189,20 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
20700
21189
|
if (resolved)
|
|
20701
21190
|
rawCandidates.push({ path: resolved, manager: classify(resolved) });
|
|
20702
21191
|
}
|
|
20703
|
-
rawCandidates.push({ path:
|
|
21192
|
+
rawCandidates.push({ path: join14(bunGlobalRoot, parentName), manager: "bun" });
|
|
20704
21193
|
if (npmRoot)
|
|
20705
|
-
rawCandidates.push({ path:
|
|
21194
|
+
rawCandidates.push({ path: join14(npmRoot, parentName), manager: "npm" });
|
|
20706
21195
|
if (pnpmRoot)
|
|
20707
|
-
rawCandidates.push({ path:
|
|
21196
|
+
rawCandidates.push({ path: join14(pnpmRoot, parentName), manager: "pnpm" });
|
|
20708
21197
|
if (yarnRoot)
|
|
20709
|
-
rawCandidates.push({ path:
|
|
21198
|
+
rawCandidates.push({ path: join14(yarnRoot, parentName), manager: "yarn" });
|
|
20710
21199
|
const seen = new Set;
|
|
20711
21200
|
const verified = [];
|
|
20712
21201
|
for (const candidate of rawCandidates) {
|
|
20713
21202
|
if (seen.has(candidate.path))
|
|
20714
21203
|
continue;
|
|
20715
21204
|
seen.add(candidate.path);
|
|
20716
|
-
if (await fsExists(
|
|
21205
|
+
if (await fsExists(join14(candidate.path, "package.json"))) {
|
|
20717
21206
|
verified.push(candidate);
|
|
20718
21207
|
}
|
|
20719
21208
|
}
|
|
@@ -20737,7 +21226,7 @@ function defaultResolveFromCwd(name, cwd) {
|
|
|
20737
21226
|
const pkgJson = require2.resolve(`${name}/package.json`, {
|
|
20738
21227
|
paths: [cwd, ...require2.resolve.paths(name) ?? []]
|
|
20739
21228
|
});
|
|
20740
|
-
return
|
|
21229
|
+
return dirname12(pkgJson);
|
|
20741
21230
|
} catch {
|
|
20742
21231
|
return null;
|
|
20743
21232
|
}
|
|
@@ -20784,7 +21273,7 @@ async function defaultQueryPackageManagerRoot(tool) {
|
|
|
20784
21273
|
const trimmed = stdout2.trim().split(/\r?\n/).pop()?.trim() ?? "";
|
|
20785
21274
|
if (!trimmed)
|
|
20786
21275
|
return done(null);
|
|
20787
|
-
done(spec.postfix ?
|
|
21276
|
+
done(spec.postfix ? join14(trimmed, spec.postfix) : trimmed);
|
|
20788
21277
|
});
|
|
20789
21278
|
});
|
|
20790
21279
|
}
|
|
@@ -20888,7 +21377,8 @@ class CommandRouter {
|
|
|
20888
21377
|
__setDiscoverPathsForTest(fn) {
|
|
20889
21378
|
this.discoverPaths = fn;
|
|
20890
21379
|
}
|
|
20891
|
-
|
|
21380
|
+
activeTurns;
|
|
21381
|
+
constructor(sessions, transport, config2, configStore, logger2, resolveSessionAgentCommand = resolveSessionAgentCommandFromIndex, orchestration, quota, scheduled, scheduledDelivery, resolveNativeSessionListFormat, activeTurns) {
|
|
20892
21382
|
this.sessions = sessions;
|
|
20893
21383
|
this.transport = transport;
|
|
20894
21384
|
this.config = config2;
|
|
@@ -20900,6 +21390,7 @@ class CommandRouter {
|
|
|
20900
21390
|
this.scheduledDelivery = scheduledDelivery;
|
|
20901
21391
|
this.resolveNativeSessionListFormat = resolveNativeSessionListFormat;
|
|
20902
21392
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
21393
|
+
this.activeTurns = activeTurns;
|
|
20903
21394
|
}
|
|
20904
21395
|
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan) {
|
|
20905
21396
|
const startedAt = Date.now();
|
|
@@ -20925,18 +21416,7 @@ class CommandRouter {
|
|
|
20925
21416
|
return await this.executeCommand(chatKey, command.kind, startedAt, async () => {
|
|
20926
21417
|
switch (command.kind) {
|
|
20927
21418
|
case "invalid":
|
|
20928
|
-
return
|
|
20929
|
-
text: [
|
|
20930
|
-
"无法识别的命令格式。",
|
|
20931
|
-
"",
|
|
20932
|
-
"正确的会话创建格式:",
|
|
20933
|
-
"/session new <别名> --agent <Agent名> --ws <工作区名>",
|
|
20934
|
-
"",
|
|
20935
|
-
"例如:",
|
|
20936
|
-
"/session new demo --agent claude --ws weacpx"
|
|
20937
|
-
].join(`
|
|
20938
|
-
`)
|
|
20939
|
-
};
|
|
21419
|
+
return handleInvalidCommand(command.recognizedCommand);
|
|
20940
21420
|
case "help":
|
|
20941
21421
|
return handleHelp(command.topic);
|
|
20942
21422
|
case "agents":
|
|
@@ -20981,6 +21461,8 @@ class CommandRouter {
|
|
|
20981
21461
|
return await handleNativeSessionSelect(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.identifier, command.alias);
|
|
20982
21462
|
case "session.use":
|
|
20983
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);
|
|
20984
21466
|
case "mode.show":
|
|
20985
21467
|
return await handleModeShow(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
20986
21468
|
case "mode.set":
|
|
@@ -20994,7 +21476,7 @@ class CommandRouter {
|
|
|
20994
21476
|
case "status":
|
|
20995
21477
|
return await handleStatus(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
20996
21478
|
case "cancel":
|
|
20997
|
-
return await handleCancel(this.createSessionHandlerContext(undefined, perfSpan), chatKey);
|
|
21479
|
+
return await handleCancel(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
|
|
20998
21480
|
case "session.reset":
|
|
20999
21481
|
return await handleSessionReset(this.createSessionHandlerContext(reply, perfSpan), chatKey);
|
|
21000
21482
|
case "session.tail":
|
|
@@ -21089,7 +21571,8 @@ class CommandRouter {
|
|
|
21089
21571
|
...this.createHandlerContext(),
|
|
21090
21572
|
lifecycle: this.createSessionLifecycleOps(reply, perfSpan),
|
|
21091
21573
|
interaction: this.createSessionInteractionOps(perfSpan),
|
|
21092
|
-
recovery: this.createSessionRenderRecoveryOps()
|
|
21574
|
+
recovery: this.createSessionRenderRecoveryOps(),
|
|
21575
|
+
...this.activeTurns ? { activeTurns: this.activeTurns } : {}
|
|
21093
21576
|
};
|
|
21094
21577
|
}
|
|
21095
21578
|
createSessionLifecycleOps(reply, perfSpan) {
|
|
@@ -21454,7 +21937,7 @@ var init_command_router = __esm(() => {
|
|
|
21454
21937
|
});
|
|
21455
21938
|
|
|
21456
21939
|
// src/config/resolve-acpx-command.ts
|
|
21457
|
-
import { readFileSync } from "node:fs";
|
|
21940
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
21458
21941
|
import { createRequire as createRequire4 } from "node:module";
|
|
21459
21942
|
import { posix, win32 } from "node:path";
|
|
21460
21943
|
function resolveAcpxCommand(options = {}) {
|
|
@@ -21470,7 +21953,7 @@ function resolveAcpxCommandMetadata(options = {}) {
|
|
|
21470
21953
|
}
|
|
21471
21954
|
const platform = options.platform ?? process.platform;
|
|
21472
21955
|
const resolvePackageJson = options.resolvePackageJson ?? ((id) => require3.resolve(id));
|
|
21473
|
-
const readPackageJson = options.readPackageJson ?? ((path14) => JSON.parse(
|
|
21956
|
+
const readPackageJson = options.readPackageJson ?? ((path14) => JSON.parse(readFileSync2(path14, "utf8")));
|
|
21474
21957
|
try {
|
|
21475
21958
|
const packageJsonPath = resolvePackageJson("acpx/package.json");
|
|
21476
21959
|
const pkg = readPackageJson(packageJsonPath);
|
|
@@ -21544,7 +22027,7 @@ var init_console_agent = __esm(() => {
|
|
|
21544
22027
|
});
|
|
21545
22028
|
|
|
21546
22029
|
// src/orchestration/orchestration-server.ts
|
|
21547
|
-
import { rm as
|
|
22030
|
+
import { rm as rm8 } from "node:fs/promises";
|
|
21548
22031
|
import { createConnection as createConnection2, createServer } from "node:net";
|
|
21549
22032
|
|
|
21550
22033
|
class OrchestrationServer {
|
|
@@ -21877,7 +22360,7 @@ class OrchestrationServer {
|
|
|
21877
22360
|
return;
|
|
21878
22361
|
}
|
|
21879
22362
|
const removeFile = this.deps.removeFile ?? (async (path14) => {
|
|
21880
|
-
await
|
|
22363
|
+
await rm8(path14, { force: true });
|
|
21881
22364
|
});
|
|
21882
22365
|
await removeFile(this.endpoint.path);
|
|
21883
22366
|
}
|
|
@@ -24588,7 +25071,11 @@ class OrchestrationService {
|
|
|
24588
25071
|
}
|
|
24589
25072
|
workspaceLabelFromCwd(cwd) {
|
|
24590
25073
|
const base = basename2(cwd).trim() || "cwd";
|
|
24591
|
-
return base
|
|
25074
|
+
return sanitizeString(base, {
|
|
25075
|
+
allow: /[a-zA-Z0-9._-]/,
|
|
25076
|
+
replacement: "_",
|
|
25077
|
+
fallback: "cwd"
|
|
25078
|
+
});
|
|
24592
25079
|
}
|
|
24593
25080
|
cwdWorkerSessionPart(cwd) {
|
|
24594
25081
|
const label = this.workspaceLabelFromCwd(cwd);
|
|
@@ -25822,6 +26309,20 @@ class SessionService {
|
|
|
25822
26309
|
}
|
|
25823
26310
|
return this.toResolvedSession(session);
|
|
25824
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
|
+
}
|
|
25825
26326
|
async getPreferredSessionForTransport(transportSession) {
|
|
25826
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));
|
|
25827
26328
|
const expectedAlias = transportSession.split(":").at(-1);
|
|
@@ -25846,18 +26347,123 @@ class SessionService {
|
|
|
25846
26347
|
return null;
|
|
25847
26348
|
}
|
|
25848
26349
|
async useSession(chatKey, alias) {
|
|
25849
|
-
await this.mutate(async () => {
|
|
26350
|
+
return await this.mutate(async () => {
|
|
25850
26351
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
25851
26352
|
const internalAlias = resolveSessionAliasForInput(channelId, alias, Object.keys(this.state.sessions));
|
|
25852
26353
|
const session = this.state.sessions[internalAlias];
|
|
25853
26354
|
if (!session) {
|
|
25854
26355
|
throw new Error(`session "${alias}" does not exist`);
|
|
25855
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;
|
|
25856
26360
|
session.last_used_at = new Date().toISOString();
|
|
25857
|
-
this.state.chat_contexts[chatKey] = {
|
|
26361
|
+
this.state.chat_contexts[chatKey] = {
|
|
26362
|
+
current_session: internalAlias,
|
|
26363
|
+
...carriedPrevious ? { previous_session: carriedPrevious } : {}
|
|
26364
|
+
};
|
|
25858
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
|
+
};
|
|
25859
26372
|
});
|
|
25860
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
|
+
};
|
|
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
|
|
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" };
|
|
26466
|
+
}
|
|
25861
26467
|
async resolveAliasForChat(chatKey, displayAlias) {
|
|
25862
26468
|
const channelId = getChannelIdFromChatKey(chatKey);
|
|
25863
26469
|
const candidate = resolveSessionAliasForInput(channelId, displayAlias, Object.keys(this.state.sessions));
|
|
@@ -25958,6 +26564,10 @@ class SessionService {
|
|
|
25958
26564
|
for (const [chatKey, ctx] of Object.entries(this.state.chat_contexts)) {
|
|
25959
26565
|
if (ctx.current_session === alias) {
|
|
25960
26566
|
delete this.state.chat_contexts[chatKey];
|
|
26567
|
+
continue;
|
|
26568
|
+
}
|
|
26569
|
+
if (ctx.previous_session === alias) {
|
|
26570
|
+
delete ctx.previous_session;
|
|
25961
26571
|
}
|
|
25962
26572
|
}
|
|
25963
26573
|
await this.persist();
|
|
@@ -25989,9 +26599,11 @@ class SessionService {
|
|
|
25989
26599
|
}
|
|
25990
26600
|
const createdAt = Date.parse(cached2.created_at);
|
|
25991
26601
|
if (Number.isNaN(createdAt)) {
|
|
26602
|
+
await this.deleteNativeSessionListIfCurrent(chatKey, cached2);
|
|
25992
26603
|
return null;
|
|
25993
26604
|
}
|
|
25994
26605
|
if (this.now() - createdAt > ttlMs) {
|
|
26606
|
+
await this.deleteNativeSessionListIfCurrent(chatKey, cached2);
|
|
25995
26607
|
return null;
|
|
25996
26608
|
}
|
|
25997
26609
|
return {
|
|
@@ -26007,6 +26619,15 @@ class SessionService {
|
|
|
26007
26619
|
...cached2.next_cursor !== undefined ? { nextCursor: cached2.next_cursor } : {}
|
|
26008
26620
|
};
|
|
26009
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
|
+
}
|
|
26010
26631
|
toResolvedSession(session) {
|
|
26011
26632
|
const agentConfig = this.config.agents[session.agent];
|
|
26012
26633
|
if (!agentConfig) {
|
|
@@ -26106,6 +26727,29 @@ var init_session_service = __esm(() => {
|
|
|
26106
26727
|
init_channel_scope();
|
|
26107
26728
|
});
|
|
26108
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
|
+
|
|
26109
26753
|
// src/state/debounced-state-store.ts
|
|
26110
26754
|
class DebouncedStateStore {
|
|
26111
26755
|
deps;
|
|
@@ -26185,6 +26829,37 @@ class DebouncedStateStore {
|
|
|
26185
26829
|
}
|
|
26186
26830
|
}
|
|
26187
26831
|
|
|
26832
|
+
// src/commands/command-hints.ts
|
|
26833
|
+
function listWeacpxCommandHints() {
|
|
26834
|
+
const hints = [{ name: "/help", description: "查看命令帮助。" }];
|
|
26835
|
+
for (const topic of HELP_TOPICS) {
|
|
26836
|
+
const name = PRIMARY_COMMAND_BY_TOPIC[topic.topic];
|
|
26837
|
+
if (!name) {
|
|
26838
|
+
throw new Error(`command-hints: 未登记 help topic 的主命令: ${topic.topic}`);
|
|
26839
|
+
}
|
|
26840
|
+
hints.push({ name, description: topic.summary });
|
|
26841
|
+
}
|
|
26842
|
+
return hints;
|
|
26843
|
+
}
|
|
26844
|
+
var PRIMARY_COMMAND_BY_TOPIC;
|
|
26845
|
+
var init_command_hints = __esm(() => {
|
|
26846
|
+
init_help_registry();
|
|
26847
|
+
PRIMARY_COMMAND_BY_TOPIC = {
|
|
26848
|
+
session: "/session",
|
|
26849
|
+
native: "/ssn",
|
|
26850
|
+
workspace: "/workspace",
|
|
26851
|
+
agent: "/agent",
|
|
26852
|
+
permission: "/permission",
|
|
26853
|
+
config: "/config",
|
|
26854
|
+
orchestration: "/delegate",
|
|
26855
|
+
mode: "/mode",
|
|
26856
|
+
replymode: "/replymode",
|
|
26857
|
+
status: "/status",
|
|
26858
|
+
cancel: "/cancel",
|
|
26859
|
+
later: "/later"
|
|
26860
|
+
};
|
|
26861
|
+
});
|
|
26862
|
+
|
|
26188
26863
|
// src/run-console.ts
|
|
26189
26864
|
var exports_run_console = {};
|
|
26190
26865
|
__export(exports_run_console, {
|
|
@@ -26298,8 +26973,12 @@ async function runConsole(paths, deps) {
|
|
|
26298
26973
|
agent: runtime.agent,
|
|
26299
26974
|
abortSignal: shutdownController.signal,
|
|
26300
26975
|
quota: runtime.quota,
|
|
26976
|
+
sessions: runtime.sessions,
|
|
26977
|
+
activeTurns: runtime.activeTurns,
|
|
26301
26978
|
logger: runtime.logger,
|
|
26302
|
-
perfTracer: runtime.perfTracer
|
|
26979
|
+
perfTracer: runtime.perfTracer,
|
|
26980
|
+
commandHints: listWeacpxCommandHints(),
|
|
26981
|
+
coreVersion: WEACPX_CORE_VERSION
|
|
26303
26982
|
});
|
|
26304
26983
|
channelStartPromise.catch(() => {});
|
|
26305
26984
|
let channelStartSettled = false;
|
|
@@ -26417,6 +27096,8 @@ async function runCleanupSequence(input) {
|
|
|
26417
27096
|
}
|
|
26418
27097
|
var init_run_console = __esm(() => {
|
|
26419
27098
|
init_consumer_lock();
|
|
27099
|
+
init_command_hints();
|
|
27100
|
+
init_version();
|
|
26420
27101
|
});
|
|
26421
27102
|
|
|
26422
27103
|
// src/transport/acpx-bridge/acpx-bridge-protocol.ts
|
|
@@ -26447,7 +27128,7 @@ function encodeBridgeSessionNoteEvent(event) {
|
|
|
26447
27128
|
|
|
26448
27129
|
// src/transport/acpx-bridge/acpx-bridge-client.ts
|
|
26449
27130
|
import { spawn as spawn7 } from "node:child_process";
|
|
26450
|
-
import { fileURLToPath as
|
|
27131
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
26451
27132
|
import { createInterface } from "node:readline";
|
|
26452
27133
|
|
|
26453
27134
|
class AcpxBridgeClient {
|
|
@@ -26572,7 +27253,7 @@ function buildBridgeSpawnSpec(options) {
|
|
|
26572
27253
|
};
|
|
26573
27254
|
}
|
|
26574
27255
|
async function spawnAcpxBridgeClient(options = {}) {
|
|
26575
|
-
const bridgeEntryPath = options.bridgeEntryPath ??
|
|
27256
|
+
const bridgeEntryPath = options.bridgeEntryPath ?? fileURLToPath4(new URL("../../bridge/bridge-main.ts", import.meta.url));
|
|
26576
27257
|
const spawnSpec = buildBridgeSpawnSpec({
|
|
26577
27258
|
execPath: process.execPath,
|
|
26578
27259
|
bridgeEntryPath
|
|
@@ -27024,7 +27705,7 @@ var init_spawn_command = __esm(() => {
|
|
|
27024
27705
|
});
|
|
27025
27706
|
|
|
27026
27707
|
// src/transport/prompt-media.ts
|
|
27027
|
-
import { mkdtemp, open as open4, rm as
|
|
27708
|
+
import { mkdtemp, open as open4, rm as rm9, writeFile as writeFile7 } from "node:fs/promises";
|
|
27028
27709
|
import { tmpdir as defaultTmpdir } from "node:os";
|
|
27029
27710
|
import path14 from "node:path";
|
|
27030
27711
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -27149,7 +27830,7 @@ var init_prompt_media = __esm(() => {
|
|
|
27149
27830
|
readImageFile: readImageFileBounded,
|
|
27150
27831
|
mkdtemp,
|
|
27151
27832
|
writeFile: writeFile7,
|
|
27152
|
-
rm:
|
|
27833
|
+
rm: rm9,
|
|
27153
27834
|
tmpdir: defaultTmpdir
|
|
27154
27835
|
};
|
|
27155
27836
|
});
|
|
@@ -27448,12 +28129,12 @@ var init_streaming_prompt = __esm(() => {
|
|
|
27448
28129
|
|
|
27449
28130
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
27450
28131
|
import { chmod as chmodFs } from "node:fs/promises";
|
|
27451
|
-
import { dirname as
|
|
28132
|
+
import { dirname as dirname13, join as join15 } from "node:path";
|
|
27452
28133
|
function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
27453
28134
|
if (platform === "win32") {
|
|
27454
28135
|
return null;
|
|
27455
28136
|
}
|
|
27456
|
-
return
|
|
28137
|
+
return join15(dirname13(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
27457
28138
|
}
|
|
27458
28139
|
async function ensureNodePtyHelperExecutable(helperPath, chmod3 = chmodFs) {
|
|
27459
28140
|
if (!helperPath) {
|
|
@@ -27473,9 +28154,9 @@ var init_node_pty_helper = () => {};
|
|
|
27473
28154
|
// src/transport/acpx-queue-owner-launcher.ts
|
|
27474
28155
|
import { createHash as createHash3 } from "node:crypto";
|
|
27475
28156
|
import { spawn as spawn8 } from "node:child_process";
|
|
27476
|
-
import { readFile as
|
|
28157
|
+
import { readFile as readFile12, unlink } from "node:fs/promises";
|
|
27477
28158
|
import { homedir as homedir8 } from "node:os";
|
|
27478
|
-
import { join as
|
|
28159
|
+
import { join as join16 } from "node:path";
|
|
27479
28160
|
function buildWeacpxMcpServerSpec(input) {
|
|
27480
28161
|
const { command, args } = splitCommandLine(input.weacpxCommand);
|
|
27481
28162
|
return {
|
|
@@ -27526,7 +28207,13 @@ class AcpxQueueOwnerLauncher {
|
|
|
27526
28207
|
const key = input.acpxRecordId;
|
|
27527
28208
|
const previous = this.launchLocks.get(key) ?? Promise.resolve();
|
|
27528
28209
|
const next = previous.then(() => this.doLaunch(input), () => this.doLaunch(input));
|
|
27529
|
-
|
|
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
|
+
});
|
|
27530
28217
|
return next;
|
|
27531
28218
|
}
|
|
27532
28219
|
async doLaunch(input) {
|
|
@@ -27629,7 +28316,7 @@ async function terminateAcpxQueueOwner(sessionId) {
|
|
|
27629
28316
|
const lockPath = queueLockFilePath(sessionId);
|
|
27630
28317
|
let owner;
|
|
27631
28318
|
try {
|
|
27632
|
-
owner = JSON.parse(await
|
|
28319
|
+
owner = JSON.parse(await readFile12(lockPath, "utf8"));
|
|
27633
28320
|
} catch {
|
|
27634
28321
|
return;
|
|
27635
28322
|
}
|
|
@@ -27639,7 +28326,7 @@ async function terminateAcpxQueueOwner(sessionId) {
|
|
|
27639
28326
|
await unlink(lockPath).catch(() => {});
|
|
27640
28327
|
}
|
|
27641
28328
|
function queueLockFilePath(sessionId) {
|
|
27642
|
-
return
|
|
28329
|
+
return join16(homedir8(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
|
|
27643
28330
|
}
|
|
27644
28331
|
function shortHash(value, length) {
|
|
27645
28332
|
return createHash3("sha256").update(value).digest("hex").slice(0, length);
|
|
@@ -27657,7 +28344,7 @@ function resolveDefaultWeacpxCommand(env) {
|
|
|
27657
28344
|
return "weacpx";
|
|
27658
28345
|
}
|
|
27659
28346
|
function quoteCommandPart(value) {
|
|
27660
|
-
return
|
|
28347
|
+
return quoteIfNeeded(value);
|
|
27661
28348
|
}
|
|
27662
28349
|
var init_acpx_queue_owner_launcher = __esm(() => {
|
|
27663
28350
|
init_spawn_command();
|
|
@@ -27677,7 +28364,6 @@ function permissionModeToFlag(permissionMode) {
|
|
|
27677
28364
|
}
|
|
27678
28365
|
|
|
27679
28366
|
// src/transport/agent-session-list.ts
|
|
27680
|
-
import path15 from "node:path";
|
|
27681
28367
|
function isUnknownFilterCwdOption(output) {
|
|
27682
28368
|
return /(?:unknown|unrecognized) option/i.test(output) && output.includes("--filter-cwd");
|
|
27683
28369
|
}
|
|
@@ -27724,27 +28410,12 @@ function isAgentSessionListResult(value) {
|
|
|
27724
28410
|
function filterAgentSessionListByCwd(result, cwd) {
|
|
27725
28411
|
return {
|
|
27726
28412
|
...result,
|
|
27727
|
-
sessions: result.sessions.filter((session) => session.cwd &&
|
|
28413
|
+
sessions: result.sessions.filter((session) => session.cwd && isSamePath(session.cwd, cwd))
|
|
27728
28414
|
};
|
|
27729
28415
|
}
|
|
27730
|
-
|
|
27731
|
-
|
|
27732
|
-
|
|
27733
|
-
if (isWindowsLikePath2(normalizedLeft) || isWindowsLikePath2(normalizedRight)) {
|
|
27734
|
-
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
27735
|
-
}
|
|
27736
|
-
return normalizedLeft === normalizedRight;
|
|
27737
|
-
}
|
|
27738
|
-
function normalizeAgentSessionCwd(input) {
|
|
27739
|
-
if (isWindowsLikePath2(input)) {
|
|
27740
|
-
return path15.win32.normalize(input).replace(/\\/g, "/");
|
|
27741
|
-
}
|
|
27742
|
-
return path15.posix.normalize(input.replace(/\\/g, "/"));
|
|
27743
|
-
}
|
|
27744
|
-
function isWindowsLikePath2(input) {
|
|
27745
|
-
return /^[a-zA-Z]:[\\/]/.test(input) || input.startsWith("\\\\");
|
|
27746
|
-
}
|
|
27747
|
-
var init_agent_session_list = () => {};
|
|
28416
|
+
var init_agent_session_list = __esm(() => {
|
|
28417
|
+
init_path();
|
|
28418
|
+
});
|
|
27748
28419
|
|
|
27749
28420
|
// src/transport/acpx-cli/acpx-cli-transport.ts
|
|
27750
28421
|
import { createRequire as createRequire5 } from "node:module";
|
|
@@ -28263,6 +28934,30 @@ var init_acpx_cli_transport = __esm(() => {
|
|
|
28263
28934
|
require4 = createRequire5(import.meta.url);
|
|
28264
28935
|
});
|
|
28265
28936
|
|
|
28937
|
+
// src/util/async.ts
|
|
28938
|
+
function settleWithinTimeout(work, timeoutMs) {
|
|
28939
|
+
return new Promise((resolve3) => {
|
|
28940
|
+
let settled = false;
|
|
28941
|
+
const finish = () => {
|
|
28942
|
+
if (!settled) {
|
|
28943
|
+
settled = true;
|
|
28944
|
+
resolve3();
|
|
28945
|
+
}
|
|
28946
|
+
};
|
|
28947
|
+
const timer = setTimeout(finish, timeoutMs);
|
|
28948
|
+
if (typeof timer.unref === "function") {
|
|
28949
|
+
timer.unref();
|
|
28950
|
+
}
|
|
28951
|
+
work.then(() => {
|
|
28952
|
+
clearTimeout(timer);
|
|
28953
|
+
finish();
|
|
28954
|
+
}, () => {
|
|
28955
|
+
clearTimeout(timer);
|
|
28956
|
+
finish();
|
|
28957
|
+
});
|
|
28958
|
+
});
|
|
28959
|
+
}
|
|
28960
|
+
|
|
28266
28961
|
// src/transport/queue-owner-reaper.ts
|
|
28267
28962
|
import { spawn as spawn10 } from "node:child_process";
|
|
28268
28963
|
async function reapQueueOwners(acpxCommand, targets, deps = {}) {
|
|
@@ -28293,28 +28988,6 @@ async function reapQueueOwners(acpxCommand, targets, deps = {}) {
|
|
|
28293
28988
|
await settleWithinTimeout(Promise.all(unique.map(reapOne)), timeoutMs);
|
|
28294
28989
|
return { terminated, attempted: unique.length };
|
|
28295
28990
|
}
|
|
28296
|
-
function settleWithinTimeout(work, timeoutMs) {
|
|
28297
|
-
return new Promise((resolve3) => {
|
|
28298
|
-
let settled = false;
|
|
28299
|
-
const finish = () => {
|
|
28300
|
-
if (!settled) {
|
|
28301
|
-
settled = true;
|
|
28302
|
-
resolve3();
|
|
28303
|
-
}
|
|
28304
|
-
};
|
|
28305
|
-
const timer = setTimeout(finish, timeoutMs);
|
|
28306
|
-
if (typeof timer.unref === "function") {
|
|
28307
|
-
timer.unref();
|
|
28308
|
-
}
|
|
28309
|
-
work.then(() => {
|
|
28310
|
-
clearTimeout(timer);
|
|
28311
|
-
finish();
|
|
28312
|
-
}, () => {
|
|
28313
|
-
clearTimeout(timer);
|
|
28314
|
-
finish();
|
|
28315
|
-
});
|
|
28316
|
-
});
|
|
28317
|
-
}
|
|
28318
28991
|
async function defaultResolveRecordId(acpxCommand, target) {
|
|
28319
28992
|
const args = [
|
|
28320
28993
|
"--format",
|
|
@@ -28498,23 +29171,33 @@ var init_channel_registry = __esm(() => {
|
|
|
28498
29171
|
});
|
|
28499
29172
|
|
|
28500
29173
|
// src/weixin/messaging/quota-manager.ts
|
|
28501
|
-
function freshState() {
|
|
28502
|
-
return { midUsed: 0, finalUsed: 0, pendingFinalChunks: [] };
|
|
29174
|
+
function freshState(now) {
|
|
29175
|
+
return { midUsed: 0, finalUsed: 0, pendingFinalChunks: [], lastTouchedAt: now };
|
|
28503
29176
|
}
|
|
28504
29177
|
|
|
28505
29178
|
class QuotaManager {
|
|
28506
29179
|
states = new Map;
|
|
28507
29180
|
observer;
|
|
28508
29181
|
normalizeKey;
|
|
28509
|
-
|
|
29182
|
+
maxStates;
|
|
29183
|
+
stateTtlMs;
|
|
29184
|
+
maxPendingFinalChunksPerChat;
|
|
29185
|
+
now;
|
|
29186
|
+
constructor(observer, normalizeKey, options = {}) {
|
|
28510
29187
|
this.observer = observer;
|
|
28511
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());
|
|
28512
29193
|
}
|
|
28513
29194
|
onInbound(chatKey) {
|
|
28514
29195
|
const key = this.normalizeKey(chatKey);
|
|
29196
|
+
this.prune();
|
|
28515
29197
|
const existing = this.states.get(key);
|
|
28516
29198
|
const pending = existing?.pendingFinalChunks ?? [];
|
|
28517
|
-
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();
|
|
28518
29201
|
this.observer?.onInbound?.(key);
|
|
28519
29202
|
}
|
|
28520
29203
|
reserveMidSegment(chatKey) {
|
|
@@ -28547,36 +29230,48 @@ class QuotaManager {
|
|
|
28547
29230
|
return;
|
|
28548
29231
|
const state = this.getOrCreate(this.normalizeKey(chatKey));
|
|
28549
29232
|
state.pendingFinalChunks.push(...chunks);
|
|
29233
|
+
this.trimPendingFinalChunks(state, "front");
|
|
28550
29234
|
}
|
|
28551
29235
|
prependPendingFinal(chatKey, chunks) {
|
|
28552
29236
|
if (chunks.length === 0)
|
|
28553
29237
|
return;
|
|
28554
29238
|
const state = this.getOrCreate(this.normalizeKey(chatKey));
|
|
28555
29239
|
state.pendingFinalChunks.unshift(...chunks);
|
|
29240
|
+
this.trimPendingFinalChunks(state, "back");
|
|
28556
29241
|
}
|
|
28557
29242
|
drainPendingFinalUpToBudget(chatKey, available) {
|
|
28558
29243
|
if (available <= 0)
|
|
28559
29244
|
return [];
|
|
28560
|
-
const
|
|
29245
|
+
const key = this.normalizeKey(chatKey);
|
|
29246
|
+
const state = this.getOrCreate(key);
|
|
28561
29247
|
if (state.pendingFinalChunks.length === 0)
|
|
28562
29248
|
return [];
|
|
28563
|
-
|
|
29249
|
+
const drained = state.pendingFinalChunks.splice(0, available);
|
|
29250
|
+
state.lastTouchedAt = this.now();
|
|
29251
|
+
this.deleteIfEmpty(key, state);
|
|
29252
|
+
return drained;
|
|
28564
29253
|
}
|
|
28565
29254
|
hasPendingFinal(chatKey) {
|
|
29255
|
+
this.prune();
|
|
28566
29256
|
return (this.states.get(this.normalizeKey(chatKey))?.pendingFinalChunks.length ?? 0) > 0;
|
|
28567
29257
|
}
|
|
28568
29258
|
countPendingFinal(chatKey) {
|
|
29259
|
+
this.prune();
|
|
28569
29260
|
return this.states.get(this.normalizeKey(chatKey))?.pendingFinalChunks.length ?? 0;
|
|
28570
29261
|
}
|
|
28571
29262
|
clearPendingFinal(chatKey) {
|
|
28572
|
-
const
|
|
29263
|
+
const key = this.normalizeKey(chatKey);
|
|
29264
|
+
const state = this.states.get(key);
|
|
28573
29265
|
if (!state)
|
|
28574
29266
|
return;
|
|
28575
29267
|
state.pendingFinalChunks = [];
|
|
29268
|
+
state.lastTouchedAt = this.now();
|
|
29269
|
+
this.deleteIfEmpty(key, state);
|
|
28576
29270
|
}
|
|
28577
29271
|
snapshot(chatKey) {
|
|
28578
29272
|
const key = this.normalizeKey(chatKey);
|
|
28579
|
-
|
|
29273
|
+
this.prune();
|
|
29274
|
+
const state = this.states.get(key) ?? freshState(this.now());
|
|
28580
29275
|
const midRemaining = MID_BUDGET - state.midUsed;
|
|
28581
29276
|
const finalRemaining = FINAL_BUDGET - state.finalUsed;
|
|
28582
29277
|
return {
|
|
@@ -28588,15 +29283,72 @@ class QuotaManager {
|
|
|
28588
29283
|
};
|
|
28589
29284
|
}
|
|
28590
29285
|
getOrCreate(key) {
|
|
29286
|
+
this.prune();
|
|
28591
29287
|
let state = this.states.get(key);
|
|
28592
29288
|
if (!state) {
|
|
28593
|
-
state = freshState();
|
|
29289
|
+
state = freshState(this.now());
|
|
28594
29290
|
this.states.set(key, state);
|
|
29291
|
+
this.enforceMaxStates();
|
|
29292
|
+
} else {
|
|
29293
|
+
state.lastTouchedAt = this.now();
|
|
28595
29294
|
}
|
|
28596
29295
|
return state;
|
|
28597
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);
|
|
28598
29342
|
}
|
|
28599
|
-
|
|
29343
|
+
function normalizeNonNegativeMs3(value, fallback) {
|
|
29344
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
29345
|
+
return fallback;
|
|
29346
|
+
return value;
|
|
29347
|
+
}
|
|
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
|
+
});
|
|
28600
29352
|
|
|
28601
29353
|
// src/main.ts
|
|
28602
29354
|
var exports_main = {};
|
|
@@ -28609,8 +29361,8 @@ __export(exports_main, {
|
|
|
28609
29361
|
});
|
|
28610
29362
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
28611
29363
|
import { homedir as homedir9 } from "node:os";
|
|
28612
|
-
import { dirname as
|
|
28613
|
-
import { fileURLToPath as
|
|
29364
|
+
import { dirname as dirname14, join as join17 } from "node:path";
|
|
29365
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
28614
29366
|
function startProgressHeartbeat(orchestration, config2, logger2, channel) {
|
|
28615
29367
|
const thresholdSeconds = config2.orchestration.progressHeartbeatSeconds;
|
|
28616
29368
|
if (thresholdSeconds <= 0) {
|
|
@@ -28690,6 +29442,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
28690
29442
|
}
|
|
28691
29443
|
});
|
|
28692
29444
|
const sessions = new SessionService(config2, debouncedStateStore, state, { stateMutex });
|
|
29445
|
+
const activeTurns = createActiveTurnRegistry();
|
|
28693
29446
|
const scheduledService = new ScheduledTaskService(state, debouncedStateStore, { stateMutex });
|
|
28694
29447
|
const pendingWorkerDispatches = new Set;
|
|
28695
29448
|
const transport = config2.transport.type === "acpx-bridge" ? await (deps.createBridgeTransport?.() ?? Promise.resolve(new AcpxBridgeTransport(await spawnAcpxBridgeClient({
|
|
@@ -29060,7 +29813,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
29060
29813
|
listScheduledTasksFromRoute: async (input) => await listScheduledTasksFromRoute(input, { state, scheduled: scheduledService }),
|
|
29061
29814
|
cancelScheduledTaskFromRoute: async (input) => await cancelScheduledTaskFromRoute(input, { state, scheduled: scheduledService })
|
|
29062
29815
|
});
|
|
29063
|
-
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);
|
|
29064
29817
|
const agent = new ConsoleAgent(router, logger2);
|
|
29065
29818
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
29066
29819
|
dispatchTask: buildScheduledDispatchTask({
|
|
@@ -29081,6 +29834,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
29081
29834
|
agent,
|
|
29082
29835
|
router,
|
|
29083
29836
|
sessions,
|
|
29837
|
+
activeTurns,
|
|
29084
29838
|
stateStore,
|
|
29085
29839
|
configStore,
|
|
29086
29840
|
logger: logger2,
|
|
@@ -29186,8 +29940,8 @@ async function main() {
|
|
|
29186
29940
|
}
|
|
29187
29941
|
}
|
|
29188
29942
|
async function prepareChannelMedia(configPath, config2) {
|
|
29189
|
-
const runtimeDir =
|
|
29190
|
-
const mediaRootDir =
|
|
29943
|
+
const runtimeDir = join17(dirname14(configPath), "runtime");
|
|
29944
|
+
const mediaRootDir = join17(runtimeDir, "media");
|
|
29191
29945
|
const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
|
|
29192
29946
|
await mediaStore.cleanupExpired().catch((error2) => {
|
|
29193
29947
|
console.error("[weacpx] media cleanup failed:", error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -29200,30 +29954,30 @@ function resolveRuntimePaths() {
|
|
|
29200
29954
|
if (!home) {
|
|
29201
29955
|
throw new Error("Unable to resolve the current user home directory");
|
|
29202
29956
|
}
|
|
29203
|
-
const configPath = process.env.WEACPX_CONFIG ??
|
|
29204
|
-
const runtimeDir =
|
|
29957
|
+
const configPath = process.env.WEACPX_CONFIG ?? join17(coreHomeDir(home), "config.json");
|
|
29958
|
+
const runtimeDir = join17(dirname14(configPath), "runtime");
|
|
29205
29959
|
return {
|
|
29206
29960
|
configPath,
|
|
29207
|
-
statePath: process.env.WEACPX_STATE ??
|
|
29208
|
-
perfLogPath:
|
|
29961
|
+
statePath: process.env.WEACPX_STATE ?? join17(coreHomeDir(home), "state.json"),
|
|
29962
|
+
perfLogPath: join17(runtimeDir, "perf.log"),
|
|
29209
29963
|
orchestrationSocketPath: process.env.WEACPX_ORCHESTRATION_SOCKET ?? resolveDaemonOrchestrationSocketPath(runtimeDir)
|
|
29210
29964
|
};
|
|
29211
29965
|
}
|
|
29212
29966
|
function resolveBridgeEntryPath() {
|
|
29213
29967
|
if (import.meta.url.includes("/dist/")) {
|
|
29214
|
-
return
|
|
29968
|
+
return fileURLToPath5(new URL("./bridge/bridge-main.js", import.meta.url));
|
|
29215
29969
|
}
|
|
29216
|
-
return
|
|
29970
|
+
return fileURLToPath5(new URL("./bridge/bridge-main.ts", import.meta.url));
|
|
29217
29971
|
}
|
|
29218
29972
|
function resolveAppLogPath(configPath) {
|
|
29219
|
-
const rootDir =
|
|
29220
|
-
const runtimeDir =
|
|
29221
|
-
return
|
|
29973
|
+
const rootDir = dirname14(configPath);
|
|
29974
|
+
const runtimeDir = join17(rootDir, "runtime");
|
|
29975
|
+
return join17(runtimeDir, "app.log");
|
|
29222
29976
|
}
|
|
29223
29977
|
function resolvePerfLogPath(configPath) {
|
|
29224
|
-
const rootDir =
|
|
29225
|
-
const runtimeDir =
|
|
29226
|
-
return
|
|
29978
|
+
const rootDir = dirname14(configPath);
|
|
29979
|
+
const runtimeDir = join17(rootDir, "runtime");
|
|
29980
|
+
return join17(runtimeDir, "perf.log");
|
|
29227
29981
|
}
|
|
29228
29982
|
function resolveOrchestrationSocketPathFromConfigPath(configPath) {
|
|
29229
29983
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
@@ -29233,6 +29987,7 @@ function shouldNotifyTaskCompletion(task) {
|
|
|
29233
29987
|
return Boolean(task.chatKey && task.replyContextToken && (task.status === "completed" || task.status === "failed"));
|
|
29234
29988
|
}
|
|
29235
29989
|
var init_main = __esm(async () => {
|
|
29990
|
+
init_core_home();
|
|
29236
29991
|
init_command_router();
|
|
29237
29992
|
init_config_store();
|
|
29238
29993
|
init_ensure_config();
|
|
@@ -29262,6 +30017,7 @@ var init_main = __esm(async () => {
|
|
|
29262
30017
|
init_media_store();
|
|
29263
30018
|
init_quota_errors();
|
|
29264
30019
|
init_inbound();
|
|
30020
|
+
init_quota_manager();
|
|
29265
30021
|
init_perf_tracer();
|
|
29266
30022
|
init_bootstrap();
|
|
29267
30023
|
if (false) {}
|
|
@@ -29453,7 +30209,7 @@ var init_config_check = __esm(async () => {
|
|
|
29453
30209
|
});
|
|
29454
30210
|
|
|
29455
30211
|
// src/doctor/checks/daemon-check.ts
|
|
29456
|
-
import { fileURLToPath as
|
|
30212
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
29457
30213
|
import { homedir as homedir10 } from "node:os";
|
|
29458
30214
|
async function checkDaemon(options = {}) {
|
|
29459
30215
|
const home = options.home ?? process.env.HOME ?? homedir10();
|
|
@@ -29547,7 +30303,7 @@ function defaultIsProcessRunning5(pid) {
|
|
|
29547
30303
|
}
|
|
29548
30304
|
}
|
|
29549
30305
|
function resolveCliEntryPath() {
|
|
29550
|
-
return process.argv[1] ??
|
|
30306
|
+
return process.argv[1] ?? fileURLToPath6(import.meta.url);
|
|
29551
30307
|
}
|
|
29552
30308
|
function formatError5(error2) {
|
|
29553
30309
|
return error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -29609,7 +30365,7 @@ async function checkOrchestrationHealth(options) {
|
|
|
29609
30365
|
// src/doctor/checks/runtime-check.ts
|
|
29610
30366
|
import { constants } from "node:fs";
|
|
29611
30367
|
import { access as access4, stat as stat3 } from "node:fs/promises";
|
|
29612
|
-
import { dirname as
|
|
30368
|
+
import { dirname as dirname15 } from "node:path";
|
|
29613
30369
|
import { homedir as homedir11 } from "node:os";
|
|
29614
30370
|
async function checkRuntime(options = {}) {
|
|
29615
30371
|
const home = options.home ?? process.env.HOME ?? homedir11();
|
|
@@ -29651,107 +30407,107 @@ async function checkRuntime(options = {}) {
|
|
|
29651
30407
|
}
|
|
29652
30408
|
function createRuntimeFsProbe() {
|
|
29653
30409
|
return {
|
|
29654
|
-
stat: async (
|
|
29655
|
-
access: async (
|
|
30410
|
+
stat: async (path15) => await stat3(path15),
|
|
30411
|
+
access: async (path15, mode) => await access4(path15, mode)
|
|
29656
30412
|
};
|
|
29657
30413
|
}
|
|
29658
|
-
async function checkDirectoryCreatable(label,
|
|
30414
|
+
async function checkDirectoryCreatable(label, path15, probe, platform) {
|
|
29659
30415
|
try {
|
|
29660
|
-
const stats = await probe.stat(
|
|
30416
|
+
const stats = await probe.stat(path15);
|
|
29661
30417
|
if (!stats.isDirectory()) {
|
|
29662
30418
|
return {
|
|
29663
30419
|
ok: false,
|
|
29664
|
-
detail: `${label}: ${
|
|
30420
|
+
detail: `${label}: ${path15} (exists but is not a directory)`
|
|
29665
30421
|
};
|
|
29666
30422
|
}
|
|
29667
|
-
await probe.access(
|
|
30423
|
+
await probe.access(path15, directoryAccessMode(platform));
|
|
29668
30424
|
return {
|
|
29669
30425
|
ok: true,
|
|
29670
|
-
detail: `${label}: ${
|
|
30426
|
+
detail: `${label}: ${path15} (writable)`
|
|
29671
30427
|
};
|
|
29672
30428
|
} catch (error2) {
|
|
29673
30429
|
if (!isMissingPathError(error2)) {
|
|
29674
30430
|
return {
|
|
29675
30431
|
ok: false,
|
|
29676
|
-
detail: `${label}: ${
|
|
30432
|
+
detail: `${label}: ${path15} (unusable: ${formatError6(error2)})`
|
|
29677
30433
|
};
|
|
29678
30434
|
}
|
|
29679
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
30435
|
+
const parentCheck = await checkCreatableAncestorDirectory(path15, probe, platform);
|
|
29680
30436
|
if (!parentCheck.ok) {
|
|
29681
30437
|
return {
|
|
29682
30438
|
ok: false,
|
|
29683
|
-
detail: `${label}: ${
|
|
30439
|
+
detail: `${label}: ${path15} (parent not writable: ${parentCheck.blockingPath})`
|
|
29684
30440
|
};
|
|
29685
30441
|
}
|
|
29686
30442
|
return {
|
|
29687
30443
|
ok: true,
|
|
29688
|
-
detail: `${label}: ${
|
|
30444
|
+
detail: `${label}: ${path15} (creatable via ${parentCheck.creatableFrom})`
|
|
29689
30445
|
};
|
|
29690
30446
|
}
|
|
29691
30447
|
}
|
|
29692
|
-
async function checkFileCreatable(label,
|
|
30448
|
+
async function checkFileCreatable(label, path15, probe, platform) {
|
|
29693
30449
|
try {
|
|
29694
|
-
const stats = await probe.stat(
|
|
30450
|
+
const stats = await probe.stat(path15);
|
|
29695
30451
|
if (stats.isDirectory()) {
|
|
29696
30452
|
return {
|
|
29697
30453
|
ok: false,
|
|
29698
|
-
detail: `${label}: ${
|
|
30454
|
+
detail: `${label}: ${path15} (exists but is a directory)`
|
|
29699
30455
|
};
|
|
29700
30456
|
}
|
|
29701
|
-
await probe.access(
|
|
30457
|
+
await probe.access(path15, constants.W_OK);
|
|
29702
30458
|
return {
|
|
29703
30459
|
ok: true,
|
|
29704
|
-
detail: `${label}: ${
|
|
30460
|
+
detail: `${label}: ${path15} (writable)`
|
|
29705
30461
|
};
|
|
29706
30462
|
} catch (error2) {
|
|
29707
30463
|
if (!isMissingPathError(error2)) {
|
|
29708
30464
|
return {
|
|
29709
30465
|
ok: false,
|
|
29710
|
-
detail: `${label}: ${
|
|
30466
|
+
detail: `${label}: ${path15} (unusable: ${formatError6(error2)})`
|
|
29711
30467
|
};
|
|
29712
30468
|
}
|
|
29713
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
30469
|
+
const parentCheck = await checkCreatableAncestorDirectory(dirname15(path15), probe, platform);
|
|
29714
30470
|
if (!parentCheck.ok) {
|
|
29715
30471
|
return {
|
|
29716
30472
|
ok: false,
|
|
29717
|
-
detail: `${label}: ${
|
|
30473
|
+
detail: `${label}: ${path15} (parent not writable: ${parentCheck.blockingPath})`
|
|
29718
30474
|
};
|
|
29719
30475
|
}
|
|
29720
30476
|
return {
|
|
29721
30477
|
ok: true,
|
|
29722
|
-
detail: `${label}: ${
|
|
30478
|
+
detail: `${label}: ${path15} (creatable via ${parentCheck.creatableFrom})`
|
|
29723
30479
|
};
|
|
29724
30480
|
}
|
|
29725
30481
|
}
|
|
29726
|
-
async function checkCreatableAncestorDirectory(
|
|
30482
|
+
async function checkCreatableAncestorDirectory(path15, probe, platform) {
|
|
29727
30483
|
try {
|
|
29728
|
-
const stats = await probe.stat(
|
|
30484
|
+
const stats = await probe.stat(path15);
|
|
29729
30485
|
if (!stats.isDirectory()) {
|
|
29730
30486
|
return {
|
|
29731
30487
|
ok: false,
|
|
29732
|
-
creatableFrom:
|
|
29733
|
-
blockingPath:
|
|
30488
|
+
creatableFrom: path15,
|
|
30489
|
+
blockingPath: path15
|
|
29734
30490
|
};
|
|
29735
30491
|
}
|
|
29736
|
-
await probe.access(
|
|
30492
|
+
await probe.access(path15, directoryAccessMode(platform));
|
|
29737
30493
|
return {
|
|
29738
30494
|
ok: true,
|
|
29739
|
-
creatableFrom:
|
|
30495
|
+
creatableFrom: path15
|
|
29740
30496
|
};
|
|
29741
30497
|
} catch (error2) {
|
|
29742
30498
|
if (!isMissingPathError(error2)) {
|
|
29743
30499
|
return {
|
|
29744
30500
|
ok: false,
|
|
29745
|
-
creatableFrom:
|
|
29746
|
-
blockingPath:
|
|
30501
|
+
creatableFrom: path15,
|
|
30502
|
+
blockingPath: path15
|
|
29747
30503
|
};
|
|
29748
30504
|
}
|
|
29749
|
-
const parent =
|
|
29750
|
-
if (parent ===
|
|
30505
|
+
const parent = dirname15(path15);
|
|
30506
|
+
if (parent === path15) {
|
|
29751
30507
|
return {
|
|
29752
30508
|
ok: false,
|
|
29753
|
-
creatableFrom:
|
|
29754
|
-
blockingPath:
|
|
30509
|
+
creatableFrom: path15,
|
|
30510
|
+
blockingPath: path15
|
|
29755
30511
|
};
|
|
29756
30512
|
}
|
|
29757
30513
|
const parentCheck = await checkCreatableAncestorDirectory(parent, probe, platform);
|
|
@@ -30169,7 +30925,7 @@ var init_render_doctor = __esm(() => {
|
|
|
30169
30925
|
|
|
30170
30926
|
// src/doctor/doctor.ts
|
|
30171
30927
|
import { homedir as homedir12 } from "node:os";
|
|
30172
|
-
import { join as
|
|
30928
|
+
import { join as join18 } from "node:path";
|
|
30173
30929
|
async function runDoctor(options = {}, deps = {}) {
|
|
30174
30930
|
const home = deps.home ?? process.env.HOME ?? homedir12();
|
|
30175
30931
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
@@ -30224,8 +30980,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
|
|
|
30224
30980
|
return resolveRuntimePaths();
|
|
30225
30981
|
}
|
|
30226
30982
|
return {
|
|
30227
|
-
configPath:
|
|
30228
|
-
statePath:
|
|
30983
|
+
configPath: join18(coreHomeDir(home), "config.json"),
|
|
30984
|
+
statePath: join18(coreHomeDir(home), "state.json")
|
|
30229
30985
|
};
|
|
30230
30986
|
}
|
|
30231
30987
|
function depsUseExplicitRuntimeOverrides() {
|
|
@@ -30290,6 +31046,7 @@ function formatError9(error2) {
|
|
|
30290
31046
|
return error2 instanceof Error ? error2.message : String(error2);
|
|
30291
31047
|
}
|
|
30292
31048
|
var init_doctor = __esm(async () => {
|
|
31049
|
+
init_core_home();
|
|
30293
31050
|
init_load_config();
|
|
30294
31051
|
init_state_store();
|
|
30295
31052
|
init_daemon_check();
|
|
@@ -30323,6 +31080,7 @@ var init_doctor2 = __esm(async () => {
|
|
|
30323
31080
|
});
|
|
30324
31081
|
|
|
30325
31082
|
// src/cli.ts
|
|
31083
|
+
init_core_home();
|
|
30326
31084
|
init_config_store();
|
|
30327
31085
|
init_load_config();
|
|
30328
31086
|
init_ensure_config();
|
|
@@ -30331,8 +31089,8 @@ init_create_daemon_controller();
|
|
|
30331
31089
|
init_daemon_files();
|
|
30332
31090
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
30333
31091
|
import { homedir as homedir13 } from "node:os";
|
|
30334
|
-
import { dirname as
|
|
30335
|
-
import { fileURLToPath as
|
|
31092
|
+
import { dirname as dirname16, join as join19, sep } from "node:path";
|
|
31093
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
30336
31094
|
|
|
30337
31095
|
// src/daemon/daemon-runtime.ts
|
|
30338
31096
|
init_daemon_status();
|
|
@@ -42745,15 +43503,17 @@ class StdioServerTransport {
|
|
|
42745
43503
|
init_version();
|
|
42746
43504
|
|
|
42747
43505
|
// src/mcp/resolve-endpoint.ts
|
|
43506
|
+
init_core_home();
|
|
42748
43507
|
init_daemon_files();
|
|
42749
43508
|
init_orchestration_ipc();
|
|
42750
43509
|
import { homedir as homedir2 } from "node:os";
|
|
43510
|
+
import { join as join4 } from "node:path";
|
|
42751
43511
|
function resolveDefaultOrchestrationEndpoint(env = process.env, platform = process.platform) {
|
|
42752
43512
|
if (typeof env.WEACPX_ORCHESTRATION_SOCKET === "string" && env.WEACPX_ORCHESTRATION_SOCKET.trim().length > 0) {
|
|
42753
43513
|
return createOrchestrationEndpoint(env.WEACPX_ORCHESTRATION_SOCKET.trim(), platform);
|
|
42754
43514
|
}
|
|
42755
43515
|
const home = requireHome(env);
|
|
42756
|
-
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");
|
|
42757
43517
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
42758
43518
|
return resolveOrchestrationEndpoint(runtimeDir, platform);
|
|
42759
43519
|
}
|
|
@@ -42853,16 +43613,15 @@ function buildWeacpxMcpToolRegistry(input) {
|
|
|
42853
43613
|
},
|
|
42854
43614
|
{
|
|
42855
43615
|
name: "task_get",
|
|
42856
|
-
description: "Fetch a single task under the current coordinator,
|
|
43616
|
+
description: "Fetch a single task under the current coordinator: its summary, latest progress, and — once terminal — the worker's final result. Prefer task_watch to follow a task; its terminal result already carries the output, so a follow-up task_get is unnecessary. Reach for task_get to recover a task you lost track of, to inspect one that requires attention, or to re-read the original delegated prompt. The full prompt is included only for needs_confirmation tasks unless you pass includePrompt:true.",
|
|
42857
43617
|
inputSchema: exports_external.object({
|
|
42858
|
-
taskId: exports_external.string().min(1)
|
|
43618
|
+
taskId: exports_external.string().min(1),
|
|
43619
|
+
includePrompt: exports_external.boolean().optional()
|
|
42859
43620
|
}).strict(),
|
|
42860
43621
|
handler: async (args) => await asToolResult(async () => {
|
|
42861
|
-
const
|
|
42862
|
-
|
|
42863
|
-
|
|
42864
|
-
});
|
|
42865
|
-
return createSuccessResult(task ? renderTaskSummary(task) : "Task not found.", { task });
|
|
43622
|
+
const { taskId, includePrompt } = args;
|
|
43623
|
+
const task = await transport.getTask({ coordinatorSession, taskId });
|
|
43624
|
+
return createSuccessResult(task ? renderTaskSummary(task, { includePrompt: includePrompt ?? false }) : "Task not found.", { task });
|
|
42866
43625
|
})
|
|
42867
43626
|
},
|
|
42868
43627
|
{
|
|
@@ -43101,12 +43860,23 @@ function renderTaskWatchResult(result) {
|
|
|
43101
43860
|
const events = result.events.length > 0 ? [
|
|
43102
43861
|
"- Events:",
|
|
43103
43862
|
...result.events.map((event) => {
|
|
43104
|
-
const
|
|
43105
|
-
return ` - #${event.seq} ${event.type} at ${event.at}${
|
|
43863
|
+
const detail2 = event.summary ?? event.message ?? event.status ?? "";
|
|
43864
|
+
return ` - #${event.seq} ${event.type} at ${event.at}${detail2 ? `: ${detail2}` : ""}`;
|
|
43106
43865
|
})
|
|
43107
43866
|
] : ["- Events: none"];
|
|
43108
|
-
const
|
|
43109
|
-
|
|
43867
|
+
const detail = [];
|
|
43868
|
+
if (result.status === "terminal") {
|
|
43869
|
+
const resultText = result.task.resultText?.trim() ?? "";
|
|
43870
|
+
const summary = result.task.summary?.trim() ?? "";
|
|
43871
|
+
if (resultText.length > 0)
|
|
43872
|
+
detail.push(`- Result: ${resultText}`);
|
|
43873
|
+
else if (summary.length > 0)
|
|
43874
|
+
detail.push(`- Summary: ${summary}`);
|
|
43875
|
+
} else if (result.status === "attention_required" && result.task.openQuestion) {
|
|
43876
|
+
detail.push(`- Open question: ${result.task.openQuestion.question}`);
|
|
43877
|
+
}
|
|
43878
|
+
const next = result.status === "terminal" ? "Next: summarize this result for the user." : result.status === "attention_required" ? "Next: resolve the pending question / contested review with the recommended action tool (coordinator_answer_question or coordinator_review_contested_result); call task_get only if you need more detail." : `Next: call task_watch again with afterSeq=${result.nextAfterSeq} to keep watching, preferably as an MCP task if your client supports background task execution.`;
|
|
43879
|
+
return [...header, ...events, ...detail, next].join(`
|
|
43110
43880
|
`);
|
|
43111
43881
|
}
|
|
43112
43882
|
function createSuccessResult(text, structuredContent) {
|
|
@@ -43122,7 +43892,7 @@ function createErrorResult(message) {
|
|
|
43122
43892
|
};
|
|
43123
43893
|
}
|
|
43124
43894
|
function renderDelegateSuccess(result) {
|
|
43125
|
-
const next = result.status === "needs_confirmation" ? `Next: this delegation requires user approval. Tell the user, then call task_approve or task_cancel based on their response.` : result.status === "queued" ? `Next: task "${result.taskId}" is queued (agent at parallel capacity). It will start automatically when a slot frees. Call task_watch to long-poll
|
|
43895
|
+
const next = result.status === "needs_confirmation" ? `Next: this delegation requires user approval. Tell the user, then call task_approve or task_cancel based on their response.` : result.status === "queued" ? `Next: task "${result.taskId}" is queued (agent at parallel capacity). It will start automatically when a slot frees. Call task_watch to long-poll until it runs and then finishes — the terminal watch carries the result. Use task_list only to resurvey in-flight tasks.` : `Next: task "${result.taskId}" is running. Return this taskId to the user, then call task_watch to long-poll until it finishes — the terminal watch carries the worker's result, so no follow-up task_get is needed. Use task_list only to resurvey in-flight tasks.`;
|
|
43126
43896
|
return [`Delegation task "${result.taskId}" created.`, `- Status: ${result.status}`, next].join(`
|
|
43127
43897
|
`);
|
|
43128
43898
|
}
|
|
@@ -43166,7 +43936,7 @@ function renderTaskListItem(task) {
|
|
|
43166
43936
|
].filter(Boolean).map((item) => `; ${item}`).join("");
|
|
43167
43937
|
return `- ${task.taskId} [${task.status}] ${task.targetAgent}${role} -> ${task.workerSession ?? "unassigned"}${group}${source}${summary}${reliability}`;
|
|
43168
43938
|
}
|
|
43169
|
-
function renderTaskSummary(task) {
|
|
43939
|
+
function renderTaskSummary(task, options = {}) {
|
|
43170
43940
|
const header = [
|
|
43171
43941
|
`Task "${task.taskId}"`,
|
|
43172
43942
|
`- Status: ${task.status}`,
|
|
@@ -43181,7 +43951,9 @@ function renderTaskSummary(task) {
|
|
|
43181
43951
|
if (task.status === "needs_confirmation") {
|
|
43182
43952
|
header.push(`- Source: ${task.sourceKind} / ${task.sourceHandle}${task.role ? ` / ${task.role}` : ""}`);
|
|
43183
43953
|
}
|
|
43184
|
-
|
|
43954
|
+
if (task.status === "needs_confirmation" || options.includePrompt) {
|
|
43955
|
+
header.push(`- Task: ${task.task}`);
|
|
43956
|
+
}
|
|
43185
43957
|
if (task.summary.trim().length > 0)
|
|
43186
43958
|
header.push(`- Summary: ${task.summary}`);
|
|
43187
43959
|
if (task.lastProgressSummary)
|
|
@@ -43232,7 +44004,7 @@ function renderTaskApprovalSuccess(task) {
|
|
|
43232
44004
|
return [
|
|
43233
44005
|
`Task "${task.taskId}" approved.`,
|
|
43234
44006
|
`- Current status: ${task.status}`,
|
|
43235
|
-
`Next:
|
|
44007
|
+
`Next: call task_watch to long-poll until the worker finishes — the terminal watch returns the result directly, so no follow-up task_get is needed. Use task_list only to resurvey in-flight tasks.`
|
|
43236
44008
|
].join(`
|
|
43237
44009
|
`);
|
|
43238
44010
|
}
|
|
@@ -43730,13 +44502,24 @@ function renderWatchMcpTaskResult(result, watchTaskId) {
|
|
|
43730
44502
|
const events = result.events.length > 0 ? [
|
|
43731
44503
|
"Events:",
|
|
43732
44504
|
...result.events.map((event) => {
|
|
43733
|
-
const
|
|
43734
|
-
return `- #${event.seq} ${event.type} at ${event.at}${
|
|
44505
|
+
const detail2 = event.summary ?? event.message ?? event.status ?? "";
|
|
44506
|
+
return `- #${event.seq} ${event.type} at ${event.at}${detail2 ? `: ${detail2}` : ""}`;
|
|
43735
44507
|
})
|
|
43736
44508
|
] : ["Events: none"];
|
|
43737
|
-
const
|
|
44509
|
+
const detail = [];
|
|
44510
|
+
if (result.status === "terminal") {
|
|
44511
|
+
const resultText = result.task.resultText.trim();
|
|
44512
|
+
const summary = result.task.summary.trim();
|
|
44513
|
+
if (resultText.length > 0)
|
|
44514
|
+
detail.push(`Result: ${resultText}`);
|
|
44515
|
+
else if (summary.length > 0)
|
|
44516
|
+
detail.push(`Summary: ${summary}`);
|
|
44517
|
+
} else if (result.status === "attention_required" && result.task.openQuestion) {
|
|
44518
|
+
detail.push(`Open question: ${result.task.openQuestion.question}`);
|
|
44519
|
+
}
|
|
44520
|
+
const next = result.status === "terminal" ? "Next: summarize this result for the user." : result.status === "attention_required" ? "Next: resolve the pending question / contested review with the recommended action tool (coordinator_answer_question or coordinator_review_contested_result)." : `Next: call task_watch again with afterSeq=${result.nextAfterSeq} to keep watching.`;
|
|
43738
44521
|
return {
|
|
43739
|
-
content: [{ type: "text", text: [...header, ...events, next].join(`
|
|
44522
|
+
content: [{ type: "text", text: [...header, ...events, ...detail, next].join(`
|
|
43740
44523
|
`) }],
|
|
43741
44524
|
structuredContent: { watchTaskId, ...result }
|
|
43742
44525
|
};
|
|
@@ -44096,8 +44879,14 @@ function inferExternalCoordinatorSession(input) {
|
|
|
44096
44879
|
return `external_${sanitizeMcpClientName(input.clientName)}:${suffix}`;
|
|
44097
44880
|
}
|
|
44098
44881
|
function sanitizeMcpClientName(input) {
|
|
44099
|
-
|
|
44100
|
-
|
|
44882
|
+
return sanitizeString((input ?? "").trim(), {
|
|
44883
|
+
allow: /[a-z0-9._-]/,
|
|
44884
|
+
replacement: "-",
|
|
44885
|
+
lowercase: true,
|
|
44886
|
+
collapse: true,
|
|
44887
|
+
trim: true,
|
|
44888
|
+
fallback: "mcp-host"
|
|
44889
|
+
});
|
|
44101
44890
|
}
|
|
44102
44891
|
|
|
44103
44892
|
// src/mcp/parse-string-flag.ts
|
|
@@ -44222,12 +45011,15 @@ function resolveTemplateChoice(answer, names) {
|
|
|
44222
45011
|
// src/cli-update.ts
|
|
44223
45012
|
init_plugin_home();
|
|
44224
45013
|
import { spawn as spawn4 } from "node:child_process";
|
|
44225
|
-
import { readFile as
|
|
44226
|
-
import { dirname as
|
|
44227
|
-
import { fileURLToPath as
|
|
45014
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
45015
|
+
import { dirname as dirname10, join as join10 } from "node:path";
|
|
45016
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
44228
45017
|
|
|
44229
45018
|
// src/plugins/package-manager.ts
|
|
45019
|
+
init_plugin_home();
|
|
44230
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";
|
|
44231
45023
|
async function defaultRunCommand(command, args, options) {
|
|
44232
45024
|
await new Promise((resolve, reject) => {
|
|
44233
45025
|
const child = spawn3(command, args, { cwd: options.cwd, stdio: "inherit" });
|
|
@@ -44267,6 +45059,10 @@ async function detectPackageManager(runCommand) {
|
|
|
44267
45059
|
async function installPluginPackage(input) {
|
|
44268
45060
|
const runCommand = input.runCommand ?? defaultRunCommand;
|
|
44269
45061
|
const packageManager = input.packageManager ?? await detectPackageManager();
|
|
45062
|
+
await normalizePluginHomeManifest(input.pluginHome);
|
|
45063
|
+
if (packageManager === "bun") {
|
|
45064
|
+
await rm4(join6(input.pluginHome, "bun.lock"), { force: true }).catch(() => {});
|
|
45065
|
+
}
|
|
44270
45066
|
const spec = input.version ? `${input.packageName}@${input.version}` : input.packageName;
|
|
44271
45067
|
if (packageManager === "bun") {
|
|
44272
45068
|
await runCommand("bun", ["add", spec], { cwd: input.pluginHome });
|
|
@@ -44290,6 +45086,7 @@ async function removePluginPackage(input) {
|
|
|
44290
45086
|
// src/cli-update.ts
|
|
44291
45087
|
init_plugin_loader();
|
|
44292
45088
|
init_validate_plugin();
|
|
45089
|
+
var SUCCESSOR = { from: "weacpx", package: "xacpx", minVersion: "0.8.0" };
|
|
44293
45090
|
async function handleUpdateCli(args, deps) {
|
|
44294
45091
|
let all = false;
|
|
44295
45092
|
const explicitTargets = [];
|
|
@@ -44306,14 +45103,20 @@ async function handleUpdateCli(args, deps) {
|
|
|
44306
45103
|
const config2 = await deps.loadConfig();
|
|
44307
45104
|
const packageName = deps.packageName ?? await readPackageName();
|
|
44308
45105
|
const latestOf = deps.getLatestVersion ?? getLatestNpmVersion;
|
|
44309
|
-
const
|
|
44310
|
-
|
|
44311
|
-
|
|
44312
|
-
|
|
44313
|
-
|
|
44314
|
-
|
|
44315
|
-
|
|
44316
|
-
|
|
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];
|
|
44317
45120
|
for (const plugin of config2.plugins ?? []) {
|
|
44318
45121
|
targets.push({
|
|
44319
45122
|
kind: "plugin",
|
|
@@ -44333,7 +45136,7 @@ async function handleUpdateCli(args, deps) {
|
|
|
44333
45136
|
deps.print(`以下项目无法检查最新版本,已取消更新:${unavailable.map((target) => target.name).join(", ")}`);
|
|
44334
45137
|
return 1;
|
|
44335
45138
|
}
|
|
44336
|
-
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));
|
|
44337
45140
|
const selected = await selectTargets(targets, candidates, { all, explicitTarget: explicitTargets[0], deps });
|
|
44338
45141
|
if (!selected.ok) {
|
|
44339
45142
|
deps.print(selected.message);
|
|
@@ -44344,6 +45147,8 @@ async function handleUpdateCli(args, deps) {
|
|
|
44344
45147
|
return 0;
|
|
44345
45148
|
}
|
|
44346
45149
|
const selfUpdater = deps.updateSelf ?? defaultUpdateSelf;
|
|
45150
|
+
const selfMigrator = deps.migrateSelf ?? defaultMigrateSelf;
|
|
45151
|
+
const stopDaemon = deps.stopDaemon ?? (async () => {});
|
|
44347
45152
|
const pluginHome = deps.pluginHome ?? resolvePluginHome();
|
|
44348
45153
|
const pluginUpdater = deps.updatePlugin ?? (async (input) => {
|
|
44349
45154
|
await ensurePluginHome(pluginHome);
|
|
@@ -44354,17 +45159,25 @@ async function handleUpdateCli(args, deps) {
|
|
|
44354
45159
|
for (const target of selected.targets) {
|
|
44355
45160
|
try {
|
|
44356
45161
|
if (target.kind === "self") {
|
|
45162
|
+
const successorPackage = target.successorPackage;
|
|
44357
45163
|
if (!all && !explicitTargets[0]) {
|
|
44358
45164
|
if (!deps.isInteractive()) {
|
|
44359
|
-
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`。");
|
|
44360
45166
|
return 1;
|
|
44361
45167
|
}
|
|
44362
|
-
const
|
|
45168
|
+
const question = successorPackage ? `weacpx 已更名为 ${successorPackage},确认迁移到 ${successorPackage}?[y/N] ` : "确认更新 weacpx 本体?[y/N] ";
|
|
45169
|
+
const answer = (await deps.promptText(question)).trim().toLowerCase();
|
|
44363
45170
|
if (answer !== "y" && answer !== "yes") {
|
|
44364
|
-
deps.print("已取消更新 weacpx 本体。");
|
|
45171
|
+
deps.print(successorPackage ? `已取消迁移到 ${successorPackage}。` : "已取消更新 weacpx 本体。");
|
|
44365
45172
|
continue;
|
|
44366
45173
|
}
|
|
44367
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
|
+
}
|
|
44368
45181
|
await selfUpdater(target.name);
|
|
44369
45182
|
deps.print(`weacpx 已更新:${target.latestVersion ?? "latest"}`);
|
|
44370
45183
|
continue;
|
|
@@ -44404,19 +45217,21 @@ async function handleUpdateCli(args, deps) {
|
|
|
44404
45217
|
function formatTarget(target) {
|
|
44405
45218
|
const current = target.currentVersion ?? "未锁定";
|
|
44406
45219
|
const latest = target.latestVersion ?? "无法检查";
|
|
44407
|
-
|
|
44408
|
-
|
|
45220
|
+
if (target.kind === "self") {
|
|
45221
|
+
return target.successorPackage ? `weacpx → ${target.successorPackage} (${current} -> ${latest},改名)` : `weacpx (${current} -> ${latest})`;
|
|
45222
|
+
}
|
|
45223
|
+
return `插件 ${target.name} (${current} -> ${latest})`;
|
|
44409
45224
|
}
|
|
44410
45225
|
async function selectTargets(targets, candidates, input) {
|
|
44411
45226
|
if (input.explicitTarget) {
|
|
44412
|
-
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));
|
|
44413
45228
|
if (!target)
|
|
44414
45229
|
return { ok: false, message: `没有找到更新项:${input.explicitTarget}`, exitCode: 1 };
|
|
44415
45230
|
if (!target.latestVersion)
|
|
44416
45231
|
return { ok: false, message: `${target.name} 无法检查最新版本,已跳过。`, exitCode: 1 };
|
|
44417
45232
|
if (target.kind === "plugin" && !target.pinned)
|
|
44418
45233
|
return { ok: false, message: `${target.name} 未记录当前版本;请先使用 \`weacpx plugin update ${target.name}\` 或显式选择版本。`, exitCode: 1 };
|
|
44419
|
-
if (target.currentVersion === target.latestVersion)
|
|
45234
|
+
if (!target.successorPackage && target.currentVersion === target.latestVersion)
|
|
44420
45235
|
return { ok: true, targets: [] };
|
|
44421
45236
|
return { ok: true, targets: [target] };
|
|
44422
45237
|
}
|
|
@@ -44441,7 +45256,7 @@ async function selectTargets(targets, candidates, input) {
|
|
|
44441
45256
|
return { ok: false, message: `${target.name} 无法检查最新版本,已跳过。`, exitCode: 1 };
|
|
44442
45257
|
if (target.kind === "plugin" && !target.pinned)
|
|
44443
45258
|
return { ok: false, message: `${target.name} 未记录当前版本;请先使用 \`weacpx plugin update ${target.name}\` 或显式选择版本。`, exitCode: 1 };
|
|
44444
|
-
if (target.currentVersion === target.latestVersion)
|
|
45259
|
+
if (!target.successorPackage && target.currentVersion === target.latestVersion)
|
|
44445
45260
|
continue;
|
|
44446
45261
|
if (!selected.includes(target))
|
|
44447
45262
|
selected.push(target);
|
|
@@ -44470,6 +45285,45 @@ async function defaultUpdateSelf(packageName) {
|
|
|
44470
45285
|
}
|
|
44471
45286
|
await runInherit("npm", ["install", "-g", packageName]);
|
|
44472
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
|
+
}
|
|
44473
45327
|
async function runCapture(command, args) {
|
|
44474
45328
|
return await new Promise((resolve, reject) => {
|
|
44475
45329
|
const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -44501,10 +45355,10 @@ async function runInherit(command, args) {
|
|
|
44501
45355
|
}
|
|
44502
45356
|
async function readPackageName() {
|
|
44503
45357
|
try {
|
|
44504
|
-
const here =
|
|
44505
|
-
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")]) {
|
|
44506
45360
|
try {
|
|
44507
|
-
const parsed = JSON.parse(await
|
|
45361
|
+
const parsed = JSON.parse(await readFile8(candidate, "utf8"));
|
|
44508
45362
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
44509
45363
|
return parsed.name.trim();
|
|
44510
45364
|
} catch {}
|
|
@@ -44521,6 +45375,7 @@ async function validatePluginDefault(packageName, pluginHome) {
|
|
|
44521
45375
|
init_version();
|
|
44522
45376
|
|
|
44523
45377
|
// src/channels/cli/channel-cli.ts
|
|
45378
|
+
init_core_home();
|
|
44524
45379
|
init_registry();
|
|
44525
45380
|
init_create_channel();
|
|
44526
45381
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -44815,7 +45670,7 @@ async function runRestart(deps) {
|
|
|
44815
45670
|
} catch (error2) {
|
|
44816
45671
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
44817
45672
|
deps.print(`配置已保存,但重启失败:${message}`);
|
|
44818
|
-
deps.print("
|
|
45673
|
+
deps.print(`请查看日志:${coreHomeDisplayPath("runtime", "stderr.log")}`);
|
|
44819
45674
|
deps.print("也可以稍后执行:weacpx start");
|
|
44820
45675
|
return 1;
|
|
44821
45676
|
}
|
|
@@ -45126,9 +45981,10 @@ async function setChannelAccountEnabled(type, accountId, enabled, rawArgs, deps)
|
|
|
45126
45981
|
}
|
|
45127
45982
|
|
|
45128
45983
|
// src/plugins/plugin-cli.ts
|
|
45984
|
+
init_core_home();
|
|
45129
45985
|
init_plugin_home();
|
|
45130
|
-
import { readFile as
|
|
45131
|
-
import { isAbsolute, join as
|
|
45986
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
45987
|
+
import { isAbsolute, join as join12, resolve } from "node:path";
|
|
45132
45988
|
init_plugin_loader();
|
|
45133
45989
|
init_validate_plugin();
|
|
45134
45990
|
|
|
@@ -45137,14 +45993,14 @@ init_channel_scope();
|
|
|
45137
45993
|
init_plugin_loader();
|
|
45138
45994
|
init_validate_plugin();
|
|
45139
45995
|
init_known_plugins();
|
|
45140
|
-
import { readFile as
|
|
45141
|
-
import { join as
|
|
45996
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
45997
|
+
import { join as join11 } from "node:path";
|
|
45142
45998
|
function suggestedPluginPackageForChannel(type) {
|
|
45143
45999
|
return findKnownPluginByChannel(type)?.packageName ?? `<npm-package-that-provides-${type}>`;
|
|
45144
46000
|
}
|
|
45145
46001
|
async function readDependencyEntries(pluginHome) {
|
|
45146
46002
|
try {
|
|
45147
|
-
const raw = await
|
|
46003
|
+
const raw = await readFile9(join11(pluginHome, "package.json"), "utf8");
|
|
45148
46004
|
const parsed = JSON.parse(raw);
|
|
45149
46005
|
const out = {};
|
|
45150
46006
|
for (const [name, value] of Object.entries(parsed.dependencies ?? {})) {
|
|
@@ -45242,11 +46098,11 @@ async function inspectPlugins(input) {
|
|
|
45242
46098
|
// src/plugins/plugin-cli.ts
|
|
45243
46099
|
init_known_plugins();
|
|
45244
46100
|
function looksLikePath(spec) {
|
|
45245
|
-
return spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/") ||
|
|
46101
|
+
return spec === "." || spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/") || spec.startsWith(".\\") || spec.startsWith("..\\") || spec.startsWith("\\") || /^[a-zA-Z]:[\\/]/.test(spec) || isAbsolute(spec);
|
|
45246
46102
|
}
|
|
45247
46103
|
async function readDependencyEntries2(pluginHome) {
|
|
45248
46104
|
try {
|
|
45249
|
-
const raw = await
|
|
46105
|
+
const raw = await readFile10(join12(pluginHome, "package.json"), "utf8");
|
|
45250
46106
|
const parsed = JSON.parse(raw);
|
|
45251
46107
|
const out = {};
|
|
45252
46108
|
for (const [name, value] of Object.entries(parsed.dependencies ?? {})) {
|
|
@@ -45272,7 +46128,7 @@ async function resolveLocalPluginName(installSpec, pluginHome, namesBeforeInstal
|
|
|
45272
46128
|
return name;
|
|
45273
46129
|
}
|
|
45274
46130
|
try {
|
|
45275
|
-
const raw = await
|
|
46131
|
+
const raw = await readFile10(join12(installSpec, "package.json"), "utf8");
|
|
45276
46132
|
const parsed = JSON.parse(raw);
|
|
45277
46133
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
45278
46134
|
return parsed.name.trim();
|
|
@@ -45694,7 +46550,7 @@ async function runRestart2(deps) {
|
|
|
45694
46550
|
return await deps.restartDaemon();
|
|
45695
46551
|
} catch (error2) {
|
|
45696
46552
|
deps.print(`配置已保存,但重启失败:${describeError(error2)}`);
|
|
45697
|
-
deps.print("
|
|
46553
|
+
deps.print(`请查看日志:${coreHomeDisplayPath("runtime", "stderr.log")}`);
|
|
45698
46554
|
deps.print("也可以稍后执行:weacpx start");
|
|
45699
46555
|
return 1;
|
|
45700
46556
|
}
|
|
@@ -45927,7 +46783,12 @@ async function runCli(args, deps = {}) {
|
|
|
45927
46783
|
print,
|
|
45928
46784
|
isInteractive: deps.isInteractive,
|
|
45929
46785
|
promptText: deps.promptText,
|
|
45930
|
-
overrides:
|
|
46786
|
+
overrides: {
|
|
46787
|
+
stopDaemon: async () => {
|
|
46788
|
+
await (deps.controller ?? createDefaultController(deps)).stop();
|
|
46789
|
+
},
|
|
46790
|
+
...deps.updateCliDeps
|
|
46791
|
+
}
|
|
45931
46792
|
})))(args.slice(1));
|
|
45932
46793
|
if (result === null) {
|
|
45933
46794
|
for (const line of HELP_LINES)
|
|
@@ -46373,7 +47234,7 @@ async function createCliScheduledTaskService() {
|
|
|
46373
47234
|
return new ScheduledTaskService(state, stateStore);
|
|
46374
47235
|
}
|
|
46375
47236
|
function resolveConfigPathForCurrentEnv() {
|
|
46376
|
-
return process.env.WEACPX_CONFIG ??
|
|
47237
|
+
return process.env.WEACPX_CONFIG ?? join19(coreHomeDir(requireHome2()), "config.json");
|
|
46377
47238
|
}
|
|
46378
47239
|
function resolveDaemonPathsForCurrentConfig() {
|
|
46379
47240
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
@@ -46740,7 +47601,7 @@ function safeDaemonLogPaths() {
|
|
|
46740
47601
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
46741
47602
|
const paths = resolveDaemonPathsForCurrentConfig();
|
|
46742
47603
|
return {
|
|
46743
|
-
appLog:
|
|
47604
|
+
appLog: join19(dirname16(configPath), "runtime", "app.log"),
|
|
46744
47605
|
stderrLog: paths.stderrLog
|
|
46745
47606
|
};
|
|
46746
47607
|
} catch {
|
|
@@ -46751,7 +47612,7 @@ function resolveCliEntryPath2() {
|
|
|
46751
47612
|
if (process.argv[1]) {
|
|
46752
47613
|
return process.argv[1];
|
|
46753
47614
|
}
|
|
46754
|
-
return
|
|
47615
|
+
return fileURLToPath7(import.meta.url);
|
|
46755
47616
|
}
|
|
46756
47617
|
function parseDoctorArgs(args) {
|
|
46757
47618
|
const options = {};
|