weacpx 0.1.5 → 0.1.7
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 +43 -19
- package/config.example.json +3 -1
- package/dist/bridge/bridge-main.js +122 -18
- package/dist/cli.js +954 -600
- package/package.json +7 -2
package/dist/cli.js
CHANGED
|
@@ -5,25 +5,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
20
38
|
var __export = (target, all) => {
|
|
21
39
|
for (var name in all)
|
|
22
40
|
__defProp(target, name, {
|
|
23
41
|
get: all[name],
|
|
24
42
|
enumerable: true,
|
|
25
43
|
configurable: true,
|
|
26
|
-
set: (
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -2811,20 +2829,26 @@ var init_slash_commands = __esm(() => {
|
|
|
2811
2829
|
// src/weixin/messaging/process-message.ts
|
|
2812
2830
|
import crypto4 from "node:crypto";
|
|
2813
2831
|
import fs6 from "node:fs/promises";
|
|
2832
|
+
import { tmpdir } from "node:os";
|
|
2814
2833
|
import path9 from "node:path";
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
ext =
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2834
|
+
function resolveMediaTempDir(customRoot) {
|
|
2835
|
+
return customRoot ?? path9.join(tmpdir(), "weacpx", "media");
|
|
2836
|
+
}
|
|
2837
|
+
function createSaveMediaBuffer(mediaTempDir) {
|
|
2838
|
+
return async function saveMediaBuffer(buffer, contentType, subdir, _maxBytes, originalFilename) {
|
|
2839
|
+
const dir = path9.join(resolveMediaTempDir(mediaTempDir), subdir ?? "");
|
|
2840
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
2841
|
+
let ext = ".bin";
|
|
2842
|
+
if (originalFilename) {
|
|
2843
|
+
ext = path9.extname(originalFilename) || ".bin";
|
|
2844
|
+
} else if (contentType) {
|
|
2845
|
+
ext = getExtensionFromMime(contentType);
|
|
2846
|
+
}
|
|
2847
|
+
const name = `${Date.now()}-${crypto4.randomBytes(4).toString("hex")}${ext}`;
|
|
2848
|
+
const filePath = path9.join(dir, name);
|
|
2849
|
+
await fs6.writeFile(filePath, buffer);
|
|
2850
|
+
return { path: filePath };
|
|
2851
|
+
};
|
|
2828
2852
|
}
|
|
2829
2853
|
function extractTextBody(itemList) {
|
|
2830
2854
|
if (!itemList?.length)
|
|
@@ -2839,10 +2863,10 @@ function extractTextBody(itemList) {
|
|
|
2839
2863
|
function findMediaItem(itemList) {
|
|
2840
2864
|
if (!itemList?.length)
|
|
2841
2865
|
return;
|
|
2842
|
-
const direct = itemList.find((
|
|
2866
|
+
const direct = itemList.find((item) => item.type === MessageItemType.IMAGE && hasDownloadableMedia(item.image_item?.media)) ?? itemList.find((item) => item.type === MessageItemType.VIDEO && hasDownloadableMedia(item.video_item?.media)) ?? itemList.find((item) => item.type === MessageItemType.FILE && hasDownloadableMedia(item.file_item?.media)) ?? itemList.find((item) => item.type === MessageItemType.VOICE && hasDownloadableMedia(item.voice_item?.media) && !item.voice_item?.text);
|
|
2843
2867
|
if (direct)
|
|
2844
2868
|
return direct;
|
|
2845
|
-
const refItem = itemList.find((
|
|
2869
|
+
const refItem = itemList.find((item) => item.type === MessageItemType.TEXT && item.ref_msg?.message_item && isMediaItem(item.ref_msg.message_item));
|
|
2846
2870
|
return refItem?.ref_msg?.message_item ?? undefined;
|
|
2847
2871
|
}
|
|
2848
2872
|
async function processOneMessage(full, deps) {
|
|
@@ -2873,7 +2897,7 @@ async function processOneMessage(full, deps) {
|
|
|
2873
2897
|
try {
|
|
2874
2898
|
const downloaded = await downloadMediaFromItem(mediaItem, {
|
|
2875
2899
|
cdnBaseUrl: deps.cdnBaseUrl,
|
|
2876
|
-
saveMedia:
|
|
2900
|
+
saveMedia: createSaveMediaBuffer(deps.mediaTempDir),
|
|
2877
2901
|
log: deps.log,
|
|
2878
2902
|
errLog: deps.errLog,
|
|
2879
2903
|
label: "inbound"
|
|
@@ -2896,7 +2920,7 @@ async function processOneMessage(full, deps) {
|
|
|
2896
2920
|
};
|
|
2897
2921
|
}
|
|
2898
2922
|
} catch (err) {
|
|
2899
|
-
|
|
2923
|
+
deps.errLog(`media download failed: ${String(err)}`);
|
|
2900
2924
|
}
|
|
2901
2925
|
}
|
|
2902
2926
|
const to = full.from_user_id ?? "";
|
|
@@ -2908,7 +2932,7 @@ async function processOneMessage(full, deps) {
|
|
|
2908
2932
|
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
2909
2933
|
});
|
|
2910
2934
|
} catch (err) {
|
|
2911
|
-
|
|
2935
|
+
deps.errLog(`intermediate reply failed: ${String(err)}`);
|
|
2912
2936
|
}
|
|
2913
2937
|
};
|
|
2914
2938
|
const request = {
|
|
@@ -2941,7 +2965,7 @@ async function processOneMessage(full, deps) {
|
|
|
2941
2965
|
let filePath;
|
|
2942
2966
|
const mediaUrl = response.media.url;
|
|
2943
2967
|
if (mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://")) {
|
|
2944
|
-
filePath = await downloadRemoteImageToTemp(mediaUrl, path9.join(
|
|
2968
|
+
filePath = await downloadRemoteImageToTemp(mediaUrl, path9.join(resolveMediaTempDir(deps.mediaTempDir), "outbound"));
|
|
2945
2969
|
} else {
|
|
2946
2970
|
filePath = path9.isAbsolute(mediaUrl) ? mediaUrl : path9.resolve(mediaUrl);
|
|
2947
2971
|
}
|
|
@@ -2960,7 +2984,8 @@ async function processOneMessage(full, deps) {
|
|
|
2960
2984
|
});
|
|
2961
2985
|
}
|
|
2962
2986
|
} catch (err) {
|
|
2963
|
-
|
|
2987
|
+
const errorText = err instanceof Error ? err.stack ?? err.message : JSON.stringify(err);
|
|
2988
|
+
deps.errLog(`processOneMessage: agent or send failed: ${errorText}`);
|
|
2964
2989
|
sendWeixinErrorNotice({
|
|
2965
2990
|
to,
|
|
2966
2991
|
contextToken,
|
|
@@ -2985,14 +3010,13 @@ async function processOneMessage(full, deps) {
|
|
|
2985
3010
|
}
|
|
2986
3011
|
}
|
|
2987
3012
|
}
|
|
2988
|
-
var
|
|
3013
|
+
var hasDownloadableMedia = (media) => media?.encrypt_query_param || media?.full_url;
|
|
2989
3014
|
var init_process_message = __esm(() => {
|
|
2990
3015
|
init_api();
|
|
2991
3016
|
init_types();
|
|
2992
3017
|
init_upload();
|
|
2993
3018
|
init_media_download();
|
|
2994
3019
|
init_mime();
|
|
2995
|
-
init_logger();
|
|
2996
3020
|
init_inbound();
|
|
2997
3021
|
init_error_notice();
|
|
2998
3022
|
init_send_media();
|
|
@@ -3309,75 +3333,6 @@ var init_weixin_sdk = __esm(() => {
|
|
|
3309
3333
|
init_weixin();
|
|
3310
3334
|
});
|
|
3311
3335
|
|
|
3312
|
-
// src/config/agent-templates.ts
|
|
3313
|
-
function getAgentTemplate(name) {
|
|
3314
|
-
const template = TEMPLATES[name];
|
|
3315
|
-
if (!template) {
|
|
3316
|
-
return null;
|
|
3317
|
-
}
|
|
3318
|
-
return {
|
|
3319
|
-
...template
|
|
3320
|
-
};
|
|
3321
|
-
}
|
|
3322
|
-
function listAgentTemplates() {
|
|
3323
|
-
return Object.keys(TEMPLATES);
|
|
3324
|
-
}
|
|
3325
|
-
var TEMPLATES;
|
|
3326
|
-
var init_agent_templates = __esm(() => {
|
|
3327
|
-
TEMPLATES = {
|
|
3328
|
-
codex: {
|
|
3329
|
-
driver: "codex"
|
|
3330
|
-
},
|
|
3331
|
-
claude: {
|
|
3332
|
-
driver: "claude"
|
|
3333
|
-
}
|
|
3334
|
-
};
|
|
3335
|
-
});
|
|
3336
|
-
|
|
3337
|
-
// src/formatting/render-text.ts
|
|
3338
|
-
function renderHelpText() {
|
|
3339
|
-
return [
|
|
3340
|
-
"可用命令:",
|
|
3341
|
-
"/agents",
|
|
3342
|
-
"/agent add <codex|claude>",
|
|
3343
|
-
"/agent rm <name>",
|
|
3344
|
-
"/workspaces",
|
|
3345
|
-
"/workspace 或 /ws",
|
|
3346
|
-
"/ws new <name> -d <path>",
|
|
3347
|
-
"/workspace rm <name>",
|
|
3348
|
-
"/sessions",
|
|
3349
|
-
"/session 或 /ss",
|
|
3350
|
-
"/ss <agent> -d <path>",
|
|
3351
|
-
"/ss new <agent> -d <path>",
|
|
3352
|
-
"/ss new <alias> -a <name> --ws <name>",
|
|
3353
|
-
"/ss attach <alias> -a <name> --ws <name> --name <transport-session>",
|
|
3354
|
-
"/pm 或 /permission",
|
|
3355
|
-
"/pm set <allow|read|deny>",
|
|
3356
|
-
"/pm auto [allow|deny|fail]",
|
|
3357
|
-
"/use <alias>",
|
|
3358
|
-
"/status",
|
|
3359
|
-
"/cancel 或 /stop",
|
|
3360
|
-
"/session reset 或 /clear"
|
|
3361
|
-
].join(`
|
|
3362
|
-
`);
|
|
3363
|
-
}
|
|
3364
|
-
function renderAgents(config) {
|
|
3365
|
-
const names = Object.keys(config.agents);
|
|
3366
|
-
if (names.length === 0) {
|
|
3367
|
-
return "还没有注册任何 Agent。";
|
|
3368
|
-
}
|
|
3369
|
-
return ["已注册的 Agent:", ...names.map((name) => `- ${name}`)].join(`
|
|
3370
|
-
`);
|
|
3371
|
-
}
|
|
3372
|
-
function renderWorkspaces(config) {
|
|
3373
|
-
const names = Object.entries(config.workspaces);
|
|
3374
|
-
if (names.length === 0) {
|
|
3375
|
-
return "还没有注册任何工作区。";
|
|
3376
|
-
}
|
|
3377
|
-
return ["已注册的工作区:", ...names.map(([name, workspace]) => `- ${name}: ${workspace.cwd}`)].join(`
|
|
3378
|
-
`);
|
|
3379
|
-
}
|
|
3380
|
-
|
|
3381
3336
|
// src/logging/app-logger.ts
|
|
3382
3337
|
import { appendFile, mkdir as mkdir5, readdir, rename, rm as rm4, stat } from "node:fs/promises";
|
|
3383
3338
|
import { basename, dirname as dirname4, join as join2 } from "node:path";
|
|
@@ -3498,6 +3453,27 @@ var init_app_logger = __esm(() => {
|
|
|
3498
3453
|
};
|
|
3499
3454
|
});
|
|
3500
3455
|
|
|
3456
|
+
// src/transport/acpx-session-index.ts
|
|
3457
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
3458
|
+
import { homedir } from "node:os";
|
|
3459
|
+
import { resolve } from "node:path";
|
|
3460
|
+
async function resolveSessionAgentCommandFromIndex(session) {
|
|
3461
|
+
const home = process.env.HOME ?? homedir();
|
|
3462
|
+
if (!home) {
|
|
3463
|
+
return;
|
|
3464
|
+
}
|
|
3465
|
+
try {
|
|
3466
|
+
const raw = await readFile3(resolve(home, ".acpx", "sessions", "index.json"), "utf8");
|
|
3467
|
+
const parsed = JSON.parse(raw);
|
|
3468
|
+
const targetCwd = resolve(session.cwd);
|
|
3469
|
+
const match = parsed.entries?.find((entry) => entry.name === session.transportSession && entry.cwd === targetCwd && typeof entry.agentCommand === "string" && entry.agentCommand.trim().length > 0);
|
|
3470
|
+
return match?.agentCommand?.trim();
|
|
3471
|
+
} catch {
|
|
3472
|
+
return;
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
var init_acpx_session_index = () => {};
|
|
3476
|
+
|
|
3501
3477
|
// src/transport/prompt-output.ts
|
|
3502
3478
|
function getPromptText(result) {
|
|
3503
3479
|
const stdoutOutput = extractPromptOutput(result.stdout);
|
|
@@ -3652,6 +3628,8 @@ function parseCommand(input) {
|
|
|
3652
3628
|
return { kind: "cancel" };
|
|
3653
3629
|
if (command === "/clear")
|
|
3654
3630
|
return { kind: "session.reset" };
|
|
3631
|
+
if (command === "/mode" && parts.length === 1)
|
|
3632
|
+
return { kind: "mode.show" };
|
|
3655
3633
|
if (command === "/permission" && parts.length === 1)
|
|
3656
3634
|
return { kind: "permission.status" };
|
|
3657
3635
|
if (command === "/session" && parts.length === 1)
|
|
@@ -3678,6 +3656,9 @@ function parseCommand(input) {
|
|
|
3678
3656
|
if (command === "/use" && parts[1]) {
|
|
3679
3657
|
return { kind: "session.use", alias: parts[1] };
|
|
3680
3658
|
}
|
|
3659
|
+
if (command === "/mode" && parts[1]) {
|
|
3660
|
+
return { kind: "mode.set", modeId: parts[1] };
|
|
3661
|
+
}
|
|
3681
3662
|
if (command === "/agent" && parts[1] === "add" && parts[2]) {
|
|
3682
3663
|
return { kind: "agent.add", template: parts[2] };
|
|
3683
3664
|
}
|
|
@@ -3830,6 +3811,7 @@ var init_parse_command = __esm(() => {
|
|
|
3830
3811
|
"/status",
|
|
3831
3812
|
"/cancel",
|
|
3832
3813
|
"/clear",
|
|
3814
|
+
"/mode",
|
|
3833
3815
|
"/permission",
|
|
3834
3816
|
"/session",
|
|
3835
3817
|
"/workspace",
|
|
@@ -3838,398 +3820,804 @@ var init_parse_command = __esm(() => {
|
|
|
3838
3820
|
]);
|
|
3839
3821
|
});
|
|
3840
3822
|
|
|
3841
|
-
// src/commands/
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
transport;
|
|
3849
|
-
config;
|
|
3850
|
-
configStore;
|
|
3851
|
-
logger;
|
|
3852
|
-
constructor(sessions, transport, config, configStore, logger2) {
|
|
3853
|
-
this.sessions = sessions;
|
|
3854
|
-
this.transport = transport;
|
|
3855
|
-
this.config = config;
|
|
3856
|
-
this.configStore = configStore;
|
|
3857
|
-
this.logger = logger2 ?? createNoopAppLogger();
|
|
3823
|
+
// src/commands/handlers/permission-handler.ts
|
|
3824
|
+
function handlePermissionStatus(context, title) {
|
|
3825
|
+
return { text: renderPermissionStatus(context.config, title) };
|
|
3826
|
+
}
|
|
3827
|
+
async function handlePermissionModeSet(context, mode) {
|
|
3828
|
+
if (!context.config || !context.configStore) {
|
|
3829
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
3858
3830
|
}
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
"无法识别的命令格式。",
|
|
3872
|
-
"",
|
|
3873
|
-
"正确的会话创建格式:",
|
|
3874
|
-
"/session new <别名> --agent <Agent名> --ws <工作区名>",
|
|
3875
|
-
"",
|
|
3876
|
-
"例如:",
|
|
3877
|
-
"/session new demo --agent claude --ws weacpx"
|
|
3878
|
-
].join(`
|
|
3879
|
-
`)
|
|
3880
|
-
};
|
|
3881
|
-
case "help":
|
|
3882
|
-
return { text: renderHelpText() };
|
|
3883
|
-
case "agents":
|
|
3884
|
-
return { text: this.config ? renderAgents(this.config) : "No config loaded." };
|
|
3885
|
-
case "agent.add": {
|
|
3886
|
-
if (!this.config || !this.configStore) {
|
|
3887
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3888
|
-
}
|
|
3889
|
-
const template = getAgentTemplate(command.template);
|
|
3890
|
-
if (!template) {
|
|
3891
|
-
return { text: `暂不支持这个 Agent 模板。当前可用:${listAgentTemplates().join("、")}` };
|
|
3892
|
-
}
|
|
3893
|
-
const updated = await this.configStore.upsertAgent(command.template, template);
|
|
3894
|
-
this.replaceConfig(updated);
|
|
3895
|
-
return { text: `Agent「${command.template}」已保存` };
|
|
3896
|
-
}
|
|
3897
|
-
case "agent.rm": {
|
|
3898
|
-
if (!this.config || !this.configStore) {
|
|
3899
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3900
|
-
}
|
|
3901
|
-
if (!this.config.agents[command.name]) {
|
|
3902
|
-
return { text: "没有找到这个 Agent。" };
|
|
3903
|
-
}
|
|
3904
|
-
const updated = await this.configStore.removeAgent(command.name);
|
|
3905
|
-
this.replaceConfig(updated);
|
|
3906
|
-
return { text: `Agent「${command.name}」已删除` };
|
|
3907
|
-
}
|
|
3908
|
-
case "permission.status":
|
|
3909
|
-
return { text: this.renderPermissionStatus("当前权限模式:") };
|
|
3910
|
-
case "permission.mode.set": {
|
|
3911
|
-
if (!this.config || !this.configStore) {
|
|
3912
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3913
|
-
}
|
|
3914
|
-
const updated = await this.configStore.updateTransport({
|
|
3915
|
-
permissionMode: command.mode
|
|
3916
|
-
});
|
|
3917
|
-
this.replaceConfig(updated);
|
|
3918
|
-
return { text: this.renderPermissionStatus("权限模式已更新:") };
|
|
3919
|
-
}
|
|
3920
|
-
case "permission.auto.status":
|
|
3921
|
-
return { text: this.renderPermissionStatus("当前非交互策略:") };
|
|
3922
|
-
case "permission.auto.set": {
|
|
3923
|
-
if (!this.config || !this.configStore) {
|
|
3924
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3925
|
-
}
|
|
3926
|
-
const updated = await this.configStore.updateTransport({
|
|
3927
|
-
nonInteractivePermissions: command.policy
|
|
3928
|
-
});
|
|
3929
|
-
this.replaceConfig(updated);
|
|
3930
|
-
return { text: this.renderPermissionStatus("非交互策略已更新:") };
|
|
3931
|
-
}
|
|
3932
|
-
case "workspaces":
|
|
3933
|
-
return { text: this.config ? renderWorkspaces(this.config) : "No config loaded." };
|
|
3934
|
-
case "workspace.new": {
|
|
3935
|
-
if (!this.config || !this.configStore) {
|
|
3936
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3937
|
-
}
|
|
3938
|
-
const wsCwd = normalizePathForWorkspace(command.cwd);
|
|
3939
|
-
if (!await pathExists(wsCwd)) {
|
|
3940
|
-
return { text: `工作区路径不存在:${command.cwd}` };
|
|
3941
|
-
}
|
|
3942
|
-
const updated = await this.configStore.upsertWorkspace(command.name, wsCwd);
|
|
3943
|
-
this.replaceConfig(updated);
|
|
3944
|
-
return { text: `工作区「${command.name}」已保存` };
|
|
3945
|
-
}
|
|
3946
|
-
case "workspace.rm": {
|
|
3947
|
-
if (!this.config || !this.configStore) {
|
|
3948
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3949
|
-
}
|
|
3950
|
-
const updated = await this.configStore.removeWorkspace(command.name);
|
|
3951
|
-
this.replaceConfig(updated);
|
|
3952
|
-
return { text: `工作区「${command.name}」已删除` };
|
|
3953
|
-
}
|
|
3954
|
-
case "sessions": {
|
|
3955
|
-
const sessions = await this.sessions.listSessions(chatKey);
|
|
3956
|
-
if (sessions.length === 0) {
|
|
3957
|
-
return { text: "还没有会话。请先执行 /session new <alias> --agent <name> --ws <name>。" };
|
|
3958
|
-
}
|
|
3959
|
-
return {
|
|
3960
|
-
text: [
|
|
3961
|
-
"会话列表:",
|
|
3962
|
-
...sessions.map((session) => `- ${session.alias} (${session.agent} @ ${session.workspace})${session.isCurrent ? " [当前]" : ""}`)
|
|
3963
|
-
].join(`
|
|
3964
|
-
`)
|
|
3965
|
-
};
|
|
3966
|
-
}
|
|
3967
|
-
case "session.new": {
|
|
3968
|
-
const session = this.sessions.resolveSession(command.alias, command.agent, command.workspace, `${command.workspace}:${command.alias}`);
|
|
3969
|
-
try {
|
|
3970
|
-
await this.ensureTransportSession(session);
|
|
3971
|
-
const exists = await this.checkTransportSession(session);
|
|
3972
|
-
if (!exists) {
|
|
3973
|
-
return this.renderSessionCreationVerificationError(session);
|
|
3974
|
-
}
|
|
3975
|
-
} catch (error) {
|
|
3976
|
-
return this.renderSessionCreationError(session, error);
|
|
3977
|
-
}
|
|
3978
|
-
await this.sessions.attachSession(command.alias, command.agent, command.workspace, session.transportSession);
|
|
3979
|
-
await this.sessions.useSession(chatKey, command.alias);
|
|
3980
|
-
await this.logger.info("session.created", "created and selected logical session", {
|
|
3981
|
-
alias: command.alias,
|
|
3982
|
-
agent: command.agent,
|
|
3983
|
-
workspace: command.workspace
|
|
3984
|
-
});
|
|
3985
|
-
return { text: `会话「${command.alias}」已创建并切换` };
|
|
3986
|
-
}
|
|
3987
|
-
case "session.shortcut":
|
|
3988
|
-
return await this.handleSessionShortcut(chatKey, command.agent, command.cwd, false);
|
|
3989
|
-
case "session.shortcut.new":
|
|
3990
|
-
return await this.handleSessionShortcut(chatKey, command.agent, command.cwd, true);
|
|
3991
|
-
case "session.attach": {
|
|
3992
|
-
const attached = this.sessions.resolveSession(command.alias, command.agent, command.workspace, command.transportSession);
|
|
3993
|
-
const exists = await this.checkTransportSession(attached);
|
|
3994
|
-
if (!exists) {
|
|
3995
|
-
return {
|
|
3996
|
-
text: [
|
|
3997
|
-
"没有找到可绑定的已有会话。",
|
|
3998
|
-
`请确认会话名是否正确,然后重新执行:/session attach ${command.alias} --agent ${command.agent} --ws ${command.workspace} --name <会话名>`
|
|
3999
|
-
].join(`
|
|
4000
|
-
`)
|
|
4001
|
-
};
|
|
4002
|
-
}
|
|
4003
|
-
await this.sessions.attachSession(command.alias, command.agent, command.workspace, command.transportSession);
|
|
4004
|
-
await this.sessions.useSession(chatKey, command.alias);
|
|
4005
|
-
await this.logger.info("session.attached", "attached existing transport session", {
|
|
4006
|
-
alias: command.alias,
|
|
4007
|
-
agent: command.agent,
|
|
4008
|
-
workspace: command.workspace,
|
|
4009
|
-
transportSession: command.transportSession
|
|
4010
|
-
});
|
|
4011
|
-
return { text: `会话「${command.alias}」已绑定并切换` };
|
|
4012
|
-
}
|
|
4013
|
-
case "session.use":
|
|
4014
|
-
await this.sessions.useSession(chatKey, command.alias);
|
|
4015
|
-
await this.logger.info("session.selected", "selected logical session", {
|
|
4016
|
-
alias: command.alias,
|
|
4017
|
-
chatKey
|
|
4018
|
-
});
|
|
4019
|
-
return { text: `已切换到会话「${command.alias}」` };
|
|
4020
|
-
case "status": {
|
|
4021
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4022
|
-
if (!session) {
|
|
4023
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4024
|
-
}
|
|
4025
|
-
return {
|
|
4026
|
-
text: [
|
|
4027
|
-
"当前会话:",
|
|
4028
|
-
`- 名称:${session.alias}`,
|
|
4029
|
-
`- Agent:${session.agent}`,
|
|
4030
|
-
`- 工作区:${session.workspace}`
|
|
4031
|
-
].join(`
|
|
4032
|
-
`)
|
|
4033
|
-
};
|
|
4034
|
-
}
|
|
4035
|
-
case "cancel": {
|
|
4036
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4037
|
-
if (!session) {
|
|
4038
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4039
|
-
}
|
|
4040
|
-
try {
|
|
4041
|
-
const result = await this.cancelTransportSession(session);
|
|
4042
|
-
return { text: result.message || "cancelled" };
|
|
4043
|
-
} catch (error) {
|
|
4044
|
-
return this.renderTransportError(session, error);
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
case "session.reset":
|
|
4048
|
-
return await this.resetCurrentSession(chatKey);
|
|
4049
|
-
case "prompt": {
|
|
4050
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4051
|
-
if (!session) {
|
|
4052
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4053
|
-
}
|
|
4054
|
-
try {
|
|
4055
|
-
const result = await this.promptTransportSession(session, command.text, reply);
|
|
4056
|
-
return { text: result.text };
|
|
4057
|
-
} catch (error) {
|
|
4058
|
-
return this.renderTransportError(session, error);
|
|
4059
|
-
}
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
});
|
|
3831
|
+
const updated = await context.configStore.updateTransport({
|
|
3832
|
+
permissionMode: mode
|
|
3833
|
+
});
|
|
3834
|
+
context.replaceConfig(updated);
|
|
3835
|
+
return { text: renderPermissionStatus(context.config, "权限模式已更新:") };
|
|
3836
|
+
}
|
|
3837
|
+
function handlePermissionAutoStatus(context, title) {
|
|
3838
|
+
return { text: renderPermissionStatus(context.config, title) };
|
|
3839
|
+
}
|
|
3840
|
+
async function handlePermissionAutoSet(context, policy) {
|
|
3841
|
+
if (!context.config || !context.configStore) {
|
|
3842
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
4063
3843
|
}
|
|
4064
|
-
|
|
4065
|
-
|
|
3844
|
+
const updated = await context.configStore.updateTransport({
|
|
3845
|
+
nonInteractivePermissions: policy
|
|
3846
|
+
});
|
|
3847
|
+
context.replaceConfig(updated);
|
|
3848
|
+
return { text: renderPermissionStatus(context.config, "非交互策略已更新:") };
|
|
3849
|
+
}
|
|
3850
|
+
function renderPermissionStatus(config, title) {
|
|
3851
|
+
const permissionMode = config?.transport.permissionMode ?? "approve-all";
|
|
3852
|
+
const nonInteractivePermissions = config?.transport.nonInteractivePermissions ?? "fail";
|
|
3853
|
+
return [title, `- mode: ${permissionMode}`, `- auto: ${nonInteractivePermissions}`].join(`
|
|
3854
|
+
`);
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
// src/commands/handlers/session-handler.ts
|
|
3858
|
+
async function handleSessions(context, chatKey) {
|
|
3859
|
+
const sessions = await context.sessions.listSessions(chatKey);
|
|
3860
|
+
if (sessions.length === 0) {
|
|
3861
|
+
return { text: "还没有会话。请先执行 /session new <alias> --agent <name> --ws <name>。" };
|
|
4066
3862
|
}
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
if (!await pathExists(cwd)) {
|
|
4073
|
-
return { text: `工作区路径不存在:${cwdInput}` };
|
|
4074
|
-
}
|
|
4075
|
-
const workspace = await this.resolveShortcutWorkspace(cwd);
|
|
4076
|
-
await this.logger.info("session.shortcut.workspace", "resolved shortcut workspace", {
|
|
4077
|
-
workspace: workspace.name,
|
|
4078
|
-
cwd: workspace.cwd,
|
|
4079
|
-
reused: workspace.reused
|
|
4080
|
-
});
|
|
4081
|
-
const baseAlias = `${workspace.name}:${agent}`;
|
|
4082
|
-
const alias = createNew ? await this.allocateUniqueSessionAlias(baseAlias, chatKey) : baseAlias;
|
|
4083
|
-
if (!createNew && await this.hasLogicalSession(alias, chatKey)) {
|
|
4084
|
-
await this.sessions.useSession(chatKey, alias);
|
|
4085
|
-
await this.logger.info("session.shortcut.reused", "reused existing logical session", {
|
|
4086
|
-
alias,
|
|
4087
|
-
workspace: workspace.name,
|
|
4088
|
-
agent
|
|
4089
|
-
});
|
|
4090
|
-
return {
|
|
4091
|
-
text: [
|
|
4092
|
-
`已切换到会话「${alias}」`,
|
|
4093
|
-
`- 复用工作区:${workspace.name}`,
|
|
4094
|
-
`- 复用会话:${alias}`
|
|
4095
|
-
].join(`
|
|
3863
|
+
return {
|
|
3864
|
+
text: [
|
|
3865
|
+
"会话列表:",
|
|
3866
|
+
...sessions.map((session) => `- ${session.alias} (${session.agent} @ ${session.workspace})${session.isCurrent ? " [当前]" : ""}`)
|
|
3867
|
+
].join(`
|
|
4096
3868
|
`)
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
} catch {
|
|
4107
|
-
return this.renderShortcutSessionCreationError(workspace, alias);
|
|
3869
|
+
};
|
|
3870
|
+
}
|
|
3871
|
+
async function handleSessionNew(context, chatKey, alias, agent, workspace) {
|
|
3872
|
+
const session = context.lifecycle.resolveSession(alias, agent, workspace, `${workspace}:${alias}`);
|
|
3873
|
+
try {
|
|
3874
|
+
await context.lifecycle.ensureTransportSession(session);
|
|
3875
|
+
const exists = await context.lifecycle.checkTransportSession(session);
|
|
3876
|
+
if (!exists) {
|
|
3877
|
+
return context.recovery.renderSessionCreationVerificationError(session);
|
|
4108
3878
|
}
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
3879
|
+
} catch (error) {
|
|
3880
|
+
return context.recovery.renderSessionCreationError(session, error);
|
|
3881
|
+
}
|
|
3882
|
+
await context.sessions.attachSession(alias, agent, workspace, session.transportSession);
|
|
3883
|
+
await context.lifecycle.refreshSessionTransportAgentCommand(alias);
|
|
3884
|
+
await context.sessions.useSession(chatKey, alias);
|
|
3885
|
+
await context.logger.info("session.created", "created and selected logical session", {
|
|
3886
|
+
alias,
|
|
3887
|
+
agent,
|
|
3888
|
+
workspace
|
|
3889
|
+
});
|
|
3890
|
+
return { text: `会话「${alias}」已创建并切换` };
|
|
3891
|
+
}
|
|
3892
|
+
async function handleSessionShortcut(context, chatKey, agent, cwdInput, createNew) {
|
|
3893
|
+
return await context.lifecycle.handleSessionShortcut(chatKey, agent, cwdInput, createNew);
|
|
3894
|
+
}
|
|
3895
|
+
async function handleSessionAttach(context, chatKey, alias, agent, workspace, transportSession) {
|
|
3896
|
+
const attached = context.lifecycle.resolveSession(alias, agent, workspace, transportSession);
|
|
3897
|
+
const exists = await context.lifecycle.checkTransportSession(attached);
|
|
3898
|
+
if (!exists) {
|
|
4117
3899
|
return {
|
|
4118
3900
|
text: [
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
`- 新增会话:${alias}`
|
|
3901
|
+
"没有找到可绑定的已有会话。",
|
|
3902
|
+
`请确认会话名是否正确,然后重新执行:/session attach ${alias} --agent ${agent} --ws ${workspace} --name <会话名>`
|
|
4122
3903
|
].join(`
|
|
4123
3904
|
`)
|
|
4124
3905
|
};
|
|
4125
3906
|
}
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
3907
|
+
await context.sessions.attachSession(alias, agent, workspace, transportSession);
|
|
3908
|
+
await context.lifecycle.refreshSessionTransportAgentCommand(alias);
|
|
3909
|
+
await context.sessions.useSession(chatKey, alias);
|
|
3910
|
+
await context.logger.info("session.attached", "attached existing transport session", {
|
|
3911
|
+
alias,
|
|
3912
|
+
agent,
|
|
3913
|
+
workspace,
|
|
3914
|
+
transportSession
|
|
3915
|
+
});
|
|
3916
|
+
return { text: `会话「${alias}」已绑定并切换` };
|
|
3917
|
+
}
|
|
3918
|
+
async function handleSessionUse(context, chatKey, alias) {
|
|
3919
|
+
await context.sessions.useSession(chatKey, alias);
|
|
3920
|
+
await context.logger.info("session.selected", "selected logical session", {
|
|
3921
|
+
alias,
|
|
3922
|
+
chatKey
|
|
3923
|
+
});
|
|
3924
|
+
return { text: `已切换到会话「${alias}」` };
|
|
3925
|
+
}
|
|
3926
|
+
async function handleModeShow(context, chatKey) {
|
|
3927
|
+
const session = await context.sessions.getCurrentSession(chatKey);
|
|
3928
|
+
if (!session) {
|
|
3929
|
+
return { text: NO_CURRENT_SESSION_TEXT };
|
|
4139
3930
|
}
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
`请先在微信里重新执行:/session new ${session.alias} --agent ${session.agent} --ws ${session.workspace}`,
|
|
4147
|
-
`如果你要绑定一个已有会话,再执行:/session attach ${session.alias} --agent ${session.agent} --ws ${session.workspace} --name <会话名>`
|
|
4148
|
-
].join(`
|
|
4149
|
-
`)
|
|
4150
|
-
};
|
|
4151
|
-
}
|
|
4152
|
-
if (!isPartialPromptOutputError(message)) {
|
|
4153
|
-
throw error;
|
|
4154
|
-
}
|
|
4155
|
-
return {
|
|
4156
|
-
text: [
|
|
4157
|
-
`当前会话「${session.alias}」执行中断,未收到最终回复。`,
|
|
4158
|
-
"请直接重试;如果长时间无响应,可先发送 /cancel 后再重试。",
|
|
4159
|
-
`错误信息:${summarizeTransportError(message)}`
|
|
4160
|
-
].join(`
|
|
3931
|
+
return {
|
|
3932
|
+
text: [
|
|
3933
|
+
"当前 mode:",
|
|
3934
|
+
`- 会话:${session.alias}`,
|
|
3935
|
+
`- mode:${session.modeId ?? "未设置"}`
|
|
3936
|
+
].join(`
|
|
4161
3937
|
`)
|
|
4162
|
-
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
async function handleModeSet(context, chatKey, modeId) {
|
|
3941
|
+
const session = await context.sessions.getCurrentSession(chatKey);
|
|
3942
|
+
if (!session) {
|
|
3943
|
+
return { text: NO_CURRENT_SESSION_TEXT };
|
|
4163
3944
|
}
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
3945
|
+
await context.interaction.setModeTransportSession(session, modeId);
|
|
3946
|
+
await context.sessions.setCurrentSessionMode(chatKey, modeId);
|
|
3947
|
+
return { text: `已设置当前会话 mode:${modeId}` };
|
|
3948
|
+
}
|
|
3949
|
+
async function handleStatus(context, chatKey) {
|
|
3950
|
+
const session = await context.sessions.getCurrentSession(chatKey);
|
|
3951
|
+
if (!session) {
|
|
3952
|
+
return { text: NO_CURRENT_SESSION_TEXT };
|
|
4170
3953
|
}
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
3954
|
+
return {
|
|
3955
|
+
text: [
|
|
3956
|
+
"当前会话:",
|
|
3957
|
+
`- 名称:${session.alias}`,
|
|
3958
|
+
`- Agent:${session.agent}`,
|
|
3959
|
+
`- 工作区:${session.workspace}`
|
|
3960
|
+
].join(`
|
|
4177
3961
|
`)
|
|
4178
|
-
|
|
3962
|
+
};
|
|
3963
|
+
}
|
|
3964
|
+
async function handleCancel(context, chatKey) {
|
|
3965
|
+
const session = await context.sessions.getCurrentSession(chatKey);
|
|
3966
|
+
if (!session) {
|
|
3967
|
+
return { text: NO_CURRENT_SESSION_TEXT };
|
|
4179
3968
|
}
|
|
4180
|
-
|
|
4181
|
-
const
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
cwd: existingByPath[1].cwd,
|
|
4186
|
-
reused: true
|
|
4187
|
-
};
|
|
4188
|
-
}
|
|
4189
|
-
const baseName = basename2(cwd);
|
|
4190
|
-
const workspaceName = this.allocateWorkspaceName(baseName, cwd);
|
|
4191
|
-
const updated = await this.configStore.upsertWorkspace(workspaceName, cwd);
|
|
4192
|
-
this.replaceConfig(updated);
|
|
4193
|
-
return {
|
|
4194
|
-
name: workspaceName,
|
|
4195
|
-
cwd,
|
|
4196
|
-
reused: false
|
|
4197
|
-
};
|
|
3969
|
+
try {
|
|
3970
|
+
const result = await context.interaction.cancelTransportSession(session);
|
|
3971
|
+
return { text: result.message || "cancelled" };
|
|
3972
|
+
} catch (error) {
|
|
3973
|
+
return context.recovery.renderTransportError(session, error);
|
|
4198
3974
|
}
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
3975
|
+
}
|
|
3976
|
+
async function handleSessionReset(context, chatKey) {
|
|
3977
|
+
return await context.lifecycle.resetCurrentSession(chatKey);
|
|
3978
|
+
}
|
|
3979
|
+
async function handlePrompt(context, chatKey, text, reply) {
|
|
3980
|
+
const session = await context.sessions.getCurrentSession(chatKey);
|
|
3981
|
+
if (!session) {
|
|
3982
|
+
return { text: NO_CURRENT_SESSION_TEXT };
|
|
3983
|
+
}
|
|
3984
|
+
try {
|
|
3985
|
+
const result = await context.interaction.promptTransportSession(session, text, reply);
|
|
3986
|
+
return { text: result.text };
|
|
3987
|
+
} catch (error) {
|
|
3988
|
+
const recovered = await context.recovery.tryRecoverMissingSession(session, error);
|
|
3989
|
+
if (recovered) {
|
|
3990
|
+
const result = await context.interaction.promptTransportSession(recovered, text, reply);
|
|
3991
|
+
return { text: result.text };
|
|
4202
3992
|
}
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
3993
|
+
return context.recovery.renderTransportError(session, error);
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
var NO_CURRENT_SESSION_TEXT = "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。";
|
|
3997
|
+
|
|
3998
|
+
// src/commands/transport-diagnostics.ts
|
|
3999
|
+
function summarizeTransportError(message) {
|
|
4000
|
+
return message.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
4001
|
+
}
|
|
4002
|
+
function summarizeTransportDiagnostic(output) {
|
|
4003
|
+
const trimmed = output.replace(/\s+/g, " ").trim();
|
|
4004
|
+
if (trimmed.length === 0) {
|
|
4005
|
+
return;
|
|
4006
|
+
}
|
|
4007
|
+
return trimmed.slice(0, 200);
|
|
4008
|
+
}
|
|
4009
|
+
function summarizeTransportDiagnosticTail(output) {
|
|
4010
|
+
const trimmed = output.replace(/\s+/g, " ").trim();
|
|
4011
|
+
if (trimmed.length === 0) {
|
|
4012
|
+
return;
|
|
4013
|
+
}
|
|
4014
|
+
return trimmed.slice(-200);
|
|
4015
|
+
}
|
|
4016
|
+
function summarizeTransportNdjson(output, prefix) {
|
|
4017
|
+
const lines = output.split(`
|
|
4018
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
4019
|
+
if (lines.length === 0) {
|
|
4020
|
+
return {};
|
|
4021
|
+
}
|
|
4022
|
+
const methods = new Set;
|
|
4023
|
+
let agentMessageChunkCount = 0;
|
|
4024
|
+
let stopReason;
|
|
4025
|
+
for (const line of lines) {
|
|
4026
|
+
try {
|
|
4027
|
+
const payload = JSON.parse(line);
|
|
4028
|
+
if (typeof payload.method === "string" && payload.method.length > 0) {
|
|
4029
|
+
methods.add(payload.method);
|
|
4030
|
+
}
|
|
4031
|
+
if (payload.params?.update?.sessionUpdate === "agent_message_chunk") {
|
|
4032
|
+
agentMessageChunkCount += 1;
|
|
4033
|
+
}
|
|
4034
|
+
if (typeof payload.result?.stopReason === "string" && payload.result.stopReason.length > 0) {
|
|
4035
|
+
stopReason = payload.result.stopReason;
|
|
4036
|
+
}
|
|
4037
|
+
} catch {
|
|
4038
|
+
continue;
|
|
4206
4039
|
}
|
|
4207
|
-
return `${baseName}-${suffix}`;
|
|
4208
4040
|
}
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4041
|
+
const summary = {
|
|
4042
|
+
[`${prefix}LineCount`]: lines.length
|
|
4043
|
+
};
|
|
4044
|
+
if (methods.size > 0) {
|
|
4045
|
+
summary[`${prefix}Methods`] = [...methods].join(",");
|
|
4046
|
+
}
|
|
4047
|
+
if (agentMessageChunkCount > 0) {
|
|
4048
|
+
summary[`${prefix}AgentMessageChunkCount`] = agentMessageChunkCount;
|
|
4049
|
+
}
|
|
4050
|
+
if (stopReason) {
|
|
4051
|
+
summary[`${prefix}StopReason`] = stopReason;
|
|
4052
|
+
}
|
|
4053
|
+
return summary;
|
|
4054
|
+
}
|
|
4055
|
+
function isPartialPromptOutputError(message) {
|
|
4056
|
+
return message.includes("未收到最终回复");
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
// src/formatting/render-text.ts
|
|
4060
|
+
function renderHelpText() {
|
|
4061
|
+
return [
|
|
4062
|
+
"可用命令:",
|
|
4063
|
+
"",
|
|
4064
|
+
"先看这 3 个:",
|
|
4065
|
+
"/ss new <agent> -d <path> - 新建会话",
|
|
4066
|
+
"/use <alias> - 切会话",
|
|
4067
|
+
"/status - 看状态",
|
|
4068
|
+
"",
|
|
4069
|
+
"Agent:",
|
|
4070
|
+
"/agents - 看 Agent",
|
|
4071
|
+
"/agent add <codex|claude> - 加 Agent",
|
|
4072
|
+
"/agent rm <name> - 删 Agent",
|
|
4073
|
+
"",
|
|
4074
|
+
"工作区:",
|
|
4075
|
+
"/workspaces - 看工作区",
|
|
4076
|
+
"/workspace 或 /ws - 工作区命令",
|
|
4077
|
+
"/ws new <name> -d <path> - 加工作区",
|
|
4078
|
+
"/workspace rm <name> - 删工作区",
|
|
4079
|
+
"",
|
|
4080
|
+
"会话:",
|
|
4081
|
+
"/sessions - 看会话",
|
|
4082
|
+
"/session 或 /ss - 会话命令",
|
|
4083
|
+
"/ss <agent> -d <path> - 快速新建",
|
|
4084
|
+
"/ss new <agent> -d <path> - 新建会话",
|
|
4085
|
+
"/ss new <alias> -a <name> --ws <name> - 指定配置新建",
|
|
4086
|
+
"/ss attach <alias> -a <name> --ws <name> --name <transport-session> - 挂已有会话",
|
|
4087
|
+
"/use <alias> - 切会话",
|
|
4088
|
+
"/session reset 或 /clear - 清上下文",
|
|
4089
|
+
"",
|
|
4090
|
+
"权限:",
|
|
4091
|
+
"/pm 或 /permission - 权限设置",
|
|
4092
|
+
"/pm set <allow|read|deny> - 设审批级别",
|
|
4093
|
+
"/pm auto [allow|deny|fail] - 设自动处理",
|
|
4094
|
+
"",
|
|
4095
|
+
"常用:",
|
|
4096
|
+
"/status - 看状态",
|
|
4097
|
+
"/cancel 或 /stop - 停当前任务"
|
|
4098
|
+
].join(`
|
|
4099
|
+
`);
|
|
4100
|
+
}
|
|
4101
|
+
function renderAgents(config) {
|
|
4102
|
+
const names = Object.keys(config.agents);
|
|
4103
|
+
if (names.length === 0) {
|
|
4104
|
+
return "还没有注册任何 Agent。";
|
|
4105
|
+
}
|
|
4106
|
+
return ["已注册的 Agent:", ...names.map((name) => `- ${name}`)].join(`
|
|
4107
|
+
`);
|
|
4108
|
+
}
|
|
4109
|
+
function renderWorkspaces(config) {
|
|
4110
|
+
const names = Object.entries(config.workspaces);
|
|
4111
|
+
if (names.length === 0) {
|
|
4112
|
+
return "还没有注册任何工作区。";
|
|
4113
|
+
}
|
|
4114
|
+
return ["已注册的工作区:", ...names.map(([name, workspace]) => `- ${name}: ${workspace.cwd}`)].join(`
|
|
4115
|
+
`);
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
// src/commands/handlers/help-handler.ts
|
|
4119
|
+
function handleHelp() {
|
|
4120
|
+
return { text: renderHelpText() };
|
|
4121
|
+
}
|
|
4122
|
+
var init_help_handler = () => {};
|
|
4123
|
+
|
|
4124
|
+
// src/config/agent-templates.ts
|
|
4125
|
+
function getAgentTemplate(name) {
|
|
4126
|
+
const template = TEMPLATES[name];
|
|
4127
|
+
if (!template) {
|
|
4128
|
+
return null;
|
|
4129
|
+
}
|
|
4130
|
+
return {
|
|
4131
|
+
...template
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
4134
|
+
function listAgentTemplates() {
|
|
4135
|
+
return Object.keys(TEMPLATES);
|
|
4136
|
+
}
|
|
4137
|
+
var TEMPLATES;
|
|
4138
|
+
var init_agent_templates = __esm(() => {
|
|
4139
|
+
TEMPLATES = {
|
|
4140
|
+
codex: {
|
|
4141
|
+
driver: "codex"
|
|
4142
|
+
},
|
|
4143
|
+
claude: {
|
|
4144
|
+
driver: "claude"
|
|
4212
4145
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4146
|
+
};
|
|
4147
|
+
});
|
|
4148
|
+
|
|
4149
|
+
// src/commands/handlers/agent-handler.ts
|
|
4150
|
+
function handleAgents(context) {
|
|
4151
|
+
return { text: context.config ? renderAgents(context.config) : "No config loaded." };
|
|
4152
|
+
}
|
|
4153
|
+
async function handleAgentAdd(context, templateName) {
|
|
4154
|
+
if (!context.config || !context.configStore) {
|
|
4155
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
4156
|
+
}
|
|
4157
|
+
const template = getAgentTemplate(templateName);
|
|
4158
|
+
if (!template) {
|
|
4159
|
+
return { text: `暂不支持这个 Agent 模板。当前可用:${listAgentTemplates().join("、")}` };
|
|
4160
|
+
}
|
|
4161
|
+
const updated = await context.configStore.upsertAgent(templateName, template);
|
|
4162
|
+
context.replaceConfig(updated);
|
|
4163
|
+
return { text: `Agent「${templateName}」已保存` };
|
|
4164
|
+
}
|
|
4165
|
+
async function handleAgentRemove(context, agentName) {
|
|
4166
|
+
if (!context.config || !context.configStore) {
|
|
4167
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
4168
|
+
}
|
|
4169
|
+
if (!context.config.agents[agentName]) {
|
|
4170
|
+
return { text: "没有找到这个 Agent。" };
|
|
4171
|
+
}
|
|
4172
|
+
const updated = await context.configStore.removeAgent(agentName);
|
|
4173
|
+
context.replaceConfig(updated);
|
|
4174
|
+
return { text: `Agent「${agentName}」已删除` };
|
|
4175
|
+
}
|
|
4176
|
+
var init_agent_handler = __esm(() => {
|
|
4177
|
+
init_agent_templates();
|
|
4178
|
+
});
|
|
4179
|
+
|
|
4180
|
+
// src/commands/handlers/workspace-handler.ts
|
|
4181
|
+
import { access } from "node:fs/promises";
|
|
4182
|
+
import { homedir as homedir2 } from "node:os";
|
|
4183
|
+
import { normalize } from "node:path";
|
|
4184
|
+
function handleWorkspaces(context) {
|
|
4185
|
+
return { text: context.config ? renderWorkspaces(context.config) : "No config loaded." };
|
|
4186
|
+
}
|
|
4187
|
+
async function handleWorkspaceCreate(context, workspaceName, cwd) {
|
|
4188
|
+
if (!context.config || !context.configStore) {
|
|
4189
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
4190
|
+
}
|
|
4191
|
+
const normalizedCwd = normalizePathForWorkspace(cwd);
|
|
4192
|
+
if (!await pathExists(normalizedCwd)) {
|
|
4193
|
+
return { text: `工作区路径不存在:${cwd}` };
|
|
4194
|
+
}
|
|
4195
|
+
const updated = await context.configStore.upsertWorkspace(workspaceName, normalizedCwd);
|
|
4196
|
+
context.replaceConfig(updated);
|
|
4197
|
+
return { text: `工作区「${workspaceName}」已保存` };
|
|
4198
|
+
}
|
|
4199
|
+
async function handleWorkspaceRemove(context, workspaceName) {
|
|
4200
|
+
if (!context.config || !context.configStore) {
|
|
4201
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
4202
|
+
}
|
|
4203
|
+
const updated = await context.configStore.removeWorkspace(workspaceName);
|
|
4204
|
+
context.replaceConfig(updated);
|
|
4205
|
+
return { text: `工作区「${workspaceName}」已删除` };
|
|
4206
|
+
}
|
|
4207
|
+
async function pathExists(path11) {
|
|
4208
|
+
try {
|
|
4209
|
+
await access(path11);
|
|
4210
|
+
return true;
|
|
4211
|
+
} catch {
|
|
4212
|
+
return false;
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
function normalizePathForWorkspace(path11) {
|
|
4216
|
+
const expanded = path11.startsWith("~") ? homedir2() + path11.slice(1) : path11;
|
|
4217
|
+
return normalize(expanded);
|
|
4218
|
+
}
|
|
4219
|
+
var init_workspace_handler = () => {};
|
|
4220
|
+
|
|
4221
|
+
// src/commands/handlers/session-shortcut-handler.ts
|
|
4222
|
+
import { access as access2 } from "node:fs/promises";
|
|
4223
|
+
import { basename as basename2, normalize as normalize2 } from "node:path";
|
|
4224
|
+
import { homedir as homedir3 } from "node:os";
|
|
4225
|
+
async function handleSessionShortcutCommand(context, ops, chatKey, agent, cwdInput, createNew) {
|
|
4226
|
+
if (!context.config || !context.configStore) {
|
|
4227
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
4228
|
+
}
|
|
4229
|
+
const cwd = normalizePathForWorkspace2(cwdInput);
|
|
4230
|
+
if (!await pathExists2(cwd)) {
|
|
4231
|
+
return { text: `工作区路径不存在:${cwdInput}` };
|
|
4232
|
+
}
|
|
4233
|
+
const workspace = await resolveShortcutWorkspace(context, cwd);
|
|
4234
|
+
await context.logger.info("session.shortcut.workspace", "resolved shortcut workspace", {
|
|
4235
|
+
workspace: workspace.name,
|
|
4236
|
+
cwd: workspace.cwd,
|
|
4237
|
+
reused: workspace.reused
|
|
4238
|
+
});
|
|
4239
|
+
const baseAlias = `${workspace.name}:${agent}`;
|
|
4240
|
+
const alias = createNew ? await allocateUniqueSessionAlias(context, baseAlias, chatKey) : baseAlias;
|
|
4241
|
+
if (!createNew && await hasLogicalSession(context, alias, chatKey)) {
|
|
4242
|
+
await context.sessions.useSession(chatKey, alias);
|
|
4243
|
+
await context.logger.info("session.shortcut.reused", "reused existing logical session", {
|
|
4244
|
+
alias,
|
|
4245
|
+
workspace: workspace.name,
|
|
4246
|
+
agent
|
|
4247
|
+
});
|
|
4248
|
+
return {
|
|
4249
|
+
text: [
|
|
4250
|
+
`已切换到会话「${alias}」`,
|
|
4251
|
+
`- 复用工作区:${workspace.name}`,
|
|
4252
|
+
`- 复用会话:${alias}`
|
|
4253
|
+
].join(`
|
|
4254
|
+
`)
|
|
4255
|
+
};
|
|
4256
|
+
}
|
|
4257
|
+
const session = ops.resolveSession(alias, agent, workspace.name, `${workspace.name}:${alias}`);
|
|
4258
|
+
try {
|
|
4259
|
+
await ops.ensureTransportSession(session);
|
|
4260
|
+
const exists = await ops.checkTransportSession(session);
|
|
4261
|
+
if (!exists) {
|
|
4262
|
+
return renderShortcutSessionCreationError(workspace, alias);
|
|
4216
4263
|
}
|
|
4217
|
-
|
|
4264
|
+
} catch {
|
|
4265
|
+
return renderShortcutSessionCreationError(workspace, alias);
|
|
4266
|
+
}
|
|
4267
|
+
await context.sessions.attachSession(alias, agent, workspace.name, session.transportSession);
|
|
4268
|
+
await ops.refreshSessionTransportAgentCommand(alias);
|
|
4269
|
+
await context.sessions.useSession(chatKey, alias);
|
|
4270
|
+
await context.logger.info("session.shortcut.created", "created new logical session from shortcut", {
|
|
4271
|
+
alias,
|
|
4272
|
+
workspace: workspace.name,
|
|
4273
|
+
agent,
|
|
4274
|
+
workspaceReused: workspace.reused
|
|
4275
|
+
});
|
|
4276
|
+
return {
|
|
4277
|
+
text: [
|
|
4278
|
+
`已创建并切换到会话「${alias}」`,
|
|
4279
|
+
workspace.reused ? `- 复用工作区:${workspace.name}` : `- 新增工作区:${workspace.name} -> ${workspace.cwd}`,
|
|
4280
|
+
`- 新增会话:${alias}`
|
|
4281
|
+
].join(`
|
|
4282
|
+
`)
|
|
4283
|
+
};
|
|
4284
|
+
}
|
|
4285
|
+
async function resolveShortcutWorkspace(context, cwd) {
|
|
4286
|
+
const existingByPath = Object.entries(context.config?.workspaces ?? {}).find(([, workspace]) => sameWorkspacePath(workspace.cwd, cwd));
|
|
4287
|
+
if (existingByPath) {
|
|
4288
|
+
return {
|
|
4289
|
+
name: existingByPath[0],
|
|
4290
|
+
cwd: existingByPath[1].cwd,
|
|
4291
|
+
reused: true
|
|
4292
|
+
};
|
|
4293
|
+
}
|
|
4294
|
+
const workspaceName = allocateWorkspaceName(context, basename2(cwd));
|
|
4295
|
+
const updated = await context.configStore.upsertWorkspace(workspaceName, cwd);
|
|
4296
|
+
context.replaceConfig(updated);
|
|
4297
|
+
return {
|
|
4298
|
+
name: workspaceName,
|
|
4299
|
+
cwd,
|
|
4300
|
+
reused: false
|
|
4301
|
+
};
|
|
4302
|
+
}
|
|
4303
|
+
function allocateWorkspaceName(context, baseName) {
|
|
4304
|
+
if (!context.config?.workspaces[baseName]) {
|
|
4305
|
+
return baseName;
|
|
4306
|
+
}
|
|
4307
|
+
let suffix = 2;
|
|
4308
|
+
while (context.config.workspaces[`${baseName}-${suffix}`]) {
|
|
4309
|
+
suffix += 1;
|
|
4310
|
+
}
|
|
4311
|
+
return `${baseName}-${suffix}`;
|
|
4312
|
+
}
|
|
4313
|
+
async function allocateUniqueSessionAlias(context, baseAlias, chatKey) {
|
|
4314
|
+
if (!await hasLogicalSession(context, baseAlias, chatKey)) {
|
|
4315
|
+
return baseAlias;
|
|
4316
|
+
}
|
|
4317
|
+
let suffix = 2;
|
|
4318
|
+
while (await hasLogicalSession(context, `${baseAlias}-${suffix}`, chatKey)) {
|
|
4319
|
+
suffix += 1;
|
|
4320
|
+
}
|
|
4321
|
+
return `${baseAlias}-${suffix}`;
|
|
4322
|
+
}
|
|
4323
|
+
async function hasLogicalSession(context, alias, chatKey) {
|
|
4324
|
+
const sessions = await context.sessions.listSessions(chatKey);
|
|
4325
|
+
return sessions.some((session) => session.alias === alias);
|
|
4326
|
+
}
|
|
4327
|
+
function renderShortcutSessionCreationError(workspace, alias) {
|
|
4328
|
+
return {
|
|
4329
|
+
text: [
|
|
4330
|
+
`会话「${alias}」创建失败。`,
|
|
4331
|
+
workspace.reused ? `- 复用工作区:${workspace.name}` : `- 已新增工作区:${workspace.name} -> ${workspace.cwd}`,
|
|
4332
|
+
"- 会话未创建,请重试。"
|
|
4333
|
+
].join(`
|
|
4334
|
+
`)
|
|
4335
|
+
};
|
|
4336
|
+
}
|
|
4337
|
+
async function pathExists2(path11) {
|
|
4338
|
+
try {
|
|
4339
|
+
await access2(path11);
|
|
4340
|
+
return true;
|
|
4341
|
+
} catch {
|
|
4342
|
+
return false;
|
|
4218
4343
|
}
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4344
|
+
}
|
|
4345
|
+
function normalizePathForWorkspace2(path11) {
|
|
4346
|
+
const expanded = path11.startsWith("~") ? homedir3() + path11.slice(1) : path11;
|
|
4347
|
+
return normalize2(expanded);
|
|
4348
|
+
}
|
|
4349
|
+
function sameWorkspacePath(left, right) {
|
|
4350
|
+
const normalizedLeft = normalizePathForWorkspace2(left);
|
|
4351
|
+
const normalizedRight = normalizePathForWorkspace2(right);
|
|
4352
|
+
if (process.platform === "win32") {
|
|
4353
|
+
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
4222
4354
|
}
|
|
4223
|
-
|
|
4355
|
+
return normalizedLeft === normalizedRight;
|
|
4356
|
+
}
|
|
4357
|
+
var init_session_shortcut_handler = () => {};
|
|
4358
|
+
|
|
4359
|
+
// src/commands/handlers/session-recovery-handler.ts
|
|
4360
|
+
function renderTransportError(session, error) {
|
|
4361
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4362
|
+
if (message.includes("No acpx session found")) {
|
|
4224
4363
|
return {
|
|
4225
4364
|
text: [
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4365
|
+
`当前会话「${session.alias}」暂时不可用。`,
|
|
4366
|
+
`请先在微信里重新执行:/session new ${session.alias} --agent ${session.agent} --ws ${session.workspace}`,
|
|
4367
|
+
`如果你要绑定一个已有会话,再执行:/session attach ${session.alias} --agent ${session.agent} --ws ${session.workspace} --name <会话名>`
|
|
4229
4368
|
].join(`
|
|
4230
4369
|
`)
|
|
4231
4370
|
};
|
|
4232
4371
|
}
|
|
4372
|
+
if (!isPartialPromptOutputError(message)) {
|
|
4373
|
+
throw error;
|
|
4374
|
+
}
|
|
4375
|
+
return {
|
|
4376
|
+
text: [
|
|
4377
|
+
`当前会话「${session.alias}」执行中断,未收到最终回复。`,
|
|
4378
|
+
"请直接重试;如果长时间无响应,可先发送 /cancel 后再重试。",
|
|
4379
|
+
`错误信息:${summarizeTransportError(message)}`
|
|
4380
|
+
].join(`
|
|
4381
|
+
`)
|
|
4382
|
+
};
|
|
4383
|
+
}
|
|
4384
|
+
function renderSessionCreationError(session, error) {
|
|
4385
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4386
|
+
if (message.includes("timed out") && message.includes("sessions new")) {
|
|
4387
|
+
return renderSessionCreationVerificationError(session);
|
|
4388
|
+
}
|
|
4389
|
+
throw error;
|
|
4390
|
+
}
|
|
4391
|
+
function renderSessionCreationVerificationError(session) {
|
|
4392
|
+
return {
|
|
4393
|
+
text: [
|
|
4394
|
+
"当前还不能直接在微信里创建新会话。",
|
|
4395
|
+
`请先准备好一个已有会话,然后在微信里执行:/session attach ${session.alias} --agent ${session.agent} --ws ${session.workspace} --name <会话名>`
|
|
4396
|
+
].join(`
|
|
4397
|
+
`)
|
|
4398
|
+
};
|
|
4399
|
+
}
|
|
4400
|
+
async function tryRecoverMissingSession(ops, session, error) {
|
|
4401
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4402
|
+
if (!message.includes("No acpx session found")) {
|
|
4403
|
+
return null;
|
|
4404
|
+
}
|
|
4405
|
+
const transportAgentCommand = await ops.resolveSessionAgentCommand(session);
|
|
4406
|
+
if (!transportAgentCommand || transportAgentCommand === session.agentCommand) {
|
|
4407
|
+
return null;
|
|
4408
|
+
}
|
|
4409
|
+
await ops.setSessionTransportAgentCommand(session.alias, transportAgentCommand);
|
|
4410
|
+
return await ops.getSession(session.alias);
|
|
4411
|
+
}
|
|
4412
|
+
var init_session_recovery_handler = () => {};
|
|
4413
|
+
|
|
4414
|
+
// src/commands/handlers/session-reset-handler.ts
|
|
4415
|
+
async function handleSessionResetCommand(context, ops, chatKey) {
|
|
4416
|
+
const session = await context.sessions.getCurrentSession(chatKey);
|
|
4417
|
+
if (!session) {
|
|
4418
|
+
return { text: NO_CURRENT_SESSION_TEXT2 };
|
|
4419
|
+
}
|
|
4420
|
+
const resetSession = ops.resolveSession(session.alias, session.agent, session.workspace, buildResetTransportSessionName(session, ops.now()));
|
|
4421
|
+
try {
|
|
4422
|
+
await ops.ensureTransportSession(resetSession);
|
|
4423
|
+
const exists = await ops.checkTransportSession(resetSession);
|
|
4424
|
+
if (!exists) {
|
|
4425
|
+
return {
|
|
4426
|
+
text: [
|
|
4427
|
+
`会话「${session.alias}」重置失败。`,
|
|
4428
|
+
"新的后端会话未创建成功,请稍后重试。"
|
|
4429
|
+
].join(`
|
|
4430
|
+
`)
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
} catch (error) {
|
|
4434
|
+
return renderTransportError(resetSession, error);
|
|
4435
|
+
}
|
|
4436
|
+
await context.sessions.attachSession(resetSession.alias, resetSession.agent, resetSession.workspace, resetSession.transportSession);
|
|
4437
|
+
await ops.refreshSessionTransportAgentCommand(resetSession.alias);
|
|
4438
|
+
await context.sessions.useSession(chatKey, resetSession.alias);
|
|
4439
|
+
await context.logger.info("session.reset", "reset current logical session", {
|
|
4440
|
+
alias: resetSession.alias,
|
|
4441
|
+
agent: resetSession.agent,
|
|
4442
|
+
workspace: resetSession.workspace,
|
|
4443
|
+
transportSession: resetSession.transportSession,
|
|
4444
|
+
chatKey
|
|
4445
|
+
});
|
|
4446
|
+
return { text: `会话「${resetSession.alias}」已重置` };
|
|
4447
|
+
}
|
|
4448
|
+
function buildResetTransportSessionName(session, now) {
|
|
4449
|
+
return `${session.workspace}:${session.alias}:reset-${now}`;
|
|
4450
|
+
}
|
|
4451
|
+
var NO_CURRENT_SESSION_TEXT2 = "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。";
|
|
4452
|
+
var init_session_reset_handler = __esm(() => {
|
|
4453
|
+
init_session_recovery_handler();
|
|
4454
|
+
});
|
|
4455
|
+
|
|
4456
|
+
// src/commands/command-router.ts
|
|
4457
|
+
class CommandRouter {
|
|
4458
|
+
sessions;
|
|
4459
|
+
transport;
|
|
4460
|
+
config;
|
|
4461
|
+
configStore;
|
|
4462
|
+
resolveSessionAgentCommand;
|
|
4463
|
+
logger;
|
|
4464
|
+
constructor(sessions, transport, config, configStore, logger2, resolveSessionAgentCommand = resolveSessionAgentCommandFromIndex) {
|
|
4465
|
+
this.sessions = sessions;
|
|
4466
|
+
this.transport = transport;
|
|
4467
|
+
this.config = config;
|
|
4468
|
+
this.configStore = configStore;
|
|
4469
|
+
this.resolveSessionAgentCommand = resolveSessionAgentCommand;
|
|
4470
|
+
this.logger = logger2 ?? createNoopAppLogger();
|
|
4471
|
+
}
|
|
4472
|
+
async handle(chatKey, input, reply) {
|
|
4473
|
+
const startedAt = Date.now();
|
|
4474
|
+
const command = parseCommand(input);
|
|
4475
|
+
await this.logger.debug("command.parsed", "parsed inbound command", {
|
|
4476
|
+
chatKey,
|
|
4477
|
+
kind: command.kind
|
|
4478
|
+
});
|
|
4479
|
+
return await this.executeCommand(chatKey, command.kind, startedAt, async () => {
|
|
4480
|
+
switch (command.kind) {
|
|
4481
|
+
case "invalid":
|
|
4482
|
+
return {
|
|
4483
|
+
text: [
|
|
4484
|
+
"无法识别的命令格式。",
|
|
4485
|
+
"",
|
|
4486
|
+
"正确的会话创建格式:",
|
|
4487
|
+
"/session new <别名> --agent <Agent名> --ws <工作区名>",
|
|
4488
|
+
"",
|
|
4489
|
+
"例如:",
|
|
4490
|
+
"/session new demo --agent claude --ws weacpx"
|
|
4491
|
+
].join(`
|
|
4492
|
+
`)
|
|
4493
|
+
};
|
|
4494
|
+
case "help":
|
|
4495
|
+
return handleHelp();
|
|
4496
|
+
case "agents":
|
|
4497
|
+
return handleAgents(this.createHandlerContext());
|
|
4498
|
+
case "agent.add":
|
|
4499
|
+
return await handleAgentAdd(this.createHandlerContext(), command.template);
|
|
4500
|
+
case "agent.rm":
|
|
4501
|
+
return await handleAgentRemove(this.createHandlerContext(), command.name);
|
|
4502
|
+
case "permission.status":
|
|
4503
|
+
return handlePermissionStatus(this.createHandlerContext(), "当前权限模式:");
|
|
4504
|
+
case "permission.mode.set":
|
|
4505
|
+
return await handlePermissionModeSet(this.createHandlerContext(), command.mode);
|
|
4506
|
+
case "permission.auto.status":
|
|
4507
|
+
return handlePermissionAutoStatus(this.createHandlerContext(), "当前非交互策略:");
|
|
4508
|
+
case "permission.auto.set":
|
|
4509
|
+
return await handlePermissionAutoSet(this.createHandlerContext(), command.policy);
|
|
4510
|
+
case "workspaces":
|
|
4511
|
+
return handleWorkspaces(this.createHandlerContext());
|
|
4512
|
+
case "workspace.new":
|
|
4513
|
+
return await handleWorkspaceCreate(this.createHandlerContext(), command.name, command.cwd);
|
|
4514
|
+
case "workspace.rm":
|
|
4515
|
+
return await handleWorkspaceRemove(this.createHandlerContext(), command.name);
|
|
4516
|
+
case "sessions":
|
|
4517
|
+
return await handleSessions(this.createSessionHandlerContext(), chatKey);
|
|
4518
|
+
case "session.new":
|
|
4519
|
+
return await handleSessionNew(this.createSessionHandlerContext(), chatKey, command.alias, command.agent, command.workspace);
|
|
4520
|
+
case "session.shortcut":
|
|
4521
|
+
return await handleSessionShortcut(this.createSessionHandlerContext(), chatKey, command.agent, command.cwd, false);
|
|
4522
|
+
case "session.shortcut.new":
|
|
4523
|
+
return await handleSessionShortcut(this.createSessionHandlerContext(), chatKey, command.agent, command.cwd, true);
|
|
4524
|
+
case "session.attach":
|
|
4525
|
+
return await handleSessionAttach(this.createSessionHandlerContext(), chatKey, command.alias, command.agent, command.workspace, command.transportSession);
|
|
4526
|
+
case "session.use":
|
|
4527
|
+
return await handleSessionUse(this.createSessionHandlerContext(), chatKey, command.alias);
|
|
4528
|
+
case "mode.show":
|
|
4529
|
+
return await handleModeShow(this.createSessionHandlerContext(), chatKey);
|
|
4530
|
+
case "mode.set":
|
|
4531
|
+
return await handleModeSet(this.createSessionHandlerContext(), chatKey, command.modeId);
|
|
4532
|
+
case "status":
|
|
4533
|
+
return await handleStatus(this.createSessionHandlerContext(), chatKey);
|
|
4534
|
+
case "cancel":
|
|
4535
|
+
return await handleCancel(this.createSessionHandlerContext(), chatKey);
|
|
4536
|
+
case "session.reset":
|
|
4537
|
+
return await handleSessionReset(this.createSessionHandlerContext(), chatKey);
|
|
4538
|
+
case "prompt":
|
|
4539
|
+
return await handlePrompt(this.createSessionHandlerContext(), chatKey, command.text, reply);
|
|
4540
|
+
}
|
|
4541
|
+
});
|
|
4542
|
+
}
|
|
4543
|
+
async clearSession(chatKey) {
|
|
4544
|
+
await handleSessionResetCommand(this.createHandlerContext(), this.createSessionResetOps(), chatKey);
|
|
4545
|
+
}
|
|
4546
|
+
createHandlerContext() {
|
|
4547
|
+
return {
|
|
4548
|
+
sessions: this.sessions,
|
|
4549
|
+
transport: this.transport,
|
|
4550
|
+
config: this.config,
|
|
4551
|
+
configStore: this.configStore,
|
|
4552
|
+
logger: this.logger,
|
|
4553
|
+
replaceConfig: (updated) => this.replaceConfig(updated)
|
|
4554
|
+
};
|
|
4555
|
+
}
|
|
4556
|
+
createSessionHandlerContext() {
|
|
4557
|
+
return {
|
|
4558
|
+
...this.createHandlerContext(),
|
|
4559
|
+
lifecycle: this.createSessionLifecycleOps(),
|
|
4560
|
+
interaction: this.createSessionInteractionOps(),
|
|
4561
|
+
recovery: this.createSessionRenderRecoveryOps()
|
|
4562
|
+
};
|
|
4563
|
+
}
|
|
4564
|
+
createSessionLifecycleOps() {
|
|
4565
|
+
return {
|
|
4566
|
+
resolveSession: (alias, agent, workspace, transportSession) => this.sessions.resolveSession(alias, agent, workspace, transportSession),
|
|
4567
|
+
ensureTransportSession: (session) => this.ensureTransportSession(session),
|
|
4568
|
+
checkTransportSession: (session) => this.checkTransportSession(session),
|
|
4569
|
+
handleSessionShortcut: (chatKey, agent, cwdInput, createNew) => handleSessionShortcutCommand(this.createHandlerContext(), this.createSessionShortcutOps(), chatKey, agent, cwdInput, createNew),
|
|
4570
|
+
resetCurrentSession: (chatKey) => handleSessionResetCommand(this.createHandlerContext(), this.createSessionResetOps(), chatKey),
|
|
4571
|
+
refreshSessionTransportAgentCommand: (alias) => this.refreshSessionTransportAgentCommand(alias)
|
|
4572
|
+
};
|
|
4573
|
+
}
|
|
4574
|
+
createSessionInteractionOps() {
|
|
4575
|
+
return {
|
|
4576
|
+
setModeTransportSession: (session, modeId) => this.setModeTransportSession(session, modeId),
|
|
4577
|
+
cancelTransportSession: (session) => this.cancelTransportSession(session),
|
|
4578
|
+
promptTransportSession: (session, text, reply) => this.promptTransportSession(session, text, reply)
|
|
4579
|
+
};
|
|
4580
|
+
}
|
|
4581
|
+
createSessionRenderRecoveryOps() {
|
|
4582
|
+
return {
|
|
4583
|
+
renderSessionCreationError: (session, error) => renderSessionCreationError(session, error),
|
|
4584
|
+
renderSessionCreationVerificationError: (session) => renderSessionCreationVerificationError(session),
|
|
4585
|
+
tryRecoverMissingSession: (session, error) => tryRecoverMissingSession(this.createSessionRecoveryOps(), session, error),
|
|
4586
|
+
renderTransportError: (session, error) => renderTransportError(session, error)
|
|
4587
|
+
};
|
|
4588
|
+
}
|
|
4589
|
+
createSessionResetOps() {
|
|
4590
|
+
return {
|
|
4591
|
+
ensureTransportSession: (session) => this.ensureTransportSession(session),
|
|
4592
|
+
checkTransportSession: (session) => this.checkTransportSession(session),
|
|
4593
|
+
resolveSession: (alias, agent, workspace, transportSession) => this.sessions.resolveSession(alias, agent, workspace, transportSession),
|
|
4594
|
+
refreshSessionTransportAgentCommand: (alias) => this.refreshSessionTransportAgentCommand(alias),
|
|
4595
|
+
now: () => Date.now()
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
createSessionRecoveryOps() {
|
|
4599
|
+
return {
|
|
4600
|
+
resolveSessionAgentCommand: (session) => this.resolveSessionAgentCommand(session),
|
|
4601
|
+
setSessionTransportAgentCommand: (alias, command) => this.sessions.setSessionTransportAgentCommand(alias, command),
|
|
4602
|
+
getSession: (alias) => this.sessions.getSession(alias)
|
|
4603
|
+
};
|
|
4604
|
+
}
|
|
4605
|
+
createSessionShortcutOps() {
|
|
4606
|
+
return {
|
|
4607
|
+
resolveSession: (alias, agent, workspace, transportSession) => this.sessions.resolveSession(alias, agent, workspace, transportSession),
|
|
4608
|
+
ensureTransportSession: (session) => this.ensureTransportSession(session),
|
|
4609
|
+
checkTransportSession: (session) => this.checkTransportSession(session),
|
|
4610
|
+
refreshSessionTransportAgentCommand: (alias) => this.refreshSessionTransportAgentCommand(alias)
|
|
4611
|
+
};
|
|
4612
|
+
}
|
|
4613
|
+
replaceConfig(updated) {
|
|
4614
|
+
if (!this.config) {
|
|
4615
|
+
return;
|
|
4616
|
+
}
|
|
4617
|
+
this.config.transport = { ...updated.transport };
|
|
4618
|
+
this.config.agents = { ...updated.agents };
|
|
4619
|
+
this.config.workspaces = { ...updated.workspaces };
|
|
4620
|
+
}
|
|
4233
4621
|
async executeCommand(chatKey, kind, startedAt, operation) {
|
|
4234
4622
|
try {
|
|
4235
4623
|
const response = await operation();
|
|
@@ -4252,50 +4640,29 @@ class CommandRouter {
|
|
|
4252
4640
|
async ensureTransportSession(session) {
|
|
4253
4641
|
await this.measureTransportCall("ensure_session", session, () => this.transport.ensureSession(session));
|
|
4254
4642
|
}
|
|
4255
|
-
async resetCurrentSession(chatKey) {
|
|
4256
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4257
|
-
if (!session) {
|
|
4258
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4259
|
-
}
|
|
4260
|
-
const resetSession = this.sessions.resolveSession(session.alias, session.agent, session.workspace, this.buildResetTransportSessionName(session));
|
|
4261
|
-
try {
|
|
4262
|
-
await this.ensureTransportSession(resetSession);
|
|
4263
|
-
const exists = await this.checkTransportSession(resetSession);
|
|
4264
|
-
if (!exists) {
|
|
4265
|
-
return {
|
|
4266
|
-
text: [
|
|
4267
|
-
`会话「${session.alias}」重置失败。`,
|
|
4268
|
-
"新的后端会话未创建成功,请稍后重试。"
|
|
4269
|
-
].join(`
|
|
4270
|
-
`)
|
|
4271
|
-
};
|
|
4272
|
-
}
|
|
4273
|
-
} catch (error) {
|
|
4274
|
-
return this.renderTransportError(resetSession, error);
|
|
4275
|
-
}
|
|
4276
|
-
await this.sessions.attachSession(resetSession.alias, resetSession.agent, resetSession.workspace, resetSession.transportSession);
|
|
4277
|
-
await this.sessions.useSession(chatKey, resetSession.alias);
|
|
4278
|
-
await this.logger.info("session.reset", "reset current logical session", {
|
|
4279
|
-
alias: resetSession.alias,
|
|
4280
|
-
agent: resetSession.agent,
|
|
4281
|
-
workspace: resetSession.workspace,
|
|
4282
|
-
transportSession: resetSession.transportSession,
|
|
4283
|
-
chatKey
|
|
4284
|
-
});
|
|
4285
|
-
return { text: `会话「${resetSession.alias}」已重置` };
|
|
4286
|
-
}
|
|
4287
|
-
buildResetTransportSessionName(session) {
|
|
4288
|
-
return `${session.workspace}:${session.alias}:reset-${Date.now()}`;
|
|
4289
|
-
}
|
|
4290
4643
|
async checkTransportSession(session) {
|
|
4291
4644
|
return await this.measureTransportCall("has_session", session, () => this.transport.hasSession(session));
|
|
4292
4645
|
}
|
|
4293
4646
|
async promptTransportSession(session, text, reply) {
|
|
4294
4647
|
return await this.measureTransportCall("prompt", session, () => this.transport.prompt(session, text, reply));
|
|
4295
4648
|
}
|
|
4649
|
+
async setModeTransportSession(session, modeId) {
|
|
4650
|
+
return await this.measureTransportCall("set_mode", session, () => this.transport.setMode(session, modeId));
|
|
4651
|
+
}
|
|
4296
4652
|
async cancelTransportSession(session) {
|
|
4297
4653
|
return await this.measureTransportCall("cancel", session, () => this.transport.cancel(session));
|
|
4298
4654
|
}
|
|
4655
|
+
async refreshSessionTransportAgentCommand(alias) {
|
|
4656
|
+
const session = await this.sessions.getSession(alias);
|
|
4657
|
+
if (!session) {
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
const transportAgentCommand = await this.resolveSessionAgentCommand(session);
|
|
4661
|
+
if (!transportAgentCommand) {
|
|
4662
|
+
return;
|
|
4663
|
+
}
|
|
4664
|
+
await this.sessions.setSessionTransportAgentCommand(alias, transportAgentCommand);
|
|
4665
|
+
}
|
|
4299
4666
|
async measureTransportCall(operation, session, callback) {
|
|
4300
4667
|
const startedAt = Date.now();
|
|
4301
4668
|
try {
|
|
@@ -4333,90 +4700,17 @@ class CommandRouter {
|
|
|
4333
4700
|
}
|
|
4334
4701
|
}
|
|
4335
4702
|
}
|
|
4336
|
-
async function pathExists(path11) {
|
|
4337
|
-
try {
|
|
4338
|
-
await access(path11);
|
|
4339
|
-
return true;
|
|
4340
|
-
} catch {
|
|
4341
|
-
return false;
|
|
4342
|
-
}
|
|
4343
|
-
}
|
|
4344
|
-
function normalizePathForWorkspace(path11) {
|
|
4345
|
-
const expanded = path11.startsWith("~") ? homedir() + path11.slice(1) : path11;
|
|
4346
|
-
return normalize(expanded);
|
|
4347
|
-
}
|
|
4348
|
-
function sameWorkspacePath(left, right) {
|
|
4349
|
-
const normalizedLeft = normalizePathForWorkspace(left);
|
|
4350
|
-
const normalizedRight = normalizePathForWorkspace(right);
|
|
4351
|
-
if (process.platform === "win32") {
|
|
4352
|
-
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
4353
|
-
}
|
|
4354
|
-
return normalizedLeft === normalizedRight;
|
|
4355
|
-
}
|
|
4356
|
-
function summarizeTransportError(message) {
|
|
4357
|
-
return message.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
4358
|
-
}
|
|
4359
|
-
function summarizeTransportDiagnostic(output) {
|
|
4360
|
-
const trimmed = output.replace(/\s+/g, " ").trim();
|
|
4361
|
-
if (trimmed.length === 0) {
|
|
4362
|
-
return;
|
|
4363
|
-
}
|
|
4364
|
-
return trimmed.slice(0, 200);
|
|
4365
|
-
}
|
|
4366
|
-
function summarizeTransportDiagnosticTail(output) {
|
|
4367
|
-
const trimmed = output.replace(/\s+/g, " ").trim();
|
|
4368
|
-
if (trimmed.length === 0) {
|
|
4369
|
-
return;
|
|
4370
|
-
}
|
|
4371
|
-
return trimmed.slice(-200);
|
|
4372
|
-
}
|
|
4373
|
-
function summarizeTransportNdjson(output, prefix) {
|
|
4374
|
-
const lines = output.split(`
|
|
4375
|
-
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
4376
|
-
if (lines.length === 0) {
|
|
4377
|
-
return {};
|
|
4378
|
-
}
|
|
4379
|
-
const methods = new Set;
|
|
4380
|
-
let agentMessageChunkCount = 0;
|
|
4381
|
-
let stopReason;
|
|
4382
|
-
for (const line of lines) {
|
|
4383
|
-
try {
|
|
4384
|
-
const payload = JSON.parse(line);
|
|
4385
|
-
if (typeof payload.method === "string" && payload.method.length > 0) {
|
|
4386
|
-
methods.add(payload.method);
|
|
4387
|
-
}
|
|
4388
|
-
if (payload.params?.update?.sessionUpdate === "agent_message_chunk") {
|
|
4389
|
-
agentMessageChunkCount += 1;
|
|
4390
|
-
}
|
|
4391
|
-
if (typeof payload.result?.stopReason === "string" && payload.result.stopReason.length > 0) {
|
|
4392
|
-
stopReason = payload.result.stopReason;
|
|
4393
|
-
}
|
|
4394
|
-
} catch {
|
|
4395
|
-
continue;
|
|
4396
|
-
}
|
|
4397
|
-
}
|
|
4398
|
-
const summary = {
|
|
4399
|
-
[`${prefix}LineCount`]: lines.length
|
|
4400
|
-
};
|
|
4401
|
-
if (methods.size > 0) {
|
|
4402
|
-
summary[`${prefix}Methods`] = [...methods].join(",");
|
|
4403
|
-
}
|
|
4404
|
-
if (agentMessageChunkCount > 0) {
|
|
4405
|
-
summary[`${prefix}AgentMessageChunkCount`] = agentMessageChunkCount;
|
|
4406
|
-
}
|
|
4407
|
-
if (stopReason) {
|
|
4408
|
-
summary[`${prefix}StopReason`] = stopReason;
|
|
4409
|
-
}
|
|
4410
|
-
return summary;
|
|
4411
|
-
}
|
|
4412
|
-
function isPartialPromptOutputError(message) {
|
|
4413
|
-
return message.includes("未收到最终回复");
|
|
4414
|
-
}
|
|
4415
4703
|
var init_command_router = __esm(() => {
|
|
4416
|
-
init_agent_templates();
|
|
4417
4704
|
init_app_logger();
|
|
4705
|
+
init_acpx_session_index();
|
|
4418
4706
|
init_prompt_output();
|
|
4419
4707
|
init_parse_command();
|
|
4708
|
+
init_help_handler();
|
|
4709
|
+
init_agent_handler();
|
|
4710
|
+
init_workspace_handler();
|
|
4711
|
+
init_session_shortcut_handler();
|
|
4712
|
+
init_session_recovery_handler();
|
|
4713
|
+
init_session_reset_handler();
|
|
4420
4714
|
});
|
|
4421
4715
|
|
|
4422
4716
|
// src/config/resolve-agent-command.ts
|
|
@@ -4435,12 +4729,12 @@ function isLegacyCodexCommand(command) {
|
|
|
4435
4729
|
}
|
|
4436
4730
|
|
|
4437
4731
|
// src/config/load-config.ts
|
|
4438
|
-
import { readFile as
|
|
4732
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
4439
4733
|
function isRecord(value) {
|
|
4440
4734
|
return typeof value === "object" && value !== null;
|
|
4441
4735
|
}
|
|
4442
4736
|
async function loadConfig(path11, options = {}) {
|
|
4443
|
-
const raw = JSON.parse(await
|
|
4737
|
+
const raw = JSON.parse(await readFile4(path11, "utf8"));
|
|
4444
4738
|
return parseConfig(raw, options);
|
|
4445
4739
|
}
|
|
4446
4740
|
function parseConfig(raw, options = {}) {
|
|
@@ -4608,7 +4902,7 @@ var init_config_store = __esm(() => {
|
|
|
4608
4902
|
});
|
|
4609
4903
|
|
|
4610
4904
|
// src/config/ensure-config.ts
|
|
4611
|
-
import { readFile as
|
|
4905
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
4612
4906
|
async function ensureConfigExists(path11) {
|
|
4613
4907
|
try {
|
|
4614
4908
|
await loadConfig(path11);
|
|
@@ -4622,7 +4916,7 @@ async function ensureConfigExists(path11) {
|
|
|
4622
4916
|
}
|
|
4623
4917
|
async function loadDefaultConfigTemplate() {
|
|
4624
4918
|
const templatePath = new URL("../../config.example.json", import.meta.url);
|
|
4625
|
-
const template = JSON.parse(await
|
|
4919
|
+
const template = JSON.parse(await readFile5(templatePath, "utf8"));
|
|
4626
4920
|
return {
|
|
4627
4921
|
...template,
|
|
4628
4922
|
agents: Object.fromEntries(Object.entries(template.agents).map(([name, agent]) => [
|
|
@@ -4725,12 +5019,20 @@ class SessionService {
|
|
|
4725
5019
|
agent,
|
|
4726
5020
|
workspace,
|
|
4727
5021
|
transport_session: transportSession,
|
|
5022
|
+
transport_agent_command: this.state.sessions[alias]?.transport_agent_command,
|
|
4728
5023
|
created_at: this.state.sessions[alias]?.created_at ?? new Date().toISOString(),
|
|
4729
5024
|
last_used_at: new Date().toISOString()
|
|
4730
5025
|
});
|
|
4731
5026
|
}
|
|
4732
|
-
async attachSession(alias, agent, workspace, transportSession) {
|
|
4733
|
-
return await this.createLogicalSession(alias, agent, workspace, transportSession);
|
|
5027
|
+
async attachSession(alias, agent, workspace, transportSession, transportAgentCommand) {
|
|
5028
|
+
return await this.createLogicalSession(alias, agent, workspace, transportSession, transportAgentCommand);
|
|
5029
|
+
}
|
|
5030
|
+
async getSession(alias) {
|
|
5031
|
+
const session = this.state.sessions[alias];
|
|
5032
|
+
if (!session) {
|
|
5033
|
+
return null;
|
|
5034
|
+
}
|
|
5035
|
+
return this.toResolvedSession(session);
|
|
4734
5036
|
}
|
|
4735
5037
|
async useSession(chatKey, alias) {
|
|
4736
5038
|
const session = this.state.sessions[alias];
|
|
@@ -4741,6 +5043,24 @@ class SessionService {
|
|
|
4741
5043
|
this.state.chat_contexts[chatKey] = { current_session: alias };
|
|
4742
5044
|
await this.persist();
|
|
4743
5045
|
}
|
|
5046
|
+
async setCurrentSessionMode(chatKey, modeId) {
|
|
5047
|
+
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
5048
|
+
if (!currentAlias) {
|
|
5049
|
+
throw new Error("no current session selected");
|
|
5050
|
+
}
|
|
5051
|
+
const session = this.state.sessions[currentAlias];
|
|
5052
|
+
if (!session) {
|
|
5053
|
+
throw new Error("no current session selected");
|
|
5054
|
+
}
|
|
5055
|
+
const normalizedModeId = modeId?.trim();
|
|
5056
|
+
if (normalizedModeId) {
|
|
5057
|
+
session.mode_id = normalizedModeId;
|
|
5058
|
+
} else {
|
|
5059
|
+
delete session.mode_id;
|
|
5060
|
+
}
|
|
5061
|
+
session.last_used_at = new Date().toISOString();
|
|
5062
|
+
await this.persist();
|
|
5063
|
+
}
|
|
4744
5064
|
async getCurrentSession(chatKey) {
|
|
4745
5065
|
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
4746
5066
|
if (!currentAlias) {
|
|
@@ -4768,24 +5088,42 @@ class SessionService {
|
|
|
4768
5088
|
return {
|
|
4769
5089
|
alias: session.alias,
|
|
4770
5090
|
agent: session.agent,
|
|
4771
|
-
agentCommand: resolveAgentCommand(agentConfig.driver, agentConfig.command),
|
|
5091
|
+
agentCommand: session.transport_agent_command ?? resolveAgentCommand(agentConfig.driver, agentConfig.command),
|
|
4772
5092
|
workspace: session.workspace,
|
|
4773
5093
|
transportSession: session.transport_session,
|
|
5094
|
+
modeId: session.mode_id,
|
|
4774
5095
|
cwd: this.config.workspaces[session.workspace].cwd
|
|
4775
5096
|
};
|
|
4776
5097
|
}
|
|
5098
|
+
async setSessionTransportAgentCommand(alias, transportAgentCommand) {
|
|
5099
|
+
const session = this.state.sessions[alias];
|
|
5100
|
+
if (!session) {
|
|
5101
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
5102
|
+
}
|
|
5103
|
+
const normalized = transportAgentCommand?.trim();
|
|
5104
|
+
if (normalized) {
|
|
5105
|
+
session.transport_agent_command = normalized;
|
|
5106
|
+
} else {
|
|
5107
|
+
delete session.transport_agent_command;
|
|
5108
|
+
}
|
|
5109
|
+
session.last_used_at = new Date().toISOString();
|
|
5110
|
+
await this.persist();
|
|
5111
|
+
}
|
|
4777
5112
|
async persist() {
|
|
4778
5113
|
await this.stateStore.save(this.state);
|
|
4779
5114
|
}
|
|
4780
|
-
async createLogicalSession(alias, agent, workspace, transportSession) {
|
|
5115
|
+
async createLogicalSession(alias, agent, workspace, transportSession, transportAgentCommand) {
|
|
4781
5116
|
this.validateSession(alias, agent, workspace);
|
|
4782
5117
|
const existingSession = this.state.sessions[alias];
|
|
4783
5118
|
const now = new Date().toISOString();
|
|
5119
|
+
const normalizedTransportAgentCommand = transportAgentCommand?.trim();
|
|
4784
5120
|
const session = {
|
|
4785
5121
|
alias,
|
|
4786
5122
|
agent,
|
|
4787
5123
|
workspace,
|
|
4788
5124
|
transport_session: transportSession,
|
|
5125
|
+
...normalizedTransportAgentCommand ? { transport_agent_command: normalizedTransportAgentCommand } : existingSession?.transport_agent_command ? { transport_agent_command: existingSession.transport_agent_command } : {},
|
|
5126
|
+
mode_id: existingSession?.mode_id,
|
|
4789
5127
|
created_at: existingSession?.created_at ?? now,
|
|
4790
5128
|
last_used_at: now
|
|
4791
5129
|
};
|
|
@@ -4813,7 +5151,7 @@ function createEmptyState() {
|
|
|
4813
5151
|
}
|
|
4814
5152
|
|
|
4815
5153
|
// src/state/state-store.ts
|
|
4816
|
-
import { mkdir as mkdir7, readFile as
|
|
5154
|
+
import { mkdir as mkdir7, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
|
|
4817
5155
|
import { dirname as dirname6 } from "node:path";
|
|
4818
5156
|
|
|
4819
5157
|
class StateStore {
|
|
@@ -4823,7 +5161,7 @@ class StateStore {
|
|
|
4823
5161
|
}
|
|
4824
5162
|
async load() {
|
|
4825
5163
|
try {
|
|
4826
|
-
const content = await
|
|
5164
|
+
const content = await readFile6(this.path, "utf8");
|
|
4827
5165
|
if (content.trim() === "") {
|
|
4828
5166
|
return createEmptyState();
|
|
4829
5167
|
}
|
|
@@ -4918,9 +5256,9 @@ class AcpxBridgeClient {
|
|
|
4918
5256
|
request(method, params) {
|
|
4919
5257
|
const id = String(this.nextId);
|
|
4920
5258
|
this.nextId += 1;
|
|
4921
|
-
return awaitable((
|
|
5259
|
+
return awaitable((resolve2, reject) => {
|
|
4922
5260
|
this.pending.set(id, {
|
|
4923
|
-
resolve: (value) =>
|
|
5261
|
+
resolve: (value) => resolve2(value),
|
|
4924
5262
|
reject
|
|
4925
5263
|
});
|
|
4926
5264
|
this.writeLine(encodeBridgeRequest({
|
|
@@ -5019,8 +5357,8 @@ async function spawnAcpxBridgeClient(options = {}) {
|
|
|
5019
5357
|
return client;
|
|
5020
5358
|
}
|
|
5021
5359
|
function awaitable(executor) {
|
|
5022
|
-
return new Promise((
|
|
5023
|
-
executor(
|
|
5360
|
+
return new Promise((resolve2, reject) => {
|
|
5361
|
+
executor(resolve2, reject);
|
|
5024
5362
|
});
|
|
5025
5363
|
}
|
|
5026
5364
|
var init_acpx_bridge_client = __esm(() => {
|
|
@@ -5042,6 +5380,12 @@ class AcpxBridgeTransport {
|
|
|
5042
5380
|
text
|
|
5043
5381
|
});
|
|
5044
5382
|
}
|
|
5383
|
+
async setMode(session, modeId) {
|
|
5384
|
+
await this.client.request("setMode", {
|
|
5385
|
+
...this.toParams(session),
|
|
5386
|
+
modeId
|
|
5387
|
+
});
|
|
5388
|
+
}
|
|
5045
5389
|
async cancel(session) {
|
|
5046
5390
|
return await this.client.request("cancel", this.toParams(session));
|
|
5047
5391
|
}
|
|
@@ -5049,9 +5393,6 @@ class AcpxBridgeTransport {
|
|
|
5049
5393
|
const result = await this.client.request("hasSession", this.toParams(session));
|
|
5050
5394
|
return result.exists;
|
|
5051
5395
|
}
|
|
5052
|
-
async listSessions() {
|
|
5053
|
-
return [];
|
|
5054
|
-
}
|
|
5055
5396
|
async dispose() {
|
|
5056
5397
|
await this.client.dispose?.();
|
|
5057
5398
|
}
|
|
@@ -5167,7 +5508,7 @@ import { createRequire as createRequire3 } from "node:module";
|
|
|
5167
5508
|
import { spawn as spawn3 } from "node:child_process";
|
|
5168
5509
|
import { spawn as spawnPty } from "node-pty";
|
|
5169
5510
|
async function defaultRunner(command, args, options) {
|
|
5170
|
-
return await new Promise((
|
|
5511
|
+
return await new Promise((resolve2, reject) => {
|
|
5171
5512
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
5172
5513
|
const child = spawn3(spawnSpec.command, spawnSpec.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
5173
5514
|
let stdout = "";
|
|
@@ -5190,14 +5531,14 @@ async function defaultRunner(command, args, options) {
|
|
|
5190
5531
|
child.on("close", (code) => {
|
|
5191
5532
|
if (timeoutId)
|
|
5192
5533
|
clearTimeout(timeoutId);
|
|
5193
|
-
|
|
5534
|
+
resolve2({ code: code ?? 1, stdout, stderr });
|
|
5194
5535
|
});
|
|
5195
5536
|
});
|
|
5196
5537
|
}
|
|
5197
5538
|
async function defaultPtyRunner(command, args, options) {
|
|
5198
5539
|
const helperPath = resolveNodePtyHelperPath(require3.resolve("node-pty/package.json"), process.platform, process.arch);
|
|
5199
5540
|
await ensureNodePtyHelperExecutable(helperPath);
|
|
5200
|
-
return await new Promise((
|
|
5541
|
+
return await new Promise((resolve2, reject) => {
|
|
5201
5542
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
5202
5543
|
const child = spawnPty(spawnSpec.command, spawnSpec.args, {
|
|
5203
5544
|
name: "xterm-color",
|
|
@@ -5217,7 +5558,7 @@ async function defaultPtyRunner(command, args, options) {
|
|
|
5217
5558
|
child.onExit(({ exitCode }) => {
|
|
5218
5559
|
if (timeoutId)
|
|
5219
5560
|
clearTimeout(timeoutId);
|
|
5220
|
-
|
|
5561
|
+
resolve2({ code: exitCode, stdout: output, stderr: "" });
|
|
5221
5562
|
});
|
|
5222
5563
|
});
|
|
5223
5564
|
}
|
|
@@ -5258,6 +5599,14 @@ class AcpxCliTransport {
|
|
|
5258
5599
|
const result = await this.runCommand(this.command, args);
|
|
5259
5600
|
return { text: getPromptText(result) };
|
|
5260
5601
|
}
|
|
5602
|
+
async setMode(session, modeId) {
|
|
5603
|
+
await this.run(this.buildArgs(session, [
|
|
5604
|
+
"set-mode",
|
|
5605
|
+
"-s",
|
|
5606
|
+
session.transportSession,
|
|
5607
|
+
modeId
|
|
5608
|
+
]));
|
|
5609
|
+
}
|
|
5261
5610
|
async cancel(session) {
|
|
5262
5611
|
const output = await this.run(this.buildArgs(session, [
|
|
5263
5612
|
"cancel",
|
|
@@ -5277,9 +5626,6 @@ class AcpxCliTransport {
|
|
|
5277
5626
|
]));
|
|
5278
5627
|
return result.code === 0;
|
|
5279
5628
|
}
|
|
5280
|
-
async listSessions() {
|
|
5281
|
-
return [];
|
|
5282
|
-
}
|
|
5283
5629
|
async run(args, options) {
|
|
5284
5630
|
const result = await this.runCommandWithTimeout(this.runCommand, args, options);
|
|
5285
5631
|
if (result.code !== 0) {
|
|
@@ -5315,7 +5661,7 @@ class AcpxCliTransport {
|
|
|
5315
5661
|
]);
|
|
5316
5662
|
}
|
|
5317
5663
|
async runStreamingPrompt(command, args, reply, maxSegmentWaitMs = 30000) {
|
|
5318
|
-
return await new Promise((
|
|
5664
|
+
return await new Promise((resolve2, reject) => {
|
|
5319
5665
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
5320
5666
|
const child = spawn3(spawnSpec.command, spawnSpec.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
5321
5667
|
let stdout = "";
|
|
@@ -5357,7 +5703,7 @@ class AcpxCliTransport {
|
|
|
5357
5703
|
if (remaining.length > 0) {
|
|
5358
5704
|
reply(remaining).catch(() => {});
|
|
5359
5705
|
}
|
|
5360
|
-
|
|
5706
|
+
resolve2({ code: code ?? 1, stdout, stderr });
|
|
5361
5707
|
});
|
|
5362
5708
|
});
|
|
5363
5709
|
}
|
|
@@ -5428,7 +5774,7 @@ __export(exports_main, {
|
|
|
5428
5774
|
main: () => main2,
|
|
5429
5775
|
buildApp: () => buildApp
|
|
5430
5776
|
});
|
|
5431
|
-
import { homedir as
|
|
5777
|
+
import { homedir as homedir4 } from "node:os";
|
|
5432
5778
|
import { dirname as dirname8, join as join4 } from "node:path";
|
|
5433
5779
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
5434
5780
|
async function buildApp(paths, deps = {}) {
|
|
@@ -5491,7 +5837,7 @@ async function main2() {
|
|
|
5491
5837
|
}
|
|
5492
5838
|
}
|
|
5493
5839
|
function resolveRuntimePaths() {
|
|
5494
|
-
const home = process.env.HOME ??
|
|
5840
|
+
const home = process.env.HOME ?? homedir4();
|
|
5495
5841
|
if (!home) {
|
|
5496
5842
|
throw new Error("Unable to resolve the current user home directory");
|
|
5497
5843
|
}
|
|
@@ -5528,7 +5874,7 @@ var init_main = __esm(async () => {
|
|
|
5528
5874
|
});
|
|
5529
5875
|
|
|
5530
5876
|
// src/cli.ts
|
|
5531
|
-
import { homedir as
|
|
5877
|
+
import { homedir as homedir5 } from "node:os";
|
|
5532
5878
|
import { sep } from "node:path";
|
|
5533
5879
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
5534
5880
|
|
|
@@ -5924,7 +6270,15 @@ class DaemonRuntime {
|
|
|
5924
6270
|
}
|
|
5925
6271
|
|
|
5926
6272
|
// src/cli.ts
|
|
5927
|
-
var HELP_LINES = [
|
|
6273
|
+
var HELP_LINES = [
|
|
6274
|
+
"用法:",
|
|
6275
|
+
"weacpx login - 微信登录",
|
|
6276
|
+
"weacpx logout - 退出登录",
|
|
6277
|
+
"weacpx run - 前台运行",
|
|
6278
|
+
"weacpx start - 后台启动",
|
|
6279
|
+
"weacpx status - 查看状态",
|
|
6280
|
+
"weacpx stop - 停止服务"
|
|
6281
|
+
];
|
|
5928
6282
|
async function runCli(args, deps = {}) {
|
|
5929
6283
|
const command = args[0];
|
|
5930
6284
|
const print = deps.print ?? ((line) => console.log(line));
|
|
@@ -6018,7 +6372,7 @@ function createDefaultController() {
|
|
|
6018
6372
|
});
|
|
6019
6373
|
}
|
|
6020
6374
|
function requireHome() {
|
|
6021
|
-
const home = process.env.HOME ??
|
|
6375
|
+
const home = process.env.HOME ?? homedir5();
|
|
6022
6376
|
if (!home) {
|
|
6023
6377
|
throw new Error("Unable to resolve the current user home directory");
|
|
6024
6378
|
}
|