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,37 @@
|
|
|
1
|
+
import type { RequestPromptSummary } from "./question-interaction.js";
|
|
2
|
+
import type { RequestTerminalReason } from "./request-store.js";
|
|
3
|
+
export type SessionReplyTarget = {
|
|
4
|
+
instanceID: string;
|
|
5
|
+
sessionID: string;
|
|
6
|
+
};
|
|
7
|
+
export type NaturalStopTerminalReason = "replied" | "continued" | "expired";
|
|
8
|
+
export type NotificationKind = "question" | "permission" | "sessionError" | "requestTerminal" | "naturalStop";
|
|
9
|
+
export type NotificationStatus = "pending" | "sent" | "resolved" | "failed" | "suppressed";
|
|
10
|
+
export type NotificationRecord = {
|
|
11
|
+
idempotencyKey: string;
|
|
12
|
+
kind: NotificationKind;
|
|
13
|
+
wechatAccountId: string;
|
|
14
|
+
userId: string;
|
|
15
|
+
registrationEpoch?: string;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
status: NotificationStatus;
|
|
18
|
+
routeKey?: string;
|
|
19
|
+
handle?: string;
|
|
20
|
+
scopeKey?: string;
|
|
21
|
+
prompt?: RequestPromptSummary;
|
|
22
|
+
sessionID?: string;
|
|
23
|
+
action?: string;
|
|
24
|
+
redactedSummary?: string;
|
|
25
|
+
severityAdvice?: string;
|
|
26
|
+
source?: "retryError";
|
|
27
|
+
replyTarget?: SessionReplyTarget;
|
|
28
|
+
naturalStopTerminalReason?: NaturalStopTerminalReason;
|
|
29
|
+
requestKind?: "question" | "permission";
|
|
30
|
+
terminalReason?: RequestTerminalReason;
|
|
31
|
+
replacementHandle?: string;
|
|
32
|
+
sentAt?: number;
|
|
33
|
+
resolvedAt?: number;
|
|
34
|
+
failedAt?: number;
|
|
35
|
+
suppressedAt?: number;
|
|
36
|
+
failureReason?: string;
|
|
37
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type OpenClawWeixinAccountHelpers = {
|
|
2
|
+
listAccountIds: () => Promise<string[]>;
|
|
3
|
+
resolveAccount: (accountId: string) => Promise<unknown>;
|
|
4
|
+
describeAccount: (accountIdOrInput: string | {
|
|
5
|
+
accountId: string;
|
|
6
|
+
}) => Promise<unknown>;
|
|
7
|
+
};
|
|
8
|
+
export type OpenClawLatestAccountState = {
|
|
9
|
+
accountId: string;
|
|
10
|
+
token: string;
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
getUpdatesBuf?: string;
|
|
13
|
+
userId?: string;
|
|
14
|
+
savedAt?: number;
|
|
15
|
+
boundAt?: number;
|
|
16
|
+
};
|
|
17
|
+
export type OpenClawMenuAccount = {
|
|
18
|
+
accountId: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
configured: boolean;
|
|
22
|
+
userId?: string;
|
|
23
|
+
boundAt?: number;
|
|
24
|
+
};
|
|
25
|
+
type BuildOpenClawMenuAccountInput = {
|
|
26
|
+
latestAccountState: OpenClawLatestAccountState | null;
|
|
27
|
+
accountHelpers: OpenClawWeixinAccountHelpers;
|
|
28
|
+
};
|
|
29
|
+
export declare function buildOpenClawMenuAccount(input: BuildOpenClawMenuAccountInput): Promise<OpenClawMenuAccount | null>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
function asObject(value) {
|
|
2
|
+
return value && typeof value === "object" ? value : {};
|
|
3
|
+
}
|
|
4
|
+
function firstNonEmptyString(...values) {
|
|
5
|
+
for (const value of values) {
|
|
6
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
function toBoolean(value) {
|
|
13
|
+
if (typeof value === "boolean") {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
async function tryDescribeAccount(accountHelpers, accountId) {
|
|
19
|
+
try {
|
|
20
|
+
const byString = await accountHelpers.describeAccount(accountId);
|
|
21
|
+
if (byString !== undefined) {
|
|
22
|
+
return byString;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// fallback to object input
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return await accountHelpers.describeAccount({ accountId });
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function buildOpenClawMenuAccount(input) {
|
|
36
|
+
const fallbackAccountId = firstNonEmptyString(input.latestAccountState?.accountId);
|
|
37
|
+
const accountIds = await input.accountHelpers.listAccountIds();
|
|
38
|
+
const accountId = firstNonEmptyString(fallbackAccountId, accountIds.at(-1));
|
|
39
|
+
if (!accountId) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const resolvedRaw = asObject(await input.accountHelpers.resolveAccount(accountId));
|
|
43
|
+
const describedRaw = asObject(await tryDescribeAccount(input.accountHelpers, accountId));
|
|
44
|
+
const enabled = toBoolean(resolvedRaw.enabled) ?? toBoolean(describedRaw.enabled) ?? false;
|
|
45
|
+
const configured = toBoolean(describedRaw.configured) ?? toBoolean(resolvedRaw.configured) ?? false;
|
|
46
|
+
const userId = firstNonEmptyString(input.latestAccountState?.userId, resolvedRaw.userId, describedRaw.userId);
|
|
47
|
+
const boundAt = [
|
|
48
|
+
input.latestAccountState?.boundAt,
|
|
49
|
+
resolvedRaw.boundAt,
|
|
50
|
+
describedRaw.boundAt,
|
|
51
|
+
].find((value) => typeof value === "number" && Number.isFinite(value));
|
|
52
|
+
return {
|
|
53
|
+
accountId,
|
|
54
|
+
name: firstNonEmptyString(resolvedRaw.name, describedRaw.name),
|
|
55
|
+
enabled,
|
|
56
|
+
configured,
|
|
57
|
+
userId,
|
|
58
|
+
boundAt,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type OperatorBinding = {
|
|
2
|
+
wechatAccountId: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
boundAt: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function readOperatorBinding(): Promise<OperatorBinding | undefined>;
|
|
7
|
+
export declare function bindOperator(input: OperatorBinding): Promise<OperatorBinding>;
|
|
8
|
+
export declare function rebindOperator(input: OperatorBinding): Promise<OperatorBinding>;
|
|
9
|
+
export declare function resetOperatorBinding(): Promise<void>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { WECHAT_FILE_MODE, ensureWechatStateLayout, operatorStatePath } from "./state-paths.js";
|
|
4
|
+
function isNonEmptyString(value) {
|
|
5
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
6
|
+
}
|
|
7
|
+
function isValidBinding(input) {
|
|
8
|
+
return (isNonEmptyString(input.wechatAccountId) &&
|
|
9
|
+
isNonEmptyString(input.userId) &&
|
|
10
|
+
typeof input.boundAt === "number" &&
|
|
11
|
+
Number.isFinite(input.boundAt));
|
|
12
|
+
}
|
|
13
|
+
function toBinding(input) {
|
|
14
|
+
return {
|
|
15
|
+
wechatAccountId: input.wechatAccountId,
|
|
16
|
+
userId: input.userId,
|
|
17
|
+
boundAt: input.boundAt,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export async function readOperatorBinding() {
|
|
21
|
+
try {
|
|
22
|
+
const raw = await readFile(operatorStatePath(), "utf8");
|
|
23
|
+
const parsed = JSON.parse(raw);
|
|
24
|
+
if (!isValidBinding(parsed)) {
|
|
25
|
+
throw new Error("invalid operator binding format");
|
|
26
|
+
}
|
|
27
|
+
return toBinding(parsed);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
const issue = error;
|
|
31
|
+
if (issue.code === "ENOENT")
|
|
32
|
+
return undefined;
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function bindOperator(input) {
|
|
37
|
+
const next = toBinding(input);
|
|
38
|
+
if (!isValidBinding(next)) {
|
|
39
|
+
throw new Error("invalid operator binding format");
|
|
40
|
+
}
|
|
41
|
+
const existing = await readOperatorBinding();
|
|
42
|
+
if (existing && (existing.userId !== next.userId || existing.wechatAccountId !== next.wechatAccountId)) {
|
|
43
|
+
throw new Error("operator already bound to another user");
|
|
44
|
+
}
|
|
45
|
+
await ensureWechatStateLayout();
|
|
46
|
+
await mkdir(path.dirname(operatorStatePath()), { recursive: true });
|
|
47
|
+
await writeFile(operatorStatePath(), JSON.stringify(next, null, 2), { mode: WECHAT_FILE_MODE });
|
|
48
|
+
return next;
|
|
49
|
+
}
|
|
50
|
+
export async function rebindOperator(input) {
|
|
51
|
+
const next = toBinding(input);
|
|
52
|
+
if (!isValidBinding(next)) {
|
|
53
|
+
throw new Error("invalid operator binding format");
|
|
54
|
+
}
|
|
55
|
+
await ensureWechatStateLayout();
|
|
56
|
+
await mkdir(path.dirname(operatorStatePath()), { recursive: true });
|
|
57
|
+
await writeFile(operatorStatePath(), JSON.stringify(next, null, 2), { mode: WECHAT_FILE_MODE });
|
|
58
|
+
return next;
|
|
59
|
+
}
|
|
60
|
+
export async function resetOperatorBinding() {
|
|
61
|
+
try {
|
|
62
|
+
await unlink(operatorStatePath());
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const issue = error;
|
|
66
|
+
if (issue.code !== "ENOENT")
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { RequestPromptSummary } from "./question-interaction.js";
|
|
2
|
+
export type BrokerCommandStatus = "queued" | "delivered" | "accepted" | "completed" | "failed";
|
|
3
|
+
export type BrokerToBridgeCommandType = "replyQuestion" | "replyPermission" | "replyNaturalStop";
|
|
4
|
+
export type BrokerToBridgeControlType = "requestReplay" | "requestFullSync";
|
|
5
|
+
export type BridgeToBrokerEventType = "instanceOnline" | "instanceOffline" | "sessionSnapshotChanged" | "questionOpened" | "questionUpdated" | "questionClosed" | "permissionOpened" | "permissionUpdated" | "permissionClosed" | "naturalStopOpened" | "naturalStopClosed" | "retryErrorUpdated" | "commandAccepted" | "commandResult" | "fullSyncCompleted";
|
|
6
|
+
export type BrokerToBridgeCommand<TPayload = unknown> = {
|
|
7
|
+
brokerSeq: number;
|
|
8
|
+
commandId: string;
|
|
9
|
+
type: BrokerToBridgeCommandType;
|
|
10
|
+
payload: TPayload;
|
|
11
|
+
};
|
|
12
|
+
export type BrokerToBridgeControl<TPayload = unknown> = {
|
|
13
|
+
brokerSeq: number;
|
|
14
|
+
controlId: string;
|
|
15
|
+
type: BrokerToBridgeControlType;
|
|
16
|
+
payload: TPayload;
|
|
17
|
+
};
|
|
18
|
+
export type BridgeToBrokerEvent<TPayload = unknown> = {
|
|
19
|
+
eventSeq: number;
|
|
20
|
+
instanceIncarnation: string;
|
|
21
|
+
type: BridgeToBrokerEventType;
|
|
22
|
+
payload: TPayload;
|
|
23
|
+
controlId?: string;
|
|
24
|
+
};
|
|
25
|
+
export type HelloRegisterPayload = {
|
|
26
|
+
protocolVersion: number;
|
|
27
|
+
stateGeneration: string;
|
|
28
|
+
instanceID: string;
|
|
29
|
+
instanceIncarnation: string;
|
|
30
|
+
lastSeenBrokerSeq?: number;
|
|
31
|
+
lastSentEventSeq?: number;
|
|
32
|
+
};
|
|
33
|
+
export type RegisterAckPayload = {
|
|
34
|
+
protocolVersion: number;
|
|
35
|
+
stateGeneration: string;
|
|
36
|
+
instanceIncarnation: string;
|
|
37
|
+
brokerSeq: number;
|
|
38
|
+
needReplay: boolean;
|
|
39
|
+
needFullSync: boolean;
|
|
40
|
+
};
|
|
41
|
+
export type BrokerAckPayload = {
|
|
42
|
+
ackedEventSeq: number;
|
|
43
|
+
instanceIncarnation: string;
|
|
44
|
+
};
|
|
45
|
+
export type HelloRegisterEnvelope = {
|
|
46
|
+
type: "hello/register";
|
|
47
|
+
payload: HelloRegisterPayload;
|
|
48
|
+
};
|
|
49
|
+
export type RegisterAckEnvelope = {
|
|
50
|
+
type: "registerAck";
|
|
51
|
+
payload: RegisterAckPayload;
|
|
52
|
+
};
|
|
53
|
+
export type BrokerAckEnvelope = {
|
|
54
|
+
type: "ack";
|
|
55
|
+
payload: BrokerAckPayload;
|
|
56
|
+
};
|
|
57
|
+
export type BrokerImplementedMessageType = "hello/register" | "registerAck" | "ack" | "ping" | "pong" | "error";
|
|
58
|
+
export type BrokerFutureMessageType = BrokerToBridgeControlType | BridgeToBrokerEventType | "replyQuestion" | "replyNaturalStop" | "rejectQuestion" | "replyPermission";
|
|
59
|
+
export type LegacyRemovedBrokerMessageType = "collectStatus" | "registerInstance" | "heartbeat" | "statusSnapshot" | "syncWechatNotifications" | "replyQuestionResult" | "replyNaturalStopResult" | "replyPermissionResult" | "showFallbackToast";
|
|
60
|
+
export type BrokerMessageType = BrokerImplementedMessageType | BrokerFutureMessageType | LegacyRemovedBrokerMessageType;
|
|
61
|
+
export declare const SHOW_FALLBACK_TOAST_DELIVERY_FAILED_REASON = "deliveryFailed";
|
|
62
|
+
export type ShowFallbackToastPayload = {
|
|
63
|
+
wechatAccountId: string;
|
|
64
|
+
userId: string;
|
|
65
|
+
message: string;
|
|
66
|
+
reason: typeof SHOW_FALLBACK_TOAST_DELIVERY_FAILED_REASON;
|
|
67
|
+
registrationEpoch: string;
|
|
68
|
+
};
|
|
69
|
+
export type ReplyMutationResult = {
|
|
70
|
+
mutationId: string;
|
|
71
|
+
ok: boolean;
|
|
72
|
+
errorMessage?: string;
|
|
73
|
+
};
|
|
74
|
+
export type ReplyQuestionPayload = {
|
|
75
|
+
mutationId: string;
|
|
76
|
+
requestID: string;
|
|
77
|
+
answers: unknown[];
|
|
78
|
+
};
|
|
79
|
+
export type ReplyPermissionPayload = {
|
|
80
|
+
mutationId: string;
|
|
81
|
+
requestID: string;
|
|
82
|
+
reply: "once" | "always" | "reject";
|
|
83
|
+
message?: string;
|
|
84
|
+
};
|
|
85
|
+
export type ReplyNaturalStopPayload = {
|
|
86
|
+
mutationId: string;
|
|
87
|
+
sessionID: string;
|
|
88
|
+
text: string;
|
|
89
|
+
};
|
|
90
|
+
export type SessionReplyTarget = {
|
|
91
|
+
instanceID: string;
|
|
92
|
+
sessionID: string;
|
|
93
|
+
};
|
|
94
|
+
export type WechatNotificationCandidate = {
|
|
95
|
+
idempotencyKey: string;
|
|
96
|
+
kind: "question" | "permission";
|
|
97
|
+
requestID: string;
|
|
98
|
+
createdAt: number;
|
|
99
|
+
routeKey: string;
|
|
100
|
+
handle: string;
|
|
101
|
+
scopeKey?: string;
|
|
102
|
+
wechatAccountId?: string;
|
|
103
|
+
userId?: string;
|
|
104
|
+
prompt?: RequestPromptSummary;
|
|
105
|
+
} | {
|
|
106
|
+
idempotencyKey: string;
|
|
107
|
+
kind: "sessionError";
|
|
108
|
+
createdAt: number;
|
|
109
|
+
sessionID: string;
|
|
110
|
+
action: string;
|
|
111
|
+
redactedSummary: string;
|
|
112
|
+
severityAdvice: string;
|
|
113
|
+
} | {
|
|
114
|
+
idempotencyKey: string;
|
|
115
|
+
kind: "naturalStop";
|
|
116
|
+
createdAt: number;
|
|
117
|
+
sessionID: string;
|
|
118
|
+
handle: string;
|
|
119
|
+
replyTarget: SessionReplyTarget;
|
|
120
|
+
redactedSummary: string;
|
|
121
|
+
severityAdvice: string;
|
|
122
|
+
};
|
|
123
|
+
export type SyncWechatNotificationsPayload = {
|
|
124
|
+
candidates: WechatNotificationCandidate[];
|
|
125
|
+
};
|
|
126
|
+
export type BrokerErrorCode = "unauthorized" | "invalidMessage" | "notImplemented" | "brokerUnavailable";
|
|
127
|
+
type EnvelopeBase = {
|
|
128
|
+
id: string;
|
|
129
|
+
type: BrokerMessageType;
|
|
130
|
+
instanceID?: string;
|
|
131
|
+
sessionToken?: string;
|
|
132
|
+
};
|
|
133
|
+
export type BrokerEnvelope<TPayload = unknown> = EnvelopeBase & {
|
|
134
|
+
payload: TPayload;
|
|
135
|
+
};
|
|
136
|
+
export type ErrorPayload = {
|
|
137
|
+
code: BrokerErrorCode;
|
|
138
|
+
message: string;
|
|
139
|
+
requestId: string;
|
|
140
|
+
};
|
|
141
|
+
export declare function createBrokerCommandEnvelope<TPayload = unknown>(envelope: BrokerToBridgeCommand<TPayload>): BrokerToBridgeCommand<TPayload>;
|
|
142
|
+
export declare function createBrokerControlEnvelope<TPayload = unknown>(envelope: BrokerToBridgeControl<TPayload>): BrokerToBridgeControl<TPayload>;
|
|
143
|
+
export declare function createBridgeEventEnvelope<TPayload = unknown>(envelope: BridgeToBrokerEvent<TPayload>): BridgeToBrokerEvent<TPayload>;
|
|
144
|
+
export declare function createHelloRegisterEnvelope(payload: HelloRegisterPayload): HelloRegisterEnvelope;
|
|
145
|
+
export declare function createRegisterAckEnvelope(payload: RegisterAckPayload): RegisterAckEnvelope;
|
|
146
|
+
export declare function createBrokerAckEnvelope(payload: BrokerAckPayload): BrokerAckEnvelope;
|
|
147
|
+
export declare function serializeEnvelope<TPayload = unknown>(envelope: BrokerEnvelope<TPayload>): string;
|
|
148
|
+
export declare function parseEnvelopeLine(line: string): BrokerEnvelope;
|
|
149
|
+
export declare function createErrorEnvelope(code: BrokerErrorCode, message: string, requestId: string): BrokerEnvelope<ErrorPayload>;
|
|
150
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
export const SHOW_FALLBACK_TOAST_DELIVERY_FAILED_REASON = "deliveryFailed";
|
|
2
|
+
function isNonEmptyString(value) {
|
|
3
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
4
|
+
}
|
|
5
|
+
function isSafeInteger(value) {
|
|
6
|
+
return typeof value === "number" && Number.isSafeInteger(value);
|
|
7
|
+
}
|
|
8
|
+
function assertNonNegativeInteger(value, fieldName) {
|
|
9
|
+
if (!isSafeInteger(value) || value < 0) {
|
|
10
|
+
throw new Error(`invalid ${fieldName}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function assertCommandType(value) {
|
|
14
|
+
if (value !== "replyQuestion" && value !== "replyPermission" && value !== "replyNaturalStop") {
|
|
15
|
+
throw new Error("invalid broker command type");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function assertControlType(value) {
|
|
19
|
+
if (value !== "requestReplay" && value !== "requestFullSync") {
|
|
20
|
+
throw new Error("invalid broker control type");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function assertBridgeEventType(value) {
|
|
24
|
+
if (value !== "instanceOnline"
|
|
25
|
+
&& value !== "instanceOffline"
|
|
26
|
+
&& value !== "sessionSnapshotChanged"
|
|
27
|
+
&& value !== "questionOpened"
|
|
28
|
+
&& value !== "questionUpdated"
|
|
29
|
+
&& value !== "questionClosed"
|
|
30
|
+
&& value !== "permissionOpened"
|
|
31
|
+
&& value !== "permissionUpdated"
|
|
32
|
+
&& value !== "permissionClosed"
|
|
33
|
+
&& value !== "naturalStopOpened"
|
|
34
|
+
&& value !== "naturalStopClosed"
|
|
35
|
+
&& value !== "retryErrorUpdated"
|
|
36
|
+
&& value !== "commandAccepted"
|
|
37
|
+
&& value !== "commandResult"
|
|
38
|
+
&& value !== "fullSyncCompleted") {
|
|
39
|
+
throw new Error("invalid bridge event type");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function createBrokerCommandEnvelope(envelope) {
|
|
43
|
+
assertNonNegativeInteger(envelope.brokerSeq, "brokerSeq");
|
|
44
|
+
if (!isNonEmptyString(envelope.commandId)) {
|
|
45
|
+
throw new Error("invalid commandId");
|
|
46
|
+
}
|
|
47
|
+
assertCommandType(envelope.type);
|
|
48
|
+
return { ...envelope };
|
|
49
|
+
}
|
|
50
|
+
export function createBrokerControlEnvelope(envelope) {
|
|
51
|
+
assertNonNegativeInteger(envelope.brokerSeq, "brokerSeq");
|
|
52
|
+
if (!isNonEmptyString(envelope.controlId)) {
|
|
53
|
+
throw new Error("invalid controlId");
|
|
54
|
+
}
|
|
55
|
+
assertControlType(envelope.type);
|
|
56
|
+
return { ...envelope };
|
|
57
|
+
}
|
|
58
|
+
export function createBridgeEventEnvelope(envelope) {
|
|
59
|
+
assertNonNegativeInteger(envelope.eventSeq, "eventSeq");
|
|
60
|
+
if (!isNonEmptyString(envelope.instanceIncarnation)) {
|
|
61
|
+
throw new Error("invalid instanceIncarnation");
|
|
62
|
+
}
|
|
63
|
+
assertBridgeEventType(envelope.type);
|
|
64
|
+
if (envelope.type === "fullSyncCompleted" && !isNonEmptyString(envelope.controlId)) {
|
|
65
|
+
throw new Error("invalid controlId");
|
|
66
|
+
}
|
|
67
|
+
return { ...envelope };
|
|
68
|
+
}
|
|
69
|
+
export function createHelloRegisterEnvelope(payload) {
|
|
70
|
+
assertNonNegativeInteger(payload.protocolVersion, "protocolVersion");
|
|
71
|
+
if (!isNonEmptyString(payload.stateGeneration)
|
|
72
|
+
|| !isNonEmptyString(payload.instanceID)
|
|
73
|
+
|| !isNonEmptyString(payload.instanceIncarnation)) {
|
|
74
|
+
throw new Error("invalid hello/register payload");
|
|
75
|
+
}
|
|
76
|
+
if (payload.lastSeenBrokerSeq !== undefined) {
|
|
77
|
+
assertNonNegativeInteger(payload.lastSeenBrokerSeq, "lastSeenBrokerSeq");
|
|
78
|
+
}
|
|
79
|
+
if (payload.lastSentEventSeq !== undefined) {
|
|
80
|
+
assertNonNegativeInteger(payload.lastSentEventSeq, "lastSentEventSeq");
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
type: "hello/register",
|
|
84
|
+
payload: { ...payload },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function createRegisterAckEnvelope(payload) {
|
|
88
|
+
assertNonNegativeInteger(payload.protocolVersion, "protocolVersion");
|
|
89
|
+
assertNonNegativeInteger(payload.brokerSeq, "brokerSeq");
|
|
90
|
+
if (!isNonEmptyString(payload.stateGeneration)
|
|
91
|
+
|| !isNonEmptyString(payload.instanceIncarnation)
|
|
92
|
+
|| typeof payload.needReplay !== "boolean"
|
|
93
|
+
|| typeof payload.needFullSync !== "boolean") {
|
|
94
|
+
throw new Error("invalid registerAck payload");
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
type: "registerAck",
|
|
98
|
+
payload: { ...payload },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export function createBrokerAckEnvelope(payload) {
|
|
102
|
+
assertNonNegativeInteger(payload.ackedEventSeq, "ackedEventSeq");
|
|
103
|
+
if (!isNonEmptyString(payload.instanceIncarnation)) {
|
|
104
|
+
throw new Error("invalid ack payload");
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
type: "ack",
|
|
108
|
+
payload: { ...payload },
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function isMessageType(value) {
|
|
112
|
+
return (value === "hello/register" ||
|
|
113
|
+
value === "registerAck" ||
|
|
114
|
+
value === "ack" ||
|
|
115
|
+
value === "ping" ||
|
|
116
|
+
value === "pong" ||
|
|
117
|
+
value === "error" ||
|
|
118
|
+
value === "requestReplay" ||
|
|
119
|
+
value === "requestFullSync" ||
|
|
120
|
+
value === "instanceOnline" ||
|
|
121
|
+
value === "instanceOffline" ||
|
|
122
|
+
value === "sessionSnapshotChanged" ||
|
|
123
|
+
value === "questionOpened" ||
|
|
124
|
+
value === "questionUpdated" ||
|
|
125
|
+
value === "questionClosed" ||
|
|
126
|
+
value === "permissionOpened" ||
|
|
127
|
+
value === "permissionUpdated" ||
|
|
128
|
+
value === "permissionClosed" ||
|
|
129
|
+
value === "naturalStopOpened" ||
|
|
130
|
+
value === "naturalStopClosed" ||
|
|
131
|
+
value === "retryErrorUpdated" ||
|
|
132
|
+
value === "commandAccepted" ||
|
|
133
|
+
value === "commandResult" ||
|
|
134
|
+
value === "fullSyncCompleted" ||
|
|
135
|
+
value === "replyQuestion" ||
|
|
136
|
+
value === "replyNaturalStop" ||
|
|
137
|
+
value === "rejectQuestion" ||
|
|
138
|
+
value === "replyPermission");
|
|
139
|
+
}
|
|
140
|
+
function isObject(value) {
|
|
141
|
+
return typeof value === "object" && value !== null;
|
|
142
|
+
}
|
|
143
|
+
function assertValidEnvelope(envelope) {
|
|
144
|
+
if (!isObject(envelope)) {
|
|
145
|
+
throw new Error("invalid message envelope");
|
|
146
|
+
}
|
|
147
|
+
if (!isNonEmptyString(envelope.id) || !isMessageType(envelope.type)) {
|
|
148
|
+
throw new Error("invalid message envelope");
|
|
149
|
+
}
|
|
150
|
+
if (!("payload" in envelope)) {
|
|
151
|
+
throw new Error("invalid message envelope");
|
|
152
|
+
}
|
|
153
|
+
if (envelope.instanceID !== undefined && !isNonEmptyString(envelope.instanceID)) {
|
|
154
|
+
throw new Error("invalid message envelope");
|
|
155
|
+
}
|
|
156
|
+
if (envelope.sessionToken !== undefined && !isNonEmptyString(envelope.sessionToken)) {
|
|
157
|
+
throw new Error("invalid message envelope");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function serializeEnvelope(envelope) {
|
|
161
|
+
assertValidEnvelope(envelope);
|
|
162
|
+
return `${JSON.stringify(envelope)}\n`;
|
|
163
|
+
}
|
|
164
|
+
export function parseEnvelopeLine(line) {
|
|
165
|
+
if (typeof line !== "string" || line.length === 0) {
|
|
166
|
+
throw new Error("invalid message line");
|
|
167
|
+
}
|
|
168
|
+
if (!line.endsWith("\n")) {
|
|
169
|
+
throw new Error("invalid message line");
|
|
170
|
+
}
|
|
171
|
+
const body = line.slice(0, -1);
|
|
172
|
+
if (body.length === 0 || body.includes("\n") || body.includes("\r")) {
|
|
173
|
+
throw new Error("invalid message line");
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const parsed = JSON.parse(body);
|
|
177
|
+
assertValidEnvelope(parsed);
|
|
178
|
+
return parsed;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
throw new Error("invalid message line");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export function createErrorEnvelope(code, message, requestId) {
|
|
185
|
+
if (!isNonEmptyString(message) || !isNonEmptyString(requestId)) {
|
|
186
|
+
throw new Error("invalid error envelope");
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
id: `err-${requestId}`,
|
|
190
|
+
type: "error",
|
|
191
|
+
payload: {
|
|
192
|
+
code,
|
|
193
|
+
message,
|
|
194
|
+
requestId,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PermissionRequest, QuestionRequest } from "@opencode-ai/sdk/v2";
|
|
2
|
+
export type QuestionPromptMode = "text" | "single" | "multiple";
|
|
3
|
+
export type QuestionPromptSummary = {
|
|
4
|
+
title?: string;
|
|
5
|
+
body?: string;
|
|
6
|
+
mode: QuestionPromptMode;
|
|
7
|
+
custom?: boolean;
|
|
8
|
+
options?: Array<{
|
|
9
|
+
index: number;
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}>;
|
|
14
|
+
};
|
|
15
|
+
export type PermissionPromptSummary = {
|
|
16
|
+
title?: string;
|
|
17
|
+
type?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
};
|
|
20
|
+
export type RequestPromptSummary = QuestionPromptSummary | PermissionPromptSummary;
|
|
21
|
+
export declare function normalizeRequestPromptSummary(kind: "question" | "permission", input: unknown): RequestPromptSummary | undefined;
|
|
22
|
+
export declare function extractQuestionPromptSummary(question: QuestionRequest): QuestionPromptSummary | undefined;
|
|
23
|
+
export declare function extractPermissionPromptSummary(permission: PermissionRequest): PermissionPromptSummary | undefined;
|
|
24
|
+
export declare function buildQuestionAnswersFromReply(prompt: QuestionPromptSummary | undefined, rawText: string): Array<Array<string>>;
|