weacpx 0.4.9 → 0.4.10
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 +20 -3
- package/dist/cli.js +319 -60
- package/dist/config/types.d.ts +7 -0
- package/dist/util/private-file.d.ts +26 -0
- package/package.json +1 -1
|
@@ -1042,6 +1042,13 @@ function normalizeBridgePermissionMode(value) {
|
|
|
1042
1042
|
function normalizeBridgeNonInteractivePermissions(value) {
|
|
1043
1043
|
return value === "deny" || value === "fail" ? value : "deny";
|
|
1044
1044
|
}
|
|
1045
|
+
function normalizeBridgeQueueOwnerTtlSeconds(value) {
|
|
1046
|
+
if (value === undefined) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const parsed = Number(value);
|
|
1050
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
|
|
1051
|
+
}
|
|
1045
1052
|
|
|
1046
1053
|
// src/bridge/bridge-server.ts
|
|
1047
1054
|
init_prompt_output();
|
|
@@ -1125,7 +1132,8 @@ class BridgeRuntime {
|
|
|
1125
1132
|
queueOwnerLauncher;
|
|
1126
1133
|
acpxVerboseSupported = undefined;
|
|
1127
1134
|
constructor(command = "acpx", run = defaultRunner, runSessionCreate = shellSessionCreateRunner, options = {}, runPromptCommand = defaultPromptRunner, repairSessionIndex = tryRepairAcpxSessionIndex, queueOwnerLauncher = new AcpxQueueOwnerLauncher({
|
|
1128
|
-
acpxCommand: command
|
|
1135
|
+
acpxCommand: command,
|
|
1136
|
+
...typeof options.queueOwnerTtlSeconds === "number" && Number.isFinite(options.queueOwnerTtlSeconds) ? { ttlMs: options.queueOwnerTtlSeconds * 1000 } : {}
|
|
1129
1137
|
})) {
|
|
1130
1138
|
this.command = command;
|
|
1131
1139
|
this.run = run;
|
|
@@ -1381,13 +1389,21 @@ class BridgeRuntime {
|
|
|
1381
1389
|
"--json-strict",
|
|
1382
1390
|
"--cwd",
|
|
1383
1391
|
input.cwd,
|
|
1384
|
-
...this.buildPermissionArgs()
|
|
1392
|
+
...this.buildPermissionArgs(),
|
|
1393
|
+
...this.buildQueueOwnerTtlArgs()
|
|
1385
1394
|
];
|
|
1386
1395
|
if (input.agentCommand) {
|
|
1387
1396
|
return [...prefix, "--agent", input.agentCommand, ...tail];
|
|
1388
1397
|
}
|
|
1389
1398
|
return [...prefix, input.agent, ...tail];
|
|
1390
1399
|
}
|
|
1400
|
+
buildQueueOwnerTtlArgs() {
|
|
1401
|
+
const ttl = this.options.queueOwnerTtlSeconds;
|
|
1402
|
+
if (typeof ttl !== "number" || !Number.isFinite(ttl)) {
|
|
1403
|
+
return [];
|
|
1404
|
+
}
|
|
1405
|
+
return ["--ttl", String(ttl)];
|
|
1406
|
+
}
|
|
1391
1407
|
buildPermissionArgs() {
|
|
1392
1408
|
const permissionMode = this.options.permissionMode ?? "approve-all";
|
|
1393
1409
|
const nonInteractivePermissions = this.options.nonInteractivePermissions ?? "deny";
|
|
@@ -1924,7 +1940,8 @@ async function processBridgeInput(options) {
|
|
|
1924
1940
|
async function runBridgeMain() {
|
|
1925
1941
|
const server = new BridgeServer(new BridgeRuntime(process.env.WEACPX_BRIDGE_ACPX_COMMAND ?? "acpx", undefined, undefined, {
|
|
1926
1942
|
permissionMode: normalizeBridgePermissionMode(process.env.WEACPX_BRIDGE_PERMISSION_MODE),
|
|
1927
|
-
nonInteractivePermissions: normalizeBridgeNonInteractivePermissions(process.env.WEACPX_BRIDGE_NON_INTERACTIVE_PERMISSIONS)
|
|
1943
|
+
nonInteractivePermissions: normalizeBridgeNonInteractivePermissions(process.env.WEACPX_BRIDGE_NON_INTERACTIVE_PERMISSIONS),
|
|
1944
|
+
queueOwnerTtlSeconds: normalizeBridgeQueueOwnerTtlSeconds(process.env.WEACPX_BRIDGE_QUEUE_OWNER_TTL_SECONDS)
|
|
1928
1945
|
}));
|
|
1929
1946
|
const input = createInterface({
|
|
1930
1947
|
input: process.stdin,
|
package/dist/cli.js
CHANGED
|
@@ -1999,6 +1999,7 @@ var require_lib = __commonJS((exports, module) => {
|
|
|
1999
1999
|
|
|
2000
2000
|
// src/util/private-file.ts
|
|
2001
2001
|
import { chmod, mkdir, writeFile } from "node:fs/promises";
|
|
2002
|
+
import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2002
2003
|
import { dirname } from "node:path";
|
|
2003
2004
|
async function writePrivateFileAtomic(path, content) {
|
|
2004
2005
|
await mkdir(dirname(path), { recursive: true });
|
|
@@ -2031,6 +2032,25 @@ async function writePrivateFileAtomic(path, content) {
|
|
|
2031
2032
|
await release();
|
|
2032
2033
|
}
|
|
2033
2034
|
}
|
|
2035
|
+
function writePrivateFileSync(path, content, deps = {}) {
|
|
2036
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
2037
|
+
const platform = deps.platform ?? process.platform;
|
|
2038
|
+
const atomicWrite = deps.atomicWrite ?? ((p, c) => import_write_file_atomic.default.sync(p, c, { mode: PRIVATE_FILE_MODE, encoding: "utf8", fsync: true }));
|
|
2039
|
+
try {
|
|
2040
|
+
atomicWrite(path, content);
|
|
2041
|
+
} catch (error) {
|
|
2042
|
+
if (!isTransientWriteError(error, platform)) {
|
|
2043
|
+
throw error;
|
|
2044
|
+
}
|
|
2045
|
+
const directWrite = deps.directWrite ?? ((p, c) => {
|
|
2046
|
+
writeFileSync(p, c, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
|
|
2047
|
+
try {
|
|
2048
|
+
chmodSync(p, PRIVATE_FILE_MODE);
|
|
2049
|
+
} catch {}
|
|
2050
|
+
});
|
|
2051
|
+
directWrite(path, content);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2034
2054
|
async function retryTransientWriteErrors(run, options = {}) {
|
|
2035
2055
|
const platform = options.platform ?? process.platform;
|
|
2036
2056
|
const maxAttempts = options.maxAttempts ?? WRITE_RETRY_MAX_ATTEMPTS;
|
|
@@ -2206,6 +2226,9 @@ function parseConfig(raw, options = {}) {
|
|
|
2206
2226
|
throw new Error("transport.permissionPolicy must be a non-empty string");
|
|
2207
2227
|
}
|
|
2208
2228
|
}
|
|
2229
|
+
if ("queueOwnerTtlSeconds" in transport && (typeof transport.queueOwnerTtlSeconds !== "number" || !Number.isFinite(transport.queueOwnerTtlSeconds) || transport.queueOwnerTtlSeconds < 0)) {
|
|
2230
|
+
throw new Error("transport.queueOwnerTtlSeconds must be a non-negative number (0 = keep alive forever)");
|
|
2231
|
+
}
|
|
2209
2232
|
if (!isRecord(raw.agents)) {
|
|
2210
2233
|
throw new Error("agents must be an object");
|
|
2211
2234
|
}
|
|
@@ -2302,7 +2325,8 @@ function parseConfig(raw, options = {}) {
|
|
|
2302
2325
|
...typeof transport.permissionPolicy === "string" ? { permissionPolicy: transport.permissionPolicy } : {},
|
|
2303
2326
|
type: transportType,
|
|
2304
2327
|
permissionMode,
|
|
2305
|
-
nonInteractivePermissions
|
|
2328
|
+
nonInteractivePermissions,
|
|
2329
|
+
queueOwnerTtlSeconds: typeof transport.queueOwnerTtlSeconds === "number" ? transport.queueOwnerTtlSeconds : DEFAULT_QUEUE_OWNER_TTL_SECONDS
|
|
2306
2330
|
},
|
|
2307
2331
|
logging: {
|
|
2308
2332
|
level: resolvedLoggingLevel,
|
|
@@ -2428,7 +2452,7 @@ function parseOrchestrationConfig(raw) {
|
|
|
2428
2452
|
maxParallelTasksPerAgent: typeof raw.maxParallelTasksPerAgent === "number" && Number.isFinite(raw.maxParallelTasksPerAgent) && raw.maxParallelTasksPerAgent >= 1 ? Math.floor(raw.maxParallelTasksPerAgent) : DEFAULT_ORCHESTRATION_CONFIG.maxParallelTasksPerAgent
|
|
2429
2453
|
};
|
|
2430
2454
|
}
|
|
2431
|
-
var DEFAULT_PERF_LOG_CONFIG, DEFAULT_LOGGING_CONFIG, DEFAULT_PERMISSION_MODE = "approve-all", DEFAULT_NON_INTERACTIVE_PERMISSIONS = "deny", DEFAULT_CHANNEL_CONFIG, DEFAULT_ORCHESTRATION_CONFIG;
|
|
2455
|
+
var DEFAULT_PERF_LOG_CONFIG, DEFAULT_LOGGING_CONFIG, DEFAULT_PERMISSION_MODE = "approve-all", DEFAULT_NON_INTERACTIVE_PERMISSIONS = "deny", DEFAULT_QUEUE_OWNER_TTL_SECONDS = 1800, DEFAULT_CHANNEL_CONFIG, DEFAULT_ORCHESTRATION_CONFIG;
|
|
2432
2456
|
var init_load_config = __esm(() => {
|
|
2433
2457
|
init_workspace_path();
|
|
2434
2458
|
DEFAULT_PERF_LOG_CONFIG = {
|
|
@@ -2729,7 +2753,7 @@ class DaemonStatusStore {
|
|
|
2729
2753
|
var init_daemon_status = () => {};
|
|
2730
2754
|
|
|
2731
2755
|
// src/daemon/daemon-controller.ts
|
|
2732
|
-
import { mkdir as mkdir3, readFile as readFile4, rm as rm2
|
|
2756
|
+
import { mkdir as mkdir3, open, readFile as readFile4, rm as rm2 } from "node:fs/promises";
|
|
2733
2757
|
import { dirname as dirname3 } from "node:path";
|
|
2734
2758
|
|
|
2735
2759
|
class DaemonController {
|
|
@@ -2788,9 +2812,19 @@ class DaemonController {
|
|
|
2788
2812
|
if (current.state === "indeterminate") {
|
|
2789
2813
|
throw new Error(`weacpx daemon process is already running (pid ${current.pid}) but status metadata is missing`);
|
|
2790
2814
|
}
|
|
2791
|
-
await this.
|
|
2792
|
-
|
|
2793
|
-
|
|
2815
|
+
const pidHandle = await this.openPidFileExclusive();
|
|
2816
|
+
let pid;
|
|
2817
|
+
try {
|
|
2818
|
+
await this.statusStore.clear();
|
|
2819
|
+
pid = await this.deps.spawnDetached(options);
|
|
2820
|
+
await pidHandle.write(`${pid}
|
|
2821
|
+
`);
|
|
2822
|
+
} catch (error) {
|
|
2823
|
+
await pidHandle.close().catch(() => {});
|
|
2824
|
+
await rm2(this.paths.pidFile, { force: true }).catch(() => {});
|
|
2825
|
+
throw error;
|
|
2826
|
+
}
|
|
2827
|
+
await pidHandle.close();
|
|
2794
2828
|
await this.waitForStartupMetadata(pid, options.firstRunOnboarding ? this.onboardingStartupTimeoutMs : this.startupTimeoutMs, options.startupWait);
|
|
2795
2829
|
return { state: "started", pid };
|
|
2796
2830
|
}
|
|
@@ -2818,10 +2852,16 @@ class DaemonController {
|
|
|
2818
2852
|
throw error;
|
|
2819
2853
|
}
|
|
2820
2854
|
}
|
|
2821
|
-
async
|
|
2855
|
+
async openPidFileExclusive() {
|
|
2822
2856
|
await mkdir3(dirname3(this.paths.pidFile), { recursive: true });
|
|
2823
|
-
|
|
2824
|
-
|
|
2857
|
+
try {
|
|
2858
|
+
return await open(this.paths.pidFile, "wx", 384);
|
|
2859
|
+
} catch (error) {
|
|
2860
|
+
if (error.code === "EEXIST") {
|
|
2861
|
+
throw new Error(`weacpx daemon pid file already exists (${this.paths.pidFile}); another start may be in progress`);
|
|
2862
|
+
}
|
|
2863
|
+
throw error;
|
|
2864
|
+
}
|
|
2825
2865
|
}
|
|
2826
2866
|
async clearRuntimeFiles() {
|
|
2827
2867
|
await rm2(this.paths.pidFile, { force: true });
|
|
@@ -2918,15 +2958,17 @@ async function defaultRunProcessCommand(command, args) {
|
|
|
2918
2958
|
var init_terminate_process_tree = () => {};
|
|
2919
2959
|
|
|
2920
2960
|
// src/daemon/create-daemon-controller.ts
|
|
2921
|
-
import { mkdir as mkdir4, open } from "node:fs/promises";
|
|
2961
|
+
import { mkdir as mkdir4, open as open2 } from "node:fs/promises";
|
|
2922
2962
|
import { spawn as spawn2 } from "node:child_process";
|
|
2923
2963
|
function createDaemonController(paths, options) {
|
|
2924
2964
|
return new DaemonController(paths, {
|
|
2925
2965
|
isProcessRunning: options.isProcessRunning ?? defaultIsProcessRunning2,
|
|
2926
2966
|
spawnDetached: async (spawnOptions) => {
|
|
2927
2967
|
await mkdir4(paths.runtimeDir, { recursive: true });
|
|
2928
|
-
const stdoutHandle = await
|
|
2929
|
-
const stderrHandle = await
|
|
2968
|
+
const stdoutHandle = await open2(paths.stdoutLog, "a", 384);
|
|
2969
|
+
const stderrHandle = await open2(paths.stderrLog, "a", 384);
|
|
2970
|
+
await stdoutHandle.chmod(384).catch(() => {});
|
|
2971
|
+
await stderrHandle.chmod(384).catch(() => {});
|
|
2930
2972
|
try {
|
|
2931
2973
|
return await (options.spawnProcess ?? defaultSpawnProcess)(buildSpawnRequest(paths, options, stdoutHandle.fd, stderrHandle.fd, spawnOptions));
|
|
2932
2974
|
} finally {
|
|
@@ -10014,7 +10056,7 @@ var init_state_store = __esm(() => {
|
|
|
10014
10056
|
});
|
|
10015
10057
|
|
|
10016
10058
|
// src/plugins/plugin-home.ts
|
|
10017
|
-
import { mkdir as mkdir6, writeFile as
|
|
10059
|
+
import { mkdir as mkdir6, writeFile as writeFile4 } from "node:fs/promises";
|
|
10018
10060
|
import { homedir as homedir3 } from "node:os";
|
|
10019
10061
|
import { join as join3 } from "node:path";
|
|
10020
10062
|
function coerceMissing(value) {
|
|
@@ -10040,7 +10082,7 @@ function resolvePluginHome(input = {}) {
|
|
|
10040
10082
|
}
|
|
10041
10083
|
async function ensurePluginHome(pluginHome) {
|
|
10042
10084
|
await mkdir6(pluginHome, { recursive: true, mode: 448 });
|
|
10043
|
-
await
|
|
10085
|
+
await writeFile4(join3(pluginHome, "package.json"), JSON.stringify({ private: true, type: "module" }, null, 2) + `
|
|
10044
10086
|
`, { flag: "wx" }).catch((error2) => {
|
|
10045
10087
|
if (error2.code !== "EEXIST")
|
|
10046
10088
|
throw error2;
|
|
@@ -10179,8 +10221,6 @@ function loadWeixinAccount(accountId) {
|
|
|
10179
10221
|
return null;
|
|
10180
10222
|
}
|
|
10181
10223
|
function saveWeixinAccount(accountId, update) {
|
|
10182
|
-
const dir = resolveAccountsDir();
|
|
10183
|
-
ensureDirSync(dir);
|
|
10184
10224
|
const existing = loadWeixinAccount(accountId) ?? {};
|
|
10185
10225
|
const token = update.token?.trim() || existing.token;
|
|
10186
10226
|
const baseUrl = update.baseUrl?.trim() || existing.baseUrl;
|
|
@@ -10190,11 +10230,7 @@ function saveWeixinAccount(accountId, update) {
|
|
|
10190
10230
|
...baseUrl ? { baseUrl } : {},
|
|
10191
10231
|
...userId ? { userId } : {}
|
|
10192
10232
|
};
|
|
10193
|
-
|
|
10194
|
-
fs3.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
10195
|
-
try {
|
|
10196
|
-
fs3.chmodSync(filePath, 384);
|
|
10197
|
-
} catch {}
|
|
10233
|
+
writePrivateFileSync(resolveAccountPath(accountId), JSON.stringify(data, null, 2));
|
|
10198
10234
|
}
|
|
10199
10235
|
function clearWeixinAccount(accountId) {
|
|
10200
10236
|
try {
|
|
@@ -10295,6 +10331,7 @@ var DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com", CDN_BASE_URL = "https://
|
|
|
10295
10331
|
var init_accounts = __esm(() => {
|
|
10296
10332
|
init_ensure_dir();
|
|
10297
10333
|
init_state_dir();
|
|
10334
|
+
init_private_file();
|
|
10298
10335
|
});
|
|
10299
10336
|
|
|
10300
10337
|
// src/weixin/util/logger.ts
|
|
@@ -12298,8 +12335,7 @@ function persistContextTokens(accountId) {
|
|
|
12298
12335
|
}
|
|
12299
12336
|
const filePath = resolveContextTokenFilePath(accountId);
|
|
12300
12337
|
try {
|
|
12301
|
-
|
|
12302
|
-
fs5.writeFileSync(filePath, JSON.stringify(tokens), "utf-8");
|
|
12338
|
+
writePrivateFileSync(filePath, JSON.stringify(tokens));
|
|
12303
12339
|
} catch (err) {
|
|
12304
12340
|
logger.warn(`persistContextTokens: failed to write ${filePath}: ${String(err)}`);
|
|
12305
12341
|
}
|
|
@@ -12422,6 +12458,7 @@ var init_inbound = __esm(() => {
|
|
|
12422
12458
|
init_random();
|
|
12423
12459
|
init_types2();
|
|
12424
12460
|
init_state_dir();
|
|
12461
|
+
init_private_file();
|
|
12425
12462
|
contextTokenStore = new Map;
|
|
12426
12463
|
});
|
|
12427
12464
|
|
|
@@ -12544,7 +12581,7 @@ function createConversationExecutor() {
|
|
|
12544
12581
|
}
|
|
12545
12582
|
|
|
12546
12583
|
// src/channels/media-store.ts
|
|
12547
|
-
import { access as access2, mkdir as mkdir7, readdir, rm as rm4, stat, writeFile as
|
|
12584
|
+
import { access as access2, mkdir as mkdir7, readdir, rm as rm4, stat, writeFile as writeFile5 } from "node:fs/promises";
|
|
12548
12585
|
import path7 from "node:path";
|
|
12549
12586
|
|
|
12550
12587
|
class RuntimeMediaStore {
|
|
@@ -12568,7 +12605,7 @@ class RuntimeMediaStore {
|
|
|
12568
12605
|
if (!isPathInside(resolvedFile, resolvedRoot)) {
|
|
12569
12606
|
throw new Error("media path escapes runtime media root");
|
|
12570
12607
|
}
|
|
12571
|
-
await
|
|
12608
|
+
await writeFile5(resolvedFile, input.buffer);
|
|
12572
12609
|
return {
|
|
12573
12610
|
kind: input.kind,
|
|
12574
12611
|
filePath: resolvedFile,
|
|
@@ -14789,13 +14826,11 @@ function loadGetUpdatesBuf(filePath) {
|
|
|
14789
14826
|
return readSyncBufFile(getLegacySyncBufDefaultJsonPath());
|
|
14790
14827
|
}
|
|
14791
14828
|
function saveGetUpdatesBuf(filePath, getUpdatesBuf) {
|
|
14792
|
-
|
|
14793
|
-
ensureDirSync(dir);
|
|
14794
|
-
fs10.writeFileSync(filePath, JSON.stringify({ get_updates_buf: getUpdatesBuf }, null, 0), "utf-8");
|
|
14829
|
+
writePrivateFileSync(filePath, JSON.stringify({ get_updates_buf: getUpdatesBuf }, null, 0));
|
|
14795
14830
|
}
|
|
14796
14831
|
var init_sync_buf = __esm(() => {
|
|
14797
14832
|
init_accounts();
|
|
14798
|
-
|
|
14833
|
+
init_private_file();
|
|
14799
14834
|
init_state_dir();
|
|
14800
14835
|
});
|
|
14801
14836
|
|
|
@@ -15408,7 +15443,7 @@ var init_deliver_coordinator_message = __esm(() => {
|
|
|
15408
15443
|
});
|
|
15409
15444
|
|
|
15410
15445
|
// src/weixin/monitor/consumer-lock.ts
|
|
15411
|
-
import { mkdir as mkdir8, open as
|
|
15446
|
+
import { mkdir as mkdir8, open as open3, readFile as readFile6, rm as rm6 } from "node:fs/promises";
|
|
15412
15447
|
import { dirname as dirname8, join as join5 } from "node:path";
|
|
15413
15448
|
import { homedir as homedir4 } from "node:os";
|
|
15414
15449
|
function createWeixinConsumerLock(options = {}) {
|
|
@@ -15420,7 +15455,7 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
15420
15455
|
await mkdir8(dirname8(lockFilePath), { recursive: true });
|
|
15421
15456
|
while (true) {
|
|
15422
15457
|
try {
|
|
15423
|
-
const handle = await
|
|
15458
|
+
const handle = await open3(lockFilePath, "wx");
|
|
15424
15459
|
try {
|
|
15425
15460
|
await handle.writeFile(`${JSON.stringify(meta2, null, 2)}
|
|
15426
15461
|
`, "utf8");
|
|
@@ -16105,21 +16140,27 @@ async function loadConfiguredPlugins(input) {
|
|
|
16105
16140
|
const importPlugin = input.importPlugin ?? importPluginFromHome;
|
|
16106
16141
|
const loaded = [];
|
|
16107
16142
|
for (const config2 of enabled) {
|
|
16108
|
-
let moduleValue;
|
|
16109
16143
|
try {
|
|
16110
|
-
moduleValue
|
|
16144
|
+
let moduleValue;
|
|
16145
|
+
try {
|
|
16146
|
+
moduleValue = await importPlugin(config2.name, pluginHome);
|
|
16147
|
+
} catch (error2) {
|
|
16148
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
16149
|
+
throw new Error(`failed to load plugin ${config2.name}: ${message}`);
|
|
16150
|
+
}
|
|
16151
|
+
const plugin = validateWeacpxPlugin(moduleValue, config2.name, {
|
|
16152
|
+
...input.currentWeacpxVersion !== undefined ? { currentWeacpxVersion: input.currentWeacpxVersion } : {}
|
|
16153
|
+
});
|
|
16154
|
+
const channels = plugin.channels ?? [];
|
|
16155
|
+
for (const channel of channels) {
|
|
16156
|
+
registerChannelPlugin(channel);
|
|
16157
|
+
}
|
|
16158
|
+
loaded.push({ name: config2.name, channels: channels.map((channel) => channel.type) });
|
|
16111
16159
|
} catch (error2) {
|
|
16112
|
-
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
const plugin = validateWeacpxPlugin(moduleValue, config2.name, {
|
|
16116
|
-
...input.currentWeacpxVersion !== undefined ? { currentWeacpxVersion: input.currentWeacpxVersion } : {}
|
|
16117
|
-
});
|
|
16118
|
-
const channels = plugin.channels ?? [];
|
|
16119
|
-
for (const channel of channels) {
|
|
16120
|
-
registerChannelPlugin(channel);
|
|
16160
|
+
if (!input.onPluginError)
|
|
16161
|
+
throw error2;
|
|
16162
|
+
input.onPluginError({ name: config2.name, error: error2 });
|
|
16121
16163
|
}
|
|
16122
|
-
loaded.push({ name: config2.name, channels: channels.map((channel) => channel.type) });
|
|
16123
16164
|
}
|
|
16124
16165
|
return loaded;
|
|
16125
16166
|
}
|
|
@@ -16140,7 +16181,7 @@ var init_bootstrap = __esm(() => {
|
|
|
16140
16181
|
});
|
|
16141
16182
|
|
|
16142
16183
|
// src/logging/app-logger.ts
|
|
16143
|
-
import { appendFile, mkdir as mkdir9 } from "node:fs/promises";
|
|
16184
|
+
import { appendFile, chmod as chmod2, mkdir as mkdir9 } from "node:fs/promises";
|
|
16144
16185
|
import { dirname as dirname10 } from "node:path";
|
|
16145
16186
|
function createNoopAppLogger() {
|
|
16146
16187
|
return {
|
|
@@ -16154,6 +16195,7 @@ function createNoopAppLogger() {
|
|
|
16154
16195
|
function createAppLogger(options) {
|
|
16155
16196
|
const now = options.now ?? (() => new Date);
|
|
16156
16197
|
let writeChain = Promise.resolve();
|
|
16198
|
+
let modeEnsured = false;
|
|
16157
16199
|
return {
|
|
16158
16200
|
debug: async (event, message, context) => {
|
|
16159
16201
|
await enqueueWrite("debug", event, message, context);
|
|
@@ -16182,8 +16224,12 @@ function createAppLogger(options) {
|
|
|
16182
16224
|
}
|
|
16183
16225
|
const line = formatLogLine(now(), level, event, message, context);
|
|
16184
16226
|
await mkdir9(dirname10(options.filePath), { recursive: true });
|
|
16227
|
+
if (!modeEnsured) {
|
|
16228
|
+
modeEnsured = true;
|
|
16229
|
+
await chmod2(options.filePath, 384).catch(() => {});
|
|
16230
|
+
}
|
|
16185
16231
|
await rotateIfNeeded(options.filePath, Buffer.byteLength(line), options.maxSizeBytes, options.maxFiles);
|
|
16186
|
-
await appendFile(options.filePath, line, "utf8");
|
|
16232
|
+
await appendFile(options.filePath, line, { encoding: "utf8", mode: 384 });
|
|
16187
16233
|
}
|
|
16188
16234
|
}
|
|
16189
16235
|
function formatLogLine(time3, level, event, message, context) {
|
|
@@ -23934,6 +23980,20 @@ class SessionService {
|
|
|
23934
23980
|
async createSession(alias, agent, workspace) {
|
|
23935
23981
|
return await this.createLogicalSession(alias, agent, workspace, `${workspace}:${alias}`);
|
|
23936
23982
|
}
|
|
23983
|
+
listAllResolvedSessions() {
|
|
23984
|
+
const seen = new Set;
|
|
23985
|
+
const resolved = [];
|
|
23986
|
+
for (const session of Object.values(this.state.sessions)) {
|
|
23987
|
+
if (seen.has(session.transport_session)) {
|
|
23988
|
+
continue;
|
|
23989
|
+
}
|
|
23990
|
+
seen.add(session.transport_session);
|
|
23991
|
+
try {
|
|
23992
|
+
resolved.push(this.toResolvedSession(session));
|
|
23993
|
+
} catch {}
|
|
23994
|
+
}
|
|
23995
|
+
return resolved;
|
|
23996
|
+
}
|
|
23937
23997
|
resolveSession(alias, agent, workspace, transportSession) {
|
|
23938
23998
|
this.validateSession(alias, agent, workspace);
|
|
23939
23999
|
return this.toResolvedSession({
|
|
@@ -24621,7 +24681,8 @@ async function spawnAcpxBridgeClient(options = {}) {
|
|
|
24621
24681
|
...process.env,
|
|
24622
24682
|
WEACPX_BRIDGE_ACPX_COMMAND: options.acpxCommand ?? "acpx",
|
|
24623
24683
|
WEACPX_BRIDGE_PERMISSION_MODE: options.permissionMode ?? "approve-all",
|
|
24624
|
-
WEACPX_BRIDGE_NON_INTERACTIVE_PERMISSIONS: options.nonInteractivePermissions ?? "deny"
|
|
24684
|
+
WEACPX_BRIDGE_NON_INTERACTIVE_PERMISSIONS: options.nonInteractivePermissions ?? "deny",
|
|
24685
|
+
...typeof options.queueOwnerTtlSeconds === "number" && Number.isFinite(options.queueOwnerTtlSeconds) ? { WEACPX_BRIDGE_QUEUE_OWNER_TTL_SECONDS: String(options.queueOwnerTtlSeconds) } : {}
|
|
24625
24686
|
},
|
|
24626
24687
|
stdio: ["pipe", "pipe", "inherit"]
|
|
24627
24688
|
});
|
|
@@ -25049,7 +25110,7 @@ var init_spawn_command = __esm(() => {
|
|
|
25049
25110
|
});
|
|
25050
25111
|
|
|
25051
25112
|
// src/transport/prompt-media.ts
|
|
25052
|
-
import { mkdtemp, open as
|
|
25113
|
+
import { mkdtemp, open as open4, rm as rm8, writeFile as writeFile7 } from "node:fs/promises";
|
|
25053
25114
|
import { tmpdir as defaultTmpdir } from "node:os";
|
|
25054
25115
|
import path14 from "node:path";
|
|
25055
25116
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -25118,7 +25179,7 @@ async function writeStructuredPromptBlocks(blocks, deps) {
|
|
|
25118
25179
|
}
|
|
25119
25180
|
}
|
|
25120
25181
|
async function readImageFileBounded(filePath, maxBytes) {
|
|
25121
|
-
const handle = await
|
|
25182
|
+
const handle = await open4(filePath, "r");
|
|
25122
25183
|
try {
|
|
25123
25184
|
const imageStats = await handle.stat();
|
|
25124
25185
|
if (!imageStats.isFile()) {
|
|
@@ -25173,7 +25234,7 @@ var init_prompt_media = __esm(() => {
|
|
|
25173
25234
|
defaultStructuredPromptFileDeps = {
|
|
25174
25235
|
readImageFile: readImageFileBounded,
|
|
25175
25236
|
mkdtemp,
|
|
25176
|
-
writeFile:
|
|
25237
|
+
writeFile: writeFile7,
|
|
25177
25238
|
rm: rm8,
|
|
25178
25239
|
tmpdir: defaultTmpdir
|
|
25179
25240
|
};
|
|
@@ -25480,12 +25541,12 @@ function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
|
25480
25541
|
}
|
|
25481
25542
|
return join12(dirname12(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
25482
25543
|
}
|
|
25483
|
-
async function ensureNodePtyHelperExecutable(helperPath,
|
|
25544
|
+
async function ensureNodePtyHelperExecutable(helperPath, chmod3 = chmodFs) {
|
|
25484
25545
|
if (!helperPath) {
|
|
25485
25546
|
return;
|
|
25486
25547
|
}
|
|
25487
25548
|
try {
|
|
25488
|
-
await
|
|
25549
|
+
await chmod3(helperPath, 493);
|
|
25489
25550
|
} catch (error2) {
|
|
25490
25551
|
if (error2.code === "ENOENT") {
|
|
25491
25552
|
return;
|
|
@@ -25778,6 +25839,7 @@ class AcpxCliTransport {
|
|
|
25778
25839
|
permissionMode;
|
|
25779
25840
|
nonInteractivePermissions;
|
|
25780
25841
|
permissionPolicy;
|
|
25842
|
+
queueOwnerTtlSeconds;
|
|
25781
25843
|
runCommand;
|
|
25782
25844
|
runPtyCommand;
|
|
25783
25845
|
queueOwnerLauncher;
|
|
@@ -25788,10 +25850,12 @@ class AcpxCliTransport {
|
|
|
25788
25850
|
this.permissionMode = options.permissionMode ?? "approve-all";
|
|
25789
25851
|
this.nonInteractivePermissions = options.nonInteractivePermissions ?? "deny";
|
|
25790
25852
|
this.permissionPolicy = options.permissionPolicy;
|
|
25853
|
+
this.queueOwnerTtlSeconds = options.queueOwnerTtlSeconds;
|
|
25791
25854
|
this.runCommand = runCommand;
|
|
25792
25855
|
this.runPtyCommand = runPtyCommand;
|
|
25793
25856
|
this.queueOwnerLauncher = queueOwnerLauncher ?? new AcpxQueueOwnerLauncher({
|
|
25794
|
-
acpxCommand: this.command
|
|
25857
|
+
acpxCommand: this.command,
|
|
25858
|
+
...typeof this.queueOwnerTtlSeconds === "number" && Number.isFinite(this.queueOwnerTtlSeconds) ? { ttlMs: this.queueOwnerTtlSeconds * 1000 } : {}
|
|
25795
25859
|
});
|
|
25796
25860
|
this.streamingHooks = streamingHooks;
|
|
25797
25861
|
}
|
|
@@ -26115,7 +26179,8 @@ ${baseText}` : "" };
|
|
|
26115
26179
|
"--json-strict",
|
|
26116
26180
|
"--cwd",
|
|
26117
26181
|
session.cwd,
|
|
26118
|
-
...this.buildPermissionArgs()
|
|
26182
|
+
...this.buildPermissionArgs(),
|
|
26183
|
+
...this.buildQueueOwnerTtlArgs()
|
|
26119
26184
|
];
|
|
26120
26185
|
const tail2 = promptFile ? ["prompt", "-s", session.transportSession, "--file", promptFile] : ["prompt", "-s", session.transportSession, text];
|
|
26121
26186
|
if (session.agentCommand) {
|
|
@@ -26123,6 +26188,12 @@ ${baseText}` : "" };
|
|
|
26123
26188
|
}
|
|
26124
26189
|
return [...prefix, session.agent, ...tail2];
|
|
26125
26190
|
}
|
|
26191
|
+
buildQueueOwnerTtlArgs() {
|
|
26192
|
+
if (typeof this.queueOwnerTtlSeconds !== "number" || !Number.isFinite(this.queueOwnerTtlSeconds)) {
|
|
26193
|
+
return [];
|
|
26194
|
+
}
|
|
26195
|
+
return ["--ttl", String(this.queueOwnerTtlSeconds)];
|
|
26196
|
+
}
|
|
26126
26197
|
buildPermissionArgs() {
|
|
26127
26198
|
const modeFlag = permissionModeToFlag(this.permissionMode);
|
|
26128
26199
|
const args = [modeFlag, "--non-interactive-permissions", this.nonInteractivePermissions];
|
|
@@ -26169,6 +26240,146 @@ var init_acpx_cli_transport = __esm(() => {
|
|
|
26169
26240
|
require4 = createRequire5(import.meta.url);
|
|
26170
26241
|
});
|
|
26171
26242
|
|
|
26243
|
+
// src/transport/queue-owner-reaper.ts
|
|
26244
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
26245
|
+
async function reapQueueOwners(acpxCommand, targets, deps = {}) {
|
|
26246
|
+
const resolveRecordId = deps.resolveRecordId ?? defaultResolveRecordId;
|
|
26247
|
+
const terminate = deps.terminate ?? terminateAcpxQueueOwner;
|
|
26248
|
+
const timeoutMs = deps.timeoutMs ?? 5000;
|
|
26249
|
+
const seen = new Set;
|
|
26250
|
+
const unique = targets.filter((target) => {
|
|
26251
|
+
if (seen.has(target.transportSession)) {
|
|
26252
|
+
return false;
|
|
26253
|
+
}
|
|
26254
|
+
seen.add(target.transportSession);
|
|
26255
|
+
return true;
|
|
26256
|
+
});
|
|
26257
|
+
let terminated = 0;
|
|
26258
|
+
const reapOne = async (target) => {
|
|
26259
|
+
try {
|
|
26260
|
+
const recordId = await resolveRecordId(acpxCommand, target);
|
|
26261
|
+
if (!recordId) {
|
|
26262
|
+
return;
|
|
26263
|
+
}
|
|
26264
|
+
await terminate(recordId);
|
|
26265
|
+
terminated += 1;
|
|
26266
|
+
} catch (error2) {
|
|
26267
|
+
deps.onError?.(target, error2);
|
|
26268
|
+
}
|
|
26269
|
+
};
|
|
26270
|
+
await settleWithinTimeout(Promise.all(unique.map(reapOne)), timeoutMs);
|
|
26271
|
+
return { terminated, attempted: unique.length };
|
|
26272
|
+
}
|
|
26273
|
+
function settleWithinTimeout(work, timeoutMs) {
|
|
26274
|
+
return new Promise((resolve3) => {
|
|
26275
|
+
let settled = false;
|
|
26276
|
+
const finish = () => {
|
|
26277
|
+
if (!settled) {
|
|
26278
|
+
settled = true;
|
|
26279
|
+
resolve3();
|
|
26280
|
+
}
|
|
26281
|
+
};
|
|
26282
|
+
const timer = setTimeout(finish, timeoutMs);
|
|
26283
|
+
if (typeof timer.unref === "function") {
|
|
26284
|
+
timer.unref();
|
|
26285
|
+
}
|
|
26286
|
+
work.then(() => {
|
|
26287
|
+
clearTimeout(timer);
|
|
26288
|
+
finish();
|
|
26289
|
+
}, () => {
|
|
26290
|
+
clearTimeout(timer);
|
|
26291
|
+
finish();
|
|
26292
|
+
});
|
|
26293
|
+
});
|
|
26294
|
+
}
|
|
26295
|
+
async function defaultResolveRecordId(acpxCommand, target) {
|
|
26296
|
+
const args = [
|
|
26297
|
+
"--format",
|
|
26298
|
+
"quiet",
|
|
26299
|
+
"--cwd",
|
|
26300
|
+
target.cwd,
|
|
26301
|
+
...target.agentCommand ? ["--agent", target.agentCommand] : [target.agent],
|
|
26302
|
+
"sessions",
|
|
26303
|
+
"show",
|
|
26304
|
+
target.transportSession
|
|
26305
|
+
];
|
|
26306
|
+
const spawnSpec = resolveSpawnCommand(acpxCommand, args);
|
|
26307
|
+
const result = await runCapture2(spawnSpec.command, spawnSpec.args, 4000);
|
|
26308
|
+
if (result.code !== 0) {
|
|
26309
|
+
return null;
|
|
26310
|
+
}
|
|
26311
|
+
return parseRecordId(result.stdout);
|
|
26312
|
+
}
|
|
26313
|
+
function parseRecordId(stdout2) {
|
|
26314
|
+
try {
|
|
26315
|
+
const parsed = JSON.parse(stdout2);
|
|
26316
|
+
if (typeof parsed.acpxRecordId === "string") {
|
|
26317
|
+
return parsed.acpxRecordId;
|
|
26318
|
+
}
|
|
26319
|
+
if (typeof parsed.id === "string") {
|
|
26320
|
+
return parsed.id;
|
|
26321
|
+
}
|
|
26322
|
+
} catch {
|
|
26323
|
+
const firstLine = stdout2.trim().split(/\r?\n/, 1)[0];
|
|
26324
|
+
if (firstLine && /^[\w.:-]+$/.test(firstLine) && firstLine.length >= 8) {
|
|
26325
|
+
return firstLine;
|
|
26326
|
+
}
|
|
26327
|
+
}
|
|
26328
|
+
return null;
|
|
26329
|
+
}
|
|
26330
|
+
function runCapture2(command, args, timeoutMs) {
|
|
26331
|
+
return new Promise((resolve3) => {
|
|
26332
|
+
const child = spawn10(command, args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
26333
|
+
let stdout2 = "";
|
|
26334
|
+
let done = false;
|
|
26335
|
+
const finish = (code) => {
|
|
26336
|
+
if (done) {
|
|
26337
|
+
return;
|
|
26338
|
+
}
|
|
26339
|
+
done = true;
|
|
26340
|
+
clearTimeout(timer);
|
|
26341
|
+
resolve3({ code, stdout: stdout2 });
|
|
26342
|
+
};
|
|
26343
|
+
const timer = setTimeout(() => {
|
|
26344
|
+
child.kill("SIGKILL");
|
|
26345
|
+
finish(1);
|
|
26346
|
+
}, timeoutMs);
|
|
26347
|
+
child.stdout?.on("data", (chunk) => {
|
|
26348
|
+
stdout2 += String(chunk);
|
|
26349
|
+
});
|
|
26350
|
+
child.once("error", () => finish(1));
|
|
26351
|
+
child.once("close", (code) => finish(code ?? 1));
|
|
26352
|
+
});
|
|
26353
|
+
}
|
|
26354
|
+
var init_queue_owner_reaper = __esm(() => {
|
|
26355
|
+
init_spawn_command();
|
|
26356
|
+
init_acpx_queue_owner_launcher();
|
|
26357
|
+
});
|
|
26358
|
+
|
|
26359
|
+
// src/transport/collect-reap-targets.ts
|
|
26360
|
+
function workerBindingReapTargets(orchestration, config2) {
|
|
26361
|
+
const targets = [];
|
|
26362
|
+
for (const [workerSession, binding] of Object.entries(orchestration.workerBindings)) {
|
|
26363
|
+
const agentConfig = config2.agents[binding.targetAgent];
|
|
26364
|
+
if (!agentConfig) {
|
|
26365
|
+
continue;
|
|
26366
|
+
}
|
|
26367
|
+
const cwd = binding.cwd ?? config2.workspaces[binding.workspace]?.cwd;
|
|
26368
|
+
if (!cwd) {
|
|
26369
|
+
continue;
|
|
26370
|
+
}
|
|
26371
|
+
const agentCommand = resolveAgentCommand(agentConfig.driver, agentConfig.command);
|
|
26372
|
+
targets.push({
|
|
26373
|
+
agent: binding.targetAgent,
|
|
26374
|
+
...agentCommand ? { agentCommand } : {},
|
|
26375
|
+
cwd,
|
|
26376
|
+
transportSession: workerSession
|
|
26377
|
+
});
|
|
26378
|
+
}
|
|
26379
|
+
return targets;
|
|
26380
|
+
}
|
|
26381
|
+
var init_collect_reap_targets = () => {};
|
|
26382
|
+
|
|
26172
26383
|
// src/channels/channel-registry.ts
|
|
26173
26384
|
var exports_channel_registry = {};
|
|
26174
26385
|
__export(exports_channel_registry, {
|
|
@@ -26364,7 +26575,11 @@ function startProgressHeartbeat(orchestration, config2, logger2, channel) {
|
|
|
26364
26575
|
if (thresholdSeconds <= 0) {
|
|
26365
26576
|
return;
|
|
26366
26577
|
}
|
|
26578
|
+
let ticking = false;
|
|
26367
26579
|
return setInterval(async () => {
|
|
26580
|
+
if (ticking)
|
|
26581
|
+
return;
|
|
26582
|
+
ticking = true;
|
|
26368
26583
|
try {
|
|
26369
26584
|
const tasks = await orchestration.listHeartbeatTasks(thresholdSeconds);
|
|
26370
26585
|
for (const task of tasks) {
|
|
@@ -26385,6 +26600,8 @@ function startProgressHeartbeat(orchestration, config2, logger2, channel) {
|
|
|
26385
26600
|
await logger2.error("orchestration.heartbeat.check_failed", "heartbeat check failed", {
|
|
26386
26601
|
message: error2 instanceof Error ? error2.message : String(error2)
|
|
26387
26602
|
});
|
|
26603
|
+
} finally {
|
|
26604
|
+
ticking = false;
|
|
26388
26605
|
}
|
|
26389
26606
|
}, 60000);
|
|
26390
26607
|
}
|
|
@@ -26437,7 +26654,8 @@ async function buildApp(paths, deps = {}) {
|
|
|
26437
26654
|
acpxCommand,
|
|
26438
26655
|
bridgeEntryPath: resolveBridgeEntryPath(),
|
|
26439
26656
|
permissionMode: config2.transport.permissionMode,
|
|
26440
|
-
nonInteractivePermissions: config2.transport.nonInteractivePermissions
|
|
26657
|
+
nonInteractivePermissions: config2.transport.nonInteractivePermissions,
|
|
26658
|
+
...typeof config2.transport.queueOwnerTtlSeconds === "number" ? { queueOwnerTtlSeconds: config2.transport.queueOwnerTtlSeconds } : {}
|
|
26441
26659
|
})))) : deps.createCliTransport?.(acpxCommand) ?? new AcpxCliTransport({ ...config2.transport, command: acpxCommand });
|
|
26442
26660
|
const quota = new QuotaManager({
|
|
26443
26661
|
onInbound: (chatKey) => {
|
|
@@ -26812,6 +27030,35 @@ async function buildApp(paths, deps = {}) {
|
|
|
26812
27030
|
clearInterval(progressHeartbeatInterval);
|
|
26813
27031
|
}
|
|
26814
27032
|
await Promise.allSettled([...pendingWorkerDispatches]);
|
|
27033
|
+
try {
|
|
27034
|
+
const targets = [
|
|
27035
|
+
...sessions.listAllResolvedSessions().map((session) => ({
|
|
27036
|
+
agent: session.agent,
|
|
27037
|
+
...session.agentCommand ? { agentCommand: session.agentCommand } : {},
|
|
27038
|
+
cwd: session.cwd,
|
|
27039
|
+
transportSession: session.transportSession
|
|
27040
|
+
})),
|
|
27041
|
+
...workerBindingReapTargets(state.orchestration, config2)
|
|
27042
|
+
];
|
|
27043
|
+
if (targets.length > 0) {
|
|
27044
|
+
const { terminated, attempted } = await reapQueueOwners(acpxCommand, targets, {
|
|
27045
|
+
onError: (target, error2) => {
|
|
27046
|
+
logger2.info("transport.queue_owner_reap.failed", "failed to reap queue owner on shutdown", {
|
|
27047
|
+
transport_session: target.transportSession,
|
|
27048
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
27049
|
+
}).catch(() => {});
|
|
27050
|
+
}
|
|
27051
|
+
});
|
|
27052
|
+
await logger2.info("transport.queue_owner_reap.completed", "reaped warm queue owners on shutdown", {
|
|
27053
|
+
terminated,
|
|
27054
|
+
attempted
|
|
27055
|
+
}).catch(() => {});
|
|
27056
|
+
}
|
|
27057
|
+
} catch (err) {
|
|
27058
|
+
await logger2.error("transport.queue_owner_reap.error", "queue owner reap failed during shutdown", {
|
|
27059
|
+
error: err instanceof Error ? err.message : String(err)
|
|
27060
|
+
}).catch(() => {});
|
|
27061
|
+
}
|
|
26815
27062
|
await debouncedStateStore.dispose();
|
|
26816
27063
|
if ("dispose" in transport && typeof transport.dispose === "function") {
|
|
26817
27064
|
await transport.dispose();
|
|
@@ -26842,7 +27089,12 @@ async function main() {
|
|
|
26842
27089
|
await ensureConfigExists(paths.configPath);
|
|
26843
27090
|
const startupConfig = await loadConfig(paths.configPath);
|
|
26844
27091
|
const { loadConfiguredPlugins: loadConfiguredPlugins2 } = await Promise.resolve().then(() => (init_plugin_loader(), exports_plugin_loader));
|
|
26845
|
-
await loadConfiguredPlugins2({
|
|
27092
|
+
await loadConfiguredPlugins2({
|
|
27093
|
+
plugins: startupConfig.plugins,
|
|
27094
|
+
onPluginError: ({ name, error: error2 }) => {
|
|
27095
|
+
console.error(`[weacpx] skipping plugin ${name}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
27096
|
+
}
|
|
27097
|
+
});
|
|
26846
27098
|
const { channelDeps } = await prepareChannelMedia(paths.configPath, startupConfig);
|
|
26847
27099
|
const channelRegistry = new MessageChannelRegistry(createMessageChannels2(startupConfig.channels, channelDeps));
|
|
26848
27100
|
await runConsole(paths, {
|
|
@@ -26926,6 +27178,8 @@ var init_main = __esm(async () => {
|
|
|
26926
27178
|
init_acpx_bridge_client();
|
|
26927
27179
|
init_acpx_bridge_transport();
|
|
26928
27180
|
init_acpx_cli_transport();
|
|
27181
|
+
init_queue_owner_reaper();
|
|
27182
|
+
init_collect_reap_targets();
|
|
26929
27183
|
init_channel_registry();
|
|
26930
27184
|
init_media_store();
|
|
26931
27185
|
init_quota_errors();
|
|
@@ -26936,7 +27190,7 @@ var init_main = __esm(async () => {
|
|
|
26936
27190
|
});
|
|
26937
27191
|
|
|
26938
27192
|
// src/doctor/checks/acpx-check.ts
|
|
26939
|
-
import { spawn as
|
|
27193
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
26940
27194
|
async function checkAcpx(options = {}) {
|
|
26941
27195
|
const runtimePaths = (options.resolveRuntimePaths ?? resolveRuntimePaths)();
|
|
26942
27196
|
try {
|
|
@@ -26983,7 +27237,7 @@ function buildDetails(metadata, version2, verbose) {
|
|
|
26983
27237
|
async function defaultRunVersion(command) {
|
|
26984
27238
|
const spawnSpec = resolveSpawnCommand(command, ["--version"]);
|
|
26985
27239
|
return await new Promise((resolve3, reject) => {
|
|
26986
|
-
const child =
|
|
27240
|
+
const child = spawn11(spawnSpec.command, spawnSpec.args, {
|
|
26987
27241
|
stdio: ["ignore", "pipe", "pipe"]
|
|
26988
27242
|
});
|
|
26989
27243
|
let stdout2 = "";
|
|
@@ -28004,7 +28258,7 @@ import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
|
28004
28258
|
|
|
28005
28259
|
// src/daemon/daemon-runtime.ts
|
|
28006
28260
|
init_daemon_status();
|
|
28007
|
-
import { mkdir as mkdir5, rm as rm3, writeFile as
|
|
28261
|
+
import { mkdir as mkdir5, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
|
|
28008
28262
|
import { dirname as dirname5 } from "node:path";
|
|
28009
28263
|
|
|
28010
28264
|
class DaemonRuntime {
|
|
@@ -28032,7 +28286,7 @@ class DaemonRuntime {
|
|
|
28032
28286
|
stderr_log: this.paths.stderrLog
|
|
28033
28287
|
};
|
|
28034
28288
|
await mkdir5(dirname5(this.paths.pidFile), { recursive: true });
|
|
28035
|
-
await
|
|
28289
|
+
await writeFile3(this.paths.pidFile, `${this.options.pid}
|
|
28036
28290
|
`);
|
|
28037
28291
|
await this.statusStore.save(this.currentStatus);
|
|
28038
28292
|
}
|
|
@@ -43916,7 +44170,12 @@ async function defaultRun(options = {}) {
|
|
|
43916
44170
|
await ensureConfigExists(runtimePaths.configPath);
|
|
43917
44171
|
const config2 = await loadConfig(runtimePaths.configPath);
|
|
43918
44172
|
const { loadConfiguredPlugins: loadConfiguredPlugins2 } = await Promise.resolve().then(() => (init_plugin_loader(), exports_plugin_loader));
|
|
43919
|
-
await loadConfiguredPlugins2({
|
|
44173
|
+
await loadConfiguredPlugins2({
|
|
44174
|
+
plugins: config2.plugins,
|
|
44175
|
+
onPluginError: ({ name, error: error2 }) => {
|
|
44176
|
+
console.error(`[weacpx] skipping plugin ${name}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
44177
|
+
}
|
|
44178
|
+
});
|
|
43920
44179
|
const { createMessageChannels: createMessageChannels2 } = await Promise.resolve().then(() => (init_create_channel(), exports_create_channel));
|
|
43921
44180
|
const { MessageChannelRegistry: MessageChannelRegistry2 } = await Promise.resolve().then(() => (init_channel_registry(), exports_channel_registry));
|
|
43922
44181
|
const daemonPaths = resolveDaemonPathsForCurrentConfig();
|
package/dist/config/types.d.ts
CHANGED
|
@@ -19,6 +19,13 @@ export interface TransportConfig {
|
|
|
19
19
|
permissionMode: PermissionMode;
|
|
20
20
|
nonInteractivePermissions: NonInteractivePermissions;
|
|
21
21
|
permissionPolicy?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Idle TTL (seconds) passed to acpx as `--ttl` on prompt commands. Governs how
|
|
24
|
+
* long the acpx queue owner (and the warm ACP agent it holds) survives between
|
|
25
|
+
* prompts, so follow-up messages in a conversation skip the agent cold start.
|
|
26
|
+
* `0` keeps the owner alive forever. Defaults to 1800 (30 min).
|
|
27
|
+
*/
|
|
28
|
+
queueOwnerTtlSeconds?: number;
|
|
22
29
|
}
|
|
23
30
|
export type LoggingLevel = "error" | "info" | "debug";
|
|
24
31
|
export interface PerfLogConfig {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare function writePrivateFileAtomic(path: string, content: string): Promise<void>;
|
|
2
|
+
/**
|
|
3
|
+
* Synchronous private-file write for hot-path callers that cannot await
|
|
4
|
+
* (e.g. per-message weixin credential/sync-buf/context-token persistence).
|
|
5
|
+
* Atomic via write-file-atomic's temp+rename, created at 0600 so the secret is
|
|
6
|
+
* never momentarily world-readable. No cross-process lock: weixin's per-account
|
|
7
|
+
* consumer lock already serializes the single writing daemon.
|
|
8
|
+
*/
|
|
9
|
+
interface WritePrivateFileSyncDeps {
|
|
10
|
+
platform?: NodeJS.Platform;
|
|
11
|
+
atomicWrite?: (path: string, content: string) => void;
|
|
12
|
+
directWrite?: (path: string, content: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function writePrivateFileSync(path: string, content: string, deps?: WritePrivateFileSyncDeps): void;
|
|
15
|
+
interface RetryTransientWriteOptions {
|
|
16
|
+
platform?: NodeJS.Platform;
|
|
17
|
+
maxAttempts?: number;
|
|
18
|
+
baseDelayMs?: number;
|
|
19
|
+
maxDelayMs?: number;
|
|
20
|
+
delay?: (ms: number) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare function retryTransientWriteErrors(run: () => Promise<void>, options?: RetryTransientWriteOptions): Promise<void>;
|
|
23
|
+
export declare const __privateFileForTests: {
|
|
24
|
+
retryTransientWriteErrors: typeof retryTransientWriteErrors;
|
|
25
|
+
};
|
|
26
|
+
export {};
|