opencode-oncall 0.1.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/LICENSE +151 -0
- package/README.md +50 -0
- package/dist/common-settings-actions.d.ts +15 -0
- package/dist/common-settings-actions.js +48 -0
- package/dist/common-settings-store.d.ts +1 -0
- package/dist/common-settings-store.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin-hooks.d.ts +51 -0
- package/dist/plugin-hooks.js +288 -0
- package/dist/plugin.d.ts +10 -0
- package/dist/plugin.js +115 -0
- package/dist/settings-store.d.ts +50 -0
- package/dist/settings-store.js +214 -0
- package/dist/store-paths.d.ts +16 -0
- package/dist/store-paths.js +61 -0
- package/dist/ui/wechat-menu.d.ts +26 -0
- package/dist/ui/wechat-menu.js +90 -0
- package/dist/wechat/bind-flow.d.ts +29 -0
- package/dist/wechat/bind-flow.js +207 -0
- package/dist/wechat/bridge.d.ts +136 -0
- package/dist/wechat/bridge.js +1059 -0
- package/dist/wechat/broker-client.d.ts +23 -0
- package/dist/wechat/broker-client.js +274 -0
- package/dist/wechat/broker-endpoint.d.ts +21 -0
- package/dist/wechat/broker-endpoint.js +78 -0
- package/dist/wechat/broker-entry.d.ts +123 -0
- package/dist/wechat/broker-entry.js +1321 -0
- package/dist/wechat/broker-launcher.d.ts +37 -0
- package/dist/wechat/broker-launcher.js +418 -0
- package/dist/wechat/broker-mutation-queue.d.ts +93 -0
- package/dist/wechat/broker-mutation-queue.js +126 -0
- package/dist/wechat/broker-server.d.ts +86 -0
- package/dist/wechat/broker-server.js +1340 -0
- package/dist/wechat/broker-state-store.d.ts +335 -0
- package/dist/wechat/broker-state-store.js +1964 -0
- package/dist/wechat/command-parser.d.ts +18 -0
- package/dist/wechat/command-parser.js +58 -0
- package/dist/wechat/compat/jiti-loader.d.ts +27 -0
- package/dist/wechat/compat/jiti-loader.js +118 -0
- package/dist/wechat/compat/openclaw-account-helpers.d.ts +29 -0
- package/dist/wechat/compat/openclaw-account-helpers.js +60 -0
- package/dist/wechat/compat/openclaw-bind-helpers.d.ts +29 -0
- package/dist/wechat/compat/openclaw-bind-helpers.js +169 -0
- package/dist/wechat/compat/openclaw-guided-smoke.d.ts +180 -0
- package/dist/wechat/compat/openclaw-guided-smoke.js +1134 -0
- package/dist/wechat/compat/openclaw-public-entry.d.ts +33 -0
- package/dist/wechat/compat/openclaw-public-entry.js +62 -0
- package/dist/wechat/compat/openclaw-public-helpers.d.ts +70 -0
- package/dist/wechat/compat/openclaw-public-helpers.js +68 -0
- package/dist/wechat/compat/openclaw-qr-gateway.d.ts +15 -0
- package/dist/wechat/compat/openclaw-qr-gateway.js +39 -0
- package/dist/wechat/compat/openclaw-smoke.d.ts +48 -0
- package/dist/wechat/compat/openclaw-smoke.js +100 -0
- package/dist/wechat/compat/openclaw-sync-buf.d.ts +24 -0
- package/dist/wechat/compat/openclaw-sync-buf.js +80 -0
- package/dist/wechat/compat/openclaw-updates-send.d.ts +47 -0
- package/dist/wechat/compat/openclaw-updates-send.js +38 -0
- package/dist/wechat/compat/qrcode-terminal-loader.d.ts +12 -0
- package/dist/wechat/compat/qrcode-terminal-loader.js +16 -0
- package/dist/wechat/compat/slash-guard.d.ts +11 -0
- package/dist/wechat/compat/slash-guard.js +24 -0
- package/dist/wechat/dead-letter-store.d.ts +48 -0
- package/dist/wechat/dead-letter-store.js +224 -0
- package/dist/wechat/debug-bundle-collector.d.ts +49 -0
- package/dist/wechat/debug-bundle-collector.js +580 -0
- package/dist/wechat/debug-bundle-flow.d.ts +37 -0
- package/dist/wechat/debug-bundle-flow.js +180 -0
- package/dist/wechat/debug-bundle-redaction.d.ts +14 -0
- package/dist/wechat/debug-bundle-redaction.js +339 -0
- package/dist/wechat/handle.d.ts +10 -0
- package/dist/wechat/handle.js +57 -0
- package/dist/wechat/ipc-auth.d.ts +6 -0
- package/dist/wechat/ipc-auth.js +39 -0
- package/dist/wechat/latest-account-state-store.d.ts +8 -0
- package/dist/wechat/latest-account-state-store.js +38 -0
- package/dist/wechat/notification-dispatcher.d.ts +34 -0
- package/dist/wechat/notification-dispatcher.js +266 -0
- package/dist/wechat/notification-format.d.ts +15 -0
- package/dist/wechat/notification-format.js +196 -0
- package/dist/wechat/notification-store.d.ts +72 -0
- package/dist/wechat/notification-store.js +807 -0
- package/dist/wechat/notification-types.d.ts +37 -0
- package/dist/wechat/notification-types.js +1 -0
- package/dist/wechat/openclaw-account-adapter.d.ts +30 -0
- package/dist/wechat/openclaw-account-adapter.js +60 -0
- package/dist/wechat/operator-store.d.ts +9 -0
- package/dist/wechat/operator-store.js +69 -0
- package/dist/wechat/protocol.d.ts +150 -0
- package/dist/wechat/protocol.js +197 -0
- package/dist/wechat/question-interaction.d.ts +24 -0
- package/dist/wechat/question-interaction.js +180 -0
- package/dist/wechat/request-store.d.ts +108 -0
- package/dist/wechat/request-store.js +669 -0
- package/dist/wechat/session-digest.d.ts +50 -0
- package/dist/wechat/session-digest.js +167 -0
- package/dist/wechat/state-paths.d.ts +26 -0
- package/dist/wechat/state-paths.js +92 -0
- package/dist/wechat/status-format.d.ts +26 -0
- package/dist/wechat/status-format.js +616 -0
- package/dist/wechat/token-store.d.ts +20 -0
- package/dist/wechat/token-store.js +193 -0
- package/dist/wechat/wechat-status-runtime.d.ts +89 -0
- package/dist/wechat/wechat-status-runtime.js +518 -0
- package/package.json +74 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
function isNonEmptyString(value) {
|
|
2
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
3
|
+
}
|
|
4
|
+
function isFiniteNumber(value) {
|
|
5
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
6
|
+
}
|
|
7
|
+
function normalizeQuestionPromptSummary(input) {
|
|
8
|
+
const record = input;
|
|
9
|
+
const mode = record.mode === "multiple" ? "multiple" : record.mode === "single" ? "single" : record.mode === "text" ? "text" : undefined;
|
|
10
|
+
if (!mode) {
|
|
11
|
+
throw new Error("invalid request prompt format");
|
|
12
|
+
}
|
|
13
|
+
const options = Array.isArray(record.options)
|
|
14
|
+
? record.options.map((option) => {
|
|
15
|
+
const item = option;
|
|
16
|
+
if (!isFiniteNumber(item.index) || !isNonEmptyString(item.label) || !isNonEmptyString(item.value)) {
|
|
17
|
+
throw new Error("invalid request prompt format");
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
index: item.index,
|
|
21
|
+
label: item.label,
|
|
22
|
+
value: item.value,
|
|
23
|
+
...(isNonEmptyString(item.description) ? { description: item.description.trim() } : {}),
|
|
24
|
+
};
|
|
25
|
+
})
|
|
26
|
+
: undefined;
|
|
27
|
+
return {
|
|
28
|
+
...(isNonEmptyString(record.title) ? { title: record.title.trim() } : {}),
|
|
29
|
+
...(isNonEmptyString(record.body) ? { body: record.body.trim() } : {}),
|
|
30
|
+
mode,
|
|
31
|
+
...(record.custom === true ? { custom: true } : {}),
|
|
32
|
+
...(options && options.length > 0 ? { options } : {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function normalizePermissionPromptSummary(input) {
|
|
36
|
+
const record = input;
|
|
37
|
+
return {
|
|
38
|
+
...(isNonEmptyString(record.title) ? { title: record.title.trim() } : {}),
|
|
39
|
+
...(isNonEmptyString(record.type) ? { type: record.type.trim() } : {}),
|
|
40
|
+
...(isNonEmptyString(record.description) ? { description: record.description.trim() } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function normalizeRequestPromptSummary(kind, input) {
|
|
44
|
+
if (input === undefined) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return kind === "question" ? normalizeQuestionPromptSummary(input) : normalizePermissionPromptSummary(input);
|
|
48
|
+
}
|
|
49
|
+
export function extractQuestionPromptSummary(question) {
|
|
50
|
+
const first = Array.isArray(question.questions) ? question.questions[0] : undefined;
|
|
51
|
+
if (!first) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const mode = first.multiple === true ? "multiple" : Array.isArray(first.options) && first.options.length > 0 ? "single" : "text";
|
|
55
|
+
const options = Array.isArray(first.options)
|
|
56
|
+
? first.options
|
|
57
|
+
.filter((option) => isNonEmptyString(option?.label))
|
|
58
|
+
.map((option, index) => ({
|
|
59
|
+
index: index + 1,
|
|
60
|
+
label: option.label.trim(),
|
|
61
|
+
value: option.label.trim(),
|
|
62
|
+
...(isNonEmptyString(option?.description) ? { description: option.description.trim() } : {}),
|
|
63
|
+
}))
|
|
64
|
+
: undefined;
|
|
65
|
+
return normalizeQuestionPromptSummary({
|
|
66
|
+
title: isNonEmptyString(first.header) ? first.header : undefined,
|
|
67
|
+
body: isNonEmptyString(first.question) ? first.question : undefined,
|
|
68
|
+
mode,
|
|
69
|
+
custom: first.custom !== false,
|
|
70
|
+
options,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export function extractPermissionPromptSummary(permission) {
|
|
74
|
+
const metadata = typeof permission.metadata === "object" && permission.metadata !== null
|
|
75
|
+
? permission.metadata
|
|
76
|
+
: {};
|
|
77
|
+
const type = isNonEmptyString(metadata.type) ? metadata.type : undefined;
|
|
78
|
+
const description = Array.isArray(permission.patterns) && permission.patterns.length > 0
|
|
79
|
+
? permission.patterns.join(", ")
|
|
80
|
+
: undefined;
|
|
81
|
+
return normalizePermissionPromptSummary({
|
|
82
|
+
title: isNonEmptyString(permission.permission) ? permission.permission : undefined,
|
|
83
|
+
type,
|
|
84
|
+
description,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function findOptionValue(options, token) {
|
|
88
|
+
const index = Number(token);
|
|
89
|
+
if (!Number.isInteger(index)) {
|
|
90
|
+
throw new Error("回复格式无效,请按题目提示填写选项编号");
|
|
91
|
+
}
|
|
92
|
+
const match = options.find((option) => option.index === index);
|
|
93
|
+
if (!match) {
|
|
94
|
+
throw new Error(`选项编号超出范围:${token}`);
|
|
95
|
+
}
|
|
96
|
+
return match.value;
|
|
97
|
+
}
|
|
98
|
+
function splitMultipleChoiceTokens(raw) {
|
|
99
|
+
return raw.split(",").map((token) => token.trim()).filter(Boolean);
|
|
100
|
+
}
|
|
101
|
+
function maybeParseMultipleChoiceValues(raw, options) {
|
|
102
|
+
if (options.length === 0) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const tokens = splitMultipleChoiceTokens(raw);
|
|
106
|
+
if (tokens.length === 0 || tokens.some((token) => !/^\d+$/.test(token))) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return parseMultipleChoiceValues(raw, options, true);
|
|
110
|
+
}
|
|
111
|
+
function parseMultipleChoiceValues(raw, options, allowCustom) {
|
|
112
|
+
const tokens = splitMultipleChoiceTokens(raw);
|
|
113
|
+
if (tokens.length === 0) {
|
|
114
|
+
throw new Error("多选题请使用逗号分隔的选项编号");
|
|
115
|
+
}
|
|
116
|
+
const seen = new Set();
|
|
117
|
+
const values = [];
|
|
118
|
+
for (const token of tokens) {
|
|
119
|
+
if (!/^\d+$/.test(token)) {
|
|
120
|
+
throw new Error(allowCustom === true
|
|
121
|
+
? "多选题请使用逗号分隔的选项编号,或直接输入自定义回答"
|
|
122
|
+
: "该问题不支持自定义回复,请按题目提示填写选项编号");
|
|
123
|
+
}
|
|
124
|
+
if (seen.has(token)) {
|
|
125
|
+
throw new Error(`选项编号不能重复:${token}`);
|
|
126
|
+
}
|
|
127
|
+
seen.add(token);
|
|
128
|
+
values.push(findOptionValue(options, token));
|
|
129
|
+
}
|
|
130
|
+
return values;
|
|
131
|
+
}
|
|
132
|
+
export function buildQuestionAnswersFromReply(prompt, rawText) {
|
|
133
|
+
const text = rawText.trim();
|
|
134
|
+
if (!text) {
|
|
135
|
+
throw new Error("回复内容不能为空");
|
|
136
|
+
}
|
|
137
|
+
if (!prompt) {
|
|
138
|
+
return [[text]];
|
|
139
|
+
}
|
|
140
|
+
const separatorIndex = text.indexOf(";");
|
|
141
|
+
const left = separatorIndex >= 0 ? text.slice(0, separatorIndex).trim() : "";
|
|
142
|
+
const right = separatorIndex >= 0 ? text.slice(separatorIndex + 1).trim() : "";
|
|
143
|
+
const options = prompt.options ?? [];
|
|
144
|
+
const mixedChoiceValues = separatorIndex >= 0 ? maybeParseMultipleChoiceValues(left, options) : undefined;
|
|
145
|
+
if (mixedChoiceValues) {
|
|
146
|
+
if (prompt.mode !== "multiple" || prompt.custom !== true) {
|
|
147
|
+
throw new Error("当前题型不支持“编号 + 自定义补充”,请按题目提示回复");
|
|
148
|
+
}
|
|
149
|
+
const customText = right.replace(/^其他:\s*/u, "").trim();
|
|
150
|
+
if (!customText) {
|
|
151
|
+
throw new Error("混合回复格式无效,请使用“编号; 自定义补充”");
|
|
152
|
+
}
|
|
153
|
+
return [[...mixedChoiceValues, customText]];
|
|
154
|
+
}
|
|
155
|
+
if (prompt.mode === "text") {
|
|
156
|
+
return [[text]];
|
|
157
|
+
}
|
|
158
|
+
if (options.length === 0) {
|
|
159
|
+
return [[text]];
|
|
160
|
+
}
|
|
161
|
+
if (prompt.mode === "single") {
|
|
162
|
+
if (prompt.custom === true && !/^\d+$/.test(text)) {
|
|
163
|
+
return [[text]];
|
|
164
|
+
}
|
|
165
|
+
if (!/^\d+$/.test(text)) {
|
|
166
|
+
throw new Error(prompt.custom === true
|
|
167
|
+
? "单选题请回复单个选项编号,或直接输入自定义回答"
|
|
168
|
+
: "该问题不支持自定义回复,请按题目提示填写选项编号");
|
|
169
|
+
}
|
|
170
|
+
return [[findOptionValue(options, text)]];
|
|
171
|
+
}
|
|
172
|
+
if (prompt.custom === true) {
|
|
173
|
+
const choiceValues = maybeParseMultipleChoiceValues(text, options);
|
|
174
|
+
if (!choiceValues) {
|
|
175
|
+
return [[text]];
|
|
176
|
+
}
|
|
177
|
+
return [choiceValues];
|
|
178
|
+
}
|
|
179
|
+
return [parseMultipleChoiceValues(text, options, false)];
|
|
180
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type WechatRequestKind } from "./state-paths.js";
|
|
2
|
+
import { type RequestPromptSummary } from "./question-interaction.js";
|
|
3
|
+
export type RequestStatus = "open" | "answered" | "rejected" | "expired" | "cleaned";
|
|
4
|
+
export type RequestTerminalReason = "answered" | "handled" | "rejected" | "expired" | "replaced";
|
|
5
|
+
export type RequestRecord = {
|
|
6
|
+
kind: WechatRequestKind;
|
|
7
|
+
requestID: string;
|
|
8
|
+
routeKey: string;
|
|
9
|
+
handle: string;
|
|
10
|
+
scopeKey?: string;
|
|
11
|
+
prompt?: RequestPromptSummary;
|
|
12
|
+
wechatAccountId: string;
|
|
13
|
+
userId: string;
|
|
14
|
+
status: RequestStatus;
|
|
15
|
+
createdAt: number;
|
|
16
|
+
answeredAt?: number;
|
|
17
|
+
rejectedAt?: number;
|
|
18
|
+
expiredAt?: number;
|
|
19
|
+
cleanedAt?: number;
|
|
20
|
+
terminalReason?: RequestTerminalReason;
|
|
21
|
+
replacementHandle?: string;
|
|
22
|
+
terminalResultSent?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export declare function upsertRequest(input: Omit<RequestRecord, "status" | "answeredAt" | "rejectedAt" | "expiredAt" | "cleanedAt">): Promise<RequestRecord>;
|
|
25
|
+
export declare function expireOpenRequestsForScope(input: {
|
|
26
|
+
scopeKey: string;
|
|
27
|
+
expiredAt: number;
|
|
28
|
+
}): Promise<RequestRecord[]>;
|
|
29
|
+
export declare function markRequestAnswered(input: {
|
|
30
|
+
kind: WechatRequestKind;
|
|
31
|
+
routeKey: string;
|
|
32
|
+
answeredAt: number;
|
|
33
|
+
}): Promise<RequestRecord>;
|
|
34
|
+
export declare function markRequestRejected(input: {
|
|
35
|
+
kind: WechatRequestKind;
|
|
36
|
+
routeKey: string;
|
|
37
|
+
rejectedAt: number;
|
|
38
|
+
}): Promise<RequestRecord>;
|
|
39
|
+
export declare function markRequestExpired(input: {
|
|
40
|
+
kind: WechatRequestKind;
|
|
41
|
+
routeKey: string;
|
|
42
|
+
expiredAt: number;
|
|
43
|
+
}): Promise<RequestRecord>;
|
|
44
|
+
export type PreparedRecoveryRequestReopen = {
|
|
45
|
+
originalRequest: RequestRecord;
|
|
46
|
+
nextHandle: string;
|
|
47
|
+
nextRouteKey: string;
|
|
48
|
+
};
|
|
49
|
+
export declare function allocateFreshRecoveryHandle(input: {
|
|
50
|
+
kind: WechatRequestKind;
|
|
51
|
+
bannedHandles?: string[];
|
|
52
|
+
}): Promise<string>;
|
|
53
|
+
export declare function prepareRecoveryRequestReopen(input: {
|
|
54
|
+
kind: WechatRequestKind;
|
|
55
|
+
routeKey: string;
|
|
56
|
+
recoveredAt: number;
|
|
57
|
+
bannedHandles?: string[];
|
|
58
|
+
}): Promise<PreparedRecoveryRequestReopen>;
|
|
59
|
+
export declare function commitPreparedRecoveryRequestReopen(prepared: PreparedRecoveryRequestReopen): Promise<RequestRecord>;
|
|
60
|
+
export declare function rollbackPreparedRecoveryRequestReopen(prepared: PreparedRecoveryRequestReopen): Promise<void>;
|
|
61
|
+
export declare function recoverRequestFromDeadLetter(input: {
|
|
62
|
+
kind: WechatRequestKind;
|
|
63
|
+
routeKey: string;
|
|
64
|
+
recoveredAt: number;
|
|
65
|
+
excludedHandles?: string[];
|
|
66
|
+
}): Promise<RequestRecord>;
|
|
67
|
+
export declare function markCleaned(input: {
|
|
68
|
+
kind: WechatRequestKind;
|
|
69
|
+
routeKey: string;
|
|
70
|
+
cleanedAt: number;
|
|
71
|
+
}): Promise<RequestRecord>;
|
|
72
|
+
export declare function purgeCleanedBefore(input: {
|
|
73
|
+
cutoffAt: number;
|
|
74
|
+
}): Promise<number>;
|
|
75
|
+
export declare function purgeCleanedRequestsBefore(input: {
|
|
76
|
+
cutoffAt: number;
|
|
77
|
+
}): Promise<RequestRecord[]>;
|
|
78
|
+
export declare function listActiveRequests(): Promise<RequestRecord[]>;
|
|
79
|
+
export declare function findOpenRequestByHandle(input: {
|
|
80
|
+
kind: WechatRequestKind;
|
|
81
|
+
handle: string;
|
|
82
|
+
}): Promise<RequestRecord | undefined>;
|
|
83
|
+
export declare function findTerminalRequestByHandle(input: {
|
|
84
|
+
kind: WechatRequestKind;
|
|
85
|
+
handle: string;
|
|
86
|
+
}): Promise<RequestRecord | undefined>;
|
|
87
|
+
export declare function findOpenRequestByIdentity(input: {
|
|
88
|
+
kind: WechatRequestKind;
|
|
89
|
+
requestID: string;
|
|
90
|
+
wechatAccountId: string;
|
|
91
|
+
userId: string;
|
|
92
|
+
scopeKey?: string;
|
|
93
|
+
}): Promise<RequestRecord | undefined>;
|
|
94
|
+
export declare function findRequestByRouteKey(input: {
|
|
95
|
+
kind: WechatRequestKind;
|
|
96
|
+
routeKey: string;
|
|
97
|
+
}): Promise<RequestRecord | undefined>;
|
|
98
|
+
export declare function markTerminalMetadata(input: {
|
|
99
|
+
kind: WechatRequestKind;
|
|
100
|
+
routeKey: string;
|
|
101
|
+
terminalReason: RequestTerminalReason;
|
|
102
|
+
replacementHandle?: string;
|
|
103
|
+
}): Promise<RequestRecord>;
|
|
104
|
+
export declare function markTerminalResultSent(input: {
|
|
105
|
+
kind: WechatRequestKind;
|
|
106
|
+
routeKey: string;
|
|
107
|
+
sentAt: number;
|
|
108
|
+
}): Promise<RequestRecord>;
|