weacpx 0.3.0 → 0.3.1
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/README.md +63 -21
- package/dist/bridge/bridge-main.js +10 -6
- package/dist/cli.js +982 -733
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -48,18 +48,386 @@ 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/util/private-file.ts
|
|
52
|
+
import { randomBytes } from "node:crypto";
|
|
53
|
+
import { chmod, mkdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
54
|
+
import { basename, dirname, join } from "node:path";
|
|
55
|
+
async function writePrivateFileAtomic(path, content) {
|
|
56
|
+
const dir = dirname(path);
|
|
57
|
+
await mkdir(dir, { recursive: true });
|
|
58
|
+
const tmpPath = join(dir, `.${basename(path)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
|
|
59
|
+
try {
|
|
60
|
+
await writeFile(tmpPath, content, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
|
|
61
|
+
await chmodPrivate(tmpPath);
|
|
62
|
+
await rename(tmpPath, path);
|
|
63
|
+
await chmodPrivate(path);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
await unlinkIfExists(tmpPath);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function chmodPrivate(path) {
|
|
70
|
+
if (process.platform === "win32")
|
|
71
|
+
return;
|
|
72
|
+
await chmod(path, PRIVATE_FILE_MODE).catch(() => {});
|
|
73
|
+
}
|
|
74
|
+
async function unlinkIfExists(path) {
|
|
75
|
+
await unlink(path).catch(() => {});
|
|
76
|
+
}
|
|
77
|
+
var PRIVATE_FILE_MODE = 384;
|
|
78
|
+
var init_private_file = () => {};
|
|
79
|
+
|
|
80
|
+
// src/commands/workspace-path.ts
|
|
81
|
+
import { access } from "node:fs/promises";
|
|
82
|
+
import { homedir } from "node:os";
|
|
83
|
+
import path from "node:path";
|
|
84
|
+
function normalizeWorkspacePath(input) {
|
|
85
|
+
const expanded = expandHome(input);
|
|
86
|
+
if (isWindowsLikePath(expanded)) {
|
|
87
|
+
return path.win32.normalize(expanded).replace(/\\/g, "/");
|
|
88
|
+
}
|
|
89
|
+
return path.posix.normalize(expanded.replace(/\\/g, "/"));
|
|
90
|
+
}
|
|
91
|
+
function basenameForWorkspacePath(input) {
|
|
92
|
+
const normalized = normalizeWorkspacePath(input);
|
|
93
|
+
if (ROOT_PATH_RE.test(normalized)) {
|
|
94
|
+
return normalized;
|
|
95
|
+
}
|
|
96
|
+
const base = path.posix.basename(normalized);
|
|
97
|
+
return base || normalized;
|
|
98
|
+
}
|
|
99
|
+
function sameWorkspacePath(left, right) {
|
|
100
|
+
const normalizedLeft = normalizeWorkspacePath(left);
|
|
101
|
+
const normalizedRight = normalizeWorkspacePath(right);
|
|
102
|
+
if (isWindowsLikePath(normalizedLeft) || isWindowsLikePath(normalizedRight)) {
|
|
103
|
+
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
104
|
+
}
|
|
105
|
+
return normalizedLeft === normalizedRight;
|
|
106
|
+
}
|
|
107
|
+
function expandHome(input) {
|
|
108
|
+
return input.startsWith("~") ? homedir() + input.slice(1) : input;
|
|
109
|
+
}
|
|
110
|
+
async function pathExists(filePath) {
|
|
111
|
+
try {
|
|
112
|
+
await access(filePath);
|
|
113
|
+
return true;
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function isWindowsLikePath(input) {
|
|
119
|
+
return WINDOWS_DRIVE_PATH_RE.test(input) || WINDOWS_UNC_PATH_RE.test(input);
|
|
120
|
+
}
|
|
121
|
+
var WINDOWS_DRIVE_PATH_RE, WINDOWS_UNC_PATH_RE, ROOT_PATH_RE;
|
|
122
|
+
var init_workspace_path = __esm(() => {
|
|
123
|
+
WINDOWS_DRIVE_PATH_RE = /^[a-zA-Z]:[\\/]/;
|
|
124
|
+
WINDOWS_UNC_PATH_RE = /^\\\\/;
|
|
125
|
+
ROOT_PATH_RE = /^(\/|[a-zA-Z]:\/?)$/;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// src/config/resolve-agent-command.ts
|
|
129
|
+
function resolveAgentCommand(driver, command) {
|
|
130
|
+
if (!command) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (driver === "codex" && isLegacyCodexCommand(command)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
return command;
|
|
137
|
+
}
|
|
138
|
+
function isLegacyCodexCommand(command) {
|
|
139
|
+
const normalized = command.trim().replaceAll("\\", "/").toLowerCase();
|
|
140
|
+
return normalized === "./node_modules/.bin/codex-acp" || normalized === "./node_modules/.bin/codex-acp.exe" || normalized.endsWith("/node_modules/.bin/codex-acp") || normalized.endsWith("/node_modules/.bin/codex-acp.exe") || normalized.includes("/@zed-industries/codex-acp/bin/codex-acp.js") || normalized.includes("@zed-industries/codex-acp/bin/codex-acp.js");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/config/load-config.ts
|
|
144
|
+
import { readFile } from "node:fs/promises";
|
|
145
|
+
function isRecord(value) {
|
|
146
|
+
return typeof value === "object" && value !== null;
|
|
147
|
+
}
|
|
148
|
+
async function loadConfig(path2, options = {}) {
|
|
149
|
+
const raw = JSON.parse(await readFile(path2, "utf8"));
|
|
150
|
+
return parseConfig(raw, options);
|
|
151
|
+
}
|
|
152
|
+
function parseConfig(raw, options = {}) {
|
|
153
|
+
if (!isRecord(raw)) {
|
|
154
|
+
throw new Error("config must be a JSON object");
|
|
155
|
+
}
|
|
156
|
+
const transport = raw.transport;
|
|
157
|
+
if (!isRecord(transport)) {
|
|
158
|
+
throw new Error("transport must be an object");
|
|
159
|
+
}
|
|
160
|
+
if ("type" in transport && transport.type !== "acpx-cli" && transport.type !== "acpx-bridge") {
|
|
161
|
+
throw new Error("transport.type must be acpx-cli or acpx-bridge");
|
|
162
|
+
}
|
|
163
|
+
if ("sessionInitTimeoutMs" in transport && (typeof transport.sessionInitTimeoutMs !== "number" || !Number.isFinite(transport.sessionInitTimeoutMs) || transport.sessionInitTimeoutMs <= 0)) {
|
|
164
|
+
throw new Error("transport.sessionInitTimeoutMs must be a positive number");
|
|
165
|
+
}
|
|
166
|
+
if ("permissionMode" in transport && transport.permissionMode !== "approve-all" && transport.permissionMode !== "approve-reads" && transport.permissionMode !== "deny-all") {
|
|
167
|
+
throw new Error("transport.permissionMode must be approve-all, approve-reads, or deny-all");
|
|
168
|
+
}
|
|
169
|
+
if ("nonInteractivePermissions" in transport && transport.nonInteractivePermissions !== "deny" && transport.nonInteractivePermissions !== "fail") {
|
|
170
|
+
throw new Error("transport.nonInteractivePermissions must be deny or fail");
|
|
171
|
+
}
|
|
172
|
+
if (!isRecord(raw.agents)) {
|
|
173
|
+
throw new Error("agents must be an object");
|
|
174
|
+
}
|
|
175
|
+
if (!isRecord(raw.workspaces)) {
|
|
176
|
+
throw new Error("workspaces must be an object");
|
|
177
|
+
}
|
|
178
|
+
const logging = raw.logging;
|
|
179
|
+
const wechat = raw.wechat;
|
|
180
|
+
const orchestration = raw.orchestration;
|
|
181
|
+
if (logging !== undefined && !isRecord(logging)) {
|
|
182
|
+
throw new Error("logging must be an object");
|
|
183
|
+
}
|
|
184
|
+
if (wechat !== undefined && !isRecord(wechat)) {
|
|
185
|
+
throw new Error("wechat must be an object");
|
|
186
|
+
}
|
|
187
|
+
if (orchestration !== undefined && !isRecord(orchestration)) {
|
|
188
|
+
throw new Error("orchestration must be an object");
|
|
189
|
+
}
|
|
190
|
+
if (isRecord(logging) && "level" in logging && logging.level !== "error" && logging.level !== "info" && logging.level !== "debug") {
|
|
191
|
+
throw new Error("logging.level must be error, info, or debug");
|
|
192
|
+
}
|
|
193
|
+
for (const field of ["maxSizeBytes", "maxFiles", "retentionDays"]) {
|
|
194
|
+
if (isRecord(logging) && field in logging && (typeof logging[field] !== "number" || !Number.isFinite(logging[field]) || logging[field] <= 0)) {
|
|
195
|
+
throw new Error(`logging.${field} must be a positive number`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (isRecord(wechat) && "replyMode" in wechat && wechat.replyMode !== "stream" && wechat.replyMode !== "final" && wechat.replyMode !== "verbose") {
|
|
199
|
+
throw new Error("wechat.replyMode must be stream, final, or verbose");
|
|
200
|
+
}
|
|
201
|
+
for (const [name, agent] of Object.entries(raw.agents)) {
|
|
202
|
+
if (!isRecord(agent) || typeof agent.driver !== "string" || agent.driver.length === 0) {
|
|
203
|
+
throw new Error(`agent "${name}" must define a non-empty driver`);
|
|
204
|
+
}
|
|
205
|
+
if ("command" in agent && (typeof agent.command !== "string" || agent.command.length === 0)) {
|
|
206
|
+
throw new Error(`agent "${name}" command must be a non-empty string`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const [name, workspace] of Object.entries(raw.workspaces)) {
|
|
210
|
+
if (!isRecord(workspace) || typeof workspace.cwd !== "string" || workspace.cwd.length === 0) {
|
|
211
|
+
throw new Error(`workspace "${name}" must define a non-empty cwd`);
|
|
212
|
+
}
|
|
213
|
+
if ("allowed_agents" in workspace && (!Array.isArray(workspace.allowed_agents) || workspace.allowed_agents.some((value) => typeof value !== "string"))) {
|
|
214
|
+
throw new Error(`workspace "${name}" allowed_agents must be an array of strings`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const rawAgents = raw.agents;
|
|
218
|
+
const agents = {};
|
|
219
|
+
for (const [name, agent] of Object.entries(rawAgents)) {
|
|
220
|
+
const driver = agent.driver;
|
|
221
|
+
const command = typeof agent.command === "string" ? resolveAgentCommand(driver, agent.command) : undefined;
|
|
222
|
+
agents[name] = {
|
|
223
|
+
driver,
|
|
224
|
+
...command ? { command } : {}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const rawWorkspaces = raw.workspaces;
|
|
228
|
+
const workspaces = {};
|
|
229
|
+
for (const [name, workspace] of Object.entries(rawWorkspaces)) {
|
|
230
|
+
workspaces[name] = {
|
|
231
|
+
cwd: normalizeWorkspacePath(workspace.cwd),
|
|
232
|
+
...typeof workspace.description === "string" ? { description: workspace.description } : {}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const transportType = transport.type === "acpx-cli" || transport.type === "acpx-bridge" ? transport.type : "acpx-bridge";
|
|
236
|
+
const permissionMode = transport.permissionMode === "approve-all" || transport.permissionMode === "approve-reads" || transport.permissionMode === "deny-all" ? transport.permissionMode : DEFAULT_PERMISSION_MODE;
|
|
237
|
+
const nonInteractivePermissions = transport.nonInteractivePermissions === "deny" || transport.nonInteractivePermissions === "fail" ? transport.nonInteractivePermissions : DEFAULT_NON_INTERACTIVE_PERMISSIONS;
|
|
238
|
+
const loggingLevel = logging?.level;
|
|
239
|
+
const resolvedLoggingLevel = loggingLevel === "error" || loggingLevel === "info" || loggingLevel === "debug" ? loggingLevel : options.defaultLoggingLevel ?? DEFAULT_LOGGING_CONFIG.level;
|
|
240
|
+
const replyMode = wechat?.replyMode === "stream" || wechat?.replyMode === "final" || wechat?.replyMode === "verbose" ? wechat.replyMode : DEFAULT_WECHAT_REPLY_MODE;
|
|
241
|
+
const orchestrationConfig = parseOrchestrationConfig(orchestration);
|
|
242
|
+
return {
|
|
243
|
+
transport: {
|
|
244
|
+
...typeof transport.command === "string" ? { command: transport.command } : {},
|
|
245
|
+
...typeof transport.sessionInitTimeoutMs === "number" ? { sessionInitTimeoutMs: transport.sessionInitTimeoutMs } : {},
|
|
246
|
+
type: transportType,
|
|
247
|
+
permissionMode,
|
|
248
|
+
nonInteractivePermissions
|
|
249
|
+
},
|
|
250
|
+
logging: {
|
|
251
|
+
level: resolvedLoggingLevel,
|
|
252
|
+
maxSizeBytes: typeof logging?.maxSizeBytes === "number" ? logging.maxSizeBytes : DEFAULT_LOGGING_CONFIG.maxSizeBytes,
|
|
253
|
+
maxFiles: typeof logging?.maxFiles === "number" ? logging.maxFiles : DEFAULT_LOGGING_CONFIG.maxFiles,
|
|
254
|
+
retentionDays: typeof logging?.retentionDays === "number" ? logging.retentionDays : DEFAULT_LOGGING_CONFIG.retentionDays
|
|
255
|
+
},
|
|
256
|
+
wechat: {
|
|
257
|
+
replyMode
|
|
258
|
+
},
|
|
259
|
+
agents,
|
|
260
|
+
workspaces,
|
|
261
|
+
orchestration: orchestrationConfig
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function parseOrchestrationConfig(raw) {
|
|
265
|
+
if (!isRecord(raw)) {
|
|
266
|
+
return {
|
|
267
|
+
...DEFAULT_ORCHESTRATION_CONFIG
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
maxPendingAgentRequestsPerCoordinator: typeof raw.maxPendingAgentRequestsPerCoordinator === "number" && Number.isFinite(raw.maxPendingAgentRequestsPerCoordinator) && raw.maxPendingAgentRequestsPerCoordinator > 0 ? raw.maxPendingAgentRequestsPerCoordinator : DEFAULT_ORCHESTRATION_CONFIG.maxPendingAgentRequestsPerCoordinator,
|
|
272
|
+
allowWorkerChainedRequests: raw.allowWorkerChainedRequests === true,
|
|
273
|
+
allowedAgentRequestTargets: Array.isArray(raw.allowedAgentRequestTargets) ? raw.allowedAgentRequestTargets.filter((value) => typeof value === "string") : [...DEFAULT_ORCHESTRATION_CONFIG.allowedAgentRequestTargets],
|
|
274
|
+
allowedAgentRequestRoles: Array.isArray(raw.allowedAgentRequestRoles) ? raw.allowedAgentRequestRoles.filter((value) => typeof value === "string") : [...DEFAULT_ORCHESTRATION_CONFIG.allowedAgentRequestRoles],
|
|
275
|
+
progressHeartbeatSeconds: typeof raw.progressHeartbeatSeconds === "number" && Number.isFinite(raw.progressHeartbeatSeconds) ? raw.progressHeartbeatSeconds : DEFAULT_ORCHESTRATION_CONFIG.progressHeartbeatSeconds
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
var DEFAULT_LOGGING_CONFIG, DEFAULT_PERMISSION_MODE = "approve-all", DEFAULT_NON_INTERACTIVE_PERMISSIONS = "deny", DEFAULT_WECHAT_REPLY_MODE = "verbose", DEFAULT_ORCHESTRATION_CONFIG;
|
|
279
|
+
var init_load_config = __esm(() => {
|
|
280
|
+
init_workspace_path();
|
|
281
|
+
DEFAULT_LOGGING_CONFIG = {
|
|
282
|
+
level: "info",
|
|
283
|
+
maxSizeBytes: 2 * 1024 * 1024,
|
|
284
|
+
maxFiles: 5,
|
|
285
|
+
retentionDays: 7
|
|
286
|
+
};
|
|
287
|
+
DEFAULT_ORCHESTRATION_CONFIG = {
|
|
288
|
+
maxPendingAgentRequestsPerCoordinator: 3,
|
|
289
|
+
allowWorkerChainedRequests: false,
|
|
290
|
+
allowedAgentRequestTargets: [],
|
|
291
|
+
allowedAgentRequestRoles: [],
|
|
292
|
+
progressHeartbeatSeconds: 300
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// src/config/config-store.ts
|
|
297
|
+
class ConfigStore {
|
|
298
|
+
path;
|
|
299
|
+
constructor(path2) {
|
|
300
|
+
this.path = path2;
|
|
301
|
+
}
|
|
302
|
+
async load() {
|
|
303
|
+
return await loadConfig(this.path);
|
|
304
|
+
}
|
|
305
|
+
async save(config) {
|
|
306
|
+
await writePrivateFileAtomic(this.path, `${JSON.stringify(config, null, 2)}
|
|
307
|
+
`);
|
|
308
|
+
}
|
|
309
|
+
async upsertWorkspace(name, cwd, description) {
|
|
310
|
+
const config = await this.load();
|
|
311
|
+
const workspace = {
|
|
312
|
+
cwd,
|
|
313
|
+
...description ? { description } : {}
|
|
314
|
+
};
|
|
315
|
+
config.workspaces[name] = workspace;
|
|
316
|
+
await this.save(config);
|
|
317
|
+
return config;
|
|
318
|
+
}
|
|
319
|
+
async removeWorkspace(name) {
|
|
320
|
+
const config = await this.load();
|
|
321
|
+
delete config.workspaces[name];
|
|
322
|
+
await this.save(config);
|
|
323
|
+
return config;
|
|
324
|
+
}
|
|
325
|
+
async upsertAgent(name, agent) {
|
|
326
|
+
const config = await this.load();
|
|
327
|
+
config.agents[name] = agent;
|
|
328
|
+
await this.save(config);
|
|
329
|
+
return config;
|
|
330
|
+
}
|
|
331
|
+
async removeAgent(name) {
|
|
332
|
+
const config = await this.load();
|
|
333
|
+
delete config.agents[name];
|
|
334
|
+
await this.save(config);
|
|
335
|
+
return config;
|
|
336
|
+
}
|
|
337
|
+
async updateTransport(transport) {
|
|
338
|
+
const config = await this.load();
|
|
339
|
+
config.transport = {
|
|
340
|
+
...config.transport,
|
|
341
|
+
...transport
|
|
342
|
+
};
|
|
343
|
+
await this.save(config);
|
|
344
|
+
return config;
|
|
345
|
+
}
|
|
346
|
+
async updateWechat(wechat) {
|
|
347
|
+
const config = await this.load();
|
|
348
|
+
config.wechat = {
|
|
349
|
+
...config.wechat,
|
|
350
|
+
...wechat
|
|
351
|
+
};
|
|
352
|
+
await this.save(config);
|
|
353
|
+
return config;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
var init_config_store = __esm(() => {
|
|
357
|
+
init_private_file();
|
|
358
|
+
init_load_config();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// src/config/ensure-config.ts
|
|
362
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
363
|
+
async function ensureConfigExists(path2) {
|
|
364
|
+
try {
|
|
365
|
+
await loadConfig(path2);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
if (!isMissingFileError(error)) {
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
const store = new ConfigStore(path2);
|
|
371
|
+
await store.save(await loadDefaultConfigTemplate());
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async function loadDefaultConfigTemplate() {
|
|
375
|
+
const candidates = [
|
|
376
|
+
new URL("../../config.example.json", import.meta.url),
|
|
377
|
+
new URL("../config.example.json", import.meta.url)
|
|
378
|
+
];
|
|
379
|
+
let raw;
|
|
380
|
+
let lastError;
|
|
381
|
+
for (const candidate of candidates) {
|
|
382
|
+
try {
|
|
383
|
+
raw = await readFile2(candidate, "utf8");
|
|
384
|
+
break;
|
|
385
|
+
} catch (error) {
|
|
386
|
+
if (!isMissingFileError(error)) {
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
lastError = error;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (!raw) {
|
|
393
|
+
throw lastError;
|
|
394
|
+
}
|
|
395
|
+
return normalizeDefaultConfigTemplate(JSON.parse(raw));
|
|
396
|
+
}
|
|
397
|
+
function normalizeDefaultConfigTemplate(raw) {
|
|
398
|
+
const template = parseConfig(raw);
|
|
399
|
+
return {
|
|
400
|
+
...template,
|
|
401
|
+
agents: Object.fromEntries(Object.entries(template.agents).map(([name, agent]) => [
|
|
402
|
+
name,
|
|
403
|
+
{
|
|
404
|
+
driver: agent.driver,
|
|
405
|
+
...resolveAgentCommand(agent.driver, agent.command) ? { command: resolveAgentCommand(agent.driver, agent.command) } : {}
|
|
406
|
+
}
|
|
407
|
+
])),
|
|
408
|
+
workspaces: {}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function isMissingFileError(error) {
|
|
412
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
413
|
+
}
|
|
414
|
+
var init_ensure_config = __esm(() => {
|
|
415
|
+
init_config_store();
|
|
416
|
+
init_load_config();
|
|
417
|
+
});
|
|
418
|
+
|
|
51
419
|
// src/daemon/daemon-status.ts
|
|
52
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
53
|
-
import { dirname } from "node:path";
|
|
420
|
+
import { mkdir as mkdir2, readFile as readFile3, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
421
|
+
import { dirname as dirname2 } from "node:path";
|
|
54
422
|
|
|
55
423
|
class DaemonStatusStore {
|
|
56
424
|
path;
|
|
57
|
-
constructor(
|
|
58
|
-
this.path =
|
|
425
|
+
constructor(path2) {
|
|
426
|
+
this.path = path2;
|
|
59
427
|
}
|
|
60
428
|
async load() {
|
|
61
429
|
try {
|
|
62
|
-
const content = await
|
|
430
|
+
const content = await readFile3(this.path, "utf8");
|
|
63
431
|
if (content.trim() === "") {
|
|
64
432
|
return null;
|
|
65
433
|
}
|
|
@@ -72,8 +440,8 @@ class DaemonStatusStore {
|
|
|
72
440
|
}
|
|
73
441
|
}
|
|
74
442
|
async save(status) {
|
|
75
|
-
await
|
|
76
|
-
await
|
|
443
|
+
await mkdir2(dirname2(this.path), { recursive: true });
|
|
444
|
+
await writeFile2(this.path, JSON.stringify(status, null, 2));
|
|
77
445
|
}
|
|
78
446
|
async clear() {
|
|
79
447
|
await rm(this.path, { force: true });
|
|
@@ -82,8 +450,8 @@ class DaemonStatusStore {
|
|
|
82
450
|
var init_daemon_status = () => {};
|
|
83
451
|
|
|
84
452
|
// src/daemon/daemon-controller.ts
|
|
85
|
-
import { mkdir as
|
|
86
|
-
import { dirname as
|
|
453
|
+
import { mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
454
|
+
import { dirname as dirname3 } from "node:path";
|
|
87
455
|
|
|
88
456
|
class DaemonController {
|
|
89
457
|
paths;
|
|
@@ -157,7 +525,7 @@ class DaemonController {
|
|
|
157
525
|
}
|
|
158
526
|
async loadPid() {
|
|
159
527
|
try {
|
|
160
|
-
const content = await
|
|
528
|
+
const content = await readFile4(this.paths.pidFile, "utf8");
|
|
161
529
|
const pid = Number(content.trim());
|
|
162
530
|
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
163
531
|
} catch (error) {
|
|
@@ -168,8 +536,8 @@ class DaemonController {
|
|
|
168
536
|
}
|
|
169
537
|
}
|
|
170
538
|
async writePid(pid) {
|
|
171
|
-
await
|
|
172
|
-
await
|
|
539
|
+
await mkdir3(dirname3(this.paths.pidFile), { recursive: true });
|
|
540
|
+
await writeFile3(this.paths.pidFile, `${pid}
|
|
173
541
|
`);
|
|
174
542
|
}
|
|
175
543
|
async clearRuntimeFiles() {
|
|
@@ -211,7 +579,7 @@ var init_daemon_controller = __esm(() => {
|
|
|
211
579
|
|
|
212
580
|
// src/process/terminate-process-tree.ts
|
|
213
581
|
import { spawn } from "node:child_process";
|
|
214
|
-
async function terminateProcessTree(pid, platform = process.platform, runCommand = defaultRunProcessCommand, killProcess = (targetPid, signal) => {
|
|
582
|
+
async function terminateProcessTree(pid, options = {}, platform = process.platform, runCommand = defaultRunProcessCommand, killProcess = (targetPid, signal) => {
|
|
215
583
|
process.kill(targetPid, signal);
|
|
216
584
|
}, isProcessRunning = defaultIsProcessRunning) {
|
|
217
585
|
if (pid <= 0) {
|
|
@@ -223,7 +591,7 @@ async function terminateProcessTree(pid, platform = process.platform, runCommand
|
|
|
223
591
|
} catch {}
|
|
224
592
|
return;
|
|
225
593
|
}
|
|
226
|
-
const targetPid =
|
|
594
|
+
const targetPid = options.detachedProcessGroup ? -pid : pid;
|
|
227
595
|
try {
|
|
228
596
|
killProcess(targetPid, "SIGTERM");
|
|
229
597
|
} catch {
|
|
@@ -258,13 +626,13 @@ async function defaultRunProcessCommand(command, args) {
|
|
|
258
626
|
var init_terminate_process_tree = () => {};
|
|
259
627
|
|
|
260
628
|
// src/daemon/create-daemon-controller.ts
|
|
261
|
-
import { mkdir as
|
|
629
|
+
import { mkdir as mkdir4, open } from "node:fs/promises";
|
|
262
630
|
import { spawn as spawn2 } from "node:child_process";
|
|
263
631
|
function createDaemonController(paths, options) {
|
|
264
632
|
return new DaemonController(paths, {
|
|
265
633
|
isProcessRunning: options.isProcessRunning ?? defaultIsProcessRunning2,
|
|
266
634
|
spawnDetached: async () => {
|
|
267
|
-
await
|
|
635
|
+
await mkdir4(paths.runtimeDir, { recursive: true });
|
|
268
636
|
const stdoutHandle = await open(paths.stdoutLog, "a");
|
|
269
637
|
const stderrHandle = await open(paths.stderrLog, "a");
|
|
270
638
|
try {
|
|
@@ -395,7 +763,7 @@ async function spawnWindowsHiddenProcess(request) {
|
|
|
395
763
|
});
|
|
396
764
|
}
|
|
397
765
|
async function defaultTerminateProcess(pid) {
|
|
398
|
-
await terminateProcessTree(pid);
|
|
766
|
+
await terminateProcessTree(pid, { detachedProcessGroup: true });
|
|
399
767
|
}
|
|
400
768
|
var init_create_daemon_controller = __esm(() => {
|
|
401
769
|
init_daemon_controller();
|
|
@@ -404,7 +772,7 @@ var init_create_daemon_controller = __esm(() => {
|
|
|
404
772
|
|
|
405
773
|
// src/orchestration/orchestration-ipc.ts
|
|
406
774
|
import { createHash } from "node:crypto";
|
|
407
|
-
import { join } from "node:path";
|
|
775
|
+
import { join as join2 } from "node:path";
|
|
408
776
|
function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
409
777
|
if (platform === "win32") {
|
|
410
778
|
const suffix = createHash("sha256").update(runtimeDir).digest("hex").slice(0, 12);
|
|
@@ -415,13 +783,13 @@ function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
|
415
783
|
}
|
|
416
784
|
return {
|
|
417
785
|
kind: "unix",
|
|
418
|
-
path:
|
|
786
|
+
path: join2(runtimeDir, "orchestration.sock")
|
|
419
787
|
};
|
|
420
788
|
}
|
|
421
|
-
function createOrchestrationEndpoint(
|
|
789
|
+
function createOrchestrationEndpoint(path2, platform = process.platform) {
|
|
422
790
|
return {
|
|
423
|
-
kind: platform === "win32" ||
|
|
424
|
-
path
|
|
791
|
+
kind: platform === "win32" || path2.startsWith("\\\\.\\pipe\\") ? "named-pipe" : "unix",
|
|
792
|
+
path: path2
|
|
425
793
|
};
|
|
426
794
|
}
|
|
427
795
|
function encodeOrchestrationRpcRequest(request) {
|
|
@@ -435,20 +803,20 @@ function encodeOrchestrationRpcResponse(response) {
|
|
|
435
803
|
var init_orchestration_ipc = () => {};
|
|
436
804
|
|
|
437
805
|
// src/daemon/daemon-files.ts
|
|
438
|
-
import { dirname as
|
|
806
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
439
807
|
function resolveDaemonPaths(options) {
|
|
440
|
-
const runtimeDir = options.runtimeDir ??
|
|
808
|
+
const runtimeDir = options.runtimeDir ?? join3(options.home, ".weacpx", "runtime");
|
|
441
809
|
return {
|
|
442
810
|
runtimeDir,
|
|
443
|
-
pidFile:
|
|
444
|
-
statusFile:
|
|
445
|
-
stdoutLog:
|
|
446
|
-
stderrLog:
|
|
447
|
-
appLog:
|
|
811
|
+
pidFile: join3(runtimeDir, "daemon.pid"),
|
|
812
|
+
statusFile: join3(runtimeDir, "status.json"),
|
|
813
|
+
stdoutLog: join3(runtimeDir, "stdout.log"),
|
|
814
|
+
stderrLog: join3(runtimeDir, "stderr.log"),
|
|
815
|
+
appLog: join3(runtimeDir, "app.log")
|
|
448
816
|
};
|
|
449
817
|
}
|
|
450
818
|
function resolveRuntimeDirFromConfigPath(configPath) {
|
|
451
|
-
return
|
|
819
|
+
return join3(dirname4(configPath), "runtime");
|
|
452
820
|
}
|
|
453
821
|
function resolveDaemonOrchestrationSocketPath(runtimeDir, platform = process.platform) {
|
|
454
822
|
return resolveOrchestrationEndpoint(runtimeDir, platform).path;
|
|
@@ -3581,8 +3949,8 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
3581
3949
|
}
|
|
3582
3950
|
return ind;
|
|
3583
3951
|
}
|
|
3584
|
-
function removeDotSegments(
|
|
3585
|
-
let input =
|
|
3952
|
+
function removeDotSegments(path2) {
|
|
3953
|
+
let input = path2;
|
|
3586
3954
|
const output = [];
|
|
3587
3955
|
let nextSlash = -1;
|
|
3588
3956
|
let len = 0;
|
|
@@ -3772,8 +4140,8 @@ var require_schemes = __commonJS((exports, module) => {
|
|
|
3772
4140
|
wsComponent.secure = undefined;
|
|
3773
4141
|
}
|
|
3774
4142
|
if (wsComponent.resourceName) {
|
|
3775
|
-
const [
|
|
3776
|
-
wsComponent.path =
|
|
4143
|
+
const [path2, query] = wsComponent.resourceName.split("?");
|
|
4144
|
+
wsComponent.path = path2 && path2 !== "/" ? path2 : undefined;
|
|
3777
4145
|
wsComponent.query = query;
|
|
3778
4146
|
wsComponent.resourceName = undefined;
|
|
3779
4147
|
}
|
|
@@ -6931,12 +7299,12 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
6931
7299
|
|
|
6932
7300
|
// src/version.ts
|
|
6933
7301
|
import fs from "node:fs";
|
|
6934
|
-
import
|
|
7302
|
+
import path2 from "node:path";
|
|
6935
7303
|
import { fileURLToPath } from "node:url";
|
|
6936
7304
|
function readVersion() {
|
|
6937
7305
|
try {
|
|
6938
|
-
const dir =
|
|
6939
|
-
const pkgPath =
|
|
7306
|
+
const dir = path2.dirname(fileURLToPath(import.meta.url));
|
|
7307
|
+
const pkgPath = path2.resolve(dir, "..", "..", "package.json");
|
|
6940
7308
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
6941
7309
|
return pkg.version ?? "unknown";
|
|
6942
7310
|
} catch {
|
|
@@ -7167,16 +7535,16 @@ var init_quota_errors = __esm(() => {
|
|
|
7167
7535
|
});
|
|
7168
7536
|
|
|
7169
7537
|
// src/weixin/monitor/consumer-lock.ts
|
|
7170
|
-
import { mkdir as
|
|
7171
|
-
import { dirname as
|
|
7172
|
-
import { homedir as
|
|
7538
|
+
import { mkdir as mkdir6, open as open2, readFile as readFile5, rm as rm4 } from "node:fs/promises";
|
|
7539
|
+
import { dirname as dirname6, join as join4 } from "node:path";
|
|
7540
|
+
import { homedir as homedir3 } from "node:os";
|
|
7173
7541
|
function createWeixinConsumerLock(options = {}) {
|
|
7174
|
-
const lockFilePath = options.lockFilePath ??
|
|
7542
|
+
const lockFilePath = options.lockFilePath ?? join4(homedir3(), ".weacpx", "runtime", "weixin-consumer.lock.json");
|
|
7175
7543
|
const isProcessRunning = options.isProcessRunning ?? defaultIsProcessRunning3;
|
|
7176
7544
|
const onDiagnostic = options.onDiagnostic;
|
|
7177
7545
|
return {
|
|
7178
7546
|
async acquire(meta2) {
|
|
7179
|
-
await
|
|
7547
|
+
await mkdir6(dirname6(lockFilePath), { recursive: true });
|
|
7180
7548
|
while (true) {
|
|
7181
7549
|
try {
|
|
7182
7550
|
const handle = await open2(lockFilePath, "wx");
|
|
@@ -7247,9 +7615,9 @@ function createWeixinConsumerLock(options = {}) {
|
|
|
7247
7615
|
}
|
|
7248
7616
|
};
|
|
7249
7617
|
}
|
|
7250
|
-
async function loadLockMetadata(
|
|
7618
|
+
async function loadLockMetadata(path3) {
|
|
7251
7619
|
try {
|
|
7252
|
-
const raw = await
|
|
7620
|
+
const raw = await readFile5(path3, "utf8");
|
|
7253
7621
|
const parsed = JSON.parse(raw);
|
|
7254
7622
|
if (!parsed || typeof parsed.pid !== "number" || !parsed.mode || !parsed.configPath || !parsed.statePath) {
|
|
7255
7623
|
return null;
|
|
@@ -8308,15 +8676,15 @@ var require_main = __commonJS((exports, module) => {
|
|
|
8308
8676
|
|
|
8309
8677
|
// src/weixin/storage/state-dir.ts
|
|
8310
8678
|
import os from "node:os";
|
|
8311
|
-
import
|
|
8679
|
+
import path3 from "node:path";
|
|
8312
8680
|
function resolveStateDir() {
|
|
8313
|
-
return process.env.OPENCLAW_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim() ||
|
|
8681
|
+
return process.env.OPENCLAW_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim() || path3.join(os.homedir(), ".openclaw");
|
|
8314
8682
|
}
|
|
8315
8683
|
var init_state_dir = () => {};
|
|
8316
8684
|
|
|
8317
8685
|
// src/weixin/auth/accounts.ts
|
|
8318
8686
|
import fs2 from "node:fs";
|
|
8319
|
-
import
|
|
8687
|
+
import path4 from "node:path";
|
|
8320
8688
|
function normalizeAccountId(raw) {
|
|
8321
8689
|
return raw.trim().toLowerCase().replace(/[@.]/g, "-");
|
|
8322
8690
|
}
|
|
@@ -8330,10 +8698,10 @@ function deriveRawAccountId(normalizedId) {
|
|
|
8330
8698
|
return;
|
|
8331
8699
|
}
|
|
8332
8700
|
function resolveWeixinStateDir() {
|
|
8333
|
-
return
|
|
8701
|
+
return path4.join(resolveStateDir(), "openclaw-weixin");
|
|
8334
8702
|
}
|
|
8335
8703
|
function resolveAccountIndexPath() {
|
|
8336
|
-
return
|
|
8704
|
+
return path4.join(resolveWeixinStateDir(), "accounts.json");
|
|
8337
8705
|
}
|
|
8338
8706
|
function listIndexedWeixinAccountIds() {
|
|
8339
8707
|
const filePath = resolveAccountIndexPath();
|
|
@@ -8355,13 +8723,13 @@ function registerWeixinAccountId(accountId) {
|
|
|
8355
8723
|
fs2.writeFileSync(resolveAccountIndexPath(), JSON.stringify([accountId], null, 2), "utf-8");
|
|
8356
8724
|
}
|
|
8357
8725
|
function resolveAccountsDir() {
|
|
8358
|
-
return
|
|
8726
|
+
return path4.join(resolveWeixinStateDir(), "accounts");
|
|
8359
8727
|
}
|
|
8360
8728
|
function resolveAccountPath(accountId) {
|
|
8361
|
-
return
|
|
8729
|
+
return path4.join(resolveAccountsDir(), `${accountId}.json`);
|
|
8362
8730
|
}
|
|
8363
8731
|
function loadLegacyToken() {
|
|
8364
|
-
const legacyPath =
|
|
8732
|
+
const legacyPath = path4.join(resolveStateDir(), "credentials", "openclaw-weixin", "credentials.json");
|
|
8365
8733
|
try {
|
|
8366
8734
|
if (!fs2.existsSync(legacyPath))
|
|
8367
8735
|
return;
|
|
@@ -8431,7 +8799,7 @@ function resolveConfigPath() {
|
|
|
8431
8799
|
const envPath = process.env.OPENCLAW_CONFIG?.trim();
|
|
8432
8800
|
if (envPath)
|
|
8433
8801
|
return envPath;
|
|
8434
|
-
return
|
|
8802
|
+
return path4.join(resolveStateDir(), "openclaw.json");
|
|
8435
8803
|
}
|
|
8436
8804
|
function loadConfigRouteTag(accountId) {
|
|
8437
8805
|
try {
|
|
@@ -8488,7 +8856,7 @@ var init_accounts = __esm(() => {
|
|
|
8488
8856
|
// src/weixin/util/logger.ts
|
|
8489
8857
|
import fs3 from "node:fs";
|
|
8490
8858
|
import os2 from "node:os";
|
|
8491
|
-
import
|
|
8859
|
+
import path5 from "node:path";
|
|
8492
8860
|
function resolveMinLevel() {
|
|
8493
8861
|
const env = process.env.OPENCLAW_LOG_LEVEL?.toUpperCase();
|
|
8494
8862
|
if (env && env in LEVEL_IDS)
|
|
@@ -8507,7 +8875,7 @@ function localDateKey(now) {
|
|
|
8507
8875
|
}
|
|
8508
8876
|
function resolveMainLogPath() {
|
|
8509
8877
|
const dateKey = localDateKey(new Date);
|
|
8510
|
-
return
|
|
8878
|
+
return path5.join(MAIN_LOG_DIR, `openclaw-${dateKey}.log`);
|
|
8511
8879
|
}
|
|
8512
8880
|
function buildLoggerName(accountId) {
|
|
8513
8881
|
return accountId ? `${SUBSYSTEM}/${accountId}` : SUBSYSTEM;
|
|
@@ -8568,7 +8936,7 @@ function createLogger(accountId) {
|
|
|
8568
8936
|
}
|
|
8569
8937
|
var MAIN_LOG_DIR, SUBSYSTEM = "gateway/channels/openclaw-weixin", RUNTIME = "node", RUNTIME_VERSION, HOSTNAME, PARENT_NAMES, LEVEL_IDS, DEFAULT_LOG_LEVEL = "INFO", minLevelId, logDirEnsured = false, logger;
|
|
8570
8938
|
var init_logger = __esm(() => {
|
|
8571
|
-
MAIN_LOG_DIR =
|
|
8939
|
+
MAIN_LOG_DIR = path5.join("/tmp", "openclaw");
|
|
8572
8940
|
RUNTIME_VERSION = process.versions.node;
|
|
8573
8941
|
HOSTNAME = os2.hostname() || "unknown";
|
|
8574
8942
|
PARENT_NAMES = ["openclaw"];
|
|
@@ -8602,6 +8970,49 @@ function redactToken(token, prefixLen = DEFAULT_TOKEN_PREFIX_LEN) {
|
|
|
8602
8970
|
function redactBody(body, maxLen = DEFAULT_BODY_MAX_LEN) {
|
|
8603
8971
|
if (!body)
|
|
8604
8972
|
return "(empty)";
|
|
8973
|
+
const parsed = parseJson(body);
|
|
8974
|
+
if (parsed !== undefined) {
|
|
8975
|
+
return truncateForBody(JSON.stringify(redactJsonValue(parsed)), maxLen);
|
|
8976
|
+
}
|
|
8977
|
+
return truncateForBody(body, maxLen);
|
|
8978
|
+
}
|
|
8979
|
+
function parseJson(body) {
|
|
8980
|
+
try {
|
|
8981
|
+
return JSON.parse(body);
|
|
8982
|
+
} catch {
|
|
8983
|
+
return;
|
|
8984
|
+
}
|
|
8985
|
+
}
|
|
8986
|
+
function redactJsonValue(value, key) {
|
|
8987
|
+
const normalizedKey = key?.toLowerCase();
|
|
8988
|
+
if (normalizedKey && SECRET_KEYS.has(normalizedKey)) {
|
|
8989
|
+
return redactScalar(value);
|
|
8990
|
+
}
|
|
8991
|
+
if (normalizedKey && CONTENT_KEYS.has(normalizedKey)) {
|
|
8992
|
+
return redactContent(value);
|
|
8993
|
+
}
|
|
8994
|
+
if (Array.isArray(value)) {
|
|
8995
|
+
return value.map((item) => redactJsonValue(item));
|
|
8996
|
+
}
|
|
8997
|
+
if (value && typeof value === "object") {
|
|
8998
|
+
return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [
|
|
8999
|
+
entryKey,
|
|
9000
|
+
redactJsonValue(entryValue, entryKey)
|
|
9001
|
+
]));
|
|
9002
|
+
}
|
|
9003
|
+
return value;
|
|
9004
|
+
}
|
|
9005
|
+
function redactScalar(value) {
|
|
9006
|
+
const len = typeof value === "string" ? value.length : JSON.stringify(value)?.length ?? 0;
|
|
9007
|
+
return `<redacted len=${len}>`;
|
|
9008
|
+
}
|
|
9009
|
+
function redactContent(value) {
|
|
9010
|
+
if (typeof value === "string") {
|
|
9011
|
+
return `<redacted len=${value.length}>`;
|
|
9012
|
+
}
|
|
9013
|
+
return redactJsonValue(value);
|
|
9014
|
+
}
|
|
9015
|
+
function truncateForBody(body, maxLen) {
|
|
8605
9016
|
if (body.length <= maxLen)
|
|
8606
9017
|
return body;
|
|
8607
9018
|
return `${body.slice(0, maxLen)}…(truncated, totalLen=${body.length})`;
|
|
@@ -8615,7 +9026,27 @@ function redactUrl(rawUrl) {
|
|
|
8615
9026
|
return truncate(rawUrl, 80);
|
|
8616
9027
|
}
|
|
8617
9028
|
}
|
|
8618
|
-
var DEFAULT_BODY_MAX_LEN = 200, DEFAULT_TOKEN_PREFIX_LEN = 6;
|
|
9029
|
+
var DEFAULT_BODY_MAX_LEN = 200, DEFAULT_TOKEN_PREFIX_LEN = 6, SECRET_KEYS, CONTENT_KEYS;
|
|
9030
|
+
var init_redact = __esm(() => {
|
|
9031
|
+
SECRET_KEYS = new Set([
|
|
9032
|
+
"authorization",
|
|
9033
|
+
"access_token",
|
|
9034
|
+
"aes_key",
|
|
9035
|
+
"aeskey",
|
|
9036
|
+
"context_token",
|
|
9037
|
+
"replycontexttoken",
|
|
9038
|
+
"secret",
|
|
9039
|
+
"signature",
|
|
9040
|
+
"token"
|
|
9041
|
+
]);
|
|
9042
|
+
CONTENT_KEYS = new Set([
|
|
9043
|
+
"content",
|
|
9044
|
+
"message",
|
|
9045
|
+
"msg",
|
|
9046
|
+
"rawtext",
|
|
9047
|
+
"text"
|
|
9048
|
+
]);
|
|
9049
|
+
});
|
|
8619
9050
|
|
|
8620
9051
|
// src/weixin/messaging/send-errors.ts
|
|
8621
9052
|
function formatMessage(input) {
|
|
@@ -8900,6 +9331,7 @@ var init_api = __esm(() => {
|
|
|
8900
9331
|
init_version();
|
|
8901
9332
|
init_accounts();
|
|
8902
9333
|
init_logger();
|
|
9334
|
+
init_redact();
|
|
8903
9335
|
init_send_errors();
|
|
8904
9336
|
CHANNEL_VERSION = readVersion();
|
|
8905
9337
|
});
|
|
@@ -9129,6 +9561,7 @@ var ACTIVE_LOGIN_TTL_MS, GET_QRCODE_TIMEOUT_MS = 5000, QR_LONG_POLL_TIMEOUT_MS =
|
|
|
9129
9561
|
var init_login_qr = __esm(() => {
|
|
9130
9562
|
init_api();
|
|
9131
9563
|
init_logger();
|
|
9564
|
+
init_redact();
|
|
9132
9565
|
ACTIVE_LOGIN_TTL_MS = 5 * 60000;
|
|
9133
9566
|
activeLogins = new Map;
|
|
9134
9567
|
});
|
|
@@ -9350,111 +9783,16 @@ var init_types = __esm(() => {
|
|
|
9350
9783
|
};
|
|
9351
9784
|
});
|
|
9352
9785
|
|
|
9353
|
-
// src/weixin/cdn/aes-ecb.ts
|
|
9354
|
-
import { createCipheriv, createDecipheriv } from "node:crypto";
|
|
9355
|
-
function encryptAesEcb(plaintext, key) {
|
|
9356
|
-
const cipher = createCipheriv("aes-128-ecb", key, null);
|
|
9357
|
-
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
9358
|
-
}
|
|
9359
|
-
function decryptAesEcb(ciphertext, key) {
|
|
9360
|
-
const decipher = createDecipheriv("aes-128-ecb", key, null);
|
|
9361
|
-
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
9362
|
-
}
|
|
9363
|
-
function aesEcbPaddedSize(plaintextSize) {
|
|
9364
|
-
return Math.ceil((plaintextSize + 1) / 16) * 16;
|
|
9365
|
-
}
|
|
9366
|
-
var init_aes_ecb = () => {};
|
|
9367
|
-
|
|
9368
|
-
// src/weixin/cdn/cdn-url.ts
|
|
9369
|
-
function buildCdnDownloadUrl(encryptedQueryParam, cdnBaseUrl) {
|
|
9370
|
-
return `${cdnBaseUrl}/download?encrypted_query_param=${encodeURIComponent(encryptedQueryParam)}`;
|
|
9371
|
-
}
|
|
9372
|
-
function buildCdnUploadUrl(params) {
|
|
9373
|
-
return `${params.cdnBaseUrl}/upload?encrypted_query_param=${encodeURIComponent(params.uploadParam)}&filekey=${encodeURIComponent(params.filekey)}`;
|
|
9374
|
-
}
|
|
9375
|
-
|
|
9376
|
-
// src/weixin/cdn/cdn-upload.ts
|
|
9377
|
-
async function uploadBufferToCdn(params) {
|
|
9378
|
-
const { buf, uploadFullUrl, uploadParam, filekey, cdnBaseUrl, label, aeskey } = params;
|
|
9379
|
-
const ciphertext = encryptAesEcb(buf, aeskey);
|
|
9380
|
-
const trimmedFull = uploadFullUrl?.trim();
|
|
9381
|
-
let cdnUrl;
|
|
9382
|
-
if (trimmedFull) {
|
|
9383
|
-
cdnUrl = trimmedFull;
|
|
9384
|
-
} else if (uploadParam) {
|
|
9385
|
-
cdnUrl = buildCdnUploadUrl({ cdnBaseUrl, uploadParam, filekey });
|
|
9386
|
-
} else {
|
|
9387
|
-
throw new Error(`${label}: CDN upload URL missing (need upload_full_url or upload_param)`);
|
|
9388
|
-
}
|
|
9389
|
-
logger.debug(`${label}: CDN POST url=${redactUrl(cdnUrl)} ciphertextSize=${ciphertext.length}`);
|
|
9390
|
-
let downloadParam;
|
|
9391
|
-
let lastError;
|
|
9392
|
-
for (let attempt = 1;attempt <= UPLOAD_MAX_RETRIES; attempt++) {
|
|
9393
|
-
try {
|
|
9394
|
-
const res = await fetch(cdnUrl, {
|
|
9395
|
-
method: "POST",
|
|
9396
|
-
headers: { "Content-Type": "application/octet-stream" },
|
|
9397
|
-
body: new Uint8Array(ciphertext)
|
|
9398
|
-
});
|
|
9399
|
-
if (res.status >= 400 && res.status < 500) {
|
|
9400
|
-
const errMsg = res.headers.get("x-error-message") ?? await res.text();
|
|
9401
|
-
logger.error(`${label}: CDN client error attempt=${attempt} status=${res.status} errMsg=${errMsg}`);
|
|
9402
|
-
throw new Error(`CDN upload client error ${res.status}: ${errMsg}`);
|
|
9403
|
-
}
|
|
9404
|
-
if (res.status !== 200) {
|
|
9405
|
-
const errMsg = res.headers.get("x-error-message") ?? `status ${res.status}`;
|
|
9406
|
-
logger.error(`${label}: CDN server error attempt=${attempt} status=${res.status} errMsg=${errMsg}`);
|
|
9407
|
-
throw new Error(`CDN upload server error: ${errMsg}`);
|
|
9408
|
-
}
|
|
9409
|
-
downloadParam = res.headers.get("x-encrypted-param") ?? undefined;
|
|
9410
|
-
if (!downloadParam) {
|
|
9411
|
-
logger.error(`${label}: CDN response missing x-encrypted-param header attempt=${attempt}`);
|
|
9412
|
-
throw new Error("CDN upload response missing x-encrypted-param header");
|
|
9413
|
-
}
|
|
9414
|
-
logger.debug(`${label}: CDN upload success attempt=${attempt}`);
|
|
9415
|
-
break;
|
|
9416
|
-
} catch (err) {
|
|
9417
|
-
lastError = err;
|
|
9418
|
-
if (err instanceof Error && err.message.includes("client error"))
|
|
9419
|
-
throw err;
|
|
9420
|
-
if (attempt < UPLOAD_MAX_RETRIES) {
|
|
9421
|
-
logger.error(`${label}: attempt ${attempt} failed, retrying... err=${String(err)}`);
|
|
9422
|
-
} else {
|
|
9423
|
-
logger.error(`${label}: all ${UPLOAD_MAX_RETRIES} attempts failed err=${String(err)}`);
|
|
9424
|
-
}
|
|
9425
|
-
}
|
|
9426
|
-
}
|
|
9427
|
-
if (!downloadParam) {
|
|
9428
|
-
throw lastError instanceof Error ? lastError : new Error(`CDN upload failed after ${UPLOAD_MAX_RETRIES} attempts`);
|
|
9429
|
-
}
|
|
9430
|
-
return { downloadParam };
|
|
9431
|
-
}
|
|
9432
|
-
var UPLOAD_MAX_RETRIES = 3;
|
|
9433
|
-
var init_cdn_upload = __esm(() => {
|
|
9434
|
-
init_aes_ecb();
|
|
9435
|
-
init_logger();
|
|
9436
|
-
});
|
|
9437
|
-
|
|
9438
9786
|
// src/weixin/media/mime.ts
|
|
9439
|
-
import
|
|
9787
|
+
import path6 from "node:path";
|
|
9440
9788
|
function getMimeFromFilename(filename) {
|
|
9441
|
-
const ext =
|
|
9789
|
+
const ext = path6.extname(filename).toLowerCase();
|
|
9442
9790
|
return EXTENSION_TO_MIME[ext] ?? "application/octet-stream";
|
|
9443
9791
|
}
|
|
9444
9792
|
function getExtensionFromMime(mimeType) {
|
|
9445
9793
|
const ct = (mimeType.split(";")[0] ?? "").trim().toLowerCase();
|
|
9446
9794
|
return MIME_TO_EXTENSION[ct] ?? ".bin";
|
|
9447
9795
|
}
|
|
9448
|
-
function getExtensionFromContentTypeOrUrl(contentType, url) {
|
|
9449
|
-
if (contentType) {
|
|
9450
|
-
const ext2 = getExtensionFromMime(contentType);
|
|
9451
|
-
if (ext2 !== ".bin")
|
|
9452
|
-
return ext2;
|
|
9453
|
-
}
|
|
9454
|
-
const ext = path5.extname(new URL(url).pathname).toLowerCase();
|
|
9455
|
-
const knownExts = new Set(Object.keys(EXTENSION_TO_MIME));
|
|
9456
|
-
return knownExts.has(ext) ? ext : ".bin";
|
|
9457
|
-
}
|
|
9458
9796
|
var EXTENSION_TO_MIME, MIME_TO_EXTENSION;
|
|
9459
9797
|
var init_mime = __esm(() => {
|
|
9460
9798
|
EXTENSION_TO_MIME = {
|
|
@@ -9509,111 +9847,28 @@ var init_mime = __esm(() => {
|
|
|
9509
9847
|
};
|
|
9510
9848
|
});
|
|
9511
9849
|
|
|
9512
|
-
// src/weixin/
|
|
9513
|
-
import
|
|
9514
|
-
function
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
function tempFileName(prefix, ext) {
|
|
9518
|
-
return `${prefix}-${Date.now()}-${crypto2.randomBytes(4).toString("hex")}${ext}`;
|
|
9519
|
-
}
|
|
9520
|
-
var init_random = () => {};
|
|
9521
|
-
|
|
9522
|
-
// src/weixin/cdn/upload.ts
|
|
9523
|
-
import crypto3 from "node:crypto";
|
|
9524
|
-
import fs4 from "node:fs/promises";
|
|
9525
|
-
import path6 from "node:path";
|
|
9526
|
-
async function downloadRemoteImageToTemp(url, destDir) {
|
|
9527
|
-
logger.debug(`downloadRemoteImageToTemp: fetching url=${url}`);
|
|
9528
|
-
const res = await fetch(url);
|
|
9529
|
-
if (!res.ok) {
|
|
9530
|
-
const msg = `remote media download failed: ${res.status} ${res.statusText} url=${url}`;
|
|
9531
|
-
logger.error(`downloadRemoteImageToTemp: ${msg}`);
|
|
9532
|
-
throw new Error(msg);
|
|
9533
|
-
}
|
|
9534
|
-
const buf = Buffer.from(await res.arrayBuffer());
|
|
9535
|
-
logger.debug(`downloadRemoteImageToTemp: downloaded ${buf.length} bytes`);
|
|
9536
|
-
await fs4.mkdir(destDir, { recursive: true });
|
|
9537
|
-
const ext = getExtensionFromContentTypeOrUrl(res.headers.get("content-type"), url);
|
|
9538
|
-
const name = tempFileName("weixin-remote", ext);
|
|
9539
|
-
const filePath = path6.join(destDir, name);
|
|
9540
|
-
await fs4.writeFile(filePath, buf);
|
|
9541
|
-
logger.debug(`downloadRemoteImageToTemp: saved to ${filePath} ext=${ext}`);
|
|
9542
|
-
return filePath;
|
|
9850
|
+
// src/weixin/cdn/aes-ecb.ts
|
|
9851
|
+
import { createCipheriv, createDecipheriv } from "node:crypto";
|
|
9852
|
+
function encryptAesEcb(plaintext, key) {
|
|
9853
|
+
const cipher = createCipheriv("aes-128-ecb", key, null);
|
|
9854
|
+
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
9543
9855
|
}
|
|
9544
|
-
|
|
9545
|
-
const
|
|
9546
|
-
|
|
9547
|
-
const rawsize = plaintext.length;
|
|
9548
|
-
const rawfilemd5 = crypto3.createHash("md5").update(plaintext).digest("hex");
|
|
9549
|
-
const filesize = aesEcbPaddedSize(rawsize);
|
|
9550
|
-
const filekey = crypto3.randomBytes(16).toString("hex");
|
|
9551
|
-
const aeskey = crypto3.randomBytes(16);
|
|
9552
|
-
logger.debug(`${label}: file=${filePath} rawsize=${rawsize} filesize=${filesize} md5=${rawfilemd5} filekey=${filekey}`);
|
|
9553
|
-
const uploadUrlResp = await getUploadUrl({
|
|
9554
|
-
...opts,
|
|
9555
|
-
filekey,
|
|
9556
|
-
media_type: mediaType,
|
|
9557
|
-
to_user_id: toUserId,
|
|
9558
|
-
rawsize,
|
|
9559
|
-
rawfilemd5,
|
|
9560
|
-
filesize,
|
|
9561
|
-
no_need_thumb: true,
|
|
9562
|
-
aeskey: aeskey.toString("hex")
|
|
9563
|
-
});
|
|
9564
|
-
const uploadFullUrl = uploadUrlResp.upload_full_url?.trim();
|
|
9565
|
-
const uploadParam = uploadUrlResp.upload_param;
|
|
9566
|
-
if (!uploadFullUrl && !uploadParam) {
|
|
9567
|
-
logger.error(`${label}: getUploadUrl returned no upload URL (need upload_full_url or upload_param), resp=${JSON.stringify(uploadUrlResp)}`);
|
|
9568
|
-
throw new Error(`${label}: getUploadUrl returned no upload URL`);
|
|
9569
|
-
}
|
|
9570
|
-
const { downloadParam: downloadEncryptedQueryParam } = await uploadBufferToCdn({
|
|
9571
|
-
buf: plaintext,
|
|
9572
|
-
uploadFullUrl: uploadFullUrl || undefined,
|
|
9573
|
-
uploadParam: uploadParam ?? undefined,
|
|
9574
|
-
filekey,
|
|
9575
|
-
cdnBaseUrl,
|
|
9576
|
-
aeskey,
|
|
9577
|
-
label: `${label}[orig filekey=${filekey}]`
|
|
9578
|
-
});
|
|
9579
|
-
return {
|
|
9580
|
-
filekey,
|
|
9581
|
-
downloadEncryptedQueryParam,
|
|
9582
|
-
aeskey: aeskey.toString("hex"),
|
|
9583
|
-
fileSize: rawsize,
|
|
9584
|
-
fileSizeCiphertext: filesize
|
|
9585
|
-
};
|
|
9856
|
+
function decryptAesEcb(ciphertext, key) {
|
|
9857
|
+
const decipher = createDecipheriv("aes-128-ecb", key, null);
|
|
9858
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
9586
9859
|
}
|
|
9587
|
-
|
|
9588
|
-
return
|
|
9589
|
-
...params,
|
|
9590
|
-
mediaType: UploadMediaType.IMAGE,
|
|
9591
|
-
label: "uploadFileToWeixin"
|
|
9592
|
-
});
|
|
9860
|
+
function aesEcbPaddedSize(plaintextSize) {
|
|
9861
|
+
return Math.ceil((plaintextSize + 1) / 16) * 16;
|
|
9593
9862
|
}
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
});
|
|
9863
|
+
var init_aes_ecb = () => {};
|
|
9864
|
+
|
|
9865
|
+
// src/weixin/cdn/cdn-url.ts
|
|
9866
|
+
function buildCdnDownloadUrl(encryptedQueryParam, cdnBaseUrl) {
|
|
9867
|
+
return `${cdnBaseUrl}/download?encrypted_query_param=${encodeURIComponent(encryptedQueryParam)}`;
|
|
9600
9868
|
}
|
|
9601
|
-
|
|
9602
|
-
return
|
|
9603
|
-
...params,
|
|
9604
|
-
mediaType: UploadMediaType.FILE,
|
|
9605
|
-
label: "uploadFileAttachmentToWeixin"
|
|
9606
|
-
});
|
|
9869
|
+
function buildCdnUploadUrl(params) {
|
|
9870
|
+
return `${params.cdnBaseUrl}/upload?encrypted_query_param=${encodeURIComponent(params.uploadParam)}&filekey=${encodeURIComponent(params.filekey)}`;
|
|
9607
9871
|
}
|
|
9608
|
-
var init_upload = __esm(() => {
|
|
9609
|
-
init_api();
|
|
9610
|
-
init_aes_ecb();
|
|
9611
|
-
init_cdn_upload();
|
|
9612
|
-
init_logger();
|
|
9613
|
-
init_mime();
|
|
9614
|
-
init_random();
|
|
9615
|
-
init_types();
|
|
9616
|
-
});
|
|
9617
9872
|
|
|
9618
9873
|
// src/weixin/cdn/pic-decrypt.ts
|
|
9619
9874
|
async function fetchCdnBytes(url, label) {
|
|
@@ -9830,6 +10085,13 @@ function buildFinalHeadsUp(input) {
|
|
|
9830
10085
|
\uD83D\uDCC4 结果共 ${total} 段,已发 ${sentSoFar} 段。回复 /jx 续看后 ${remaining} 段。`;
|
|
9831
10086
|
}
|
|
9832
10087
|
|
|
10088
|
+
// src/weixin/util/random.ts
|
|
10089
|
+
import crypto2 from "node:crypto";
|
|
10090
|
+
function generateId(prefix) {
|
|
10091
|
+
return `${prefix}:${Date.now()}-${crypto2.randomBytes(4).toString("hex")}`;
|
|
10092
|
+
}
|
|
10093
|
+
var init_random = () => {};
|
|
10094
|
+
|
|
9833
10095
|
// src/weixin/messaging/inbound.ts
|
|
9834
10096
|
function contextTokenKey(accountId, userId) {
|
|
9835
10097
|
return `${accountId}:${userId}`;
|
|
@@ -10069,6 +10331,146 @@ var init_error_notice = __esm(() => {
|
|
|
10069
10331
|
init_send();
|
|
10070
10332
|
});
|
|
10071
10333
|
|
|
10334
|
+
// src/weixin/cdn/cdn-upload.ts
|
|
10335
|
+
async function uploadBufferToCdn(params) {
|
|
10336
|
+
const { buf, uploadFullUrl, uploadParam, filekey, cdnBaseUrl, label, aeskey } = params;
|
|
10337
|
+
const ciphertext = encryptAesEcb(buf, aeskey);
|
|
10338
|
+
const trimmedFull = uploadFullUrl?.trim();
|
|
10339
|
+
let cdnUrl;
|
|
10340
|
+
if (trimmedFull) {
|
|
10341
|
+
cdnUrl = trimmedFull;
|
|
10342
|
+
} else if (uploadParam) {
|
|
10343
|
+
cdnUrl = buildCdnUploadUrl({ cdnBaseUrl, uploadParam, filekey });
|
|
10344
|
+
} else {
|
|
10345
|
+
throw new Error(`${label}: CDN upload URL missing (need upload_full_url or upload_param)`);
|
|
10346
|
+
}
|
|
10347
|
+
logger.debug(`${label}: CDN POST url=${redactUrl(cdnUrl)} ciphertextSize=${ciphertext.length}`);
|
|
10348
|
+
let downloadParam;
|
|
10349
|
+
let lastError;
|
|
10350
|
+
for (let attempt = 1;attempt <= UPLOAD_MAX_RETRIES; attempt++) {
|
|
10351
|
+
try {
|
|
10352
|
+
const res = await fetch(cdnUrl, {
|
|
10353
|
+
method: "POST",
|
|
10354
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
10355
|
+
body: new Uint8Array(ciphertext)
|
|
10356
|
+
});
|
|
10357
|
+
if (res.status >= 400 && res.status < 500) {
|
|
10358
|
+
const errMsg = res.headers.get("x-error-message") ?? await res.text();
|
|
10359
|
+
logger.error(`${label}: CDN client error attempt=${attempt} status=${res.status} errMsg=${errMsg}`);
|
|
10360
|
+
throw new Error(`CDN upload client error ${res.status}: ${errMsg}`);
|
|
10361
|
+
}
|
|
10362
|
+
if (res.status !== 200) {
|
|
10363
|
+
const errMsg = res.headers.get("x-error-message") ?? `status ${res.status}`;
|
|
10364
|
+
logger.error(`${label}: CDN server error attempt=${attempt} status=${res.status} errMsg=${errMsg}`);
|
|
10365
|
+
throw new Error(`CDN upload server error: ${errMsg}`);
|
|
10366
|
+
}
|
|
10367
|
+
downloadParam = res.headers.get("x-encrypted-param") ?? undefined;
|
|
10368
|
+
if (!downloadParam) {
|
|
10369
|
+
logger.error(`${label}: CDN response missing x-encrypted-param header attempt=${attempt}`);
|
|
10370
|
+
throw new Error("CDN upload response missing x-encrypted-param header");
|
|
10371
|
+
}
|
|
10372
|
+
logger.debug(`${label}: CDN upload success attempt=${attempt}`);
|
|
10373
|
+
break;
|
|
10374
|
+
} catch (err) {
|
|
10375
|
+
lastError = err;
|
|
10376
|
+
if (err instanceof Error && err.message.includes("client error"))
|
|
10377
|
+
throw err;
|
|
10378
|
+
if (attempt < UPLOAD_MAX_RETRIES) {
|
|
10379
|
+
logger.error(`${label}: attempt ${attempt} failed, retrying... err=${String(err)}`);
|
|
10380
|
+
} else {
|
|
10381
|
+
logger.error(`${label}: all ${UPLOAD_MAX_RETRIES} attempts failed err=${String(err)}`);
|
|
10382
|
+
}
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
if (!downloadParam) {
|
|
10386
|
+
throw lastError instanceof Error ? lastError : new Error(`CDN upload failed after ${UPLOAD_MAX_RETRIES} attempts`);
|
|
10387
|
+
}
|
|
10388
|
+
return { downloadParam };
|
|
10389
|
+
}
|
|
10390
|
+
var UPLOAD_MAX_RETRIES = 3;
|
|
10391
|
+
var init_cdn_upload = __esm(() => {
|
|
10392
|
+
init_aes_ecb();
|
|
10393
|
+
init_logger();
|
|
10394
|
+
init_redact();
|
|
10395
|
+
});
|
|
10396
|
+
|
|
10397
|
+
// src/weixin/cdn/upload.ts
|
|
10398
|
+
import crypto3 from "node:crypto";
|
|
10399
|
+
import fs4 from "node:fs/promises";
|
|
10400
|
+
async function uploadMediaToCdn(params) {
|
|
10401
|
+
const { filePath, toUserId, opts, cdnBaseUrl, mediaType, label } = params;
|
|
10402
|
+
const plaintext = await fs4.readFile(filePath);
|
|
10403
|
+
const rawsize = plaintext.length;
|
|
10404
|
+
const rawfilemd5 = crypto3.createHash("md5").update(plaintext).digest("hex");
|
|
10405
|
+
const filesize = aesEcbPaddedSize(rawsize);
|
|
10406
|
+
const filekey = crypto3.randomBytes(16).toString("hex");
|
|
10407
|
+
const aeskey = crypto3.randomBytes(16);
|
|
10408
|
+
logger.debug(`${label}: file=${filePath} rawsize=${rawsize} filesize=${filesize} md5=${rawfilemd5} filekey=${filekey}`);
|
|
10409
|
+
const uploadUrlResp = await getUploadUrl({
|
|
10410
|
+
...opts,
|
|
10411
|
+
filekey,
|
|
10412
|
+
media_type: mediaType,
|
|
10413
|
+
to_user_id: toUserId,
|
|
10414
|
+
rawsize,
|
|
10415
|
+
rawfilemd5,
|
|
10416
|
+
filesize,
|
|
10417
|
+
no_need_thumb: true,
|
|
10418
|
+
aeskey: aeskey.toString("hex")
|
|
10419
|
+
});
|
|
10420
|
+
const uploadFullUrl = uploadUrlResp.upload_full_url?.trim();
|
|
10421
|
+
const uploadParam = uploadUrlResp.upload_param;
|
|
10422
|
+
if (!uploadFullUrl && !uploadParam) {
|
|
10423
|
+
logger.error(`${label}: getUploadUrl returned no upload URL (need upload_full_url or upload_param), resp=${JSON.stringify(uploadUrlResp)}`);
|
|
10424
|
+
throw new Error(`${label}: getUploadUrl returned no upload URL`);
|
|
10425
|
+
}
|
|
10426
|
+
const { downloadParam: downloadEncryptedQueryParam } = await uploadBufferToCdn({
|
|
10427
|
+
buf: plaintext,
|
|
10428
|
+
uploadFullUrl: uploadFullUrl || undefined,
|
|
10429
|
+
uploadParam: uploadParam ?? undefined,
|
|
10430
|
+
filekey,
|
|
10431
|
+
cdnBaseUrl,
|
|
10432
|
+
aeskey,
|
|
10433
|
+
label: `${label}[orig filekey=${filekey}]`
|
|
10434
|
+
});
|
|
10435
|
+
return {
|
|
10436
|
+
filekey,
|
|
10437
|
+
downloadEncryptedQueryParam,
|
|
10438
|
+
aeskey: aeskey.toString("hex"),
|
|
10439
|
+
fileSize: rawsize,
|
|
10440
|
+
fileSizeCiphertext: filesize
|
|
10441
|
+
};
|
|
10442
|
+
}
|
|
10443
|
+
async function uploadFileToWeixin(params) {
|
|
10444
|
+
return uploadMediaToCdn({
|
|
10445
|
+
...params,
|
|
10446
|
+
mediaType: UploadMediaType.IMAGE,
|
|
10447
|
+
label: "uploadFileToWeixin"
|
|
10448
|
+
});
|
|
10449
|
+
}
|
|
10450
|
+
async function uploadVideoToWeixin(params) {
|
|
10451
|
+
return uploadMediaToCdn({
|
|
10452
|
+
...params,
|
|
10453
|
+
mediaType: UploadMediaType.VIDEO,
|
|
10454
|
+
label: "uploadVideoToWeixin"
|
|
10455
|
+
});
|
|
10456
|
+
}
|
|
10457
|
+
async function uploadFileAttachmentToWeixin(params) {
|
|
10458
|
+
return uploadMediaToCdn({
|
|
10459
|
+
...params,
|
|
10460
|
+
mediaType: UploadMediaType.FILE,
|
|
10461
|
+
label: "uploadFileAttachmentToWeixin"
|
|
10462
|
+
});
|
|
10463
|
+
}
|
|
10464
|
+
var init_upload = __esm(() => {
|
|
10465
|
+
init_api();
|
|
10466
|
+
init_aes_ecb();
|
|
10467
|
+
init_cdn_upload();
|
|
10468
|
+
init_logger();
|
|
10469
|
+
init_mime();
|
|
10470
|
+
init_random();
|
|
10471
|
+
init_types();
|
|
10472
|
+
});
|
|
10473
|
+
|
|
10072
10474
|
// src/weixin/messaging/send-media.ts
|
|
10073
10475
|
import path7 from "node:path";
|
|
10074
10476
|
async function sendWeixinMediaFile(params) {
|
|
@@ -10387,6 +10789,35 @@ function hardCutByCodepoint(s, maxBytes) {
|
|
|
10387
10789
|
function resolveMediaTempDir(customRoot) {
|
|
10388
10790
|
return customRoot ?? path9.join(tmpdir(), "weacpx", "media");
|
|
10389
10791
|
}
|
|
10792
|
+
async function resolveSafeOutboundMediaPath(mediaUrl, mediaTempDir) {
|
|
10793
|
+
if (mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://")) {
|
|
10794
|
+
return null;
|
|
10795
|
+
}
|
|
10796
|
+
const candidate = path9.isAbsolute(mediaUrl) ? mediaUrl : path9.resolve(mediaUrl);
|
|
10797
|
+
const allowedRoots = [mediaTempDir, process.cwd()];
|
|
10798
|
+
const realCandidate = await realpathOrNull(candidate);
|
|
10799
|
+
if (!realCandidate) {
|
|
10800
|
+
return null;
|
|
10801
|
+
}
|
|
10802
|
+
for (const root of allowedRoots) {
|
|
10803
|
+
const realRoot = await realpathOrNull(root);
|
|
10804
|
+
if (realRoot && isPathInside(realCandidate, realRoot)) {
|
|
10805
|
+
return realCandidate;
|
|
10806
|
+
}
|
|
10807
|
+
}
|
|
10808
|
+
return null;
|
|
10809
|
+
}
|
|
10810
|
+
async function realpathOrNull(filePath) {
|
|
10811
|
+
try {
|
|
10812
|
+
return await fs6.realpath(filePath);
|
|
10813
|
+
} catch {
|
|
10814
|
+
return null;
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
function isPathInside(candidate, root) {
|
|
10818
|
+
const relative = path9.relative(root, candidate);
|
|
10819
|
+
return relative === "" || !relative.startsWith("..") && !path9.isAbsolute(relative);
|
|
10820
|
+
}
|
|
10390
10821
|
function createSaveMediaBuffer(mediaTempDir) {
|
|
10391
10822
|
return async function saveMediaBuffer(buffer, contentType, subdir, _maxBytes, originalFilename) {
|
|
10392
10823
|
const dir = path9.join(resolveMediaTempDir(mediaTempDir), subdir ?? "");
|
|
@@ -10573,12 +11004,11 @@ async function handleWeixinMessageTurn(full, deps) {
|
|
|
10573
11004
|
onReplySegment: sendReplySegment
|
|
10574
11005
|
});
|
|
10575
11006
|
if (turn.media) {
|
|
10576
|
-
let filePath;
|
|
10577
11007
|
const mediaUrl = turn.media.url;
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
11008
|
+
const filePath = await resolveSafeOutboundMediaPath(mediaUrl, resolveMediaTempDir(deps.mediaTempDir));
|
|
11009
|
+
if (!filePath) {
|
|
11010
|
+
deps.errLog(`outbound media rejected: url=${mediaUrl}`);
|
|
11011
|
+
return;
|
|
10582
11012
|
}
|
|
10583
11013
|
const reservedMedia = deps.reserveFinal ? deps.reserveFinal(to) : true;
|
|
10584
11014
|
if (!reservedMedia) {
|
|
@@ -10685,7 +11115,6 @@ var MAX_FINAL_CHUNK_BYTES = 1800, hasDownloadableMedia = (media) => media?.encry
|
|
|
10685
11115
|
var init_handle_weixin_message_turn = __esm(() => {
|
|
10686
11116
|
init_api();
|
|
10687
11117
|
init_types();
|
|
10688
|
-
init_upload();
|
|
10689
11118
|
init_media_download();
|
|
10690
11119
|
init_mime();
|
|
10691
11120
|
init_inbound();
|
|
@@ -11071,8 +11500,8 @@ var init_weixin_sdk = __esm(() => {
|
|
|
11071
11500
|
});
|
|
11072
11501
|
|
|
11073
11502
|
// src/logging/app-logger.ts
|
|
11074
|
-
import { appendFile, mkdir as
|
|
11075
|
-
import { basename, dirname as
|
|
11503
|
+
import { appendFile, mkdir as mkdir7, readdir, rename as rename2, rm as rm5, stat } from "node:fs/promises";
|
|
11504
|
+
import { basename as basename2, dirname as dirname7, join as join5 } from "node:path";
|
|
11076
11505
|
function createNoopAppLogger() {
|
|
11077
11506
|
return {
|
|
11078
11507
|
debug: async () => {},
|
|
@@ -11112,7 +11541,7 @@ function createAppLogger(options) {
|
|
|
11112
11541
|
return;
|
|
11113
11542
|
}
|
|
11114
11543
|
const line = formatLogLine(now(), level, event, message, context);
|
|
11115
|
-
await
|
|
11544
|
+
await mkdir7(dirname7(options.filePath), { recursive: true });
|
|
11116
11545
|
await rotateIfNeeded(options.filePath, Buffer.byteLength(line), options.maxSizeBytes, options.maxFiles);
|
|
11117
11546
|
await appendFile(options.filePath, line, "utf8");
|
|
11118
11547
|
}
|
|
@@ -11122,7 +11551,7 @@ async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
|
11122
11551
|
try {
|
|
11123
11552
|
currentSize = (await stat(filePath)).size;
|
|
11124
11553
|
} catch (error2) {
|
|
11125
|
-
if (!
|
|
11554
|
+
if (!isMissingFileError2(error2)) {
|
|
11126
11555
|
throw error2;
|
|
11127
11556
|
}
|
|
11128
11557
|
}
|
|
@@ -11140,24 +11569,24 @@ async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
|
11140
11569
|
for (let index = maxFiles - 1;index >= 1; index -= 1) {
|
|
11141
11570
|
const source = `${filePath}.${index}`;
|
|
11142
11571
|
try {
|
|
11143
|
-
await
|
|
11572
|
+
await rename2(source, `${filePath}.${index + 1}`);
|
|
11144
11573
|
} catch (error2) {
|
|
11145
|
-
if (!
|
|
11574
|
+
if (!isMissingFileError2(error2)) {
|
|
11146
11575
|
throw error2;
|
|
11147
11576
|
}
|
|
11148
11577
|
}
|
|
11149
11578
|
}
|
|
11150
|
-
await
|
|
11579
|
+
await rename2(filePath, `${filePath}.1`);
|
|
11151
11580
|
}
|
|
11152
11581
|
async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
11153
|
-
const parentDir =
|
|
11154
|
-
const prefix = `${
|
|
11582
|
+
const parentDir = dirname7(filePath);
|
|
11583
|
+
const prefix = `${basename2(filePath)}.`;
|
|
11155
11584
|
const cutoff = now().getTime() - retentionDays * 24 * 60 * 60 * 1000;
|
|
11156
11585
|
let files = [];
|
|
11157
11586
|
try {
|
|
11158
11587
|
files = await readdir(parentDir);
|
|
11159
11588
|
} catch (error2) {
|
|
11160
|
-
if (
|
|
11589
|
+
if (isMissingFileError2(error2)) {
|
|
11161
11590
|
return;
|
|
11162
11591
|
}
|
|
11163
11592
|
throw error2;
|
|
@@ -11166,7 +11595,7 @@ async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
|
11166
11595
|
if (!file.startsWith(prefix) || !/^\d+$/.test(file.slice(prefix.length))) {
|
|
11167
11596
|
continue;
|
|
11168
11597
|
}
|
|
11169
|
-
const candidate =
|
|
11598
|
+
const candidate = join5(parentDir, file);
|
|
11170
11599
|
const details = await stat(candidate);
|
|
11171
11600
|
if (details.mtime.getTime() < cutoff) {
|
|
11172
11601
|
await rm5(candidate, { force: true });
|
|
@@ -11188,7 +11617,7 @@ function formatValue(value) {
|
|
|
11188
11617
|
}
|
|
11189
11618
|
return JSON.stringify(value);
|
|
11190
11619
|
}
|
|
11191
|
-
function
|
|
11620
|
+
function isMissingFileError2(error2) {
|
|
11192
11621
|
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
11193
11622
|
}
|
|
11194
11623
|
var LEVEL_ORDER;
|
|
@@ -11201,16 +11630,16 @@ var init_app_logger = __esm(() => {
|
|
|
11201
11630
|
});
|
|
11202
11631
|
|
|
11203
11632
|
// src/transport/acpx-session-index.ts
|
|
11204
|
-
import { readFile as
|
|
11205
|
-
import { homedir as
|
|
11633
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
11634
|
+
import { homedir as homedir4 } from "node:os";
|
|
11206
11635
|
import { resolve } from "node:path";
|
|
11207
11636
|
async function resolveSessionAgentCommandFromIndex(session) {
|
|
11208
|
-
const home = process.env.HOME ??
|
|
11637
|
+
const home = process.env.HOME ?? homedir4();
|
|
11209
11638
|
if (!home) {
|
|
11210
11639
|
return;
|
|
11211
11640
|
}
|
|
11212
11641
|
try {
|
|
11213
|
-
const raw = await
|
|
11642
|
+
const raw = await readFile6(resolve(home, ".acpx", "sessions", "index.json"), "utf8");
|
|
11214
11643
|
const parsed = JSON.parse(raw);
|
|
11215
11644
|
const targetCwd = resolve(session.cwd);
|
|
11216
11645
|
const match = parsed.entries?.find((entry) => entry.name === session.transportSession && entry.cwd === targetCwd && typeof entry.agentCommand === "string" && entry.agentCommand.trim().length > 0);
|
|
@@ -13222,54 +13651,6 @@ var init_agent_handler = __esm(() => {
|
|
|
13222
13651
|
};
|
|
13223
13652
|
});
|
|
13224
13653
|
|
|
13225
|
-
// src/commands/workspace-path.ts
|
|
13226
|
-
import { access } from "node:fs/promises";
|
|
13227
|
-
import { homedir as homedir4 } from "node:os";
|
|
13228
|
-
import path11 from "node:path";
|
|
13229
|
-
function normalizeWorkspacePath(input) {
|
|
13230
|
-
const expanded = expandHome(input);
|
|
13231
|
-
if (isWindowsLikePath(expanded)) {
|
|
13232
|
-
return path11.win32.normalize(expanded).replace(/\\/g, "/");
|
|
13233
|
-
}
|
|
13234
|
-
return path11.posix.normalize(expanded.replace(/\\/g, "/"));
|
|
13235
|
-
}
|
|
13236
|
-
function basenameForWorkspacePath(input) {
|
|
13237
|
-
const normalized = normalizeWorkspacePath(input);
|
|
13238
|
-
if (ROOT_PATH_RE.test(normalized)) {
|
|
13239
|
-
return normalized;
|
|
13240
|
-
}
|
|
13241
|
-
const base = path11.posix.basename(normalized);
|
|
13242
|
-
return base || normalized;
|
|
13243
|
-
}
|
|
13244
|
-
function sameWorkspacePath(left, right) {
|
|
13245
|
-
const normalizedLeft = normalizeWorkspacePath(left);
|
|
13246
|
-
const normalizedRight = normalizeWorkspacePath(right);
|
|
13247
|
-
if (isWindowsLikePath(normalizedLeft) || isWindowsLikePath(normalizedRight)) {
|
|
13248
|
-
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
13249
|
-
}
|
|
13250
|
-
return normalizedLeft === normalizedRight;
|
|
13251
|
-
}
|
|
13252
|
-
function expandHome(input) {
|
|
13253
|
-
return input.startsWith("~") ? homedir4() + input.slice(1) : input;
|
|
13254
|
-
}
|
|
13255
|
-
async function pathExists(filePath) {
|
|
13256
|
-
try {
|
|
13257
|
-
await access(filePath);
|
|
13258
|
-
return true;
|
|
13259
|
-
} catch {
|
|
13260
|
-
return false;
|
|
13261
|
-
}
|
|
13262
|
-
}
|
|
13263
|
-
function isWindowsLikePath(input) {
|
|
13264
|
-
return WINDOWS_DRIVE_PATH_RE.test(input) || WINDOWS_UNC_PATH_RE.test(input);
|
|
13265
|
-
}
|
|
13266
|
-
var WINDOWS_DRIVE_PATH_RE, WINDOWS_UNC_PATH_RE, ROOT_PATH_RE;
|
|
13267
|
-
var init_workspace_path = __esm(() => {
|
|
13268
|
-
WINDOWS_DRIVE_PATH_RE = /^[a-zA-Z]:[\\/]/;
|
|
13269
|
-
WINDOWS_UNC_PATH_RE = /^\\\\/;
|
|
13270
|
-
ROOT_PATH_RE = /^(\/|[a-zA-Z]:\/?)$/;
|
|
13271
|
-
});
|
|
13272
|
-
|
|
13273
13654
|
// src/commands/handlers/workspace-handler.ts
|
|
13274
13655
|
function handleWorkspaces(context) {
|
|
13275
13656
|
return { text: context.config ? renderWorkspaces(context.config) : "No config loaded." };
|
|
@@ -13667,9 +14048,9 @@ var init_session_recovery_handler = __esm(() => {
|
|
|
13667
14048
|
// src/recovery/auto-install-optional-dep.ts
|
|
13668
14049
|
import { spawn as spawn3 } from "node:child_process";
|
|
13669
14050
|
import { createWriteStream } from "node:fs";
|
|
13670
|
-
import { mkdir as
|
|
14051
|
+
import { mkdir as mkdir8 } from "node:fs/promises";
|
|
13671
14052
|
import { homedir as homedir5 } from "node:os";
|
|
13672
|
-
import { join as
|
|
14053
|
+
import { join as join6 } from "node:path";
|
|
13673
14054
|
async function autoInstallOptionalDep(pkg, parentPackages, options = {}) {
|
|
13674
14055
|
const runCli = options.runCli ?? defaultRunCli;
|
|
13675
14056
|
const openLog = options.openLog ?? defaultLogSink;
|
|
@@ -13784,13 +14165,13 @@ ${err.message}`, reason: "spawn" });
|
|
|
13784
14165
|
});
|
|
13785
14166
|
});
|
|
13786
14167
|
}, defaultLogSink = async () => {
|
|
13787
|
-
const dir =
|
|
13788
|
-
await
|
|
14168
|
+
const dir = join6(homedir5(), ".weacpx", "logs");
|
|
14169
|
+
await mkdir8(dir, { recursive: true });
|
|
13789
14170
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "").replace(/-/g, "");
|
|
13790
|
-
const
|
|
13791
|
-
const stream = createWriteStream(
|
|
14171
|
+
const path11 = join6(dir, `auto-install-${timestamp}.log`);
|
|
14172
|
+
const stream = createWriteStream(path11, { flags: "a" });
|
|
13792
14173
|
return {
|
|
13793
|
-
path:
|
|
14174
|
+
path: path11,
|
|
13794
14175
|
append: async (chunk) => {
|
|
13795
14176
|
await new Promise((resolve2, reject) => stream.write(chunk, (err) => err ? reject(err) : resolve2()));
|
|
13796
14177
|
},
|
|
@@ -13810,9 +14191,10 @@ var init_auto_install_optional_dep = __esm(() => {
|
|
|
13810
14191
|
|
|
13811
14192
|
// src/recovery/discover-parent-package-paths.ts
|
|
13812
14193
|
import { spawn as spawn4 } from "node:child_process";
|
|
14194
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
13813
14195
|
import { access as access2 } from "node:fs/promises";
|
|
13814
14196
|
import { homedir as homedir6 } from "node:os";
|
|
13815
|
-
import { dirname as
|
|
14197
|
+
import { dirname as dirname8, join as join7 } from "node:path";
|
|
13816
14198
|
function deriveParentPackageName(platformPackage) {
|
|
13817
14199
|
return platformPackage.replace(/-(?:linux|darwin|win32|windows|freebsd|openbsd|sunos|aix)(?:-(?:x64|arm64|ia32|arm|ppc64|s390x))?(?:-(?:baseline|musl|gnu|gnueabihf|musleabihf|msvc))?$/, "");
|
|
13818
14200
|
}
|
|
@@ -13825,7 +14207,7 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
13825
14207
|
const queryRoot = deps.queryPackageManagerRoot ?? defaultQueryPackageManagerRoot;
|
|
13826
14208
|
const parentName = deriveParentPackageName(platformPackage);
|
|
13827
14209
|
const rawCandidates = [];
|
|
13828
|
-
const bunGlobalRoot = env.BUN_INSTALL ?
|
|
14210
|
+
const bunGlobalRoot = env.BUN_INSTALL ? join7(env.BUN_INSTALL, "install", "global", "node_modules") : join7(home, ".bun", "install", "global", "node_modules");
|
|
13829
14211
|
const [npmRoot, pnpmRoot, yarnRoot] = await Promise.all([
|
|
13830
14212
|
queryRoot("npm"),
|
|
13831
14213
|
queryRoot("pnpm"),
|
|
@@ -13848,20 +14230,20 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
|
|
|
13848
14230
|
if (resolved)
|
|
13849
14231
|
rawCandidates.push({ path: resolved, manager: classify(resolved) });
|
|
13850
14232
|
}
|
|
13851
|
-
rawCandidates.push({ path:
|
|
14233
|
+
rawCandidates.push({ path: join7(bunGlobalRoot, parentName), manager: "bun" });
|
|
13852
14234
|
if (npmRoot)
|
|
13853
|
-
rawCandidates.push({ path:
|
|
14235
|
+
rawCandidates.push({ path: join7(npmRoot, parentName), manager: "npm" });
|
|
13854
14236
|
if (pnpmRoot)
|
|
13855
|
-
rawCandidates.push({ path:
|
|
14237
|
+
rawCandidates.push({ path: join7(pnpmRoot, parentName), manager: "pnpm" });
|
|
13856
14238
|
if (yarnRoot)
|
|
13857
|
-
rawCandidates.push({ path:
|
|
14239
|
+
rawCandidates.push({ path: join7(yarnRoot, parentName), manager: "yarn" });
|
|
13858
14240
|
const seen = new Set;
|
|
13859
14241
|
const verified = [];
|
|
13860
14242
|
for (const candidate of rawCandidates) {
|
|
13861
14243
|
if (seen.has(candidate.path))
|
|
13862
14244
|
continue;
|
|
13863
14245
|
seen.add(candidate.path);
|
|
13864
|
-
if (await fsExists(
|
|
14246
|
+
if (await fsExists(join7(candidate.path, "package.json"))) {
|
|
13865
14247
|
verified.push(candidate);
|
|
13866
14248
|
}
|
|
13867
14249
|
}
|
|
@@ -13872,9 +14254,9 @@ function isUnder(child, parent) {
|
|
|
13872
14254
|
const p = parent.replace(/[\\/]+$/, "");
|
|
13873
14255
|
return c === p || c.startsWith(p + "/") || c.startsWith(p + "\\");
|
|
13874
14256
|
}
|
|
13875
|
-
async function defaultFsExists(
|
|
14257
|
+
async function defaultFsExists(path11) {
|
|
13876
14258
|
try {
|
|
13877
|
-
await access2(
|
|
14259
|
+
await access2(path11);
|
|
13878
14260
|
return true;
|
|
13879
14261
|
} catch {
|
|
13880
14262
|
return false;
|
|
@@ -13882,10 +14264,10 @@ async function defaultFsExists(path12) {
|
|
|
13882
14264
|
}
|
|
13883
14265
|
function defaultResolveFromCwd(name, cwd) {
|
|
13884
14266
|
try {
|
|
13885
|
-
const pkgJson =
|
|
13886
|
-
paths: [cwd, ...
|
|
14267
|
+
const pkgJson = require2.resolve(`${name}/package.json`, {
|
|
14268
|
+
paths: [cwd, ...require2.resolve.paths(name) ?? []]
|
|
13887
14269
|
});
|
|
13888
|
-
return
|
|
14270
|
+
return dirname8(pkgJson);
|
|
13889
14271
|
} catch {
|
|
13890
14272
|
return null;
|
|
13891
14273
|
}
|
|
@@ -13932,11 +14314,14 @@ async function defaultQueryPackageManagerRoot(tool) {
|
|
|
13932
14314
|
const trimmed = stdout2.trim().split(/\r?\n/).pop()?.trim() ?? "";
|
|
13933
14315
|
if (!trimmed)
|
|
13934
14316
|
return done(null);
|
|
13935
|
-
done(spec.postfix ?
|
|
14317
|
+
done(spec.postfix ? join7(trimmed, spec.postfix) : trimmed);
|
|
13936
14318
|
});
|
|
13937
14319
|
});
|
|
13938
14320
|
}
|
|
13939
|
-
var
|
|
14321
|
+
var require2;
|
|
14322
|
+
var init_discover_parent_package_paths = __esm(() => {
|
|
14323
|
+
require2 = createRequire2(import.meta.url);
|
|
14324
|
+
});
|
|
13940
14325
|
|
|
13941
14326
|
// src/commands/translate-acpx-note.ts
|
|
13942
14327
|
function translateAcpxNote(raw) {
|
|
@@ -14415,284 +14800,9 @@ var init_command_router = __esm(() => {
|
|
|
14415
14800
|
init_session_reset_handler();
|
|
14416
14801
|
});
|
|
14417
14802
|
|
|
14418
|
-
// src/config/resolve-agent-command.ts
|
|
14419
|
-
function resolveAgentCommand(driver, command) {
|
|
14420
|
-
if (!command) {
|
|
14421
|
-
return;
|
|
14422
|
-
}
|
|
14423
|
-
if (driver === "codex" && isLegacyCodexCommand(command)) {
|
|
14424
|
-
return;
|
|
14425
|
-
}
|
|
14426
|
-
return command;
|
|
14427
|
-
}
|
|
14428
|
-
function isLegacyCodexCommand(command) {
|
|
14429
|
-
const normalized = command.trim().replaceAll("\\", "/").toLowerCase();
|
|
14430
|
-
return normalized === "./node_modules/.bin/codex-acp" || normalized === "./node_modules/.bin/codex-acp.exe" || normalized.endsWith("/node_modules/.bin/codex-acp") || normalized.endsWith("/node_modules/.bin/codex-acp.exe") || normalized.includes("/@zed-industries/codex-acp/bin/codex-acp.js") || normalized.includes("@zed-industries/codex-acp/bin/codex-acp.js");
|
|
14431
|
-
}
|
|
14432
|
-
|
|
14433
|
-
// src/config/load-config.ts
|
|
14434
|
-
import { readFile as readFile5 } from "node:fs/promises";
|
|
14435
|
-
function isRecord(value) {
|
|
14436
|
-
return typeof value === "object" && value !== null;
|
|
14437
|
-
}
|
|
14438
|
-
async function loadConfig(path12, options = {}) {
|
|
14439
|
-
const raw = JSON.parse(await readFile5(path12, "utf8"));
|
|
14440
|
-
return parseConfig(raw, options);
|
|
14441
|
-
}
|
|
14442
|
-
function parseConfig(raw, options = {}) {
|
|
14443
|
-
if (!isRecord(raw)) {
|
|
14444
|
-
throw new Error("config must be a JSON object");
|
|
14445
|
-
}
|
|
14446
|
-
const transport = raw.transport;
|
|
14447
|
-
if (!isRecord(transport)) {
|
|
14448
|
-
throw new Error("transport must be an object");
|
|
14449
|
-
}
|
|
14450
|
-
if ("type" in transport && transport.type !== "acpx-cli" && transport.type !== "acpx-bridge") {
|
|
14451
|
-
throw new Error("transport.type must be acpx-cli or acpx-bridge");
|
|
14452
|
-
}
|
|
14453
|
-
if ("sessionInitTimeoutMs" in transport && (typeof transport.sessionInitTimeoutMs !== "number" || !Number.isFinite(transport.sessionInitTimeoutMs) || transport.sessionInitTimeoutMs <= 0)) {
|
|
14454
|
-
throw new Error("transport.sessionInitTimeoutMs must be a positive number");
|
|
14455
|
-
}
|
|
14456
|
-
if ("permissionMode" in transport && transport.permissionMode !== "approve-all" && transport.permissionMode !== "approve-reads" && transport.permissionMode !== "deny-all") {
|
|
14457
|
-
throw new Error("transport.permissionMode must be approve-all, approve-reads, or deny-all");
|
|
14458
|
-
}
|
|
14459
|
-
if ("nonInteractivePermissions" in transport && transport.nonInteractivePermissions !== "deny" && transport.nonInteractivePermissions !== "fail") {
|
|
14460
|
-
throw new Error("transport.nonInteractivePermissions must be deny or fail");
|
|
14461
|
-
}
|
|
14462
|
-
if (!isRecord(raw.agents)) {
|
|
14463
|
-
throw new Error("agents must be an object");
|
|
14464
|
-
}
|
|
14465
|
-
if (!isRecord(raw.workspaces)) {
|
|
14466
|
-
throw new Error("workspaces must be an object");
|
|
14467
|
-
}
|
|
14468
|
-
const logging = raw.logging;
|
|
14469
|
-
const wechat = raw.wechat;
|
|
14470
|
-
const orchestration = raw.orchestration;
|
|
14471
|
-
if (logging !== undefined && !isRecord(logging)) {
|
|
14472
|
-
throw new Error("logging must be an object");
|
|
14473
|
-
}
|
|
14474
|
-
if (wechat !== undefined && !isRecord(wechat)) {
|
|
14475
|
-
throw new Error("wechat must be an object");
|
|
14476
|
-
}
|
|
14477
|
-
if (orchestration !== undefined && !isRecord(orchestration)) {
|
|
14478
|
-
throw new Error("orchestration must be an object");
|
|
14479
|
-
}
|
|
14480
|
-
if (isRecord(logging) && "level" in logging && logging.level !== "error" && logging.level !== "info" && logging.level !== "debug") {
|
|
14481
|
-
throw new Error("logging.level must be error, info, or debug");
|
|
14482
|
-
}
|
|
14483
|
-
for (const field of ["maxSizeBytes", "maxFiles", "retentionDays"]) {
|
|
14484
|
-
if (isRecord(logging) && field in logging && (typeof logging[field] !== "number" || !Number.isFinite(logging[field]) || logging[field] <= 0)) {
|
|
14485
|
-
throw new Error(`logging.${field} must be a positive number`);
|
|
14486
|
-
}
|
|
14487
|
-
}
|
|
14488
|
-
if (isRecord(wechat) && "replyMode" in wechat && wechat.replyMode !== "stream" && wechat.replyMode !== "final" && wechat.replyMode !== "verbose") {
|
|
14489
|
-
throw new Error("wechat.replyMode must be stream, final, or verbose");
|
|
14490
|
-
}
|
|
14491
|
-
for (const [name, agent] of Object.entries(raw.agents)) {
|
|
14492
|
-
if (!isRecord(agent) || typeof agent.driver !== "string" || agent.driver.length === 0) {
|
|
14493
|
-
throw new Error(`agent "${name}" must define a non-empty driver`);
|
|
14494
|
-
}
|
|
14495
|
-
if ("command" in agent && (typeof agent.command !== "string" || agent.command.length === 0)) {
|
|
14496
|
-
throw new Error(`agent "${name}" command must be a non-empty string`);
|
|
14497
|
-
}
|
|
14498
|
-
}
|
|
14499
|
-
for (const [name, workspace] of Object.entries(raw.workspaces)) {
|
|
14500
|
-
if (!isRecord(workspace) || typeof workspace.cwd !== "string" || workspace.cwd.length === 0) {
|
|
14501
|
-
throw new Error(`workspace "${name}" must define a non-empty cwd`);
|
|
14502
|
-
}
|
|
14503
|
-
if ("allowed_agents" in workspace && (!Array.isArray(workspace.allowed_agents) || workspace.allowed_agents.some((value) => typeof value !== "string"))) {
|
|
14504
|
-
throw new Error(`workspace "${name}" allowed_agents must be an array of strings`);
|
|
14505
|
-
}
|
|
14506
|
-
}
|
|
14507
|
-
const rawAgents = raw.agents;
|
|
14508
|
-
const agents = {};
|
|
14509
|
-
for (const [name, agent] of Object.entries(rawAgents)) {
|
|
14510
|
-
const driver = agent.driver;
|
|
14511
|
-
const command = typeof agent.command === "string" ? resolveAgentCommand(driver, agent.command) : undefined;
|
|
14512
|
-
agents[name] = {
|
|
14513
|
-
driver,
|
|
14514
|
-
...command ? { command } : {}
|
|
14515
|
-
};
|
|
14516
|
-
}
|
|
14517
|
-
const rawWorkspaces = raw.workspaces;
|
|
14518
|
-
const workspaces = {};
|
|
14519
|
-
for (const [name, workspace] of Object.entries(rawWorkspaces)) {
|
|
14520
|
-
workspaces[name] = {
|
|
14521
|
-
cwd: normalizeWorkspacePath(workspace.cwd),
|
|
14522
|
-
...typeof workspace.description === "string" ? { description: workspace.description } : {}
|
|
14523
|
-
};
|
|
14524
|
-
}
|
|
14525
|
-
const transportType = transport.type === "acpx-cli" || transport.type === "acpx-bridge" ? transport.type : "acpx-bridge";
|
|
14526
|
-
const permissionMode = transport.permissionMode === "approve-all" || transport.permissionMode === "approve-reads" || transport.permissionMode === "deny-all" ? transport.permissionMode : DEFAULT_PERMISSION_MODE;
|
|
14527
|
-
const nonInteractivePermissions = transport.nonInteractivePermissions === "deny" || transport.nonInteractivePermissions === "fail" ? transport.nonInteractivePermissions : DEFAULT_NON_INTERACTIVE_PERMISSIONS;
|
|
14528
|
-
const loggingLevel = logging?.level;
|
|
14529
|
-
const resolvedLoggingLevel = loggingLevel === "error" || loggingLevel === "info" || loggingLevel === "debug" ? loggingLevel : options.defaultLoggingLevel ?? DEFAULT_LOGGING_CONFIG.level;
|
|
14530
|
-
const replyMode = wechat?.replyMode === "stream" || wechat?.replyMode === "final" || wechat?.replyMode === "verbose" ? wechat.replyMode : DEFAULT_WECHAT_REPLY_MODE;
|
|
14531
|
-
const orchestrationConfig = parseOrchestrationConfig(orchestration);
|
|
14532
|
-
return {
|
|
14533
|
-
transport: {
|
|
14534
|
-
...typeof transport.command === "string" ? { command: transport.command } : {},
|
|
14535
|
-
...typeof transport.sessionInitTimeoutMs === "number" ? { sessionInitTimeoutMs: transport.sessionInitTimeoutMs } : {},
|
|
14536
|
-
type: transportType,
|
|
14537
|
-
permissionMode,
|
|
14538
|
-
nonInteractivePermissions
|
|
14539
|
-
},
|
|
14540
|
-
logging: {
|
|
14541
|
-
level: resolvedLoggingLevel,
|
|
14542
|
-
maxSizeBytes: typeof logging?.maxSizeBytes === "number" ? logging.maxSizeBytes : DEFAULT_LOGGING_CONFIG.maxSizeBytes,
|
|
14543
|
-
maxFiles: typeof logging?.maxFiles === "number" ? logging.maxFiles : DEFAULT_LOGGING_CONFIG.maxFiles,
|
|
14544
|
-
retentionDays: typeof logging?.retentionDays === "number" ? logging.retentionDays : DEFAULT_LOGGING_CONFIG.retentionDays
|
|
14545
|
-
},
|
|
14546
|
-
wechat: {
|
|
14547
|
-
replyMode
|
|
14548
|
-
},
|
|
14549
|
-
agents,
|
|
14550
|
-
workspaces,
|
|
14551
|
-
orchestration: orchestrationConfig
|
|
14552
|
-
};
|
|
14553
|
-
}
|
|
14554
|
-
function parseOrchestrationConfig(raw) {
|
|
14555
|
-
if (!isRecord(raw)) {
|
|
14556
|
-
return {
|
|
14557
|
-
...DEFAULT_ORCHESTRATION_CONFIG
|
|
14558
|
-
};
|
|
14559
|
-
}
|
|
14560
|
-
return {
|
|
14561
|
-
maxPendingAgentRequestsPerCoordinator: typeof raw.maxPendingAgentRequestsPerCoordinator === "number" && Number.isFinite(raw.maxPendingAgentRequestsPerCoordinator) && raw.maxPendingAgentRequestsPerCoordinator > 0 ? raw.maxPendingAgentRequestsPerCoordinator : DEFAULT_ORCHESTRATION_CONFIG.maxPendingAgentRequestsPerCoordinator,
|
|
14562
|
-
allowWorkerChainedRequests: raw.allowWorkerChainedRequests === true,
|
|
14563
|
-
allowedAgentRequestTargets: Array.isArray(raw.allowedAgentRequestTargets) ? raw.allowedAgentRequestTargets.filter((value) => typeof value === "string") : [...DEFAULT_ORCHESTRATION_CONFIG.allowedAgentRequestTargets],
|
|
14564
|
-
allowedAgentRequestRoles: Array.isArray(raw.allowedAgentRequestRoles) ? raw.allowedAgentRequestRoles.filter((value) => typeof value === "string") : [...DEFAULT_ORCHESTRATION_CONFIG.allowedAgentRequestRoles],
|
|
14565
|
-
progressHeartbeatSeconds: typeof raw.progressHeartbeatSeconds === "number" && Number.isFinite(raw.progressHeartbeatSeconds) ? raw.progressHeartbeatSeconds : DEFAULT_ORCHESTRATION_CONFIG.progressHeartbeatSeconds
|
|
14566
|
-
};
|
|
14567
|
-
}
|
|
14568
|
-
var DEFAULT_LOGGING_CONFIG, DEFAULT_PERMISSION_MODE = "approve-all", DEFAULT_NON_INTERACTIVE_PERMISSIONS = "deny", DEFAULT_WECHAT_REPLY_MODE = "verbose", DEFAULT_ORCHESTRATION_CONFIG;
|
|
14569
|
-
var init_load_config = __esm(() => {
|
|
14570
|
-
init_workspace_path();
|
|
14571
|
-
DEFAULT_LOGGING_CONFIG = {
|
|
14572
|
-
level: "info",
|
|
14573
|
-
maxSizeBytes: 2 * 1024 * 1024,
|
|
14574
|
-
maxFiles: 5,
|
|
14575
|
-
retentionDays: 7
|
|
14576
|
-
};
|
|
14577
|
-
DEFAULT_ORCHESTRATION_CONFIG = {
|
|
14578
|
-
maxPendingAgentRequestsPerCoordinator: 3,
|
|
14579
|
-
allowWorkerChainedRequests: false,
|
|
14580
|
-
allowedAgentRequestTargets: [],
|
|
14581
|
-
allowedAgentRequestRoles: [],
|
|
14582
|
-
progressHeartbeatSeconds: 300
|
|
14583
|
-
};
|
|
14584
|
-
});
|
|
14585
|
-
|
|
14586
|
-
// src/config/config-store.ts
|
|
14587
|
-
import { mkdir as mkdir8, writeFile as writeFile5 } from "node:fs/promises";
|
|
14588
|
-
import { dirname as dirname8 } from "node:path";
|
|
14589
|
-
|
|
14590
|
-
class ConfigStore {
|
|
14591
|
-
path;
|
|
14592
|
-
constructor(path12) {
|
|
14593
|
-
this.path = path12;
|
|
14594
|
-
}
|
|
14595
|
-
async load() {
|
|
14596
|
-
return await loadConfig(this.path);
|
|
14597
|
-
}
|
|
14598
|
-
async save(config2) {
|
|
14599
|
-
await mkdir8(dirname8(this.path), { recursive: true });
|
|
14600
|
-
await writeFile5(this.path, `${JSON.stringify(config2, null, 2)}
|
|
14601
|
-
`, "utf8");
|
|
14602
|
-
}
|
|
14603
|
-
async upsertWorkspace(name, cwd, description) {
|
|
14604
|
-
const config2 = await this.load();
|
|
14605
|
-
const workspace = {
|
|
14606
|
-
cwd,
|
|
14607
|
-
...description ? { description } : {}
|
|
14608
|
-
};
|
|
14609
|
-
config2.workspaces[name] = workspace;
|
|
14610
|
-
await this.save(config2);
|
|
14611
|
-
return config2;
|
|
14612
|
-
}
|
|
14613
|
-
async removeWorkspace(name) {
|
|
14614
|
-
const config2 = await this.load();
|
|
14615
|
-
delete config2.workspaces[name];
|
|
14616
|
-
await this.save(config2);
|
|
14617
|
-
return config2;
|
|
14618
|
-
}
|
|
14619
|
-
async upsertAgent(name, agent) {
|
|
14620
|
-
const config2 = await this.load();
|
|
14621
|
-
config2.agents[name] = agent;
|
|
14622
|
-
await this.save(config2);
|
|
14623
|
-
return config2;
|
|
14624
|
-
}
|
|
14625
|
-
async removeAgent(name) {
|
|
14626
|
-
const config2 = await this.load();
|
|
14627
|
-
delete config2.agents[name];
|
|
14628
|
-
await this.save(config2);
|
|
14629
|
-
return config2;
|
|
14630
|
-
}
|
|
14631
|
-
async updateTransport(transport) {
|
|
14632
|
-
const config2 = await this.load();
|
|
14633
|
-
config2.transport = {
|
|
14634
|
-
...config2.transport,
|
|
14635
|
-
...transport
|
|
14636
|
-
};
|
|
14637
|
-
await this.save(config2);
|
|
14638
|
-
return config2;
|
|
14639
|
-
}
|
|
14640
|
-
async updateWechat(wechat) {
|
|
14641
|
-
const config2 = await this.load();
|
|
14642
|
-
config2.wechat = {
|
|
14643
|
-
...config2.wechat,
|
|
14644
|
-
...wechat
|
|
14645
|
-
};
|
|
14646
|
-
await this.save(config2);
|
|
14647
|
-
return config2;
|
|
14648
|
-
}
|
|
14649
|
-
}
|
|
14650
|
-
var init_config_store = __esm(() => {
|
|
14651
|
-
init_load_config();
|
|
14652
|
-
});
|
|
14653
|
-
|
|
14654
|
-
// src/config/ensure-config.ts
|
|
14655
|
-
import { readFile as readFile6 } from "node:fs/promises";
|
|
14656
|
-
async function ensureConfigExists(path12) {
|
|
14657
|
-
try {
|
|
14658
|
-
await loadConfig(path12);
|
|
14659
|
-
} catch (error2) {
|
|
14660
|
-
if (!isMissingFileError2(error2)) {
|
|
14661
|
-
throw error2;
|
|
14662
|
-
}
|
|
14663
|
-
const store = new ConfigStore(path12);
|
|
14664
|
-
await store.save(await loadDefaultConfigTemplate());
|
|
14665
|
-
}
|
|
14666
|
-
}
|
|
14667
|
-
async function loadDefaultConfigTemplate() {
|
|
14668
|
-
const templatePath = new URL("../../config.example.json", import.meta.url);
|
|
14669
|
-
return normalizeDefaultConfigTemplate(JSON.parse(await readFile6(templatePath, "utf8")));
|
|
14670
|
-
}
|
|
14671
|
-
function normalizeDefaultConfigTemplate(raw) {
|
|
14672
|
-
const template = parseConfig(raw);
|
|
14673
|
-
return {
|
|
14674
|
-
...template,
|
|
14675
|
-
agents: Object.fromEntries(Object.entries(template.agents).map(([name, agent]) => [
|
|
14676
|
-
name,
|
|
14677
|
-
{
|
|
14678
|
-
driver: agent.driver,
|
|
14679
|
-
...resolveAgentCommand(agent.driver, agent.command) ? { command: resolveAgentCommand(agent.driver, agent.command) } : {}
|
|
14680
|
-
}
|
|
14681
|
-
])),
|
|
14682
|
-
workspaces: {}
|
|
14683
|
-
};
|
|
14684
|
-
}
|
|
14685
|
-
function isMissingFileError2(error2) {
|
|
14686
|
-
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
14687
|
-
}
|
|
14688
|
-
var init_ensure_config = __esm(() => {
|
|
14689
|
-
init_config_store();
|
|
14690
|
-
init_load_config();
|
|
14691
|
-
});
|
|
14692
|
-
|
|
14693
14803
|
// src/config/resolve-acpx-command.ts
|
|
14694
14804
|
import { readFileSync } from "node:fs";
|
|
14695
|
-
import { createRequire as
|
|
14805
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
14696
14806
|
import { posix, win32 } from "node:path";
|
|
14697
14807
|
function resolveAcpxCommand(options = {}) {
|
|
14698
14808
|
return resolveAcpxCommandMetadata(options).command;
|
|
@@ -14706,8 +14816,8 @@ function resolveAcpxCommandMetadata(options = {}) {
|
|
|
14706
14816
|
};
|
|
14707
14817
|
}
|
|
14708
14818
|
const platform = options.platform ?? process.platform;
|
|
14709
|
-
const resolvePackageJson = options.resolvePackageJson ?? ((id) =>
|
|
14710
|
-
const readPackageJson = options.readPackageJson ?? ((
|
|
14819
|
+
const resolvePackageJson = options.resolvePackageJson ?? ((id) => require3.resolve(id));
|
|
14820
|
+
const readPackageJson = options.readPackageJson ?? ((path11) => JSON.parse(readFileSync(path11, "utf8")));
|
|
14711
14821
|
try {
|
|
14712
14822
|
const packageJsonPath = resolvePackageJson("acpx/package.json");
|
|
14713
14823
|
const pkg = readPackageJson(packageJsonPath);
|
|
@@ -14728,9 +14838,9 @@ function resolveAcpxCommandMetadata(options = {}) {
|
|
|
14728
14838
|
explanation: "transport.command is unset and no bundled acpx was found, so PATH is the fallback."
|
|
14729
14839
|
};
|
|
14730
14840
|
}
|
|
14731
|
-
var
|
|
14841
|
+
var require3;
|
|
14732
14842
|
var init_resolve_acpx_command = __esm(() => {
|
|
14733
|
-
|
|
14843
|
+
require3 = createRequire3(import.meta.url);
|
|
14734
14844
|
});
|
|
14735
14845
|
|
|
14736
14846
|
// src/console-agent.ts
|
|
@@ -14979,8 +15089,8 @@ class OrchestrationServer {
|
|
|
14979
15089
|
if (this.endpoint.kind !== "unix") {
|
|
14980
15090
|
return;
|
|
14981
15091
|
}
|
|
14982
|
-
const removeFile = this.deps.removeFile ?? (async (
|
|
14983
|
-
await rm6(
|
|
15092
|
+
const removeFile = this.deps.removeFile ?? (async (path11) => {
|
|
15093
|
+
await rm6(path11, { force: true });
|
|
14984
15094
|
});
|
|
14985
15095
|
await removeFile(this.endpoint.path);
|
|
14986
15096
|
}
|
|
@@ -15113,9 +15223,9 @@ function requireTaskQuestions(params, key) {
|
|
|
15113
15223
|
};
|
|
15114
15224
|
});
|
|
15115
15225
|
}
|
|
15116
|
-
async function canConnectToEndpoint(
|
|
15226
|
+
async function canConnectToEndpoint(path11) {
|
|
15117
15227
|
return await new Promise((resolve2) => {
|
|
15118
|
-
const socket = createConnection2(
|
|
15228
|
+
const socket = createConnection2(path11);
|
|
15119
15229
|
let settled = false;
|
|
15120
15230
|
const finish = (result) => {
|
|
15121
15231
|
if (settled) {
|
|
@@ -15136,7 +15246,7 @@ async function canConnectToEndpoint(path12) {
|
|
|
15136
15246
|
});
|
|
15137
15247
|
});
|
|
15138
15248
|
}
|
|
15139
|
-
async function listen(server,
|
|
15249
|
+
async function listen(server, path11) {
|
|
15140
15250
|
await new Promise((resolve2, reject) => {
|
|
15141
15251
|
const onError = (error2) => {
|
|
15142
15252
|
server.off("listening", onListening);
|
|
@@ -15148,7 +15258,7 @@ async function listen(server, path12) {
|
|
|
15148
15258
|
};
|
|
15149
15259
|
server.once("error", onError);
|
|
15150
15260
|
server.once("listening", onListening);
|
|
15151
|
-
server.listen(
|
|
15261
|
+
server.listen(path11);
|
|
15152
15262
|
});
|
|
15153
15263
|
}
|
|
15154
15264
|
function isServerNotRunningError(error2) {
|
|
@@ -17748,8 +17858,7 @@ function createEmptyState() {
|
|
|
17748
17858
|
var init_types2 = () => {};
|
|
17749
17859
|
|
|
17750
17860
|
// src/state/state-store.ts
|
|
17751
|
-
import {
|
|
17752
|
-
import { dirname as dirname9 } from "node:path";
|
|
17861
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
17753
17862
|
function isRecord2(value) {
|
|
17754
17863
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17755
17864
|
}
|
|
@@ -17842,69 +17951,69 @@ function isHumanQuestionPackageRecord(value) {
|
|
|
17842
17951
|
const messages = value.messages;
|
|
17843
17952
|
return isString(value.packageId) && isString(value.coordinatorSession) && (value.status === "active" || value.status === "closed") && isString(value.createdAt) && isString(value.updatedAt) && isOptionalString(value.closedAt) && Array.isArray(initialTaskIds) && initialTaskIds.every(isString) && Array.isArray(openTaskIds) && openTaskIds.every(isString) && Array.isArray(resolvedTaskIds) && resolvedTaskIds.every(isString) && Array.isArray(messages) && messages.every(isHumanQuestionPackageMessageRecord) && isOptionalString(value.awaitingReplyMessageId);
|
|
17844
17953
|
}
|
|
17845
|
-
function parseOrchestrationState(raw,
|
|
17954
|
+
function parseOrchestrationState(raw, path11) {
|
|
17846
17955
|
if (raw === undefined) {
|
|
17847
17956
|
return createEmptyOrchestrationState();
|
|
17848
17957
|
}
|
|
17849
17958
|
if (!isRecord2(raw)) {
|
|
17850
|
-
throw new Error(`state file "${
|
|
17959
|
+
throw new Error(`state file "${path11}" must contain an object field "orchestration"`);
|
|
17851
17960
|
}
|
|
17852
17961
|
const tasks = raw.tasks;
|
|
17853
17962
|
if (tasks !== undefined && !isRecord2(tasks)) {
|
|
17854
|
-
throw new Error(`state file "${
|
|
17963
|
+
throw new Error(`state file "${path11}" must contain an object field "orchestration.tasks"`);
|
|
17855
17964
|
}
|
|
17856
17965
|
const workerBindings = raw.workerBindings;
|
|
17857
17966
|
if (workerBindings !== undefined && !isRecord2(workerBindings)) {
|
|
17858
|
-
throw new Error(`state file "${
|
|
17967
|
+
throw new Error(`state file "${path11}" must contain an object field "orchestration.workerBindings"`);
|
|
17859
17968
|
}
|
|
17860
17969
|
const groups = raw.groups;
|
|
17861
17970
|
if (groups !== undefined && !isRecord2(groups)) {
|
|
17862
|
-
throw new Error(`state file "${
|
|
17971
|
+
throw new Error(`state file "${path11}" must contain an object field "orchestration.groups"`);
|
|
17863
17972
|
}
|
|
17864
17973
|
const humanQuestionPackages = raw.humanQuestionPackages;
|
|
17865
17974
|
if (humanQuestionPackages !== undefined && !isRecord2(humanQuestionPackages)) {
|
|
17866
|
-
throw new Error(`state file "${
|
|
17975
|
+
throw new Error(`state file "${path11}" must contain an object field "orchestration.humanQuestionPackages"`);
|
|
17867
17976
|
}
|
|
17868
17977
|
const coordinatorQuestionState = raw.coordinatorQuestionState;
|
|
17869
17978
|
if (coordinatorQuestionState !== undefined && !isRecord2(coordinatorQuestionState)) {
|
|
17870
|
-
throw new Error(`state file "${
|
|
17979
|
+
throw new Error(`state file "${path11}" must contain an object field "orchestration.coordinatorQuestionState"`);
|
|
17871
17980
|
}
|
|
17872
17981
|
const coordinatorRoutes = raw.coordinatorRoutes;
|
|
17873
17982
|
if (coordinatorRoutes !== undefined && !isRecord2(coordinatorRoutes)) {
|
|
17874
|
-
throw new Error(`state file "${
|
|
17983
|
+
throw new Error(`state file "${path11}" must contain an object field "orchestration.coordinatorRoutes"`);
|
|
17875
17984
|
}
|
|
17876
17985
|
const parsedTasks = {};
|
|
17877
17986
|
for (const [taskId, task] of Object.entries(tasks ?? {})) {
|
|
17878
17987
|
if (!isTaskRecord(task)) {
|
|
17879
|
-
throw new Error(`state file "${
|
|
17988
|
+
throw new Error(`state file "${path11}" contains an invalid orchestration task at "${taskId}"`);
|
|
17880
17989
|
}
|
|
17881
17990
|
parsedTasks[taskId] = task;
|
|
17882
17991
|
}
|
|
17883
17992
|
const parsedWorkerBindings = {};
|
|
17884
17993
|
for (const [workerSession, binding] of Object.entries(workerBindings ?? {})) {
|
|
17885
17994
|
if (!isWorkerBindingRecord(binding)) {
|
|
17886
|
-
throw new Error(`state file "${
|
|
17995
|
+
throw new Error(`state file "${path11}" contains an invalid orchestration worker binding at "${workerSession}"`);
|
|
17887
17996
|
}
|
|
17888
17997
|
parsedWorkerBindings[workerSession] = binding;
|
|
17889
17998
|
}
|
|
17890
17999
|
const parsedGroups = {};
|
|
17891
18000
|
for (const [groupId, group] of Object.entries(groups ?? {})) {
|
|
17892
18001
|
if (!isGroupRecord(group)) {
|
|
17893
|
-
throw new Error(`state file "${
|
|
18002
|
+
throw new Error(`state file "${path11}" contains an invalid orchestration group at "${groupId}"`);
|
|
17894
18003
|
}
|
|
17895
18004
|
parsedGroups[groupId] = group;
|
|
17896
18005
|
}
|
|
17897
18006
|
const parsedHumanQuestionPackages = {};
|
|
17898
18007
|
for (const [packageId, packageRecord] of Object.entries(humanQuestionPackages ?? {})) {
|
|
17899
18008
|
if (!isHumanQuestionPackageRecord(packageRecord)) {
|
|
17900
|
-
throw new Error(`state file "${
|
|
18009
|
+
throw new Error(`state file "${path11}" contains an invalid human question package at "${packageId}"`);
|
|
17901
18010
|
}
|
|
17902
18011
|
parsedHumanQuestionPackages[packageId] = packageRecord;
|
|
17903
18012
|
}
|
|
17904
18013
|
const parsedCoordinatorQuestionState = {};
|
|
17905
18014
|
for (const [coordinatorSession, questionState] of Object.entries(coordinatorQuestionState ?? {})) {
|
|
17906
18015
|
if (!isCoordinatorQuestionStateRecord(questionState)) {
|
|
17907
|
-
throw new Error(`state file "${
|
|
18016
|
+
throw new Error(`state file "${path11}" contains an invalid coordinator question state at "${coordinatorSession}"`);
|
|
17908
18017
|
}
|
|
17909
18018
|
parsedCoordinatorQuestionState[coordinatorSession] = {
|
|
17910
18019
|
activePackageId: questionState.activePackageId,
|
|
@@ -17914,7 +18023,7 @@ function parseOrchestrationState(raw, path12) {
|
|
|
17914
18023
|
const parsedCoordinatorRoutes = {};
|
|
17915
18024
|
for (const [coordinatorSession, route] of Object.entries(coordinatorRoutes ?? {})) {
|
|
17916
18025
|
if (!isCoordinatorRouteContextRecord(route)) {
|
|
17917
|
-
throw new Error(`state file "${
|
|
18026
|
+
throw new Error(`state file "${path11}" contains an invalid coordinator route at "${coordinatorSession}"`);
|
|
17918
18027
|
}
|
|
17919
18028
|
parsedCoordinatorRoutes[coordinatorSession] = route;
|
|
17920
18029
|
}
|
|
@@ -17927,30 +18036,62 @@ function parseOrchestrationState(raw, path12) {
|
|
|
17927
18036
|
coordinatorRoutes: parsedCoordinatorRoutes
|
|
17928
18037
|
};
|
|
17929
18038
|
}
|
|
17930
|
-
function
|
|
18039
|
+
function isReplyMode(value) {
|
|
18040
|
+
return value === "stream" || value === "final" || value === "verbose";
|
|
18041
|
+
}
|
|
18042
|
+
function isSessionRecord(value) {
|
|
18043
|
+
if (!isRecord2(value)) {
|
|
18044
|
+
return false;
|
|
18045
|
+
}
|
|
18046
|
+
return isString(value.alias) && isString(value.agent) && isString(value.workspace) && isString(value.transport_session) && isOptionalString(value.transport_agent_command) && isOptionalString(value.mode_id) && (value.reply_mode === undefined || isReplyMode(value.reply_mode)) && isString(value.created_at) && isString(value.last_used_at);
|
|
18047
|
+
}
|
|
18048
|
+
function parseSessions(raw, path11) {
|
|
18049
|
+
const sessions = {};
|
|
18050
|
+
for (const [alias, value] of Object.entries(raw)) {
|
|
18051
|
+
if (!isSessionRecord(value)) {
|
|
18052
|
+
throw new Error(`state file "${path11}" contains malformed session record "${alias}"`);
|
|
18053
|
+
}
|
|
18054
|
+
sessions[alias] = value;
|
|
18055
|
+
}
|
|
18056
|
+
return sessions;
|
|
18057
|
+
}
|
|
18058
|
+
function isChatContextRecord(value) {
|
|
18059
|
+
return isRecord2(value) && isString(value.current_session);
|
|
18060
|
+
}
|
|
18061
|
+
function parseChatContexts(raw, path11) {
|
|
18062
|
+
const chatContexts = {};
|
|
18063
|
+
for (const [chatKey, value] of Object.entries(raw)) {
|
|
18064
|
+
if (!isChatContextRecord(value)) {
|
|
18065
|
+
throw new Error(`state file "${path11}" contains malformed chat context record "${chatKey}"`);
|
|
18066
|
+
}
|
|
18067
|
+
chatContexts[chatKey] = value;
|
|
18068
|
+
}
|
|
18069
|
+
return chatContexts;
|
|
18070
|
+
}
|
|
18071
|
+
function parseState(raw, path11) {
|
|
17931
18072
|
if (!isRecord2(raw)) {
|
|
17932
|
-
throw new Error(`state file "${
|
|
18073
|
+
throw new Error(`state file "${path11}" must contain a JSON object`);
|
|
17933
18074
|
}
|
|
17934
18075
|
const sessions = raw.sessions;
|
|
17935
18076
|
if (!isRecord2(sessions)) {
|
|
17936
|
-
throw new Error(`state file "${
|
|
18077
|
+
throw new Error(`state file "${path11}" must contain an object field "sessions"`);
|
|
17937
18078
|
}
|
|
17938
18079
|
const chatContexts = raw.chat_contexts;
|
|
17939
18080
|
if (!isRecord2(chatContexts)) {
|
|
17940
|
-
throw new Error(`state file "${
|
|
18081
|
+
throw new Error(`state file "${path11}" must contain an object field "chat_contexts"`);
|
|
17941
18082
|
}
|
|
17942
|
-
const orchestration = parseOrchestrationState(raw.orchestration,
|
|
18083
|
+
const orchestration = parseOrchestrationState(raw.orchestration, path11);
|
|
17943
18084
|
return {
|
|
17944
|
-
sessions,
|
|
17945
|
-
chat_contexts: chatContexts,
|
|
18085
|
+
sessions: parseSessions(sessions, path11),
|
|
18086
|
+
chat_contexts: parseChatContexts(chatContexts, path11),
|
|
17946
18087
|
orchestration
|
|
17947
18088
|
};
|
|
17948
18089
|
}
|
|
17949
18090
|
|
|
17950
18091
|
class StateStore {
|
|
17951
18092
|
path;
|
|
17952
|
-
constructor(
|
|
17953
|
-
this.path =
|
|
18093
|
+
constructor(path11) {
|
|
18094
|
+
this.path = path11;
|
|
17954
18095
|
}
|
|
17955
18096
|
async load() {
|
|
17956
18097
|
try {
|
|
@@ -17975,11 +18116,11 @@ class StateStore {
|
|
|
17975
18116
|
}
|
|
17976
18117
|
}
|
|
17977
18118
|
async save(state) {
|
|
17978
|
-
await
|
|
17979
|
-
await writeFile6(this.path, JSON.stringify(state, null, 2));
|
|
18119
|
+
await writePrivateFileAtomic(this.path, JSON.stringify(state, null, 2));
|
|
17980
18120
|
}
|
|
17981
18121
|
}
|
|
17982
18122
|
var init_state_store = __esm(() => {
|
|
18123
|
+
init_private_file();
|
|
17983
18124
|
init_types2();
|
|
17984
18125
|
});
|
|
17985
18126
|
|
|
@@ -18318,7 +18459,7 @@ async function spawnAcpxBridgeClient(options = {}) {
|
|
|
18318
18459
|
await client.request("shutdown", {});
|
|
18319
18460
|
} finally {
|
|
18320
18461
|
child.stdin.end();
|
|
18321
|
-
await terminateProcessTree(child.pid ?? 0);
|
|
18462
|
+
await terminateProcessTree(child.pid ?? 0, { detachedProcessGroup: false });
|
|
18322
18463
|
}
|
|
18323
18464
|
};
|
|
18324
18465
|
await client.waitUntilReady();
|
|
@@ -18781,19 +18922,19 @@ var init_streaming_prompt = __esm(() => {
|
|
|
18781
18922
|
|
|
18782
18923
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
18783
18924
|
import { chmod as chmodFs } from "node:fs/promises";
|
|
18784
|
-
import { dirname as
|
|
18925
|
+
import { dirname as dirname9, join as join8 } from "node:path";
|
|
18785
18926
|
function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
18786
18927
|
if (platform === "win32") {
|
|
18787
18928
|
return null;
|
|
18788
18929
|
}
|
|
18789
|
-
return
|
|
18930
|
+
return join8(dirname9(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
18790
18931
|
}
|
|
18791
|
-
async function ensureNodePtyHelperExecutable(helperPath,
|
|
18932
|
+
async function ensureNodePtyHelperExecutable(helperPath, chmod2 = chmodFs) {
|
|
18792
18933
|
if (!helperPath) {
|
|
18793
18934
|
return;
|
|
18794
18935
|
}
|
|
18795
18936
|
try {
|
|
18796
|
-
await
|
|
18937
|
+
await chmod2(helperPath, 493);
|
|
18797
18938
|
} catch (error2) {
|
|
18798
18939
|
if (error2.code === "ENOENT") {
|
|
18799
18940
|
return;
|
|
@@ -18806,9 +18947,9 @@ var init_node_pty_helper = () => {};
|
|
|
18806
18947
|
// src/transport/acpx-queue-owner-launcher.ts
|
|
18807
18948
|
import { createHash as createHash2 } from "node:crypto";
|
|
18808
18949
|
import { spawn as spawn6 } from "node:child_process";
|
|
18809
|
-
import { readFile as readFile8, unlink } from "node:fs/promises";
|
|
18950
|
+
import { readFile as readFile8, unlink as unlink2 } from "node:fs/promises";
|
|
18810
18951
|
import { homedir as homedir7 } from "node:os";
|
|
18811
|
-
import { join as
|
|
18952
|
+
import { join as join9 } from "node:path";
|
|
18812
18953
|
function buildWeacpxMcpServerSpec(input) {
|
|
18813
18954
|
const { command, args } = splitCommandLine(input.weacpxCommand);
|
|
18814
18955
|
return {
|
|
@@ -18965,12 +19106,12 @@ async function terminateAcpxQueueOwner(sessionId) {
|
|
|
18965
19106
|
return;
|
|
18966
19107
|
}
|
|
18967
19108
|
if (typeof owner.pid === "number" && Number.isInteger(owner.pid) && owner.pid > 0) {
|
|
18968
|
-
await terminateProcessTree(owner.pid);
|
|
19109
|
+
await terminateProcessTree(owner.pid, { detachedProcessGroup: true });
|
|
18969
19110
|
}
|
|
18970
|
-
await
|
|
19111
|
+
await unlink2(lockPath).catch(() => {});
|
|
18971
19112
|
}
|
|
18972
19113
|
function queueLockFilePath(sessionId) {
|
|
18973
|
-
return
|
|
19114
|
+
return join9(homedir7(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
|
|
18974
19115
|
}
|
|
18975
19116
|
function shortHash(value, length) {
|
|
18976
19117
|
return createHash2("sha256").update(value).digest("hex").slice(0, length);
|
|
@@ -19008,7 +19149,7 @@ function permissionModeToFlag(permissionMode) {
|
|
|
19008
19149
|
}
|
|
19009
19150
|
|
|
19010
19151
|
// src/transport/acpx-cli/acpx-cli-transport.ts
|
|
19011
|
-
import { createRequire as
|
|
19152
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
19012
19153
|
import { spawn as spawn7 } from "node:child_process";
|
|
19013
19154
|
import { spawn as spawnPty } from "node-pty";
|
|
19014
19155
|
async function defaultRunner(command, args, options) {
|
|
@@ -19017,8 +19158,12 @@ async function defaultRunner(command, args, options) {
|
|
|
19017
19158
|
const child = spawn7(spawnSpec.command, spawnSpec.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
19018
19159
|
let stdout2 = "";
|
|
19019
19160
|
let stderr = "";
|
|
19161
|
+
const onAbort = () => {
|
|
19162
|
+
terminateProcessTree(child.pid ?? 0, { detachedProcessGroup: false });
|
|
19163
|
+
};
|
|
19164
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
19020
19165
|
const timeoutId = options?.timeoutMs ? setTimeout(() => {
|
|
19021
|
-
|
|
19166
|
+
onAbort();
|
|
19022
19167
|
reject(new Error(`acpx command timed out after ${options.timeoutMs}ms: ${renderCommandForError(args)}`));
|
|
19023
19168
|
}, options.timeoutMs) : undefined;
|
|
19024
19169
|
child.stdout.on("data", (chunk) => {
|
|
@@ -19028,11 +19173,13 @@ async function defaultRunner(command, args, options) {
|
|
|
19028
19173
|
stderr += String(chunk);
|
|
19029
19174
|
});
|
|
19030
19175
|
child.on("error", (error2) => {
|
|
19176
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
19031
19177
|
if (timeoutId)
|
|
19032
19178
|
clearTimeout(timeoutId);
|
|
19033
19179
|
reject(error2);
|
|
19034
19180
|
});
|
|
19035
19181
|
child.on("close", (code) => {
|
|
19182
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
19036
19183
|
if (timeoutId)
|
|
19037
19184
|
clearTimeout(timeoutId);
|
|
19038
19185
|
resolve2({ code: code ?? 1, stdout: stdout2, stderr });
|
|
@@ -19040,7 +19187,7 @@ async function defaultRunner(command, args, options) {
|
|
|
19040
19187
|
});
|
|
19041
19188
|
}
|
|
19042
19189
|
async function defaultPtyRunner(command, args, options) {
|
|
19043
|
-
const helperPath = resolveNodePtyHelperPath(
|
|
19190
|
+
const helperPath = resolveNodePtyHelperPath(require4.resolve("node-pty/package.json"), process.platform, process.arch);
|
|
19044
19191
|
await ensureNodePtyHelperExecutable(helperPath);
|
|
19045
19192
|
return await new Promise((resolve2, reject) => {
|
|
19046
19193
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
@@ -19052,14 +19199,19 @@ async function defaultPtyRunner(command, args, options) {
|
|
|
19052
19199
|
env: process.env
|
|
19053
19200
|
});
|
|
19054
19201
|
let output = "";
|
|
19055
|
-
const
|
|
19202
|
+
const onAbort = () => {
|
|
19056
19203
|
child.kill();
|
|
19204
|
+
};
|
|
19205
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
19206
|
+
const timeoutId = options?.timeoutMs ? setTimeout(() => {
|
|
19207
|
+
onAbort();
|
|
19057
19208
|
reject(new Error(`acpx command timed out after ${options.timeoutMs}ms: ${renderCommandForError(args)}`));
|
|
19058
19209
|
}, options.timeoutMs) : undefined;
|
|
19059
19210
|
child.onData((chunk) => {
|
|
19060
19211
|
output += chunk;
|
|
19061
19212
|
});
|
|
19062
19213
|
child.onExit(({ exitCode }) => {
|
|
19214
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
19063
19215
|
if (timeoutId)
|
|
19064
19216
|
clearTimeout(timeoutId);
|
|
19065
19217
|
resolve2({ code: exitCode, stdout: output, stderr: "" });
|
|
@@ -19217,15 +19369,17 @@ ${baseText}` : "" };
|
|
|
19217
19369
|
if (!options?.timeoutMs) {
|
|
19218
19370
|
return await runner(spawnSpec.command, spawnSpec.args, undefined);
|
|
19219
19371
|
}
|
|
19372
|
+
const abortController = new AbortController;
|
|
19220
19373
|
let timeoutId;
|
|
19221
19374
|
return await Promise.race([
|
|
19222
|
-
runner(spawnSpec.command, spawnSpec.args, options).finally(() => {
|
|
19375
|
+
runner(spawnSpec.command, spawnSpec.args, { ...options, signal: abortController.signal }).finally(() => {
|
|
19223
19376
|
if (timeoutId)
|
|
19224
19377
|
clearTimeout(timeoutId);
|
|
19225
19378
|
}),
|
|
19226
19379
|
new Promise((_, reject) => {
|
|
19227
19380
|
timeoutId = setTimeout(() => {
|
|
19228
19381
|
reject(new Error(`acpx command timed out after ${options.timeoutMs}ms: ${renderCommandForError(args)}`));
|
|
19382
|
+
abortController.abort();
|
|
19229
19383
|
}, options.timeoutMs);
|
|
19230
19384
|
})
|
|
19231
19385
|
]);
|
|
@@ -19349,7 +19503,7 @@ function renderCommandForError(args) {
|
|
|
19349
19503
|
}
|
|
19350
19504
|
return rendered.join(" ");
|
|
19351
19505
|
}
|
|
19352
|
-
var
|
|
19506
|
+
var require4;
|
|
19353
19507
|
var init_acpx_cli_transport = __esm(() => {
|
|
19354
19508
|
init_spawn_command();
|
|
19355
19509
|
init_prompt_output();
|
|
@@ -19358,7 +19512,7 @@ var init_acpx_cli_transport = __esm(() => {
|
|
|
19358
19512
|
init_node_pty_helper();
|
|
19359
19513
|
init_terminate_process_tree();
|
|
19360
19514
|
init_acpx_queue_owner_launcher();
|
|
19361
|
-
|
|
19515
|
+
require4 = createRequire4(import.meta.url);
|
|
19362
19516
|
});
|
|
19363
19517
|
|
|
19364
19518
|
// src/weixin/messaging/orchestration-notice-accounts.ts
|
|
@@ -19700,7 +19854,7 @@ __export(exports_main, {
|
|
|
19700
19854
|
});
|
|
19701
19855
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
19702
19856
|
import { homedir as homedir8 } from "node:os";
|
|
19703
|
-
import { dirname as
|
|
19857
|
+
import { dirname as dirname10, join as join10 } from "node:path";
|
|
19704
19858
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
19705
19859
|
function startProgressHeartbeat(orchestration, config2, logger2, quota) {
|
|
19706
19860
|
const thresholdSeconds = config2.orchestration.progressHeartbeatSeconds;
|
|
@@ -20111,7 +20265,7 @@ function resolveRuntimePaths() {
|
|
|
20111
20265
|
throw new Error("Unable to resolve the current user home directory");
|
|
20112
20266
|
}
|
|
20113
20267
|
const configPath = process.env.WEACPX_CONFIG ?? `${home}/.weacpx/config.json`;
|
|
20114
|
-
const runtimeDir =
|
|
20268
|
+
const runtimeDir = join10(dirname10(configPath), "runtime");
|
|
20115
20269
|
return {
|
|
20116
20270
|
configPath,
|
|
20117
20271
|
statePath: process.env.WEACPX_STATE ?? `${home}/.weacpx/state.json`,
|
|
@@ -20125,9 +20279,9 @@ function resolveBridgeEntryPath() {
|
|
|
20125
20279
|
return fileURLToPath3(new URL("./bridge/bridge-main.ts", import.meta.url));
|
|
20126
20280
|
}
|
|
20127
20281
|
function resolveAppLogPath(configPath) {
|
|
20128
|
-
const rootDir =
|
|
20129
|
-
const runtimeDir =
|
|
20130
|
-
return
|
|
20282
|
+
const rootDir = dirname10(configPath);
|
|
20283
|
+
const runtimeDir = join10(rootDir, "runtime");
|
|
20284
|
+
return join10(runtimeDir, "app.log");
|
|
20131
20285
|
}
|
|
20132
20286
|
function resolveOrchestrationSocketPathFromConfigPath(configPath) {
|
|
20133
20287
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
@@ -20530,7 +20684,7 @@ async function checkOrchestrationHealth(options) {
|
|
|
20530
20684
|
// src/doctor/checks/runtime-check.ts
|
|
20531
20685
|
import { constants } from "node:fs";
|
|
20532
20686
|
import { access as access3, stat as stat2 } from "node:fs/promises";
|
|
20533
|
-
import { dirname as
|
|
20687
|
+
import { dirname as dirname11 } from "node:path";
|
|
20534
20688
|
import { homedir as homedir10 } from "node:os";
|
|
20535
20689
|
async function checkRuntime(options = {}) {
|
|
20536
20690
|
const home = options.home ?? process.env.HOME ?? homedir10();
|
|
@@ -20568,107 +20722,107 @@ async function checkRuntime(options = {}) {
|
|
|
20568
20722
|
}
|
|
20569
20723
|
function createRuntimeFsProbe() {
|
|
20570
20724
|
return {
|
|
20571
|
-
stat: async (
|
|
20572
|
-
access: async (
|
|
20725
|
+
stat: async (path11) => await stat2(path11),
|
|
20726
|
+
access: async (path11, mode) => await access3(path11, mode)
|
|
20573
20727
|
};
|
|
20574
20728
|
}
|
|
20575
|
-
async function checkDirectoryCreatable(label,
|
|
20729
|
+
async function checkDirectoryCreatable(label, path11, probe, platform) {
|
|
20576
20730
|
try {
|
|
20577
|
-
const stats = await probe.stat(
|
|
20731
|
+
const stats = await probe.stat(path11);
|
|
20578
20732
|
if (!stats.isDirectory()) {
|
|
20579
20733
|
return {
|
|
20580
20734
|
ok: false,
|
|
20581
|
-
detail: `${label}: ${
|
|
20735
|
+
detail: `${label}: ${path11} (exists but is not a directory)`
|
|
20582
20736
|
};
|
|
20583
20737
|
}
|
|
20584
|
-
await probe.access(
|
|
20738
|
+
await probe.access(path11, directoryAccessMode(platform));
|
|
20585
20739
|
return {
|
|
20586
20740
|
ok: true,
|
|
20587
|
-
detail: `${label}: ${
|
|
20741
|
+
detail: `${label}: ${path11} (writable)`
|
|
20588
20742
|
};
|
|
20589
20743
|
} catch (error2) {
|
|
20590
20744
|
if (!isMissingPathError(error2)) {
|
|
20591
20745
|
return {
|
|
20592
20746
|
ok: false,
|
|
20593
|
-
detail: `${label}: ${
|
|
20747
|
+
detail: `${label}: ${path11} (unusable: ${formatError6(error2)})`
|
|
20594
20748
|
};
|
|
20595
20749
|
}
|
|
20596
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
20750
|
+
const parentCheck = await checkCreatableAncestorDirectory(path11, probe, platform);
|
|
20597
20751
|
if (!parentCheck.ok) {
|
|
20598
20752
|
return {
|
|
20599
20753
|
ok: false,
|
|
20600
|
-
detail: `${label}: ${
|
|
20754
|
+
detail: `${label}: ${path11} (parent not writable: ${parentCheck.blockingPath})`
|
|
20601
20755
|
};
|
|
20602
20756
|
}
|
|
20603
20757
|
return {
|
|
20604
20758
|
ok: true,
|
|
20605
|
-
detail: `${label}: ${
|
|
20759
|
+
detail: `${label}: ${path11} (creatable via ${parentCheck.creatableFrom})`
|
|
20606
20760
|
};
|
|
20607
20761
|
}
|
|
20608
20762
|
}
|
|
20609
|
-
async function checkFileCreatable(label,
|
|
20763
|
+
async function checkFileCreatable(label, path11, probe, platform) {
|
|
20610
20764
|
try {
|
|
20611
|
-
const stats = await probe.stat(
|
|
20765
|
+
const stats = await probe.stat(path11);
|
|
20612
20766
|
if (stats.isDirectory()) {
|
|
20613
20767
|
return {
|
|
20614
20768
|
ok: false,
|
|
20615
|
-
detail: `${label}: ${
|
|
20769
|
+
detail: `${label}: ${path11} (exists but is a directory)`
|
|
20616
20770
|
};
|
|
20617
20771
|
}
|
|
20618
|
-
await probe.access(
|
|
20772
|
+
await probe.access(path11, constants.W_OK);
|
|
20619
20773
|
return {
|
|
20620
20774
|
ok: true,
|
|
20621
|
-
detail: `${label}: ${
|
|
20775
|
+
detail: `${label}: ${path11} (writable)`
|
|
20622
20776
|
};
|
|
20623
20777
|
} catch (error2) {
|
|
20624
20778
|
if (!isMissingPathError(error2)) {
|
|
20625
20779
|
return {
|
|
20626
20780
|
ok: false,
|
|
20627
|
-
detail: `${label}: ${
|
|
20781
|
+
detail: `${label}: ${path11} (unusable: ${formatError6(error2)})`
|
|
20628
20782
|
};
|
|
20629
20783
|
}
|
|
20630
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
20784
|
+
const parentCheck = await checkCreatableAncestorDirectory(dirname11(path11), probe, platform);
|
|
20631
20785
|
if (!parentCheck.ok) {
|
|
20632
20786
|
return {
|
|
20633
20787
|
ok: false,
|
|
20634
|
-
detail: `${label}: ${
|
|
20788
|
+
detail: `${label}: ${path11} (parent not writable: ${parentCheck.blockingPath})`
|
|
20635
20789
|
};
|
|
20636
20790
|
}
|
|
20637
20791
|
return {
|
|
20638
20792
|
ok: true,
|
|
20639
|
-
detail: `${label}: ${
|
|
20793
|
+
detail: `${label}: ${path11} (creatable via ${parentCheck.creatableFrom})`
|
|
20640
20794
|
};
|
|
20641
20795
|
}
|
|
20642
20796
|
}
|
|
20643
|
-
async function checkCreatableAncestorDirectory(
|
|
20797
|
+
async function checkCreatableAncestorDirectory(path11, probe, platform) {
|
|
20644
20798
|
try {
|
|
20645
|
-
const stats = await probe.stat(
|
|
20799
|
+
const stats = await probe.stat(path11);
|
|
20646
20800
|
if (!stats.isDirectory()) {
|
|
20647
20801
|
return {
|
|
20648
20802
|
ok: false,
|
|
20649
|
-
creatableFrom:
|
|
20650
|
-
blockingPath:
|
|
20803
|
+
creatableFrom: path11,
|
|
20804
|
+
blockingPath: path11
|
|
20651
20805
|
};
|
|
20652
20806
|
}
|
|
20653
|
-
await probe.access(
|
|
20807
|
+
await probe.access(path11, directoryAccessMode(platform));
|
|
20654
20808
|
return {
|
|
20655
20809
|
ok: true,
|
|
20656
|
-
creatableFrom:
|
|
20810
|
+
creatableFrom: path11
|
|
20657
20811
|
};
|
|
20658
20812
|
} catch (error2) {
|
|
20659
20813
|
if (!isMissingPathError(error2)) {
|
|
20660
20814
|
return {
|
|
20661
20815
|
ok: false,
|
|
20662
|
-
creatableFrom:
|
|
20663
|
-
blockingPath:
|
|
20816
|
+
creatableFrom: path11,
|
|
20817
|
+
blockingPath: path11
|
|
20664
20818
|
};
|
|
20665
20819
|
}
|
|
20666
|
-
const parent =
|
|
20667
|
-
if (parent ===
|
|
20820
|
+
const parent = dirname11(path11);
|
|
20821
|
+
if (parent === path11) {
|
|
20668
20822
|
return {
|
|
20669
20823
|
ok: false,
|
|
20670
|
-
creatableFrom:
|
|
20671
|
-
blockingPath:
|
|
20824
|
+
creatableFrom: path11,
|
|
20825
|
+
blockingPath: path11
|
|
20672
20826
|
};
|
|
20673
20827
|
}
|
|
20674
20828
|
const parentCheck = await checkCreatableAncestorDirectory(parent, probe, platform);
|
|
@@ -21086,7 +21240,7 @@ var init_render_doctor = __esm(() => {
|
|
|
21086
21240
|
|
|
21087
21241
|
// src/doctor/doctor.ts
|
|
21088
21242
|
import { homedir as homedir11 } from "node:os";
|
|
21089
|
-
import { join as
|
|
21243
|
+
import { join as join11 } from "node:path";
|
|
21090
21244
|
async function runDoctor(options = {}, deps = {}) {
|
|
21091
21245
|
const home = deps.home ?? process.env.HOME ?? homedir11();
|
|
21092
21246
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
@@ -21139,8 +21293,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
|
|
|
21139
21293
|
return resolveRuntimePaths();
|
|
21140
21294
|
}
|
|
21141
21295
|
return {
|
|
21142
|
-
configPath:
|
|
21143
|
-
statePath:
|
|
21296
|
+
configPath: join11(home, ".weacpx", "config.json"),
|
|
21297
|
+
statePath: join11(home, ".weacpx", "state.json")
|
|
21144
21298
|
};
|
|
21145
21299
|
}
|
|
21146
21300
|
function depsUseExplicitRuntimeOverrides() {
|
|
@@ -21238,6 +21392,8 @@ var init_doctor2 = __esm(async () => {
|
|
|
21238
21392
|
});
|
|
21239
21393
|
|
|
21240
21394
|
// src/cli.ts
|
|
21395
|
+
init_config_store();
|
|
21396
|
+
init_ensure_config();
|
|
21241
21397
|
init_create_daemon_controller();
|
|
21242
21398
|
init_daemon_files();
|
|
21243
21399
|
import { homedir as homedir12 } from "node:os";
|
|
@@ -21246,8 +21402,8 @@ import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
|
21246
21402
|
|
|
21247
21403
|
// src/daemon/daemon-runtime.ts
|
|
21248
21404
|
init_daemon_status();
|
|
21249
|
-
import { mkdir as
|
|
21250
|
-
import { dirname as
|
|
21405
|
+
import { mkdir as mkdir5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
21406
|
+
import { dirname as dirname5 } from "node:path";
|
|
21251
21407
|
|
|
21252
21408
|
class DaemonRuntime {
|
|
21253
21409
|
paths;
|
|
@@ -21273,8 +21429,8 @@ class DaemonRuntime {
|
|
|
21273
21429
|
stdout_log: this.paths.stdoutLog,
|
|
21274
21430
|
stderr_log: this.paths.stderrLog
|
|
21275
21431
|
};
|
|
21276
|
-
await
|
|
21277
|
-
await
|
|
21432
|
+
await mkdir5(dirname5(this.paths.pidFile), { recursive: true });
|
|
21433
|
+
await writeFile4(this.paths.pidFile, `${this.options.pid}
|
|
21278
21434
|
`);
|
|
21279
21435
|
await this.statusStore.save(this.currentStatus);
|
|
21280
21436
|
}
|
|
@@ -21544,10 +21700,10 @@ function mergeDefs(...defs) {
|
|
|
21544
21700
|
function cloneDef(schema) {
|
|
21545
21701
|
return mergeDefs(schema._zod.def);
|
|
21546
21702
|
}
|
|
21547
|
-
function getElementAtPath(obj,
|
|
21548
|
-
if (!
|
|
21703
|
+
function getElementAtPath(obj, path2) {
|
|
21704
|
+
if (!path2)
|
|
21549
21705
|
return obj;
|
|
21550
|
-
return
|
|
21706
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
21551
21707
|
}
|
|
21552
21708
|
function promiseAllObject(promisesObj) {
|
|
21553
21709
|
const keys = Object.keys(promisesObj);
|
|
@@ -21928,11 +22084,11 @@ function aborted(x, startIndex = 0) {
|
|
|
21928
22084
|
}
|
|
21929
22085
|
return false;
|
|
21930
22086
|
}
|
|
21931
|
-
function prefixIssues(
|
|
22087
|
+
function prefixIssues(path2, issues) {
|
|
21932
22088
|
return issues.map((iss) => {
|
|
21933
22089
|
var _a;
|
|
21934
22090
|
(_a = iss).path ?? (_a.path = []);
|
|
21935
|
-
iss.path.unshift(
|
|
22091
|
+
iss.path.unshift(path2);
|
|
21936
22092
|
return iss;
|
|
21937
22093
|
});
|
|
21938
22094
|
}
|
|
@@ -27554,8 +27710,8 @@ function getErrorMap() {
|
|
|
27554
27710
|
}
|
|
27555
27711
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
27556
27712
|
var makeIssue = (params) => {
|
|
27557
|
-
const { data, path, errorMaps, issueData } = params;
|
|
27558
|
-
const fullPath = [...
|
|
27713
|
+
const { data, path: path2, errorMaps, issueData } = params;
|
|
27714
|
+
const fullPath = [...path2, ...issueData.path || []];
|
|
27559
27715
|
const fullIssue = {
|
|
27560
27716
|
...issueData,
|
|
27561
27717
|
path: fullPath
|
|
@@ -27667,11 +27823,11 @@ var errorUtil;
|
|
|
27667
27823
|
|
|
27668
27824
|
// node_modules/zod/v3/types.js
|
|
27669
27825
|
class ParseInputLazyPath {
|
|
27670
|
-
constructor(parent, value,
|
|
27826
|
+
constructor(parent, value, path2, key) {
|
|
27671
27827
|
this._cachedPath = [];
|
|
27672
27828
|
this.parent = parent;
|
|
27673
27829
|
this.data = value;
|
|
27674
|
-
this._path =
|
|
27830
|
+
this._path = path2;
|
|
27675
27831
|
this._key = key;
|
|
27676
27832
|
}
|
|
27677
27833
|
get path() {
|
|
@@ -33657,7 +33813,7 @@ init_version();
|
|
|
33657
33813
|
// src/mcp/resolve-endpoint.ts
|
|
33658
33814
|
init_daemon_files();
|
|
33659
33815
|
init_orchestration_ipc();
|
|
33660
|
-
import { homedir } from "node:os";
|
|
33816
|
+
import { homedir as homedir2 } from "node:os";
|
|
33661
33817
|
function resolveDefaultOrchestrationEndpoint(env = process.env, platform = process.platform) {
|
|
33662
33818
|
if (typeof env.WEACPX_ORCHESTRATION_SOCKET === "string" && env.WEACPX_ORCHESTRATION_SOCKET.trim().length > 0) {
|
|
33663
33819
|
return createOrchestrationEndpoint(env.WEACPX_ORCHESTRATION_SOCKET.trim(), platform);
|
|
@@ -33668,7 +33824,7 @@ function resolveDefaultOrchestrationEndpoint(env = process.env, platform = proce
|
|
|
33668
33824
|
return resolveOrchestrationEndpoint(runtimeDir, platform);
|
|
33669
33825
|
}
|
|
33670
33826
|
function requireHome(env) {
|
|
33671
|
-
const home = env.HOME ??
|
|
33827
|
+
const home = env.HOME ?? homedir2();
|
|
33672
33828
|
if (!home) {
|
|
33673
33829
|
throw new Error("Unable to resolve the current user home directory");
|
|
33674
33830
|
}
|
|
@@ -34226,8 +34382,8 @@ function normalizeInputSchemaJson(schema) {
|
|
|
34226
34382
|
}
|
|
34227
34383
|
function formatZodError(error2) {
|
|
34228
34384
|
return error2.issues.map((issue2) => {
|
|
34229
|
-
const
|
|
34230
|
-
return `${
|
|
34385
|
+
const path3 = issue2.path.length > 0 ? issue2.path.join(".") : "arguments";
|
|
34386
|
+
return `${path3}: ${issue2.message}`;
|
|
34231
34387
|
}).join("; ");
|
|
34232
34388
|
}
|
|
34233
34389
|
|
|
@@ -34264,6 +34420,7 @@ function parseSourceHandle(args, env = process.env) {
|
|
|
34264
34420
|
}
|
|
34265
34421
|
|
|
34266
34422
|
// src/cli.ts
|
|
34423
|
+
init_workspace_path();
|
|
34267
34424
|
init_version();
|
|
34268
34425
|
init_consumer_lock();
|
|
34269
34426
|
var HELP_LINES = [
|
|
@@ -34276,6 +34433,7 @@ var HELP_LINES = [
|
|
|
34276
34433
|
"weacpx stop - 停止服务",
|
|
34277
34434
|
"weacpx doctor - 运行诊断",
|
|
34278
34435
|
"weacpx version - 查看版本",
|
|
34436
|
+
"weacpx workspace list|add|rm - 管理本机工作区(别名:ws)",
|
|
34279
34437
|
"weacpx mcp-stdio --coordinator-session <session> [--source-handle <handle>] - 启动 MCP stdio 服务"
|
|
34280
34438
|
];
|
|
34281
34439
|
async function runCli(args, deps = {}) {
|
|
@@ -34313,6 +34471,20 @@ async function runCli(args, deps = {}) {
|
|
|
34313
34471
|
}
|
|
34314
34472
|
return await (deps.doctor ?? defaultDoctor)(parsed.options);
|
|
34315
34473
|
}
|
|
34474
|
+
case "workspace":
|
|
34475
|
+
case "ws": {
|
|
34476
|
+
const result = await handleWorkspaceCli(args.slice(1), {
|
|
34477
|
+
print,
|
|
34478
|
+
cwd: deps.cwd ?? (() => process.cwd())
|
|
34479
|
+
});
|
|
34480
|
+
if (result === null) {
|
|
34481
|
+
for (const line of HELP_LINES) {
|
|
34482
|
+
print(line);
|
|
34483
|
+
}
|
|
34484
|
+
return 1;
|
|
34485
|
+
}
|
|
34486
|
+
return result;
|
|
34487
|
+
}
|
|
34316
34488
|
case "mcp-stdio":
|
|
34317
34489
|
return await (deps.mcpStdio ?? ((subArgs) => defaultMcpStdio(subArgs, { stderr: deps.stderr })))(args.slice(1));
|
|
34318
34490
|
case "start": {
|
|
@@ -34367,6 +34539,83 @@ async function runCli(args, deps = {}) {
|
|
|
34367
34539
|
return 1;
|
|
34368
34540
|
}
|
|
34369
34541
|
}
|
|
34542
|
+
async function handleWorkspaceCli(args, deps) {
|
|
34543
|
+
const subcommand = args[0];
|
|
34544
|
+
switch (subcommand) {
|
|
34545
|
+
case "list":
|
|
34546
|
+
if (args.length !== 1)
|
|
34547
|
+
return null;
|
|
34548
|
+
return await workspaceList(deps.print);
|
|
34549
|
+
case "add":
|
|
34550
|
+
if (args.length > 2)
|
|
34551
|
+
return null;
|
|
34552
|
+
return await workspaceAdd(args[1], deps);
|
|
34553
|
+
case "rm":
|
|
34554
|
+
if (args.length !== 2 || !args[1])
|
|
34555
|
+
return null;
|
|
34556
|
+
return await workspaceRemove(args[1], deps.print);
|
|
34557
|
+
default:
|
|
34558
|
+
return null;
|
|
34559
|
+
}
|
|
34560
|
+
}
|
|
34561
|
+
async function workspaceList(print) {
|
|
34562
|
+
const store = await createCliConfigStore();
|
|
34563
|
+
const config2 = await store.load();
|
|
34564
|
+
const entries = Object.entries(config2.workspaces);
|
|
34565
|
+
if (entries.length === 0) {
|
|
34566
|
+
print("还没有工作区。");
|
|
34567
|
+
return 0;
|
|
34568
|
+
}
|
|
34569
|
+
print("工作区列表:");
|
|
34570
|
+
for (const [name, workspace] of entries) {
|
|
34571
|
+
print(`- ${name}: ${workspace.cwd}`);
|
|
34572
|
+
}
|
|
34573
|
+
return 0;
|
|
34574
|
+
}
|
|
34575
|
+
async function workspaceAdd(rawName, deps) {
|
|
34576
|
+
const cwd = normalizeWorkspacePath(deps.cwd());
|
|
34577
|
+
const name = rawName === undefined ? basenameForWorkspacePath(cwd) : rawName.trim();
|
|
34578
|
+
if (name.trim().length === 0) {
|
|
34579
|
+
deps.print("工作区名称不能为空。");
|
|
34580
|
+
return 1;
|
|
34581
|
+
}
|
|
34582
|
+
const store = await createCliConfigStore();
|
|
34583
|
+
const config2 = await store.load();
|
|
34584
|
+
const existing = config2.workspaces[name];
|
|
34585
|
+
if (existing) {
|
|
34586
|
+
if (sameWorkspacePath(existing.cwd, cwd)) {
|
|
34587
|
+
deps.print(`工作区「${name}」已存在:${existing.cwd}`);
|
|
34588
|
+
return 0;
|
|
34589
|
+
}
|
|
34590
|
+
deps.print(`工作区「${name}」已存在,但路径不同:${existing.cwd}`);
|
|
34591
|
+
deps.print(`请换一个名称,或先执行:weacpx workspace rm ${name}`);
|
|
34592
|
+
return 1;
|
|
34593
|
+
}
|
|
34594
|
+
await store.upsertWorkspace(name, cwd);
|
|
34595
|
+
deps.print(`工作区「${name}」已保存:${cwd}`);
|
|
34596
|
+
return 0;
|
|
34597
|
+
}
|
|
34598
|
+
async function workspaceRemove(rawName, print) {
|
|
34599
|
+
const name = rawName.trim();
|
|
34600
|
+
if (name.length === 0) {
|
|
34601
|
+
print("工作区名称不能为空。");
|
|
34602
|
+
return 1;
|
|
34603
|
+
}
|
|
34604
|
+
const store = await createCliConfigStore();
|
|
34605
|
+
const config2 = await store.load();
|
|
34606
|
+
if (!config2.workspaces[name]) {
|
|
34607
|
+
print(`没有找到工作区「${name}」。`);
|
|
34608
|
+
return 1;
|
|
34609
|
+
}
|
|
34610
|
+
await store.removeWorkspace(name);
|
|
34611
|
+
print(`工作区「${name}」已删除`);
|
|
34612
|
+
return 0;
|
|
34613
|
+
}
|
|
34614
|
+
async function createCliConfigStore() {
|
|
34615
|
+
const configPath = process.env.WEACPX_CONFIG ?? `${requireHome2()}/.weacpx/config.json`;
|
|
34616
|
+
await ensureConfigExists(configPath);
|
|
34617
|
+
return new ConfigStore(configPath);
|
|
34618
|
+
}
|
|
34370
34619
|
async function defaultLogin() {
|
|
34371
34620
|
const { main: main4 } = await init_login().then(() => exports_login);
|
|
34372
34621
|
await main4();
|