@ganglion/xacpx 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge/bridge-main.js +170 -78
- package/dist/channels/types.d.ts +11 -0
- package/dist/channels/weixin-channel.d.ts +8 -0
- package/dist/cli.js +1236 -501
- package/dist/commands/handlers/later-handler.d.ts +3 -3
- package/dist/commands/parse-command.d.ts +1 -0
- package/dist/commands/router-types.d.ts +1 -1
- package/dist/config/config-store.d.ts +44 -5
- package/dist/config/types.d.ts +8 -0
- package/dist/i18n/types.d.ts +7 -2
- package/dist/perf/perf-log-writer.d.ts +1 -0
- package/dist/plugin-api.js +24 -6
- package/dist/scheduled/scheduled-service.d.ts +5 -2
- package/dist/sessions/session-service.d.ts +7 -3
- package/dist/state/state-store.d.ts +69 -2
- package/dist/util/private-file.d.ts +12 -0
- package/package.json +1 -1
|
@@ -3,10 +3,10 @@ import type { ScheduledSessionMode } from "../../scheduled/scheduled-types";
|
|
|
3
3
|
import type { HelpTopicMetadata } from "../help/help-types";
|
|
4
4
|
export declare function laterHelp(): HelpTopicMetadata;
|
|
5
5
|
export declare function handleLaterHelp(): RouterResponse;
|
|
6
|
-
export declare function handleLaterCreate(tokens: string[], scheduled: ScheduledRouterOps, chatKey: string, currentSession: {
|
|
6
|
+
export declare function handleLaterCreate(tokens: string[], tails: string[], scheduled: ScheduledRouterOps, chatKey: string, currentSession: {
|
|
7
7
|
alias: string;
|
|
8
8
|
agent: string;
|
|
9
9
|
workspace: string;
|
|
10
10
|
} | null, defaultMode: ScheduledSessionMode, accountId?: string, replyContextToken?: string): Promise<RouterResponse>;
|
|
11
|
-
export declare function handleLaterList(scheduled: ScheduledRouterOps): RouterResponse;
|
|
12
|
-
export declare function handleLaterCancel(id: string, scheduled: ScheduledRouterOps): Promise<RouterResponse>;
|
|
11
|
+
export declare function handleLaterList(scheduled: ScheduledRouterOps, chatKey: string): RouterResponse;
|
|
12
|
+
export declare function handleLaterCancel(id: string, scheduled: ScheduledRouterOps, chatKey: string): Promise<RouterResponse>;
|
|
@@ -41,7 +41,7 @@ export interface ActiveHumanQuestionPackageContext {
|
|
|
41
41
|
}>;
|
|
42
42
|
queuedCount: number;
|
|
43
43
|
}
|
|
44
|
-
export type WritableConfigStore = Pick<ConfigStore, "load" | "
|
|
44
|
+
export type WritableConfigStore = Pick<ConfigStore, "load" | "upsertWorkspace" | "removeWorkspace" | "upsertAgent" | "removeAgent" | "updateTransport" | "updateChannel" | "getRawValue" | "setRawValue" | "unsetRawValue">;
|
|
45
45
|
export interface CommandRouterContext {
|
|
46
46
|
sessions: SessionService;
|
|
47
47
|
transport: SessionTransport;
|
|
@@ -1,13 +1,52 @@
|
|
|
1
|
-
import type { AppConfig } from "./types";
|
|
1
|
+
import type { AgentConfig, AppConfig, ChannelRuntimeConfig, PluginConfig } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Raw-patch config persistence.
|
|
4
|
+
*
|
|
5
|
+
* The parsed `AppConfig` is a READ model only: parsing drops unknown keys
|
|
6
|
+
* (e.g. `workspaces.*.allowed_agents`), expands `~` in workspace cwds, and
|
|
7
|
+
* materializes every default. Serializing it back would destroy a hand-edited
|
|
8
|
+
* config.json. Every mutation therefore patches the raw JSON document read
|
|
9
|
+
* straight from disk, touching only the targeted subtree, and never writes a
|
|
10
|
+
* parsed config object.
|
|
11
|
+
*/
|
|
12
|
+
export type RawConfigPathSegment = string | {
|
|
13
|
+
/** Addresses the entry of a JSON array whose `id` property equals this value. */
|
|
14
|
+
id: string;
|
|
15
|
+
/** When set, a missing entry is materialized from this template on writes. */
|
|
16
|
+
createWith?: Record<string, unknown>;
|
|
17
|
+
};
|
|
18
|
+
export type RawConfigPath = readonly RawConfigPathSegment[];
|
|
19
|
+
export type RawConfigLookup = {
|
|
20
|
+
present: true;
|
|
21
|
+
value: unknown;
|
|
22
|
+
} | {
|
|
23
|
+
present: false;
|
|
24
|
+
};
|
|
2
25
|
export declare class ConfigStore {
|
|
3
26
|
private readonly path;
|
|
4
27
|
constructor(path: string);
|
|
5
28
|
load(): Promise<AppConfig>;
|
|
6
|
-
|
|
29
|
+
/** Reads the raw (unparsed) value at `path`, e.g. to capture it for a rollback. */
|
|
30
|
+
getRawValue(path: RawConfigPath): Promise<RawConfigLookup>;
|
|
31
|
+
setRawValue(path: RawConfigPath, value: unknown): Promise<AppConfig>;
|
|
32
|
+
unsetRawValue(path: RawConfigPath): Promise<AppConfig>;
|
|
7
33
|
upsertWorkspace(name: string, cwd: string, description?: string): Promise<AppConfig>;
|
|
8
34
|
removeWorkspace(name: string): Promise<AppConfig>;
|
|
9
|
-
upsertAgent(name: string, agent:
|
|
35
|
+
upsertAgent(name: string, agent: AgentConfig): Promise<AppConfig>;
|
|
10
36
|
removeAgent(name: string): Promise<AppConfig>;
|
|
11
|
-
|
|
12
|
-
|
|
37
|
+
/** Sets only the given transport keys; a key explicitly set to `undefined` is removed. */
|
|
38
|
+
updateTransport(patch: Partial<AppConfig["transport"]>): Promise<AppConfig>;
|
|
39
|
+
/** Sets only the given channel keys; a key explicitly set to `undefined` is removed. */
|
|
40
|
+
updateChannel(patch: Partial<AppConfig["channel"]>): Promise<AppConfig>;
|
|
41
|
+
/** Replaces the tool-managed `plugins` array; everything else stays untouched. */
|
|
42
|
+
replacePlugins(plugins: PluginConfig[]): Promise<AppConfig>;
|
|
43
|
+
/** Replaces the tool-managed `channels` array; everything else stays untouched. */
|
|
44
|
+
replaceChannels(channels: ChannelRuntimeConfig[]): Promise<AppConfig>;
|
|
45
|
+
private patchRaw;
|
|
46
|
+
private readRaw;
|
|
13
47
|
}
|
|
48
|
+
export declare function serializeRawConfig(raw: Record<string, unknown>): string;
|
|
49
|
+
export declare function readRawConfigValue(root: Record<string, unknown>, path: RawConfigPath): RawConfigLookup;
|
|
50
|
+
export declare function setRawConfigValue(root: Record<string, unknown>, path: RawConfigPath, value: unknown): void;
|
|
51
|
+
export declare function unsetRawConfigValue(root: Record<string, unknown>, path: RawConfigPath): void;
|
|
52
|
+
export declare function assertSafeConfigKey(key: string): void;
|
package/dist/config/types.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export type WechatReplyMode = ReplyMode;
|
|
|
7
7
|
export interface ChannelConfig {
|
|
8
8
|
type: string;
|
|
9
9
|
replyMode: ReplyMode;
|
|
10
|
+
/**
|
|
11
|
+
* Sender ids the operator trusts as channel owners. Group turns from these
|
|
12
|
+
* senders pass owner-gated command authorization even when the channel
|
|
13
|
+
* protocol carries no group-role information (e.g. WeChat).
|
|
14
|
+
*/
|
|
15
|
+
ownerIds?: string[];
|
|
10
16
|
options?: Record<string, unknown>;
|
|
11
17
|
}
|
|
12
18
|
/** @deprecated Legacy input shape only. Use ChannelConfig. */
|
|
@@ -67,6 +73,8 @@ export interface ChannelRuntimeConfig {
|
|
|
67
73
|
type: string;
|
|
68
74
|
enabled: boolean;
|
|
69
75
|
replyMode?: ReplyMode;
|
|
76
|
+
/** See ChannelConfig.ownerIds — per-channel trusted owner sender ids. */
|
|
77
|
+
ownerIds?: string[];
|
|
70
78
|
options?: Record<string, unknown>;
|
|
71
79
|
}
|
|
72
80
|
export interface PluginConfig {
|
package/dist/i18n/types.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface SessionMessages {
|
|
|
11
11
|
currentLabel: string;
|
|
12
12
|
sessionListItem: (alias: string, agent: string, workspace: string) => string;
|
|
13
13
|
sessionCreated: (alias: string) => string;
|
|
14
|
+
sessionAlreadyExists: (alias: string, agent: string, workspace: string) => string;
|
|
14
15
|
sessionAttachNotFound: (alias: string, agent: string, workspace: string) => string;
|
|
15
16
|
sessionAttached: (alias: string) => string;
|
|
16
17
|
switched: (alias: string, agent: string, workspace: string) => string;
|
|
@@ -43,6 +44,7 @@ export interface SessionMessages {
|
|
|
43
44
|
sessionBlockedByTasksHint: string;
|
|
44
45
|
sessionRemoved: (alias: string) => string;
|
|
45
46
|
sessionRemovedWasActive: string;
|
|
47
|
+
sessionRemovedWasActivePromoted: (alias: string) => string;
|
|
46
48
|
sessionTransportShared: (transportSession: string, count: number) => string;
|
|
47
49
|
sessionOrchestrationPurgeFailed: (warning: string) => string;
|
|
48
50
|
sessionTransportTeardownFailed: (warning: string) => string;
|
|
@@ -689,6 +691,8 @@ export interface PluginCliMessages {
|
|
|
689
691
|
noPlugins: string;
|
|
690
692
|
pluginListHeader: string;
|
|
691
693
|
unrecognizedArgs: (args: string) => string;
|
|
694
|
+
pluginSpecHasDoubleQuote: (spec: string) => string;
|
|
695
|
+
pluginSpecHasPercentOnWindows: (spec: string) => string;
|
|
692
696
|
pluginInstallFailed: (packageSpec: string, error: string) => string;
|
|
693
697
|
pluginValidateFailed: (recordedName: string, error: string) => string;
|
|
694
698
|
pluginInstalled: (recordedName: string) => string;
|
|
@@ -775,8 +779,6 @@ export interface WeixinMessages {
|
|
|
775
779
|
debugEnabled: string;
|
|
776
780
|
debugDisabled: string;
|
|
777
781
|
sessionCleared: string;
|
|
778
|
-
noAccountsLoggedIn: string;
|
|
779
|
-
logoutSuccess: string;
|
|
780
782
|
commandFailed: (detail: string) => string;
|
|
781
783
|
}
|
|
782
784
|
export interface MigrateMessages {
|
|
@@ -802,6 +804,7 @@ export interface MiscMessages {
|
|
|
802
804
|
quotaHeadsUp: string;
|
|
803
805
|
quotaOverflowSummary: (count: number) => string;
|
|
804
806
|
finalHeadsUp: (total: number, sentSoFar: number, remaining: number) => string;
|
|
807
|
+
finalAllParked: (count: number) => string;
|
|
805
808
|
quotedMessagePrefix: (parts: string) => string;
|
|
806
809
|
scheduledTaskFailed: (message: string) => string;
|
|
807
810
|
orchestrationTaskCompleted: (taskId: string, workerSession: string, result: string) => string;
|
|
@@ -824,6 +827,8 @@ export interface MiscMessages {
|
|
|
824
827
|
delegateQPackageInstr3: string;
|
|
825
828
|
commandAccessDeniedSuffix: string;
|
|
826
829
|
commandAccessDeniedHint: string;
|
|
830
|
+
commandAccessDeniedChatTypeMissingSuffix: string;
|
|
831
|
+
commandAccessDeniedChatTypeMissingHint: string;
|
|
827
832
|
commandLabelThisMessage: string;
|
|
828
833
|
sessionResetNoCurrentSession: string;
|
|
829
834
|
sessionResetFailed: (alias: string) => string;
|
package/dist/plugin-api.js
CHANGED
|
@@ -68,6 +68,11 @@ var init_session = __esm(() => {
|
|
|
68
68
|
currentLabel: "[current]",
|
|
69
69
|
sessionListItem: (alias, agent, workspace) => `- ${alias} (${agent} @ ${workspace})`,
|
|
70
70
|
sessionCreated: (alias) => `Session "${alias}" created and switched.`,
|
|
71
|
+
sessionAlreadyExists: (alias, agent, workspace) => [
|
|
72
|
+
`Session "${alias}" already exists (${agent} @ ${workspace}).`,
|
|
73
|
+
`Switch to it with /use ${alias}, or remove it first with /session rm ${alias}.`
|
|
74
|
+
].join(`
|
|
75
|
+
`),
|
|
71
76
|
sessionAttachNotFound: (alias, agent, workspace) => [
|
|
72
77
|
"No existing session found to attach.",
|
|
73
78
|
`Check the session name and retry: /session attach ${alias} --agent ${agent} --ws ${workspace} --name <session-name>`
|
|
@@ -104,6 +109,7 @@ var init_session = __esm(() => {
|
|
|
104
109
|
sessionBlockedByTasksHint: "Use /tasks to list tasks, or /task cancel <id> to cancel one.",
|
|
105
110
|
sessionRemoved: (alias) => `Session "${alias}" removed.`,
|
|
106
111
|
sessionRemovedWasActive: "This was the active session. Its chat context has been cleared.",
|
|
112
|
+
sessionRemovedWasActivePromoted: (alias) => `This was the active session. Switched back to the previous session "${alias}".`,
|
|
107
113
|
sessionTransportShared: (transportSession, count) => `Note: backend session "${transportSession}" is still referenced by ${count} other session(s) and was not closed.`,
|
|
108
114
|
sessionOrchestrationPurgeFailed: (warning) => `Note: failed to purge orchestration references (${warning}). Run /tasks clean manually to clean up.`,
|
|
109
115
|
sessionTransportTeardownFailed: (warning) => `Note: backend session could not be closed automatically (${warning}). Run acpx sessions close manually if needed.`,
|
|
@@ -397,7 +403,7 @@ var init_later = __esm(() => {
|
|
|
397
403
|
helpNote2: "Time must be at least 10 seconds and at most 7 days away",
|
|
398
404
|
helpNote3: "By default runs in a new temporary session that is destroyed after completion",
|
|
399
405
|
helpNote4: "Use --bind to send to the session that was current when the task was created (configurable via later.defaultMode)",
|
|
400
|
-
helpNote5: "/lt list shows
|
|
406
|
+
helpNote5: "/lt list shows only this chat's pending tasks; in group chats only the owner can cancel",
|
|
401
407
|
helpNote6: "Scheduling slash-prefixed xacpx commands is not supported",
|
|
402
408
|
helpNote7: "Full time format reference: docs/later-command.md"
|
|
403
409
|
};
|
|
@@ -890,6 +896,8 @@ var init_plugin_cli = __esm(() => {
|
|
|
890
896
|
noPlugins: "No plugins installed yet.",
|
|
891
897
|
pluginListHeader: "Plugins:",
|
|
892
898
|
unrecognizedArgs: (args) => `Unrecognized arguments: ${args}`,
|
|
899
|
+
pluginSpecHasDoubleQuote: (spec) => `Invalid plugin spec ${spec}: double quotes (") are never valid in an npm package spec.`,
|
|
900
|
+
pluginSpecHasPercentOnWindows: (spec) => `Invalid plugin spec ${spec}: "%" would be mangled by cmd.exe on Windows. Install the package with npm directly instead.`,
|
|
893
901
|
pluginInstallFailed: (packageSpec, error) => `Plugin ${packageSpec} install failed: ${error}`,
|
|
894
902
|
pluginValidateFailed: (recordedName, error) => `Plugin ${recordedName} validation failed: ${error}`,
|
|
895
903
|
pluginInstalled: (recordedName) => `Plugin ${recordedName} installed`,
|
|
@@ -1001,8 +1009,6 @@ var init_weixin = __esm(() => {
|
|
|
1001
1009
|
debugEnabled: "Debug mode enabled",
|
|
1002
1010
|
debugDisabled: "Debug mode disabled",
|
|
1003
1011
|
sessionCleared: "✅ Session cleared. Starting a fresh conversation.",
|
|
1004
|
-
noAccountsLoggedIn: "No accounts are currently logged in.",
|
|
1005
|
-
logoutSuccess: "✅ Logged out. All account credentials cleared.",
|
|
1006
1012
|
commandFailed: (detail) => `❌ Command failed: ${detail}`
|
|
1007
1013
|
};
|
|
1008
1014
|
});
|
|
@@ -1040,6 +1046,7 @@ var init_misc = __esm(() => {
|
|
|
1040
1046
|
quotaOverflowSummary: (count) => `(${count} progress updates omitted due to message limit; see final result below)`,
|
|
1041
1047
|
finalHeadsUp: (total, sentSoFar, remaining) => `—
|
|
1042
1048
|
\uD83D\uDCC4 Result: ${total} parts total, ${sentSoFar} sent. Reply /jx to see the next ${remaining} parts.`,
|
|
1049
|
+
finalAllParked: (count) => `\uD83D\uDCC4 Message limit reached: the result (${count} parts) is parked. Reply /jx to receive it.`,
|
|
1043
1050
|
quotedMessagePrefix: (parts) => `[Quote: ${parts}]`,
|
|
1044
1051
|
scheduledTaskFailed: (message) => `Scheduled task failed: ${message}`,
|
|
1045
1052
|
orchestrationTaskCompleted: (taskId, workerSession, result) => `Delegation task "${taskId}" completed
|
|
@@ -1068,6 +1075,8 @@ var init_misc = __esm(() => {
|
|
|
1068
1075
|
delegateQPackageInstr3: "Do not forward the human's exact words to the worker",
|
|
1069
1076
|
commandAccessDeniedSuffix: " is restricted to group owner only.",
|
|
1070
1077
|
commandAccessDeniedHint: "To perform control operations, have the owner send them in the group, or use a private chat.",
|
|
1078
|
+
commandAccessDeniedChatTypeMissingSuffix: " was blocked: this channel did not report the chat type (direct or group), so control commands are disabled here.",
|
|
1079
|
+
commandAccessDeniedChatTypeMissingHint: "Read-only commands and prompts still work. This is a channel metadata issue — update or report the channel plugin.",
|
|
1071
1080
|
commandLabelThisMessage: "This message",
|
|
1072
1081
|
sessionResetNoCurrentSession: "No session is currently selected. Run /session new ... or /use <alias> first.",
|
|
1073
1082
|
sessionResetFailed: (alias) => `Session "${alias}" reset failed. The new backend session was not created, please try again later.`,
|
|
@@ -1138,6 +1147,11 @@ var init_session2 = __esm(() => {
|
|
|
1138
1147
|
currentLabel: "[当前]",
|
|
1139
1148
|
sessionListItem: (alias, agent2, workspace2) => `- ${alias} (${agent2} @ ${workspace2})`,
|
|
1140
1149
|
sessionCreated: (alias) => `会话「${alias}」已创建并切换`,
|
|
1150
|
+
sessionAlreadyExists: (alias, agent2, workspace2) => [
|
|
1151
|
+
`会话「${alias}」已存在(${agent2} @ ${workspace2})。`,
|
|
1152
|
+
`发送 /use ${alias} 切换到它,或先执行 /session rm ${alias} 删除后再创建。`
|
|
1153
|
+
].join(`
|
|
1154
|
+
`),
|
|
1141
1155
|
sessionAttachNotFound: (alias, agent2, workspace2) => [
|
|
1142
1156
|
"没有找到可绑定的已有会话。",
|
|
1143
1157
|
`请确认会话名是否正确,然后重新执行:/session attach ${alias} --agent ${agent2} --ws ${workspace2} --name <会话名>`
|
|
@@ -1174,6 +1188,7 @@ var init_session2 = __esm(() => {
|
|
|
1174
1188
|
sessionBlockedByTasksHint: "使用 /tasks 查看任务列表,或 /task cancel <id> 取消任务。",
|
|
1175
1189
|
sessionRemoved: (alias) => `已删除会话「${alias}」。`,
|
|
1176
1190
|
sessionRemovedWasActive: "该会话是当前活跃会话,已自动清除相关聊天上下文。",
|
|
1191
|
+
sessionRemovedWasActivePromoted: (alias) => `该会话是当前活跃会话,已切换回上一个会话「${alias}」。`,
|
|
1177
1192
|
sessionTransportShared: (transportSession, count) => `提示:后端会话「${transportSession}」仍被其他 ${count} 个会话引用,未关闭。`,
|
|
1178
1193
|
sessionOrchestrationPurgeFailed: (warning) => `提示:清理任务编排引用失败(${warning}),请稍后执行 /tasks clean 手动清理。`,
|
|
1179
1194
|
sessionTransportTeardownFailed: (warning) => `提示:后端会话未能自动关闭(${warning}),如有残留请手动执行 acpx sessions close。`,
|
|
@@ -1467,7 +1482,7 @@ var init_later2 = __esm(() => {
|
|
|
1467
1482
|
helpNote2: "时间必须在 10 秒之后、7 天之内",
|
|
1468
1483
|
helpNote3: "默认在为本次任务新建的临时会话里执行,跑完即销毁",
|
|
1469
1484
|
helpNote4: "加 --bind 改为发送到创建时绑定的当前会话(默认模式可用 later.defaultMode 配置)",
|
|
1470
|
-
helpNote5: "/lt list
|
|
1485
|
+
helpNote5: "/lt list 只显示本聊天创建的待执行任务;群聊中只有群主可取消",
|
|
1471
1486
|
helpNote6: "不支持延迟执行 / 开头的 xacpx 命令",
|
|
1472
1487
|
helpNote7: "完整时间格式与说明见 docs/later-command.md"
|
|
1473
1488
|
};
|
|
@@ -1960,6 +1975,8 @@ var init_plugin_cli2 = __esm(() => {
|
|
|
1960
1975
|
noPlugins: "还没有安装插件。",
|
|
1961
1976
|
pluginListHeader: "插件:",
|
|
1962
1977
|
unrecognizedArgs: (args) => `未识别的参数:${args}`,
|
|
1978
|
+
pluginSpecHasDoubleQuote: (spec) => `非法插件 spec ${spec}:npm 包 spec 不允许包含双引号 (")。`,
|
|
1979
|
+
pluginSpecHasPercentOnWindows: (spec) => `非法插件 spec ${spec}:Windows 上 cmd.exe 会展开 %,无法安全传递。请改用 npm 直接安装该包。`,
|
|
1963
1980
|
pluginInstallFailed: (packageSpec, error) => `插件 ${packageSpec} 安装失败:${error}`,
|
|
1964
1981
|
pluginValidateFailed: (recordedName, error) => `插件 ${recordedName} 校验失败:${error}`,
|
|
1965
1982
|
pluginInstalled: (recordedName) => `插件 ${recordedName} 已安装`,
|
|
@@ -2071,8 +2088,6 @@ var init_weixin2 = __esm(() => {
|
|
|
2071
2088
|
debugEnabled: "Debug 模式已开启",
|
|
2072
2089
|
debugDisabled: "Debug 模式已关闭",
|
|
2073
2090
|
sessionCleared: "✅ 会话已清除,重新开始对话",
|
|
2074
|
-
noAccountsLoggedIn: "当前没有已登录的账号",
|
|
2075
|
-
logoutSuccess: "✅ 已退出登录,清除所有账号凭证",
|
|
2076
2091
|
commandFailed: (detail) => `❌ 指令执行失败: ${detail}`
|
|
2077
2092
|
};
|
|
2078
2093
|
});
|
|
@@ -2110,6 +2125,7 @@ var init_misc2 = __esm(() => {
|
|
|
2110
2125
|
quotaOverflowSummary: (count) => `(因消息次数限制省略 ${count} 条进度,请继续查看下方最终结果)`,
|
|
2111
2126
|
finalHeadsUp: (total, sentSoFar, remaining) => `—
|
|
2112
2127
|
\uD83D\uDCC4 结果共 ${total} 段,已发 ${sentSoFar} 段。回复 /jx 续看后 ${remaining} 段。`,
|
|
2128
|
+
finalAllParked: (count) => `\uD83D\uDCC4 已达消息上限:结果共 ${count} 段已暂存。回复 /jx 接收。`,
|
|
2113
2129
|
quotedMessagePrefix: (parts) => `[引用: ${parts}]`,
|
|
2114
2130
|
scheduledTaskFailed: (message) => `定时任务执行失败:${message}`,
|
|
2115
2131
|
orchestrationTaskCompleted: (taskId, workerSession, result) => `委派任务「${taskId}」已完成
|
|
@@ -2138,6 +2154,8 @@ var init_misc2 = __esm(() => {
|
|
|
2138
2154
|
delegateQPackageInstr3: "不要直接把 human 原话转发给 worker",
|
|
2139
2155
|
commandAccessDeniedSuffix: " 仅限群创建者/频道 owner 使用。",
|
|
2140
2156
|
commandAccessDeniedHint: "如果需要执行控制类操作,请由 owner 在群内发送,或改用私聊。",
|
|
2157
|
+
commandAccessDeniedChatTypeMissingSuffix: " 已被拦截:该频道未上报会话类型(直聊/群聊),控制类命令在此暂不可用。",
|
|
2158
|
+
commandAccessDeniedChatTypeMissingHint: "只读命令与普通对话不受影响。这是频道元数据问题,请升级或反馈该频道插件。",
|
|
2141
2159
|
commandLabelThisMessage: "该消息",
|
|
2142
2160
|
sessionResetNoCurrentSession: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。",
|
|
2143
2161
|
sessionResetFailed: (alias) => `会话「${alias}」重置失败。
|
|
@@ -28,8 +28,11 @@ export declare class ScheduledTaskService {
|
|
|
28
28
|
private readonly claimedInThisSession;
|
|
29
29
|
constructor(state: AppState, stateStore: Pick<StateStore, "save">, options?: ScheduledTaskServiceOptions);
|
|
30
30
|
createTask(input: CreateScheduledTaskInput): Promise<ScheduledTaskRecord>;
|
|
31
|
-
listPending(): ScheduledTaskRecord[];
|
|
32
|
-
|
|
31
|
+
listPending(chatKey: string): ScheduledTaskRecord[];
|
|
32
|
+
listPendingAllChats(): ScheduledTaskRecord[];
|
|
33
|
+
cancelPending(inputId: string, chatKey: string): Promise<boolean>;
|
|
34
|
+
cancelPendingAnyChat(inputId: string): Promise<boolean>;
|
|
35
|
+
private cancelPendingWhere;
|
|
33
36
|
markStartupMissed(): Promise<void>;
|
|
34
37
|
claimDueTasks(): Promise<ScheduledTaskRecord[]>;
|
|
35
38
|
markExecuted(id: string): Promise<void>;
|
|
@@ -67,9 +67,13 @@ export declare class SessionService {
|
|
|
67
67
|
createSession(alias: string, agent: string, workspace: string): Promise<ResolvedSession>;
|
|
68
68
|
/**
|
|
69
69
|
* All currently-known logical sessions resolved to transport sessions, deduped by
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
70
|
+
* the composite identity acpx keys its session records on (agent + agent command +
|
|
71
|
+
* cwd + transport session name). Two aliases sharing a transport-session *name*
|
|
72
|
+
* but differing in agent/cwd (possible via /session attach) resolve to different
|
|
73
|
+
* acpx records with their own warm queue owners, so both must survive. Sessions
|
|
74
|
+
* whose agent or workspace is no longer registered are skipped (toResolvedSession
|
|
75
|
+
* would throw). Used by startup/shutdown cleanup to reap warm acpx queue owners;
|
|
76
|
+
* never throws.
|
|
73
77
|
*/
|
|
74
78
|
listAllResolvedSessions(): ResolvedSession[];
|
|
75
79
|
resolveSession(alias: string, agent: string, workspace: string, transportSession: string): ResolvedSession;
|
|
@@ -1,8 +1,75 @@
|
|
|
1
1
|
import { type AppState } from "./types";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Format version written on every save. The parser tolerates files without it
|
|
4
|
+
* (older releases) and ignores it on load; no migration chain exists yet.
|
|
5
|
+
*/
|
|
6
|
+
export declare const STATE_FILE_VERSION = 1;
|
|
7
|
+
/** A record or section that was skipped/repaired while loading state.json. */
|
|
8
|
+
export interface StateLoadDroppedRecord {
|
|
9
|
+
section: string;
|
|
10
|
+
key: string;
|
|
11
|
+
reason: string;
|
|
12
|
+
}
|
|
13
|
+
export interface StateLoadReport {
|
|
14
|
+
dropped: StateLoadDroppedRecord[];
|
|
15
|
+
/** Backup copy of the original file, written because records were dropped. */
|
|
16
|
+
quarantinePath?: string;
|
|
17
|
+
/** Unreadable original renamed aside (whole-file JSON corruption). */
|
|
18
|
+
corruptPath?: string;
|
|
19
|
+
/** Best-effort backup/rename failure; load still returned the cleaned state. */
|
|
20
|
+
backupError?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Lenient state parser: a malformed record (or wrong-typed section) is skipped
|
|
24
|
+
* and collected in `dropped` instead of throwing, so one bad record can never
|
|
25
|
+
* brick daemon startup. The per-record shape checks themselves stay strict — an
|
|
26
|
+
* invalid record is quarantined, it never flows into dispatch logic. Only a
|
|
27
|
+
* non-object top level still throws (StateStore.load treats that as a corrupt
|
|
28
|
+
* file and renames it aside).
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseState(raw: unknown, path: string, dropped?: StateLoadDroppedRecord[]): AppState;
|
|
31
|
+
export interface StateStoreOptions {
|
|
32
|
+
/** Injectable clock used for quarantine/corrupt backup file names. */
|
|
33
|
+
now?: () => Date;
|
|
34
|
+
/**
|
|
35
|
+
* Injectable backup writer (tests simulate backup failures). May return the
|
|
36
|
+
* path actually written when it differs from the requested one (the default
|
|
37
|
+
* writer suffix-retries instead of overwriting an existing backup).
|
|
38
|
+
*/
|
|
39
|
+
writeBackup?: (targetPath: string, content: string) => Promise<string | void>;
|
|
40
|
+
}
|
|
41
|
+
/** Result of a side-effect-free {@link StateStore.inspect}. */
|
|
42
|
+
export interface StateLoadInspection {
|
|
43
|
+
state: AppState;
|
|
44
|
+
/** Null when the file is fully valid (or missing/empty). */
|
|
45
|
+
report: StateLoadReport | null;
|
|
46
|
+
}
|
|
3
47
|
export declare class StateStore {
|
|
4
48
|
private readonly path;
|
|
5
|
-
|
|
49
|
+
private readonly options;
|
|
50
|
+
private loadReport;
|
|
51
|
+
constructor(path: string, options?: StateStoreOptions);
|
|
52
|
+
/**
|
|
53
|
+
* Report of the most recent load(): null when the file was fully valid (or
|
|
54
|
+
* missing/empty), otherwise the dropped/repaired records plus the quarantine
|
|
55
|
+
* or corrupt backup path. Callers log/print this so silent repair is visible.
|
|
56
|
+
*/
|
|
57
|
+
get lastLoadReport(): StateLoadReport | null;
|
|
6
58
|
load(): Promise<AppState>;
|
|
59
|
+
/**
|
|
60
|
+
* Side-effect-free variant of load() for diagnostic callers (doctor): parses
|
|
61
|
+
* and reports exactly what load() would drop/repair, but never writes a
|
|
62
|
+
* quarantine backup, never renames a corrupt file, and does not touch
|
|
63
|
+
* {@link lastLoadReport}.
|
|
64
|
+
*/
|
|
65
|
+
inspect(): Promise<StateLoadInspection>;
|
|
66
|
+
private readAndParse;
|
|
7
67
|
save(state: AppState): Promise<void>;
|
|
68
|
+
private fileTimestamp;
|
|
69
|
+
/**
|
|
70
|
+
* Whole-file corruption (JSON syntax error / non-object top level): rename
|
|
71
|
+
* the file aside — not copy — so the next save does not fight the corrupt
|
|
72
|
+
* bytes, then start from an empty state.
|
|
73
|
+
*/
|
|
74
|
+
private recoverFromCorruptFile;
|
|
8
75
|
}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs `fn` while holding the file's proper-lockfile lock, so a caller can make
|
|
3
|
+
* a whole read→modify→write span atomic with respect to other xacpx-aware
|
|
4
|
+
* processes (writePrivateFileAtomic alone only serializes the write).
|
|
5
|
+
*
|
|
6
|
+
* proper-lockfile is NOT reentrant: inside `fn`, write through the provided
|
|
7
|
+
* `writeLocked` (same atomic semantics, no re-lock) — never call
|
|
8
|
+
* writePrivateFileAtomic on the same path or it will deadlock until the lock
|
|
9
|
+
* goes stale. `realpath: false` keeps locking working for a not-yet-existing
|
|
10
|
+
* target file (the lock lives at `<path>.lock`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function withPrivateFileLock<T>(path: string, fn: (writeLocked: (content: string) => Promise<void>) => Promise<T>): Promise<T>;
|
|
1
13
|
export declare function writePrivateFileAtomic(path: string, content: string): Promise<void>;
|
|
2
14
|
/**
|
|
3
15
|
* Synchronous private-file write for hot-path callers that cannot await
|