weacpx 0.1.6 → 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 +39 -39
- package/dist/bridge/bridge-main.js +85 -20
- package/dist/cli.js +766 -599
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2829,20 +2829,26 @@ var init_slash_commands = __esm(() => {
|
|
|
2829
2829
|
// src/weixin/messaging/process-message.ts
|
|
2830
2830
|
import crypto4 from "node:crypto";
|
|
2831
2831
|
import fs6 from "node:fs/promises";
|
|
2832
|
+
import { tmpdir } from "node:os";
|
|
2832
2833
|
import path9 from "node:path";
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
ext =
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
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
|
+
};
|
|
2846
2852
|
}
|
|
2847
2853
|
function extractTextBody(itemList) {
|
|
2848
2854
|
if (!itemList?.length)
|
|
@@ -2857,10 +2863,10 @@ function extractTextBody(itemList) {
|
|
|
2857
2863
|
function findMediaItem(itemList) {
|
|
2858
2864
|
if (!itemList?.length)
|
|
2859
2865
|
return;
|
|
2860
|
-
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);
|
|
2861
2867
|
if (direct)
|
|
2862
2868
|
return direct;
|
|
2863
|
-
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));
|
|
2864
2870
|
return refItem?.ref_msg?.message_item ?? undefined;
|
|
2865
2871
|
}
|
|
2866
2872
|
async function processOneMessage(full, deps) {
|
|
@@ -2891,7 +2897,7 @@ async function processOneMessage(full, deps) {
|
|
|
2891
2897
|
try {
|
|
2892
2898
|
const downloaded = await downloadMediaFromItem(mediaItem, {
|
|
2893
2899
|
cdnBaseUrl: deps.cdnBaseUrl,
|
|
2894
|
-
saveMedia:
|
|
2900
|
+
saveMedia: createSaveMediaBuffer(deps.mediaTempDir),
|
|
2895
2901
|
log: deps.log,
|
|
2896
2902
|
errLog: deps.errLog,
|
|
2897
2903
|
label: "inbound"
|
|
@@ -2914,7 +2920,7 @@ async function processOneMessage(full, deps) {
|
|
|
2914
2920
|
};
|
|
2915
2921
|
}
|
|
2916
2922
|
} catch (err) {
|
|
2917
|
-
|
|
2923
|
+
deps.errLog(`media download failed: ${String(err)}`);
|
|
2918
2924
|
}
|
|
2919
2925
|
}
|
|
2920
2926
|
const to = full.from_user_id ?? "";
|
|
@@ -2926,7 +2932,7 @@ async function processOneMessage(full, deps) {
|
|
|
2926
2932
|
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken }
|
|
2927
2933
|
});
|
|
2928
2934
|
} catch (err) {
|
|
2929
|
-
|
|
2935
|
+
deps.errLog(`intermediate reply failed: ${String(err)}`);
|
|
2930
2936
|
}
|
|
2931
2937
|
};
|
|
2932
2938
|
const request = {
|
|
@@ -2959,7 +2965,7 @@ async function processOneMessage(full, deps) {
|
|
|
2959
2965
|
let filePath;
|
|
2960
2966
|
const mediaUrl = response.media.url;
|
|
2961
2967
|
if (mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://")) {
|
|
2962
|
-
filePath = await downloadRemoteImageToTemp(mediaUrl, path9.join(
|
|
2968
|
+
filePath = await downloadRemoteImageToTemp(mediaUrl, path9.join(resolveMediaTempDir(deps.mediaTempDir), "outbound"));
|
|
2963
2969
|
} else {
|
|
2964
2970
|
filePath = path9.isAbsolute(mediaUrl) ? mediaUrl : path9.resolve(mediaUrl);
|
|
2965
2971
|
}
|
|
@@ -2978,7 +2984,8 @@ async function processOneMessage(full, deps) {
|
|
|
2978
2984
|
});
|
|
2979
2985
|
}
|
|
2980
2986
|
} catch (err) {
|
|
2981
|
-
|
|
2987
|
+
const errorText = err instanceof Error ? err.stack ?? err.message : JSON.stringify(err);
|
|
2988
|
+
deps.errLog(`processOneMessage: agent or send failed: ${errorText}`);
|
|
2982
2989
|
sendWeixinErrorNotice({
|
|
2983
2990
|
to,
|
|
2984
2991
|
contextToken,
|
|
@@ -3003,14 +3010,13 @@ async function processOneMessage(full, deps) {
|
|
|
3003
3010
|
}
|
|
3004
3011
|
}
|
|
3005
3012
|
}
|
|
3006
|
-
var
|
|
3013
|
+
var hasDownloadableMedia = (media) => media?.encrypt_query_param || media?.full_url;
|
|
3007
3014
|
var init_process_message = __esm(() => {
|
|
3008
3015
|
init_api();
|
|
3009
3016
|
init_types();
|
|
3010
3017
|
init_upload();
|
|
3011
3018
|
init_media_download();
|
|
3012
3019
|
init_mime();
|
|
3013
|
-
init_logger();
|
|
3014
3020
|
init_inbound();
|
|
3015
3021
|
init_error_notice();
|
|
3016
3022
|
init_send_media();
|
|
@@ -3327,90 +3333,6 @@ var init_weixin_sdk = __esm(() => {
|
|
|
3327
3333
|
init_weixin();
|
|
3328
3334
|
});
|
|
3329
3335
|
|
|
3330
|
-
// src/config/agent-templates.ts
|
|
3331
|
-
function getAgentTemplate(name) {
|
|
3332
|
-
const template = TEMPLATES[name];
|
|
3333
|
-
if (!template) {
|
|
3334
|
-
return null;
|
|
3335
|
-
}
|
|
3336
|
-
return {
|
|
3337
|
-
...template
|
|
3338
|
-
};
|
|
3339
|
-
}
|
|
3340
|
-
function listAgentTemplates() {
|
|
3341
|
-
return Object.keys(TEMPLATES);
|
|
3342
|
-
}
|
|
3343
|
-
var TEMPLATES;
|
|
3344
|
-
var init_agent_templates = __esm(() => {
|
|
3345
|
-
TEMPLATES = {
|
|
3346
|
-
codex: {
|
|
3347
|
-
driver: "codex"
|
|
3348
|
-
},
|
|
3349
|
-
claude: {
|
|
3350
|
-
driver: "claude"
|
|
3351
|
-
}
|
|
3352
|
-
};
|
|
3353
|
-
});
|
|
3354
|
-
|
|
3355
|
-
// src/formatting/render-text.ts
|
|
3356
|
-
function renderHelpText() {
|
|
3357
|
-
return [
|
|
3358
|
-
"可用命令:",
|
|
3359
|
-
"",
|
|
3360
|
-
"先看这 3 个:",
|
|
3361
|
-
"/ss new <agent> -d <path> - 新建会话",
|
|
3362
|
-
"/use <alias> - 切会话",
|
|
3363
|
-
"/status - 看状态",
|
|
3364
|
-
"",
|
|
3365
|
-
"Agent:",
|
|
3366
|
-
"/agents - 看 Agent",
|
|
3367
|
-
"/agent add <codex|claude> - 加 Agent",
|
|
3368
|
-
"/agent rm <name> - 删 Agent",
|
|
3369
|
-
"",
|
|
3370
|
-
"工作区:",
|
|
3371
|
-
"/workspaces - 看工作区",
|
|
3372
|
-
"/workspace 或 /ws - 工作区命令",
|
|
3373
|
-
"/ws new <name> -d <path> - 加工作区",
|
|
3374
|
-
"/workspace rm <name> - 删工作区",
|
|
3375
|
-
"",
|
|
3376
|
-
"会话:",
|
|
3377
|
-
"/sessions - 看会话",
|
|
3378
|
-
"/session 或 /ss - 会话命令",
|
|
3379
|
-
"/ss <agent> -d <path> - 快速新建",
|
|
3380
|
-
"/ss new <agent> -d <path> - 新建会话",
|
|
3381
|
-
"/ss new <alias> -a <name> --ws <name> - 指定配置新建",
|
|
3382
|
-
"/ss attach <alias> -a <name> --ws <name> --name <transport-session> - 挂已有会话",
|
|
3383
|
-
"/use <alias> - 切会话",
|
|
3384
|
-
"/session reset 或 /clear - 清上下文",
|
|
3385
|
-
"",
|
|
3386
|
-
"权限:",
|
|
3387
|
-
"/pm 或 /permission - 权限设置",
|
|
3388
|
-
"/pm set <allow|read|deny> - 设审批级别",
|
|
3389
|
-
"/pm auto [allow|deny|fail] - 设自动处理",
|
|
3390
|
-
"",
|
|
3391
|
-
"常用:",
|
|
3392
|
-
"/status - 看状态",
|
|
3393
|
-
"/cancel 或 /stop - 停当前任务"
|
|
3394
|
-
].join(`
|
|
3395
|
-
`);
|
|
3396
|
-
}
|
|
3397
|
-
function renderAgents(config) {
|
|
3398
|
-
const names = Object.keys(config.agents);
|
|
3399
|
-
if (names.length === 0) {
|
|
3400
|
-
return "还没有注册任何 Agent。";
|
|
3401
|
-
}
|
|
3402
|
-
return ["已注册的 Agent:", ...names.map((name) => `- ${name}`)].join(`
|
|
3403
|
-
`);
|
|
3404
|
-
}
|
|
3405
|
-
function renderWorkspaces(config) {
|
|
3406
|
-
const names = Object.entries(config.workspaces);
|
|
3407
|
-
if (names.length === 0) {
|
|
3408
|
-
return "还没有注册任何工作区。";
|
|
3409
|
-
}
|
|
3410
|
-
return ["已注册的工作区:", ...names.map(([name, workspace]) => `- ${name}: ${workspace.cwd}`)].join(`
|
|
3411
|
-
`);
|
|
3412
|
-
}
|
|
3413
|
-
|
|
3414
3336
|
// src/logging/app-logger.ts
|
|
3415
3337
|
import { appendFile, mkdir as mkdir5, readdir, rename, rm as rm4, stat } from "node:fs/promises";
|
|
3416
3338
|
import { basename, dirname as dirname4, join as join2 } from "node:path";
|
|
@@ -3898,11 +3820,640 @@ var init_parse_command = __esm(() => {
|
|
|
3898
3820
|
]);
|
|
3899
3821
|
});
|
|
3900
3822
|
|
|
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: "当前没有加载可写入的配置。" };
|
|
3830
|
+
}
|
|
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: "当前没有加载可写入的配置。" };
|
|
3843
|
+
}
|
|
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>。" };
|
|
3862
|
+
}
|
|
3863
|
+
return {
|
|
3864
|
+
text: [
|
|
3865
|
+
"会话列表:",
|
|
3866
|
+
...sessions.map((session) => `- ${session.alias} (${session.agent} @ ${session.workspace})${session.isCurrent ? " [当前]" : ""}`)
|
|
3867
|
+
].join(`
|
|
3868
|
+
`)
|
|
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);
|
|
3878
|
+
}
|
|
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) {
|
|
3899
|
+
return {
|
|
3900
|
+
text: [
|
|
3901
|
+
"没有找到可绑定的已有会话。",
|
|
3902
|
+
`请确认会话名是否正确,然后重新执行:/session attach ${alias} --agent ${agent} --ws ${workspace} --name <会话名>`
|
|
3903
|
+
].join(`
|
|
3904
|
+
`)
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
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 };
|
|
3930
|
+
}
|
|
3931
|
+
return {
|
|
3932
|
+
text: [
|
|
3933
|
+
"当前 mode:",
|
|
3934
|
+
`- 会话:${session.alias}`,
|
|
3935
|
+
`- mode:${session.modeId ?? "未设置"}`
|
|
3936
|
+
].join(`
|
|
3937
|
+
`)
|
|
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 };
|
|
3944
|
+
}
|
|
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 };
|
|
3953
|
+
}
|
|
3954
|
+
return {
|
|
3955
|
+
text: [
|
|
3956
|
+
"当前会话:",
|
|
3957
|
+
`- 名称:${session.alias}`,
|
|
3958
|
+
`- Agent:${session.agent}`,
|
|
3959
|
+
`- 工作区:${session.workspace}`
|
|
3960
|
+
].join(`
|
|
3961
|
+
`)
|
|
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 };
|
|
3968
|
+
}
|
|
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);
|
|
3974
|
+
}
|
|
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 };
|
|
3992
|
+
}
|
|
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;
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
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"
|
|
4145
|
+
}
|
|
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);
|
|
4263
|
+
}
|
|
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;
|
|
4343
|
+
}
|
|
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();
|
|
4354
|
+
}
|
|
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")) {
|
|
4363
|
+
return {
|
|
4364
|
+
text: [
|
|
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 <会话名>`
|
|
4368
|
+
].join(`
|
|
4369
|
+
`)
|
|
4370
|
+
};
|
|
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
|
+
|
|
3901
4456
|
// src/commands/command-router.ts
|
|
3902
|
-
import { access } from "node:fs/promises";
|
|
3903
|
-
import { basename as basename2, normalize } from "node:path";
|
|
3904
|
-
import { homedir as homedir2 } from "node:os";
|
|
3905
|
-
|
|
3906
4457
|
class CommandRouter {
|
|
3907
4458
|
sessions;
|
|
3908
4459
|
transport;
|
|
@@ -3941,388 +4492,132 @@ class CommandRouter {
|
|
|
3941
4492
|
`)
|
|
3942
4493
|
};
|
|
3943
4494
|
case "help":
|
|
3944
|
-
return
|
|
4495
|
+
return handleHelp();
|
|
3945
4496
|
case "agents":
|
|
3946
|
-
return
|
|
3947
|
-
case "agent.add":
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
const template = getAgentTemplate(command.template);
|
|
3952
|
-
if (!template) {
|
|
3953
|
-
return { text: `暂不支持这个 Agent 模板。当前可用:${listAgentTemplates().join("、")}` };
|
|
3954
|
-
}
|
|
3955
|
-
const updated = await this.configStore.upsertAgent(command.template, template);
|
|
3956
|
-
this.replaceConfig(updated);
|
|
3957
|
-
return { text: `Agent「${command.template}」已保存` };
|
|
3958
|
-
}
|
|
3959
|
-
case "agent.rm": {
|
|
3960
|
-
if (!this.config || !this.configStore) {
|
|
3961
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3962
|
-
}
|
|
3963
|
-
if (!this.config.agents[command.name]) {
|
|
3964
|
-
return { text: "没有找到这个 Agent。" };
|
|
3965
|
-
}
|
|
3966
|
-
const updated = await this.configStore.removeAgent(command.name);
|
|
3967
|
-
this.replaceConfig(updated);
|
|
3968
|
-
return { text: `Agent「${command.name}」已删除` };
|
|
3969
|
-
}
|
|
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);
|
|
3970
4502
|
case "permission.status":
|
|
3971
|
-
return
|
|
3972
|
-
case "permission.mode.set":
|
|
3973
|
-
|
|
3974
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3975
|
-
}
|
|
3976
|
-
const updated = await this.configStore.updateTransport({
|
|
3977
|
-
permissionMode: command.mode
|
|
3978
|
-
});
|
|
3979
|
-
this.replaceConfig(updated);
|
|
3980
|
-
return { text: this.renderPermissionStatus("权限模式已更新:") };
|
|
3981
|
-
}
|
|
4503
|
+
return handlePermissionStatus(this.createHandlerContext(), "当前权限模式:");
|
|
4504
|
+
case "permission.mode.set":
|
|
4505
|
+
return await handlePermissionModeSet(this.createHandlerContext(), command.mode);
|
|
3982
4506
|
case "permission.auto.status":
|
|
3983
|
-
return
|
|
3984
|
-
case "permission.auto.set":
|
|
3985
|
-
|
|
3986
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
3987
|
-
}
|
|
3988
|
-
const updated = await this.configStore.updateTransport({
|
|
3989
|
-
nonInteractivePermissions: command.policy
|
|
3990
|
-
});
|
|
3991
|
-
this.replaceConfig(updated);
|
|
3992
|
-
return { text: this.renderPermissionStatus("非交互策略已更新:") };
|
|
3993
|
-
}
|
|
4507
|
+
return handlePermissionAutoStatus(this.createHandlerContext(), "当前非交互策略:");
|
|
4508
|
+
case "permission.auto.set":
|
|
4509
|
+
return await handlePermissionAutoSet(this.createHandlerContext(), command.policy);
|
|
3994
4510
|
case "workspaces":
|
|
3995
|
-
return
|
|
3996
|
-
case "workspace.new":
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
const updated = await this.configStore.upsertWorkspace(command.name, wsCwd);
|
|
4005
|
-
this.replaceConfig(updated);
|
|
4006
|
-
return { text: `工作区「${command.name}」已保存` };
|
|
4007
|
-
}
|
|
4008
|
-
case "workspace.rm": {
|
|
4009
|
-
if (!this.config || !this.configStore) {
|
|
4010
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
4011
|
-
}
|
|
4012
|
-
const updated = await this.configStore.removeWorkspace(command.name);
|
|
4013
|
-
this.replaceConfig(updated);
|
|
4014
|
-
return { text: `工作区「${command.name}」已删除` };
|
|
4015
|
-
}
|
|
4016
|
-
case "sessions": {
|
|
4017
|
-
const sessions = await this.sessions.listSessions(chatKey);
|
|
4018
|
-
if (sessions.length === 0) {
|
|
4019
|
-
return { text: "还没有会话。请先执行 /session new <alias> --agent <name> --ws <name>。" };
|
|
4020
|
-
}
|
|
4021
|
-
return {
|
|
4022
|
-
text: [
|
|
4023
|
-
"会话列表:",
|
|
4024
|
-
...sessions.map((session) => `- ${session.alias} (${session.agent} @ ${session.workspace})${session.isCurrent ? " [当前]" : ""}`)
|
|
4025
|
-
].join(`
|
|
4026
|
-
`)
|
|
4027
|
-
};
|
|
4028
|
-
}
|
|
4029
|
-
case "session.new": {
|
|
4030
|
-
const session = this.sessions.resolveSession(command.alias, command.agent, command.workspace, `${command.workspace}:${command.alias}`);
|
|
4031
|
-
try {
|
|
4032
|
-
await this.ensureTransportSession(session);
|
|
4033
|
-
const exists = await this.checkTransportSession(session);
|
|
4034
|
-
if (!exists) {
|
|
4035
|
-
return this.renderSessionCreationVerificationError(session);
|
|
4036
|
-
}
|
|
4037
|
-
} catch (error) {
|
|
4038
|
-
return this.renderSessionCreationError(session, error);
|
|
4039
|
-
}
|
|
4040
|
-
await this.sessions.attachSession(command.alias, command.agent, command.workspace, session.transportSession);
|
|
4041
|
-
await this.refreshSessionTransportAgentCommand(command.alias);
|
|
4042
|
-
await this.sessions.useSession(chatKey, command.alias);
|
|
4043
|
-
await this.logger.info("session.created", "created and selected logical session", {
|
|
4044
|
-
alias: command.alias,
|
|
4045
|
-
agent: command.agent,
|
|
4046
|
-
workspace: command.workspace
|
|
4047
|
-
});
|
|
4048
|
-
return { text: `会话「${command.alias}」已创建并切换` };
|
|
4049
|
-
}
|
|
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);
|
|
4050
4520
|
case "session.shortcut":
|
|
4051
|
-
return await this.
|
|
4521
|
+
return await handleSessionShortcut(this.createSessionHandlerContext(), chatKey, command.agent, command.cwd, false);
|
|
4052
4522
|
case "session.shortcut.new":
|
|
4053
|
-
return await this.
|
|
4054
|
-
case "session.attach":
|
|
4055
|
-
|
|
4056
|
-
const exists = await this.checkTransportSession(attached);
|
|
4057
|
-
if (!exists) {
|
|
4058
|
-
return {
|
|
4059
|
-
text: [
|
|
4060
|
-
"没有找到可绑定的已有会话。",
|
|
4061
|
-
`请确认会话名是否正确,然后重新执行:/session attach ${command.alias} --agent ${command.agent} --ws ${command.workspace} --name <会话名>`
|
|
4062
|
-
].join(`
|
|
4063
|
-
`)
|
|
4064
|
-
};
|
|
4065
|
-
}
|
|
4066
|
-
await this.sessions.attachSession(command.alias, command.agent, command.workspace, command.transportSession);
|
|
4067
|
-
await this.refreshSessionTransportAgentCommand(command.alias);
|
|
4068
|
-
await this.sessions.useSession(chatKey, command.alias);
|
|
4069
|
-
await this.logger.info("session.attached", "attached existing transport session", {
|
|
4070
|
-
alias: command.alias,
|
|
4071
|
-
agent: command.agent,
|
|
4072
|
-
workspace: command.workspace,
|
|
4073
|
-
transportSession: command.transportSession
|
|
4074
|
-
});
|
|
4075
|
-
return { text: `会话「${command.alias}」已绑定并切换` };
|
|
4076
|
-
}
|
|
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);
|
|
4077
4526
|
case "session.use":
|
|
4078
|
-
await this.
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4088
|
-
}
|
|
4089
|
-
return {
|
|
4090
|
-
text: [
|
|
4091
|
-
"当前 mode:",
|
|
4092
|
-
`- 会话:${session.alias}`,
|
|
4093
|
-
`- mode:${session.modeId ?? "未设置"}`
|
|
4094
|
-
].join(`
|
|
4095
|
-
`)
|
|
4096
|
-
};
|
|
4097
|
-
}
|
|
4098
|
-
case "mode.set": {
|
|
4099
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4100
|
-
if (!session) {
|
|
4101
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4102
|
-
}
|
|
4103
|
-
await this.setModeTransportSession(session, command.modeId);
|
|
4104
|
-
await this.sessions.setCurrentSessionMode(chatKey, command.modeId);
|
|
4105
|
-
return { text: `已设置当前会话 mode:${command.modeId}` };
|
|
4106
|
-
}
|
|
4107
|
-
case "status": {
|
|
4108
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4109
|
-
if (!session) {
|
|
4110
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4111
|
-
}
|
|
4112
|
-
return {
|
|
4113
|
-
text: [
|
|
4114
|
-
"当前会话:",
|
|
4115
|
-
`- 名称:${session.alias}`,
|
|
4116
|
-
`- Agent:${session.agent}`,
|
|
4117
|
-
`- 工作区:${session.workspace}`
|
|
4118
|
-
].join(`
|
|
4119
|
-
`)
|
|
4120
|
-
};
|
|
4121
|
-
}
|
|
4122
|
-
case "cancel": {
|
|
4123
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4124
|
-
if (!session) {
|
|
4125
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4126
|
-
}
|
|
4127
|
-
try {
|
|
4128
|
-
const result = await this.cancelTransportSession(session);
|
|
4129
|
-
return { text: result.message || "cancelled" };
|
|
4130
|
-
} catch (error) {
|
|
4131
|
-
return this.renderTransportError(session, error);
|
|
4132
|
-
}
|
|
4133
|
-
}
|
|
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);
|
|
4134
4536
|
case "session.reset":
|
|
4135
|
-
return await this.
|
|
4136
|
-
case "prompt":
|
|
4137
|
-
|
|
4138
|
-
if (!session) {
|
|
4139
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4140
|
-
}
|
|
4141
|
-
try {
|
|
4142
|
-
const result = await this.promptTransportSession(session, command.text, reply);
|
|
4143
|
-
return { text: result.text };
|
|
4144
|
-
} catch (error) {
|
|
4145
|
-
const recovered = await this.tryRecoverMissingSession(session, error);
|
|
4146
|
-
if (recovered) {
|
|
4147
|
-
const result = await this.promptTransportSession(recovered, command.text, reply);
|
|
4148
|
-
return { text: result.text };
|
|
4149
|
-
}
|
|
4150
|
-
return this.renderTransportError(session, error);
|
|
4151
|
-
}
|
|
4152
|
-
}
|
|
4537
|
+
return await handleSessionReset(this.createSessionHandlerContext(), chatKey);
|
|
4538
|
+
case "prompt":
|
|
4539
|
+
return await handlePrompt(this.createSessionHandlerContext(), chatKey, command.text, reply);
|
|
4153
4540
|
}
|
|
4154
4541
|
});
|
|
4155
4542
|
}
|
|
4156
4543
|
async clearSession(chatKey) {
|
|
4157
|
-
await this.
|
|
4544
|
+
await handleSessionResetCommand(this.createHandlerContext(), this.createSessionResetOps(), chatKey);
|
|
4158
4545
|
}
|
|
4159
|
-
|
|
4160
|
-
if (!this.config || !this.configStore) {
|
|
4161
|
-
return { text: "当前没有加载可写入的配置。" };
|
|
4162
|
-
}
|
|
4163
|
-
const cwd = normalizePathForWorkspace(cwdInput);
|
|
4164
|
-
if (!await pathExists(cwd)) {
|
|
4165
|
-
return { text: `工作区路径不存在:${cwdInput}` };
|
|
4166
|
-
}
|
|
4167
|
-
const workspace = await this.resolveShortcutWorkspace(cwd);
|
|
4168
|
-
await this.logger.info("session.shortcut.workspace", "resolved shortcut workspace", {
|
|
4169
|
-
workspace: workspace.name,
|
|
4170
|
-
cwd: workspace.cwd,
|
|
4171
|
-
reused: workspace.reused
|
|
4172
|
-
});
|
|
4173
|
-
const baseAlias = `${workspace.name}:${agent}`;
|
|
4174
|
-
const alias = createNew ? await this.allocateUniqueSessionAlias(baseAlias, chatKey) : baseAlias;
|
|
4175
|
-
if (!createNew && await this.hasLogicalSession(alias, chatKey)) {
|
|
4176
|
-
await this.sessions.useSession(chatKey, alias);
|
|
4177
|
-
await this.logger.info("session.shortcut.reused", "reused existing logical session", {
|
|
4178
|
-
alias,
|
|
4179
|
-
workspace: workspace.name,
|
|
4180
|
-
agent
|
|
4181
|
-
});
|
|
4182
|
-
return {
|
|
4183
|
-
text: [
|
|
4184
|
-
`已切换到会话「${alias}」`,
|
|
4185
|
-
`- 复用工作区:${workspace.name}`,
|
|
4186
|
-
`- 复用会话:${alias}`
|
|
4187
|
-
].join(`
|
|
4188
|
-
`)
|
|
4189
|
-
};
|
|
4190
|
-
}
|
|
4191
|
-
const session = this.sessions.resolveSession(alias, agent, workspace.name, `${workspace.name}:${alias}`);
|
|
4192
|
-
try {
|
|
4193
|
-
await this.ensureTransportSession(session);
|
|
4194
|
-
const exists = await this.checkTransportSession(session);
|
|
4195
|
-
if (!exists) {
|
|
4196
|
-
return this.renderShortcutSessionCreationError(workspace, alias);
|
|
4197
|
-
}
|
|
4198
|
-
} catch {
|
|
4199
|
-
return this.renderShortcutSessionCreationError(workspace, alias);
|
|
4200
|
-
}
|
|
4201
|
-
await this.sessions.attachSession(alias, agent, workspace.name, session.transportSession);
|
|
4202
|
-
await this.refreshSessionTransportAgentCommand(alias);
|
|
4203
|
-
await this.sessions.useSession(chatKey, alias);
|
|
4204
|
-
await this.logger.info("session.shortcut.created", "created new logical session from shortcut", {
|
|
4205
|
-
alias,
|
|
4206
|
-
workspace: workspace.name,
|
|
4207
|
-
agent,
|
|
4208
|
-
workspaceReused: workspace.reused
|
|
4209
|
-
});
|
|
4546
|
+
createHandlerContext() {
|
|
4210
4547
|
return {
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
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)
|
|
4217
4554
|
};
|
|
4218
4555
|
}
|
|
4219
|
-
|
|
4220
|
-
if (!this.config) {
|
|
4221
|
-
return;
|
|
4222
|
-
}
|
|
4223
|
-
this.config.transport = { ...updated.transport };
|
|
4224
|
-
this.config.agents = { ...updated.agents };
|
|
4225
|
-
this.config.workspaces = { ...updated.workspaces };
|
|
4226
|
-
}
|
|
4227
|
-
renderPermissionStatus(title) {
|
|
4228
|
-
const permissionMode = this.config?.transport.permissionMode ?? "approve-all";
|
|
4229
|
-
const nonInteractivePermissions = this.config?.transport.nonInteractivePermissions ?? "fail";
|
|
4230
|
-
return [title, `- mode: ${permissionMode}`, `- auto: ${nonInteractivePermissions}`].join(`
|
|
4231
|
-
`);
|
|
4232
|
-
}
|
|
4233
|
-
renderTransportError(session, error) {
|
|
4234
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4235
|
-
if (message.includes("No acpx session found")) {
|
|
4236
|
-
return {
|
|
4237
|
-
text: [
|
|
4238
|
-
`当前会话「${session.alias}」暂时不可用。`,
|
|
4239
|
-
`请先在微信里重新执行:/session new ${session.alias} --agent ${session.agent} --ws ${session.workspace}`,
|
|
4240
|
-
`如果你要绑定一个已有会话,再执行:/session attach ${session.alias} --agent ${session.agent} --ws ${session.workspace} --name <会话名>`
|
|
4241
|
-
].join(`
|
|
4242
|
-
`)
|
|
4243
|
-
};
|
|
4244
|
-
}
|
|
4245
|
-
if (!isPartialPromptOutputError(message)) {
|
|
4246
|
-
throw error;
|
|
4247
|
-
}
|
|
4556
|
+
createSessionHandlerContext() {
|
|
4248
4557
|
return {
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
].join(`
|
|
4254
|
-
`)
|
|
4558
|
+
...this.createHandlerContext(),
|
|
4559
|
+
lifecycle: this.createSessionLifecycleOps(),
|
|
4560
|
+
interaction: this.createSessionInteractionOps(),
|
|
4561
|
+
recovery: this.createSessionRenderRecoveryOps()
|
|
4255
4562
|
};
|
|
4256
4563
|
}
|
|
4257
|
-
|
|
4258
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4259
|
-
if (message.includes("timed out") && message.includes("sessions new")) {
|
|
4260
|
-
return this.renderSessionCreationVerificationError(session);
|
|
4261
|
-
}
|
|
4262
|
-
throw error;
|
|
4263
|
-
}
|
|
4264
|
-
renderSessionCreationVerificationError(session) {
|
|
4564
|
+
createSessionLifecycleOps() {
|
|
4265
4565
|
return {
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
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)
|
|
4271
4572
|
};
|
|
4272
4573
|
}
|
|
4273
|
-
|
|
4274
|
-
const existingByPath = Object.entries(this.config?.workspaces ?? {}).find(([, workspace]) => sameWorkspacePath(workspace.cwd, cwd));
|
|
4275
|
-
if (existingByPath) {
|
|
4276
|
-
return {
|
|
4277
|
-
name: existingByPath[0],
|
|
4278
|
-
cwd: existingByPath[1].cwd,
|
|
4279
|
-
reused: true
|
|
4280
|
-
};
|
|
4281
|
-
}
|
|
4282
|
-
const baseName = basename2(cwd);
|
|
4283
|
-
const workspaceName = this.allocateWorkspaceName(baseName, cwd);
|
|
4284
|
-
const updated = await this.configStore.upsertWorkspace(workspaceName, cwd);
|
|
4285
|
-
this.replaceConfig(updated);
|
|
4574
|
+
createSessionInteractionOps() {
|
|
4286
4575
|
return {
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4576
|
+
setModeTransportSession: (session, modeId) => this.setModeTransportSession(session, modeId),
|
|
4577
|
+
cancelTransportSession: (session) => this.cancelTransportSession(session),
|
|
4578
|
+
promptTransportSession: (session, text, reply) => this.promptTransportSession(session, text, reply)
|
|
4290
4579
|
};
|
|
4291
4580
|
}
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
}
|
|
4300
|
-
return `${baseName}-${suffix}`;
|
|
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
|
+
};
|
|
4301
4588
|
}
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
}
|
|
4310
|
-
return `${baseAlias}-${suffix}`;
|
|
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
|
+
};
|
|
4311
4597
|
}
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
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
|
+
};
|
|
4315
4604
|
}
|
|
4316
|
-
|
|
4605
|
+
createSessionShortcutOps() {
|
|
4317
4606
|
return {
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
].join(`
|
|
4323
|
-
`)
|
|
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)
|
|
4324
4611
|
};
|
|
4325
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
|
+
}
|
|
4326
4621
|
async executeCommand(chatKey, kind, startedAt, operation) {
|
|
4327
4622
|
try {
|
|
4328
4623
|
const response = await operation();
|
|
@@ -4345,42 +4640,6 @@ class CommandRouter {
|
|
|
4345
4640
|
async ensureTransportSession(session) {
|
|
4346
4641
|
await this.measureTransportCall("ensure_session", session, () => this.transport.ensureSession(session));
|
|
4347
4642
|
}
|
|
4348
|
-
async resetCurrentSession(chatKey) {
|
|
4349
|
-
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4350
|
-
if (!session) {
|
|
4351
|
-
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4352
|
-
}
|
|
4353
|
-
const resetSession = this.sessions.resolveSession(session.alias, session.agent, session.workspace, this.buildResetTransportSessionName(session));
|
|
4354
|
-
try {
|
|
4355
|
-
await this.ensureTransportSession(resetSession);
|
|
4356
|
-
const exists = await this.checkTransportSession(resetSession);
|
|
4357
|
-
if (!exists) {
|
|
4358
|
-
return {
|
|
4359
|
-
text: [
|
|
4360
|
-
`会话「${session.alias}」重置失败。`,
|
|
4361
|
-
"新的后端会话未创建成功,请稍后重试。"
|
|
4362
|
-
].join(`
|
|
4363
|
-
`)
|
|
4364
|
-
};
|
|
4365
|
-
}
|
|
4366
|
-
} catch (error) {
|
|
4367
|
-
return this.renderTransportError(resetSession, error);
|
|
4368
|
-
}
|
|
4369
|
-
await this.sessions.attachSession(resetSession.alias, resetSession.agent, resetSession.workspace, resetSession.transportSession);
|
|
4370
|
-
await this.refreshSessionTransportAgentCommand(resetSession.alias);
|
|
4371
|
-
await this.sessions.useSession(chatKey, resetSession.alias);
|
|
4372
|
-
await this.logger.info("session.reset", "reset current logical session", {
|
|
4373
|
-
alias: resetSession.alias,
|
|
4374
|
-
agent: resetSession.agent,
|
|
4375
|
-
workspace: resetSession.workspace,
|
|
4376
|
-
transportSession: resetSession.transportSession,
|
|
4377
|
-
chatKey
|
|
4378
|
-
});
|
|
4379
|
-
return { text: `会话「${resetSession.alias}」已重置` };
|
|
4380
|
-
}
|
|
4381
|
-
buildResetTransportSessionName(session) {
|
|
4382
|
-
return `${session.workspace}:${session.alias}:reset-${Date.now()}`;
|
|
4383
|
-
}
|
|
4384
4643
|
async checkTransportSession(session) {
|
|
4385
4644
|
return await this.measureTransportCall("has_session", session, () => this.transport.hasSession(session));
|
|
4386
4645
|
}
|
|
@@ -4404,18 +4663,6 @@ class CommandRouter {
|
|
|
4404
4663
|
}
|
|
4405
4664
|
await this.sessions.setSessionTransportAgentCommand(alias, transportAgentCommand);
|
|
4406
4665
|
}
|
|
4407
|
-
async tryRecoverMissingSession(session, error) {
|
|
4408
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4409
|
-
if (!message.includes("No acpx session found")) {
|
|
4410
|
-
return null;
|
|
4411
|
-
}
|
|
4412
|
-
const transportAgentCommand = await this.resolveSessionAgentCommand(session);
|
|
4413
|
-
if (!transportAgentCommand || transportAgentCommand === session.agentCommand) {
|
|
4414
|
-
return null;
|
|
4415
|
-
}
|
|
4416
|
-
await this.sessions.setSessionTransportAgentCommand(session.alias, transportAgentCommand);
|
|
4417
|
-
return await this.sessions.getSession(session.alias);
|
|
4418
|
-
}
|
|
4419
4666
|
async measureTransportCall(operation, session, callback) {
|
|
4420
4667
|
const startedAt = Date.now();
|
|
4421
4668
|
try {
|
|
@@ -4453,91 +4700,17 @@ class CommandRouter {
|
|
|
4453
4700
|
}
|
|
4454
4701
|
}
|
|
4455
4702
|
}
|
|
4456
|
-
async function pathExists(path11) {
|
|
4457
|
-
try {
|
|
4458
|
-
await access(path11);
|
|
4459
|
-
return true;
|
|
4460
|
-
} catch {
|
|
4461
|
-
return false;
|
|
4462
|
-
}
|
|
4463
|
-
}
|
|
4464
|
-
function normalizePathForWorkspace(path11) {
|
|
4465
|
-
const expanded = path11.startsWith("~") ? homedir2() + path11.slice(1) : path11;
|
|
4466
|
-
return normalize(expanded);
|
|
4467
|
-
}
|
|
4468
|
-
function sameWorkspacePath(left, right) {
|
|
4469
|
-
const normalizedLeft = normalizePathForWorkspace(left);
|
|
4470
|
-
const normalizedRight = normalizePathForWorkspace(right);
|
|
4471
|
-
if (process.platform === "win32") {
|
|
4472
|
-
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
4473
|
-
}
|
|
4474
|
-
return normalizedLeft === normalizedRight;
|
|
4475
|
-
}
|
|
4476
|
-
function summarizeTransportError(message) {
|
|
4477
|
-
return message.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
4478
|
-
}
|
|
4479
|
-
function summarizeTransportDiagnostic(output) {
|
|
4480
|
-
const trimmed = output.replace(/\s+/g, " ").trim();
|
|
4481
|
-
if (trimmed.length === 0) {
|
|
4482
|
-
return;
|
|
4483
|
-
}
|
|
4484
|
-
return trimmed.slice(0, 200);
|
|
4485
|
-
}
|
|
4486
|
-
function summarizeTransportDiagnosticTail(output) {
|
|
4487
|
-
const trimmed = output.replace(/\s+/g, " ").trim();
|
|
4488
|
-
if (trimmed.length === 0) {
|
|
4489
|
-
return;
|
|
4490
|
-
}
|
|
4491
|
-
return trimmed.slice(-200);
|
|
4492
|
-
}
|
|
4493
|
-
function summarizeTransportNdjson(output, prefix) {
|
|
4494
|
-
const lines = output.split(`
|
|
4495
|
-
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
4496
|
-
if (lines.length === 0) {
|
|
4497
|
-
return {};
|
|
4498
|
-
}
|
|
4499
|
-
const methods = new Set;
|
|
4500
|
-
let agentMessageChunkCount = 0;
|
|
4501
|
-
let stopReason;
|
|
4502
|
-
for (const line of lines) {
|
|
4503
|
-
try {
|
|
4504
|
-
const payload = JSON.parse(line);
|
|
4505
|
-
if (typeof payload.method === "string" && payload.method.length > 0) {
|
|
4506
|
-
methods.add(payload.method);
|
|
4507
|
-
}
|
|
4508
|
-
if (payload.params?.update?.sessionUpdate === "agent_message_chunk") {
|
|
4509
|
-
agentMessageChunkCount += 1;
|
|
4510
|
-
}
|
|
4511
|
-
if (typeof payload.result?.stopReason === "string" && payload.result.stopReason.length > 0) {
|
|
4512
|
-
stopReason = payload.result.stopReason;
|
|
4513
|
-
}
|
|
4514
|
-
} catch {
|
|
4515
|
-
continue;
|
|
4516
|
-
}
|
|
4517
|
-
}
|
|
4518
|
-
const summary = {
|
|
4519
|
-
[`${prefix}LineCount`]: lines.length
|
|
4520
|
-
};
|
|
4521
|
-
if (methods.size > 0) {
|
|
4522
|
-
summary[`${prefix}Methods`] = [...methods].join(",");
|
|
4523
|
-
}
|
|
4524
|
-
if (agentMessageChunkCount > 0) {
|
|
4525
|
-
summary[`${prefix}AgentMessageChunkCount`] = agentMessageChunkCount;
|
|
4526
|
-
}
|
|
4527
|
-
if (stopReason) {
|
|
4528
|
-
summary[`${prefix}StopReason`] = stopReason;
|
|
4529
|
-
}
|
|
4530
|
-
return summary;
|
|
4531
|
-
}
|
|
4532
|
-
function isPartialPromptOutputError(message) {
|
|
4533
|
-
return message.includes("未收到最终回复");
|
|
4534
|
-
}
|
|
4535
4703
|
var init_command_router = __esm(() => {
|
|
4536
|
-
init_agent_templates();
|
|
4537
4704
|
init_app_logger();
|
|
4538
4705
|
init_acpx_session_index();
|
|
4539
4706
|
init_prompt_output();
|
|
4540
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();
|
|
4541
4714
|
});
|
|
4542
4715
|
|
|
4543
4716
|
// src/config/resolve-agent-command.ts
|
|
@@ -5220,9 +5393,6 @@ class AcpxBridgeTransport {
|
|
|
5220
5393
|
const result = await this.client.request("hasSession", this.toParams(session));
|
|
5221
5394
|
return result.exists;
|
|
5222
5395
|
}
|
|
5223
|
-
async listSessions() {
|
|
5224
|
-
return [];
|
|
5225
|
-
}
|
|
5226
5396
|
async dispose() {
|
|
5227
5397
|
await this.client.dispose?.();
|
|
5228
5398
|
}
|
|
@@ -5456,9 +5626,6 @@ class AcpxCliTransport {
|
|
|
5456
5626
|
]));
|
|
5457
5627
|
return result.code === 0;
|
|
5458
5628
|
}
|
|
5459
|
-
async listSessions() {
|
|
5460
|
-
return [];
|
|
5461
|
-
}
|
|
5462
5629
|
async run(args, options) {
|
|
5463
5630
|
const result = await this.runCommandWithTimeout(this.runCommand, args, options);
|
|
5464
5631
|
if (result.code !== 0) {
|
|
@@ -5607,7 +5774,7 @@ __export(exports_main, {
|
|
|
5607
5774
|
main: () => main2,
|
|
5608
5775
|
buildApp: () => buildApp
|
|
5609
5776
|
});
|
|
5610
|
-
import { homedir as
|
|
5777
|
+
import { homedir as homedir4 } from "node:os";
|
|
5611
5778
|
import { dirname as dirname8, join as join4 } from "node:path";
|
|
5612
5779
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
5613
5780
|
async function buildApp(paths, deps = {}) {
|
|
@@ -5670,7 +5837,7 @@ async function main2() {
|
|
|
5670
5837
|
}
|
|
5671
5838
|
}
|
|
5672
5839
|
function resolveRuntimePaths() {
|
|
5673
|
-
const home = process.env.HOME ??
|
|
5840
|
+
const home = process.env.HOME ?? homedir4();
|
|
5674
5841
|
if (!home) {
|
|
5675
5842
|
throw new Error("Unable to resolve the current user home directory");
|
|
5676
5843
|
}
|
|
@@ -5707,7 +5874,7 @@ var init_main = __esm(async () => {
|
|
|
5707
5874
|
});
|
|
5708
5875
|
|
|
5709
5876
|
// src/cli.ts
|
|
5710
|
-
import { homedir as
|
|
5877
|
+
import { homedir as homedir5 } from "node:os";
|
|
5711
5878
|
import { sep } from "node:path";
|
|
5712
5879
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
5713
5880
|
|
|
@@ -6205,7 +6372,7 @@ function createDefaultController() {
|
|
|
6205
6372
|
});
|
|
6206
6373
|
}
|
|
6207
6374
|
function requireHome() {
|
|
6208
|
-
const home = process.env.HOME ??
|
|
6375
|
+
const home = process.env.HOME ?? homedir5();
|
|
6209
6376
|
if (!home) {
|
|
6210
6377
|
throw new Error("Unable to resolve the current user home directory");
|
|
6211
6378
|
}
|