mail.send 工具契约 2.0.0(to 数组 / html / 附件 / inReplyTo / dryRun;保留 v1 的 body 与 queued);契约快照刷新,五道门禁绿

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
This commit is contained in:
xcs
2026-08-26 02:23:24 +08:00
co-authored by Claude Fable 5
parent b6ec37a231
commit e0fc16db33
10 changed files with 1340 additions and 8 deletions
@@ -0,0 +1 @@
export {};
+1
View File
@@ -2,3 +2,4 @@ export * from './http.ts';
export * from './workbench.ts';
export * from './governance.ts';
export * from './ingest.ts';
export * from './mail.ts';
+690
View File
@@ -0,0 +1,690 @@
/**
* 邮件收发的路由契约(设计稿 §7.2;ADR-013)。
*
* 读写都归运营中心(设计稿 §7.2 + 2026-08-26 裁决):邮箱那一屏落在运营中心,设计端没有消费方;
* 而入站正文装的是客户来往内容 —— ADR-012 已把「设计端能读客户主数据」记为一处后果,不再无故加宽。
* `/api/gov/*` 的写路由由 app 中间件自动广播 `{kind:'system', id:'gov'}`。
* 探测 / 试发 / 拉取的**厂商失败不是接口错误**:200 的 body 里带原话(脱敏)。
*/
import { z } from 'zod';
import type { RouteContract } from './http.ts';
export declare const listMailAccountsRoute: {
readonly method: "GET";
readonly path: "/api/gov/mail/accounts";
readonly access: "ops";
readonly output: z.ZodObject<{
accounts: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"imap-smtp">;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">>;
}, z.core.$strip>;
};
export declare const createMailAccountRoute: {
readonly method: "POST";
readonly path: "/api/gov/mail/accounts";
readonly access: "ops";
readonly body: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodPrefault<z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>>;
secret: z.ZodOptional<z.ZodObject<{
password: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"imap-smtp">;
address: z.ZodEmail;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
secret: z.ZodOptional<z.ZodObject<{
corpSecret: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
readonly output: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"imap-smtp">;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
};
/** 未存的配置探测 —— 表单上正填的那份,一个字节都不落库 */
export declare const probeMailDraftRoute: {
readonly method: "POST";
readonly path: "/api/gov/mail/accounts/probe";
readonly access: "ops";
/** 改已存账号时带上 ?accountId=:表单没重填密码就借库里那份密文 */
readonly query: z.ZodObject<{
accountId: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
readonly body: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodPrefault<z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>>;
secret: z.ZodOptional<z.ZodObject<{
password: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"imap-smtp">;
address: z.ZodEmail;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
secret: z.ZodOptional<z.ZodObject<{
corpSecret: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
readonly output: z.ZodObject<{
kind: z.ZodEnum<{
"imap-smtp": "imap-smtp";
wecom: "wecom";
}>;
address: z.ZodOptional<z.ZodString>;
receive: z.ZodObject<{
ok: z.ZodBoolean;
error: z.ZodOptional<z.ZodString>;
latencyMs: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>;
send: z.ZodObject<{
checked: z.ZodBoolean;
ok: z.ZodOptional<z.ZodBoolean>;
error: z.ZodOptional<z.ZodString>;
note: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
}, z.core.$strip>;
};
export declare const getMailAccountRoute: {
readonly method: "GET";
readonly path: "/api/gov/mail/accounts/:accountId";
readonly access: "ops";
readonly params: z.ZodObject<{
accountId: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"imap-smtp">;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
};
/** secret 缺省 = 不改密文;kind / settings 变了游标清空重锚 */
export declare const updateMailAccountRoute: {
readonly method: "PUT";
readonly path: "/api/gov/mail/accounts/:accountId";
readonly access: "ops";
readonly params: z.ZodObject<{
accountId: z.ZodString;
}, z.core.$strip>;
readonly body: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodPrefault<z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>>;
secret: z.ZodOptional<z.ZodObject<{
password: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"imap-smtp">;
address: z.ZodEmail;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
secret: z.ZodOptional<z.ZodObject<{
corpSecret: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
readonly output: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"imap-smtp">;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
};
export declare const deleteMailAccountRoute: {
readonly method: "DELETE";
readonly path: "/api/gov/mail/accounts/:accountId";
readonly access: "ops";
readonly params: z.ZodObject<{
accountId: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodObject<{
ok: z.ZodLiteral<true>;
}, z.core.$strip>;
};
/** 已存账号探测(用库里凭证);凭证空 412 */
export declare const probeMailAccountRoute: {
readonly method: "POST";
readonly path: "/api/gov/mail/accounts/:accountId/probe";
readonly access: "ops";
readonly params: z.ZodObject<{
accountId: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodObject<{
kind: z.ZodEnum<{
"imap-smtp": "imap-smtp";
wecom: "wecom";
}>;
address: z.ZodOptional<z.ZodString>;
receive: z.ZodObject<{
ok: z.ZodBoolean;
error: z.ZodOptional<z.ZodString>;
latencyMs: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>;
send: z.ZodObject<{
checked: z.ZodBoolean;
ok: z.ZodOptional<z.ZodBoolean>;
error: z.ZodOptional<z.ZodString>;
note: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
}, z.core.$strip>;
};
/** 立即拉一轮;另一进程占着租约 409;停用 412 */
export declare const pollMailAccountRoute: {
readonly method: "POST";
readonly path: "/api/gov/mail/accounts/:accountId/poll";
readonly access: "ops";
readonly params: z.ZodObject<{
accountId: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodObject<{
accountId: z.ZodString;
pulled: z.ZodNumber;
stored: z.ZodNumber;
dispatched: z.ZodNumber;
more: z.ZodBoolean;
error: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
};
export declare const sendTestMailRoute: {
readonly method: "POST";
readonly path: "/api/gov/mail/accounts/:accountId/send-test";
readonly access: "ops";
readonly params: z.ZodObject<{
accountId: z.ZodString;
}, z.core.$strip>;
readonly body: z.ZodObject<{
to: z.ZodEmail;
subject: z.ZodString;
text: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodObject<{
outboundId: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
sent: "sent";
}>;
error: z.ZodOptional<z.ZodString>;
providerRef: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
};
export declare const listMailInboundRoute: {
readonly method: "GET";
readonly path: "/api/gov/mail/inbound";
readonly access: "ops";
readonly query: z.ZodObject<{
accountId: z.ZodOptional<z.ZodString>;
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
}, z.core.$strip>;
readonly output: z.ZodObject<{
mails: z.ZodArray<z.ZodObject<{
id: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
from: z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>;
to: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
cc: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
subject: z.ZodString;
sentAt: z.ZodNullable<z.ZodISODateTime>;
receivedAt: z.ZodISODateTime;
textPreview: z.ZodString;
hasHtml: z.ZodBoolean;
attachments: z.ZodArray<z.ZodObject<{
index: z.ZodNumber;
name: z.ZodString;
contentType: z.ZodString;
size: z.ZodNumber;
ref: z.ZodNullable<z.ZodString>;
note: z.ZodString;
}, z.core.$strip>>;
deliveries: z.ZodArray<z.ZodObject<{
subscriber: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
done: "done";
dispatching: "dispatching";
skipped: "skipped";
}>;
error: z.ZodNullable<z.ZodString>;
startedAt: z.ZodISODateTime;
finishedAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>>;
}, z.core.$strip>>;
}, z.core.$strip>;
};
export declare const getMailInboundRoute: {
readonly method: "GET";
readonly path: "/api/gov/mail/inbound/:mailId";
readonly access: "ops";
readonly params: z.ZodObject<{
mailId: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodObject<{
id: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
from: z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>;
to: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
cc: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
subject: z.ZodString;
sentAt: z.ZodNullable<z.ZodISODateTime>;
receivedAt: z.ZodISODateTime;
textPreview: z.ZodString;
hasHtml: z.ZodBoolean;
attachments: z.ZodArray<z.ZodObject<{
index: z.ZodNumber;
name: z.ZodString;
contentType: z.ZodString;
size: z.ZodNumber;
ref: z.ZodNullable<z.ZodString>;
note: z.ZodString;
}, z.core.$strip>>;
deliveries: z.ZodArray<z.ZodObject<{
subscriber: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
done: "done";
dispatching: "dispatching";
skipped: "skipped";
}>;
error: z.ZodNullable<z.ZodString>;
startedAt: z.ZodISODateTime;
finishedAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>>;
inReplyTo: z.ZodNullable<z.ZodString>;
references: z.ZodArray<z.ZodString>;
text: z.ZodNullable<z.ZodString>;
html: z.ZodNullable<z.ZodString>;
size: z.ZodNumber;
rawAvailable: z.ZodBoolean;
}, z.core.$strip>;
};
/** 附件原件(JSON + base64,与 /api/gov/upload/:id 同形);可带 ?ticket= 下载(ADR-012 §6) */
export declare const mailAttachmentRoute: {
readonly method: "GET";
readonly path: "/api/gov/mail/inbound/:mailId/attachments/:index";
readonly access: "ops";
readonly params: z.ZodObject<{
mailId: z.ZodString;
index: z.ZodCoercedNumber<unknown>;
}, z.core.$strip>;
readonly output: z.ZodObject<{
name: z.ZodString;
contentType: z.ZodString;
dataBase64: z.ZodString;
}, z.core.$strip>;
};
/** 原件 .eml;超限没存 404 */
export declare const mailRawRoute: {
readonly method: "GET";
readonly path: "/api/gov/mail/inbound/:mailId/raw";
readonly access: "ops";
readonly params: z.ZodObject<{
mailId: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodObject<{
name: z.ZodString;
contentType: z.ZodLiteral<"message/rfc822">;
dataBase64: z.ZodString;
}, z.core.$strip>;
};
/** 人按的钮:重新投给订阅方(缺省全部);从此是至少一次 */
export declare const redeliverMailInboundRoute: {
readonly method: "POST";
readonly path: "/api/gov/mail/inbound/:mailId/redeliver";
readonly access: "ops";
readonly params: z.ZodObject<{
mailId: z.ZodString;
}, z.core.$strip>;
readonly body: z.ZodDefault<z.ZodObject<{
subscriber: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>;
readonly output: z.ZodObject<{
deliveries: z.ZodArray<z.ZodObject<{
subscriber: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
done: "done";
dispatching: "dispatching";
skipped: "skipped";
}>;
error: z.ZodNullable<z.ZodString>;
startedAt: z.ZodISODateTime;
finishedAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>>;
}, z.core.$strip>;
};
export declare const listMailOutboundRoute: {
readonly method: "GET";
readonly path: "/api/gov/mail/outbound";
readonly access: "ops";
readonly query: z.ZodObject<{
accountId: z.ZodOptional<z.ZodString>;
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
}, z.core.$strip>;
readonly output: z.ZodObject<{
mails: z.ZodArray<z.ZodObject<{
id: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
to: z.ZodArray<z.ZodString>;
cc: z.ZodArray<z.ZodString>;
bcc: z.ZodArray<z.ZodString>;
subject: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
sending: "sending";
sent: "sent";
}>;
error: z.ZodNullable<z.ZodString>;
providerRef: z.ZodNullable<z.ZodString>;
requestedBy: z.ZodRecord<z.ZodString, z.ZodUnknown>;
createdAt: z.ZodISODateTime;
sentAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>>;
}, z.core.$strip>;
};
/** 全部 15 条 —— 文档总表与覆盖测试按它数 */
export declare const MAIL_ROUTES: readonly RouteContract[];
+1
View File
@@ -13,4 +13,5 @@ export * from './ledger.ts';
export * from './process-package.ts';
export * from './manifest.ts';
export * from './org.ts';
export * from './mail.ts';
export * from './api/index.ts';
+364
View File
@@ -0,0 +1,364 @@
/**
* 邮件收发的契约(设计稿 docs/superpowers/specs/2026-08-25-mail-design.md §7.1;ADR-013)。
*
* 只有 zod schema 与推导类型。账号视图**永不含密文**(ADR-009 §4):只回 `secretBound`。
* 入参按 `kind` 分两形 —— 标准协议(IMAP/SMTP)与企业微信邮件 API(应用邮箱),
* 两条路的共同点是「轮询拉 + MIME 解析」,腾讯没有带内容的推送。
*/
import { z } from 'zod';
export declare const MAIL_ACCOUNT_KINDS: readonly ["imap-smtp", "wecom"];
export declare const mailAccountKindSchema: z.ZodEnum<{
"imap-smtp": "imap-smtp";
wecom: "wecom";
}>;
export type MailAccountKind = z.infer<typeof mailAccountKindSchema>;
export declare const MAIL_ACCOUNT_KIND_LABEL: Readonly<Record<MailAccountKind, string>>;
/** 标准协议:默认值就是腾讯企业邮箱官方帮助给的服务器与 SSL 端口 */
export declare const imapSmtpSettingsSchema: z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>;
export type ImapSmtpSettings = z.infer<typeof imapSmtpSettingsSchema>;
/** 企业微信邮件 API:corpid + 自建应用 secret(密封,不在 settings 里) */
export declare const wecomSettingsSchema: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
export type WecomSettings = z.infer<typeof wecomSettingsSchema>;
export declare const mailAccountInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodPrefault<z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>>;
secret: z.ZodOptional<z.ZodObject<{
password: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"imap-smtp">;
address: z.ZodEmail;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
secret: z.ZodOptional<z.ZodObject<{
corpSecret: z.ZodString;
}, z.core.$strip>>;
name: z.ZodString;
displayName: z.ZodDefault<z.ZodString>;
enabled: z.ZodDefault<z.ZodBoolean>;
isDefault: z.ZodDefault<z.ZodBoolean>;
pollIntervalSec: z.ZodDefault<z.ZodNumber>;
channelId: z.ZodDefault<z.ZodNullable<z.core.$ZodBranded<z.ZodString, "ChannelId", "out">>>;
backfillFrom: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
export type MailAccountInput = z.infer<typeof mailAccountInputSchema>;
export declare const mailAccountStatusSchema: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
export declare const mailAccountViewSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
settings: z.ZodObject<{
imapHost: z.ZodDefault<z.ZodString>;
imapPort: z.ZodDefault<z.ZodNumber>;
imapTls: z.ZodDefault<z.ZodBoolean>;
smtpHost: z.ZodDefault<z.ZodString>;
smtpPort: z.ZodDefault<z.ZodNumber>;
smtpTls: z.ZodDefault<z.ZodBoolean>;
login: z.ZodOptional<z.ZodString>;
folder: z.ZodDefault<z.ZodString>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"imap-smtp">;
}, z.core.$strip>, z.ZodObject<{
settings: z.ZodObject<{
corpId: z.ZodString;
apiBase: z.ZodDefault<z.ZodURL>;
}, z.core.$strip>;
id: z.ZodString;
name: z.ZodString;
address: z.ZodString;
displayName: z.ZodString;
enabled: z.ZodBoolean;
isDefault: z.ZodBoolean;
pollIntervalSec: z.ZodNumber;
channelId: z.ZodNullable<z.ZodString>;
backfillFrom: z.ZodNullable<z.ZodISODateTime>;
secretBound: z.ZodBoolean;
status: z.ZodObject<{
lastPolledAt: z.ZodNullable<z.ZodISODateTime>;
lastPollError: z.ZodNullable<z.ZodString>;
lastInboundAt: z.ZodNullable<z.ZodISODateTime>;
cursor: z.ZodNullable<z.ZodString>;
polling: z.ZodBoolean;
}, z.core.$strip>;
createdAt: z.ZodISODateTime;
updatedAt: z.ZodISODateTime;
kind: z.ZodLiteral<"wecom">;
}, z.core.$strip>], "kind">;
export type MailAccountView = z.infer<typeof mailAccountViewSchema>;
/** 探测报告:收 / 发各说各的;wecom 的发信一栏 `checked:false` —— 拿到 token 不等于能发 */
export declare const mailProbeReportSchema: z.ZodObject<{
kind: z.ZodEnum<{
"imap-smtp": "imap-smtp";
wecom: "wecom";
}>;
address: z.ZodOptional<z.ZodString>;
receive: z.ZodObject<{
ok: z.ZodBoolean;
error: z.ZodOptional<z.ZodString>;
latencyMs: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>;
send: z.ZodObject<{
checked: z.ZodBoolean;
ok: z.ZodOptional<z.ZodBoolean>;
error: z.ZodOptional<z.ZodString>;
note: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
}, z.core.$strip>;
export type MailProbeReport = z.infer<typeof mailProbeReportSchema>;
export declare const MAIL_DELIVERY_STATES: readonly ["dispatching", "done", "failed", "skipped"];
export declare const mailDeliveryStateSchema: z.ZodEnum<{
failed: "failed";
done: "done";
dispatching: "dispatching";
skipped: "skipped";
}>;
export type MailDeliveryState = z.infer<typeof mailDeliveryStateSchema>;
export declare const mailDeliveryViewSchema: z.ZodObject<{
subscriber: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
done: "done";
dispatching: "dispatching";
skipped: "skipped";
}>;
error: z.ZodNullable<z.ZodString>;
startedAt: z.ZodISODateTime;
finishedAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>;
export type MailDeliveryView = z.infer<typeof mailDeliveryViewSchema>;
export declare const mailAddressSchema: z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>;
export type MailAddress = z.infer<typeof mailAddressSchema>;
export declare const mailInboundAttachmentSchema: z.ZodObject<{
index: z.ZodNumber;
name: z.ZodString;
contentType: z.ZodString;
size: z.ZodNumber;
ref: z.ZodNullable<z.ZodString>;
note: z.ZodString;
}, z.core.$strip>;
export type MailInboundAttachment = z.infer<typeof mailInboundAttachmentSchema>;
export declare const mailInboundSummarySchema: z.ZodObject<{
id: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
from: z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>;
to: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
cc: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
subject: z.ZodString;
sentAt: z.ZodNullable<z.ZodISODateTime>;
receivedAt: z.ZodISODateTime;
textPreview: z.ZodString;
hasHtml: z.ZodBoolean;
attachments: z.ZodArray<z.ZodObject<{
index: z.ZodNumber;
name: z.ZodString;
contentType: z.ZodString;
size: z.ZodNumber;
ref: z.ZodNullable<z.ZodString>;
note: z.ZodString;
}, z.core.$strip>>;
deliveries: z.ZodArray<z.ZodObject<{
subscriber: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
done: "done";
dispatching: "dispatching";
skipped: "skipped";
}>;
error: z.ZodNullable<z.ZodString>;
startedAt: z.ZodISODateTime;
finishedAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>>;
}, z.core.$strip>;
export type MailInboundSummary = z.infer<typeof mailInboundSummarySchema>;
export declare const mailInboundDetailSchema: z.ZodObject<{
id: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
from: z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>;
to: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
cc: z.ZodArray<z.ZodObject<{
address: z.ZodString;
name: z.ZodString;
}, z.core.$strip>>;
subject: z.ZodString;
sentAt: z.ZodNullable<z.ZodISODateTime>;
receivedAt: z.ZodISODateTime;
textPreview: z.ZodString;
hasHtml: z.ZodBoolean;
attachments: z.ZodArray<z.ZodObject<{
index: z.ZodNumber;
name: z.ZodString;
contentType: z.ZodString;
size: z.ZodNumber;
ref: z.ZodNullable<z.ZodString>;
note: z.ZodString;
}, z.core.$strip>>;
deliveries: z.ZodArray<z.ZodObject<{
subscriber: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
done: "done";
dispatching: "dispatching";
skipped: "skipped";
}>;
error: z.ZodNullable<z.ZodString>;
startedAt: z.ZodISODateTime;
finishedAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>>;
inReplyTo: z.ZodNullable<z.ZodString>;
references: z.ZodArray<z.ZodString>;
text: z.ZodNullable<z.ZodString>;
html: z.ZodNullable<z.ZodString>;
size: z.ZodNumber;
rawAvailable: z.ZodBoolean;
}, z.core.$strip>;
export type MailInboundDetail = z.infer<typeof mailInboundDetailSchema>;
export declare const mailSendInputSchema: z.ZodObject<{
accountId: z.ZodOptional<z.ZodString>;
from: z.ZodOptional<z.ZodString>;
to: z.ZodArray<z.ZodEmail>;
cc: z.ZodDefault<z.ZodArray<z.ZodEmail>>;
bcc: z.ZodDefault<z.ZodArray<z.ZodEmail>>;
subject: z.ZodString;
text: z.ZodOptional<z.ZodString>;
html: z.ZodOptional<z.ZodString>;
inReplyTo: z.ZodOptional<z.ZodString>;
references: z.ZodDefault<z.ZodArray<z.ZodString>>;
attachments: z.ZodDefault<z.ZodArray<z.ZodObject<{
name: z.ZodString;
contentType: z.ZodDefault<z.ZodString>;
dataBase64: z.ZodOptional<z.ZodString>;
uploadRef: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>>;
idempotencyKey: z.ZodOptional<z.ZodString>;
requestedBy: z.ZodObject<{
kind: z.ZodEnum<{
channel: "channel";
tool: "tool";
ops: "ops";
}>;
taskId: z.ZodOptional<z.ZodString>;
staffId: z.ZodOptional<z.ZodString>;
tool: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
}, z.core.$strip>;
export type MailSendInput = z.infer<typeof mailSendInputSchema>;
export declare const MAIL_OUTBOUND_STATES: readonly ["sending", "sent", "failed"];
export declare const mailOutboundStateSchema: z.ZodEnum<{
failed: "failed";
sending: "sending";
sent: "sent";
}>;
export type MailOutboundState = z.infer<typeof mailOutboundStateSchema>;
export declare const mailSendResultSchema: z.ZodObject<{
outboundId: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
sent: "sent";
}>;
error: z.ZodOptional<z.ZodString>;
providerRef: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type MailSendResult = z.infer<typeof mailSendResultSchema>;
export declare const mailOutboundViewSchema: z.ZodObject<{
id: z.ZodString;
accountId: z.ZodString;
messageId: z.ZodString;
to: z.ZodArray<z.ZodString>;
cc: z.ZodArray<z.ZodString>;
bcc: z.ZodArray<z.ZodString>;
subject: z.ZodString;
state: z.ZodEnum<{
failed: "failed";
sending: "sending";
sent: "sent";
}>;
error: z.ZodNullable<z.ZodString>;
providerRef: z.ZodNullable<z.ZodString>;
requestedBy: z.ZodRecord<z.ZodString, z.ZodUnknown>;
createdAt: z.ZodISODateTime;
sentAt: z.ZodNullable<z.ZodISODateTime>;
}, z.core.$strip>;
export type MailOutboundView = z.infer<typeof mailOutboundViewSchema>;
export declare const mailPollOutcomeSchema: z.ZodObject<{
accountId: z.ZodString;
pulled: z.ZodNumber;
stored: z.ZodNumber;
dispatched: z.ZodNumber;
more: z.ZodBoolean;
error: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type MailPollOutcome = z.infer<typeof mailPollOutcomeSchema>;