@vicfei/dsh-super-agent 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/README.zh-CN.md +10 -1
- package/dist/cli.js +73 -7
- package/dist/cli.js.map +1 -1
- package/dist/domain.d.ts +6 -2
- package/dist/domain.js.map +1 -1
- package/dist/engine.d.ts +11 -5
- package/dist/engine.js +28 -13
- package/dist/engine.js.map +1 -1
- package/dist/feishu.d.ts +66 -0
- package/dist/feishu.js +112 -0
- package/dist/feishu.js.map +1 -0
- package/dist/github/source.d.ts +4 -2
- package/dist/github/source.js +1 -0
- package/dist/github/source.js.map +1 -1
- package/dist/github/state.d.ts +2 -4
- package/dist/github/state.js.map +1 -1
- package/dist/merge.d.ts +2 -1
- package/dist/merge.js +6 -4
- package/dist/merge.js.map +1 -1
- package/dist/slack.d.ts +57 -0
- package/dist/slack.js +106 -0
- package/dist/slack.js.map +1 -0
- package/dist/source.d.ts +25 -0
- package/dist/source.js +11 -0
- package/dist/source.js.map +1 -0
- package/dist/telegram.d.ts +84 -0
- package/dist/telegram.js +134 -0
- package/dist/telegram.js.map +1 -0
- package/dist/usage.d.ts +1 -1
- package/dist/usage.js +4 -1
- package/dist/usage.js.map +1 -1
- package/dist/watch.d.ts +5 -2
- package/dist/watch.js +11 -7
- package/dist/watch.js.map +1 -1
- package/dist/wechat.d.ts +142 -0
- package/dist/wechat.js +262 -0
- package/dist/wechat.js.map +1 -0
- package/dist/work.d.ts +14 -2
- package/dist/work.js +31 -9
- package/dist/work.js.map +1 -1
- package/examples/policy.json +12 -0
- package/package.json +4 -1
package/dist/feishu.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Feishu channel (v0.8 M8-2, issue #45) — a self-built enterprise app over the official SDK's
|
|
2
|
+
// WebSocket long connection (no public callback URL needed). The push-based event stream is
|
|
3
|
+
// bridged onto the pull-based Source contract through an internal buffer; dedup is the engine's
|
|
4
|
+
// trigger-id exactly-once (`feishu:msg:<message_id>`). Replies go out via im/v1 messages.
|
|
5
|
+
// Pure mapping is separated from transport so it is unit-testable without an app.
|
|
6
|
+
const BUFFER_CAP = 1000;
|
|
7
|
+
export function feishuTriggerFromEvent(e, botOpenId) {
|
|
8
|
+
const msg = e.message;
|
|
9
|
+
if (!msg?.message_id || !msg.chat_id)
|
|
10
|
+
return null;
|
|
11
|
+
const text = extractText(msg.content, msg.message_type);
|
|
12
|
+
const addressing = msg.chat_type !== "group"
|
|
13
|
+
? true // a DM is inherently addressed
|
|
14
|
+
: botOpenId
|
|
15
|
+
? (msg.mentions ?? []).some((m) => m.id?.open_id === botOpenId)
|
|
16
|
+
: false;
|
|
17
|
+
return {
|
|
18
|
+
id: `feishu:msg:${msg.message_id}`,
|
|
19
|
+
source: "feishu",
|
|
20
|
+
scope: msg.chat_type === "group" ? "im_group" : "im_dm",
|
|
21
|
+
threadId: `feishu:chat/${msg.chat_id}`,
|
|
22
|
+
author: { login: e.sender?.sender_id?.open_id ?? "unknown", isBot: e.sender?.sender_type === "app" },
|
|
23
|
+
addressing,
|
|
24
|
+
body: text,
|
|
25
|
+
ts: msg.create_time ? new Date(Number(msg.create_time)).toISOString() : new Date().toISOString(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function extractText(content, type) {
|
|
29
|
+
if (type !== "text" || !content)
|
|
30
|
+
return type ? `[${type}]` : "";
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(content).text ?? "";
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class FeishuSource {
|
|
39
|
+
opts;
|
|
40
|
+
subscribe;
|
|
41
|
+
kind = "feishu";
|
|
42
|
+
buffer = [];
|
|
43
|
+
starting = null;
|
|
44
|
+
constructor(opts,
|
|
45
|
+
/** Test seam: replaces the SDK's WebSocket subscription. */
|
|
46
|
+
subscribe) {
|
|
47
|
+
this.opts = opts;
|
|
48
|
+
this.subscribe = subscribe;
|
|
49
|
+
}
|
|
50
|
+
/** Connect the long connection (idempotent, concurrency-safe). */
|
|
51
|
+
start() {
|
|
52
|
+
this.starting ??= this.connect();
|
|
53
|
+
return this.starting;
|
|
54
|
+
}
|
|
55
|
+
async connect() {
|
|
56
|
+
const onEvent = (e) => {
|
|
57
|
+
const t = feishuTriggerFromEvent(e, this.opts.botOpenId);
|
|
58
|
+
if (!t)
|
|
59
|
+
return;
|
|
60
|
+
if (this.buffer.length >= BUFFER_CAP)
|
|
61
|
+
this.buffer.shift(); // drop oldest, never grow unbounded
|
|
62
|
+
this.buffer.push(t);
|
|
63
|
+
};
|
|
64
|
+
if (this.subscribe) {
|
|
65
|
+
await this.subscribe(onEvent);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const lark = await import("@larksuiteoapi/node-sdk");
|
|
69
|
+
const ws = new lark.WSClient({ appId: this.opts.appId, appSecret: this.opts.appSecret });
|
|
70
|
+
const dispatcher = new lark.EventDispatcher({}).register({
|
|
71
|
+
"im.message.receive_v1": async (data) => onEvent(data),
|
|
72
|
+
});
|
|
73
|
+
await ws.start({ eventDispatcher: dispatcher });
|
|
74
|
+
}
|
|
75
|
+
async collect(from, opts = {}) {
|
|
76
|
+
await this.start();
|
|
77
|
+
const triggers = this.buffer.splice(0).filter((t) => !opts.exclude?.has(t.id));
|
|
78
|
+
return { triggers, next: from }; // push source: replay safety is the engine's idempotence
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export class FeishuReplyChannel {
|
|
82
|
+
send;
|
|
83
|
+
constructor(send) {
|
|
84
|
+
this.send = send;
|
|
85
|
+
}
|
|
86
|
+
matches(threadId) {
|
|
87
|
+
return threadId.startsWith("feishu:");
|
|
88
|
+
}
|
|
89
|
+
async deliver(threadId, body) {
|
|
90
|
+
const chatId = threadId.slice("feishu:chat/".length);
|
|
91
|
+
if (!chatId || chatId.includes("/"))
|
|
92
|
+
throw new Error(`cannot resolve chat id from thread ${threadId}`);
|
|
93
|
+
const { messageId } = await this.send(chatId, body);
|
|
94
|
+
return { externalId: `msg:${messageId}` };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Build the real sender (tenant token managed by the SDK). */
|
|
98
|
+
export async function createFeishuSender(appId, appSecret) {
|
|
99
|
+
const lark = await import("@larksuiteoapi/node-sdk");
|
|
100
|
+
const client = new lark.Client({ appId, appSecret });
|
|
101
|
+
return async (chatId, text) => {
|
|
102
|
+
const res = await client.im.message.create({
|
|
103
|
+
params: { receive_id_type: "chat_id" },
|
|
104
|
+
data: { receive_id: chatId, content: JSON.stringify({ text }), msg_type: "text" },
|
|
105
|
+
});
|
|
106
|
+
const messageId = res.data?.message_id;
|
|
107
|
+
if (!messageId)
|
|
108
|
+
throw new Error("feishu send returned no message_id");
|
|
109
|
+
return { messageId };
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=feishu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feishu.js","sourceRoot":"","sources":["../src/feishu.ts"],"names":[],"mappings":"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"}
|
package/dist/github/source.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Trigger } from "../domain.js";
|
|
2
|
+
import type { Source, SourceState } from "../source.js";
|
|
2
3
|
import { GitHubClient } from "./client.js";
|
|
3
|
-
import type {
|
|
4
|
+
import type { StateStore } from "./state.js";
|
|
4
5
|
export interface GitHubSourceOptions {
|
|
5
6
|
/** owner/repo */
|
|
6
7
|
repo: string;
|
|
@@ -12,8 +13,9 @@ export interface GitHubSourceOptions {
|
|
|
12
13
|
}
|
|
13
14
|
/** True for App accounts and `[bot]` logins — bots cap at inferred strength in the gate. */
|
|
14
15
|
export declare function isBotActor(login: string, type: string): boolean;
|
|
15
|
-
export declare class GitHubSource {
|
|
16
|
+
export declare class GitHubSource implements Source {
|
|
16
17
|
private readonly opts;
|
|
18
|
+
readonly kind: "github";
|
|
17
19
|
constructor(opts: GitHubSourceOptions);
|
|
18
20
|
/**
|
|
19
21
|
* Pure collection: compute triggers newer than `from` and the next state, persisting nothing.
|
package/dist/github/source.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/github/source.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,8FAA8F;AAC9F,qGAAqG;
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/github/source.ts"],"names":[],"mappings":"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"}
|
package/dist/github/state.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
+
import type { SourceState } from "../source.js";
|
|
1
2
|
export type Feed = "issue" | "issue_comment" | "pr" | "review_comment";
|
|
2
|
-
export
|
|
3
|
-
/** Max item id already taken per feed; 0 = never polled. Item ids are increasing ints. */
|
|
4
|
-
positions: Record<Feed, number>;
|
|
5
|
-
}
|
|
3
|
+
export type { SourceState } from "../source.js";
|
|
6
4
|
export interface StateStore {
|
|
7
5
|
load(): SourceState;
|
|
8
6
|
save(state: SourceState): void;
|
package/dist/github/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/github/state.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;AACjG,mGAAmG;AACnG,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/github/state.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;AACjG,mGAAmG;AACnG,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAY1C,MAAM,OAAO,eAAe;IACT,IAAI,CAAS;IAE9B,YAAY,IAAY;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAI;QACF,MAAM,KAAK,GAA2B,EAAE,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;QAC/F,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAgB,CAAC;YACvE,OAAO,EAAE,SAAS,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,IAAI,CAAC,KAAkB;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACpF,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB;IAChD,CAAC;CACF"}
|
package/dist/merge.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { MergePolicy, Policy } from "./domain.js";
|
|
2
2
|
import type { Engine } from "./engine.js";
|
|
3
3
|
import type { GitHubClient, GhPullDetail, GhReview } from "./github/client.js";
|
|
4
|
+
import type { ReplyChannel } from "./source.js";
|
|
4
5
|
export interface MergeCheck {
|
|
5
6
|
ok: boolean;
|
|
6
7
|
reasons: string[];
|
|
@@ -31,4 +32,4 @@ export interface MergeSweepLine {
|
|
|
31
32
|
* merge only zero-reason PRs, post a clipped merge reply through the outbox, prune the branch.
|
|
32
33
|
* Per-delivery errors degrade to "waiting" — the next cycle retries (fail-closed).
|
|
33
34
|
*/
|
|
34
|
-
export declare function runMergeSweep(engine: Engine, client: GitHubClient, repo: string, policy: Policy, log?: (l: string) => void): Promise<MergeSweepLine[]>;
|
|
35
|
+
export declare function runMergeSweep(engine: Engine, client: GitHubClient, repo: string, policy: Policy, channels?: ReplyChannel[], log?: (l: string) => void): Promise<MergeSweepLine[]>;
|
package/dist/merge.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// exact SHA that was approved — if the head moved, GitHub rejects and the next cycle re-evaluates.
|
|
5
5
|
import { clipReply } from "./clip.js";
|
|
6
6
|
import { mergePolicyOf } from "./domain.js";
|
|
7
|
-
import { drainReplyEffects } from "./work.js";
|
|
7
|
+
import { GitHubReplyChannel, drainReplyEffects } from "./work.js";
|
|
8
8
|
const TICKET_BRANCH = /^dsh\/ticket-\d+$/;
|
|
9
9
|
function isBotReview(r) {
|
|
10
10
|
return !r.user || r.user.type === "Bot" || r.user.login.endsWith("[bot]");
|
|
@@ -82,7 +82,7 @@ export function evaluateMerge(pr, reviews, policy, allowedList, ci) {
|
|
|
82
82
|
* merge only zero-reason PRs, post a clipped merge reply through the outbox, prune the branch.
|
|
83
83
|
* Per-delivery errors degrade to "waiting" — the next cycle retries (fail-closed).
|
|
84
84
|
*/
|
|
85
|
-
export async function runMergeSweep(engine, client, repo, policy, log = () => { }) {
|
|
85
|
+
export async function runMergeSweep(engine, client, repo, policy, channels = [], log = () => { }) {
|
|
86
86
|
const merge = mergePolicyOf(policy);
|
|
87
87
|
if (!merge.enabled)
|
|
88
88
|
return [];
|
|
@@ -133,8 +133,10 @@ export async function runMergeSweep(engine, client, repo, policy, log = () => {
|
|
|
133
133
|
log(`#${d.prNumber} sweep error: ${e instanceof Error ? e.message : e}`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
-
if (out.length > 0)
|
|
137
|
-
|
|
136
|
+
if (out.length > 0) {
|
|
137
|
+
// Merge receipts go to the ticket's thread — which may be an IM thread (v0.8 routing).
|
|
138
|
+
await drainReplyEffects(engine, channels.length > 0 ? channels : [new GitHubReplyChannel(client, repo)]);
|
|
139
|
+
}
|
|
138
140
|
return out;
|
|
139
141
|
}
|
|
140
142
|
//# sourceMappingURL=merge.js.map
|
package/dist/merge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,6FAA6F;AAC7F,8FAA8F;AAC9F,mGAAmG;AAEnG,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
|
package/dist/slack.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Trigger } from "./domain.js";
|
|
2
|
+
import type { ReplyChannel, Source, SourceState } from "./source.js";
|
|
3
|
+
/** The Events API `message` body (fields we consume). */
|
|
4
|
+
export interface SlackMessageEvent {
|
|
5
|
+
type?: string;
|
|
6
|
+
subtype?: string;
|
|
7
|
+
channel?: string;
|
|
8
|
+
channel_type?: "channel" | "group" | "im" | "mpim";
|
|
9
|
+
user?: string;
|
|
10
|
+
bot_id?: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
ts?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SlackOptions {
|
|
15
|
+
/** xapp-… token (Socket Mode). */
|
|
16
|
+
appToken: string;
|
|
17
|
+
/** xoxb-… token (Web API). */
|
|
18
|
+
botToken: string;
|
|
19
|
+
/** The bot's own user id — defaults to auth.test discovery. */
|
|
20
|
+
botUserId?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function slackTriggerFromEvent(e: SlackMessageEvent, botUserId?: string): Trigger | null;
|
|
23
|
+
export declare class SlackSource implements Source {
|
|
24
|
+
private readonly opts;
|
|
25
|
+
/** Test seam: replaces the Socket Mode subscription. */
|
|
26
|
+
private readonly subscribe?;
|
|
27
|
+
readonly kind: "slack";
|
|
28
|
+
private readonly buffer;
|
|
29
|
+
private starting;
|
|
30
|
+
constructor(opts: SlackOptions,
|
|
31
|
+
/** Test seam: replaces the Socket Mode subscription. */
|
|
32
|
+
subscribe?: ((onEvent: (e: SlackMessageEvent) => void) => Promise<void>) | undefined);
|
|
33
|
+
start(): Promise<void>;
|
|
34
|
+
private connect;
|
|
35
|
+
collect(from: SourceState, opts?: {
|
|
36
|
+
exclude?: ReadonlySet<string>;
|
|
37
|
+
}): Promise<{
|
|
38
|
+
triggers: Trigger[];
|
|
39
|
+
next: SourceState;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
export type SlackSender = (channel: string, text: string) => Promise<{
|
|
43
|
+
ts: string;
|
|
44
|
+
}>;
|
|
45
|
+
export declare class SlackReplyChannel implements ReplyChannel {
|
|
46
|
+
private readonly send;
|
|
47
|
+
constructor(send: SlackSender);
|
|
48
|
+
matches(threadId: string): boolean;
|
|
49
|
+
deliver(threadId: string, body: string): Promise<{
|
|
50
|
+
externalId: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
/** Build the real sender; also resolves the bot's own user id (auth.test) for @mention detection. */
|
|
54
|
+
export declare function createSlackSender(botToken: string): Promise<{
|
|
55
|
+
send: SlackSender;
|
|
56
|
+
botUserId?: string;
|
|
57
|
+
}>;
|
package/dist/slack.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Slack channel (v0.8 M8-3, issue #45) — Socket Mode (no public URL) bridged onto the
|
|
2
|
+
// pull-based Source contract via a capped buffer, same pattern as the Feishu channel; replies
|
|
3
|
+
// via chat.postMessage. The bot's own user id is fetched once via auth.test, so group @mention
|
|
4
|
+
// detection (<@U…> in text) is precise without manual configuration.
|
|
5
|
+
const BUFFER_CAP = 1000;
|
|
6
|
+
export function slackTriggerFromEvent(e, botUserId) {
|
|
7
|
+
if (!e.channel || !e.ts)
|
|
8
|
+
return null;
|
|
9
|
+
if (e.subtype && e.subtype !== "thread_broadcast")
|
|
10
|
+
return null; // joins/edits/deletes are not work requests
|
|
11
|
+
const isDm = e.channel_type === "im";
|
|
12
|
+
const text = e.text ?? "";
|
|
13
|
+
const addressing = isDm ? true // a DM is inherently addressed
|
|
14
|
+
: botUserId ? text.includes(`<@${botUserId}>`)
|
|
15
|
+
: false; // unknown bot id → fail closed (inferred via listedNotAddressing remains)
|
|
16
|
+
const author = e.user ?? e.bot_id ?? "unknown";
|
|
17
|
+
return {
|
|
18
|
+
id: `slack:msg:${e.channel}/${e.ts}`,
|
|
19
|
+
source: "slack",
|
|
20
|
+
scope: isDm ? "im_dm" : "im_group",
|
|
21
|
+
threadId: `slack:${isDm ? "dm" : "channel"}/${e.channel}`,
|
|
22
|
+
author: { login: author, isBot: e.bot_id !== undefined },
|
|
23
|
+
addressing,
|
|
24
|
+
body: text,
|
|
25
|
+
ts: new Date(Number(e.ts.split(".")[0]) * 1000).toISOString(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export class SlackSource {
|
|
29
|
+
opts;
|
|
30
|
+
subscribe;
|
|
31
|
+
kind = "slack";
|
|
32
|
+
buffer = [];
|
|
33
|
+
starting = null;
|
|
34
|
+
constructor(opts,
|
|
35
|
+
/** Test seam: replaces the Socket Mode subscription. */
|
|
36
|
+
subscribe) {
|
|
37
|
+
this.opts = opts;
|
|
38
|
+
this.subscribe = subscribe;
|
|
39
|
+
}
|
|
40
|
+
start() {
|
|
41
|
+
this.starting ??= this.connect();
|
|
42
|
+
return this.starting;
|
|
43
|
+
}
|
|
44
|
+
async connect() {
|
|
45
|
+
const onEvent = (e) => {
|
|
46
|
+
const t = slackTriggerFromEvent(e, this.opts.botUserId);
|
|
47
|
+
if (!t)
|
|
48
|
+
return;
|
|
49
|
+
if (this.buffer.length >= BUFFER_CAP)
|
|
50
|
+
this.buffer.shift();
|
|
51
|
+
this.buffer.push(t);
|
|
52
|
+
};
|
|
53
|
+
if (this.subscribe) {
|
|
54
|
+
await this.subscribe(onEvent);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const { SocketModeClient } = await import("@slack/socket-mode");
|
|
58
|
+
const socket = new SocketModeClient({ appToken: this.opts.appToken });
|
|
59
|
+
socket.on("message", ({ body }) => {
|
|
60
|
+
// Events API envelope: { type: "event_callback", event: {...} }
|
|
61
|
+
const inner = body.event;
|
|
62
|
+
if (inner)
|
|
63
|
+
onEvent(inner);
|
|
64
|
+
});
|
|
65
|
+
await socket.start();
|
|
66
|
+
}
|
|
67
|
+
async collect(from, opts = {}) {
|
|
68
|
+
await this.start();
|
|
69
|
+
const triggers = this.buffer.splice(0).filter((t) => !opts.exclude?.has(t.id));
|
|
70
|
+
return { triggers, next: from }; // push source: replay safety is the engine's idempotence
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export class SlackReplyChannel {
|
|
74
|
+
send;
|
|
75
|
+
constructor(send) {
|
|
76
|
+
this.send = send;
|
|
77
|
+
}
|
|
78
|
+
matches(threadId) {
|
|
79
|
+
return threadId.startsWith("slack:");
|
|
80
|
+
}
|
|
81
|
+
async deliver(threadId, body) {
|
|
82
|
+
const channel = threadId.slice(threadId.indexOf("/") + 1);
|
|
83
|
+
if (!channel || channel.includes("/"))
|
|
84
|
+
throw new Error(`cannot resolve channel from thread ${threadId}`);
|
|
85
|
+
const { ts } = await this.send(channel, body);
|
|
86
|
+
return { externalId: `msg:${channel}/${ts}` };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Build the real sender; also resolves the bot's own user id (auth.test) for @mention detection. */
|
|
90
|
+
export async function createSlackSender(botToken) {
|
|
91
|
+
const { WebClient } = await import("@slack/web-api");
|
|
92
|
+
const web = new WebClient(botToken);
|
|
93
|
+
const auth = await web.auth.test();
|
|
94
|
+
const botUserId = auth.user_id;
|
|
95
|
+
return {
|
|
96
|
+
botUserId,
|
|
97
|
+
send: async (channel, text) => {
|
|
98
|
+
const res = await web.chat.postMessage({ channel, text });
|
|
99
|
+
const ts = res.ts;
|
|
100
|
+
if (!ts)
|
|
101
|
+
throw new Error("slack postMessage returned no ts");
|
|
102
|
+
return { ts };
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=slack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../src/slack.ts"],"names":[],"mappings":"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"}
|
package/dist/source.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Trigger } from "./domain.js";
|
|
2
|
+
export interface SourceState {
|
|
3
|
+
/** Max item id already taken per feed; 0 = never polled. Feed names are source-specific. */
|
|
4
|
+
positions: Record<string, number>;
|
|
5
|
+
}
|
|
6
|
+
export interface Source {
|
|
7
|
+
/** Channel namespace — also the threadId prefix and the positions kv suffix. */
|
|
8
|
+
readonly kind: string;
|
|
9
|
+
/** Pure collection: triggers newer than `from` plus the next state; persists nothing. */
|
|
10
|
+
collect(from: SourceState, opts: {
|
|
11
|
+
exclude?: ReadonlySet<string>;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
triggers: Trigger[];
|
|
14
|
+
next: SourceState;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export interface ReplyChannel {
|
|
18
|
+
matches(threadId: string): boolean;
|
|
19
|
+
/** Deliver a reply body into the thread; returns the source-scoped external message id. */
|
|
20
|
+
deliver(threadId: string, body: string): Promise<{
|
|
21
|
+
externalId: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
/** GitHub's channel: threads are `github:owner/repo/(issues|pulls)/N`, delivery is an issue comment. */
|
|
25
|
+
export declare function threadSource(threadId: string): string;
|
package/dist/source.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Source & ReplyChannel — the multi-source contracts (v0.8, issue #45).
|
|
2
|
+
// A Source turns one channel's events into domain Triggers and owns its read state, namespaced
|
|
3
|
+
// by `kind`; the Engine persists that state in the same transaction as the triggers it produced
|
|
4
|
+
// (exactly-once across restarts — docs/001: "a Source holds its own read position").
|
|
5
|
+
// A ReplyChannel delivers outbox reply effects back where the trigger came from; routing is by
|
|
6
|
+
// threadId prefix ("github:…", "feishu:…", "slack:…", "wechat:…").
|
|
7
|
+
/** GitHub's channel: threads are `github:owner/repo/(issues|pulls)/N`, delivery is an issue comment. */
|
|
8
|
+
export function threadSource(threadId) {
|
|
9
|
+
return threadId.split(":")[0] ?? "";
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,+FAA+F;AAC/F,gGAAgG;AAChG,qFAAqF;AACrF,+FAA+F;AAC/F,mEAAmE;AAsBnE,wGAAwG;AACxG,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Trigger } from "./domain.js";
|
|
2
|
+
import type { ReplyChannel, Source, SourceState } from "./source.js";
|
|
3
|
+
export declare const TELEGRAM_DEFAULT_BASE = "https://api.telegram.org";
|
|
4
|
+
/** The Update.message body (fields we consume). */
|
|
5
|
+
export interface TgMessage {
|
|
6
|
+
message_id?: number;
|
|
7
|
+
date?: number;
|
|
8
|
+
chat?: {
|
|
9
|
+
id?: number;
|
|
10
|
+
type?: "private" | "group" | "supergroup" | "channel" | string;
|
|
11
|
+
};
|
|
12
|
+
from?: {
|
|
13
|
+
id?: number;
|
|
14
|
+
username?: string;
|
|
15
|
+
is_bot?: boolean;
|
|
16
|
+
};
|
|
17
|
+
text?: string;
|
|
18
|
+
caption?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface TgUpdate {
|
|
21
|
+
update_id?: number;
|
|
22
|
+
message?: TgMessage;
|
|
23
|
+
}
|
|
24
|
+
export interface TelegramClientOptions {
|
|
25
|
+
token: string;
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
fetchImpl?: typeof fetch;
|
|
28
|
+
}
|
|
29
|
+
export declare class TelegramClient {
|
|
30
|
+
private readonly fetchImpl;
|
|
31
|
+
private readonly base;
|
|
32
|
+
private readonly token;
|
|
33
|
+
constructor(opts: TelegramClientOptions);
|
|
34
|
+
private call;
|
|
35
|
+
getMe(): Promise<{
|
|
36
|
+
result?: {
|
|
37
|
+
id?: number;
|
|
38
|
+
username?: string;
|
|
39
|
+
};
|
|
40
|
+
}>;
|
|
41
|
+
getUpdates(offset: number, timeoutSec: number, signal?: AbortSignal): Promise<{
|
|
42
|
+
result?: TgUpdate[];
|
|
43
|
+
}>;
|
|
44
|
+
sendMessage(chatId: number, text: string): Promise<{
|
|
45
|
+
result?: {
|
|
46
|
+
message_id?: number;
|
|
47
|
+
};
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
export declare function telegramTriggerFromMessage(m: TgMessage, botUsername?: string): Trigger | null;
|
|
51
|
+
export interface TelegramSourceOptions {
|
|
52
|
+
token: string;
|
|
53
|
+
/** Persisted offset (update_id + 1); kv in production, memory in tests. */
|
|
54
|
+
loadOffset(): number;
|
|
55
|
+
saveOffset(offset: number): void;
|
|
56
|
+
/** Abort the long-poll after this many ms so a watch cycle is never blocked (default 8s). */
|
|
57
|
+
pollTimeoutMs?: number;
|
|
58
|
+
baseUrl?: string;
|
|
59
|
+
fetchImpl?: typeof fetch;
|
|
60
|
+
log?: (l: string) => void;
|
|
61
|
+
}
|
|
62
|
+
export declare class TelegramSource implements Source {
|
|
63
|
+
private readonly opts;
|
|
64
|
+
readonly kind: "telegram";
|
|
65
|
+
private botUsername;
|
|
66
|
+
private me;
|
|
67
|
+
constructor(opts: TelegramSourceOptions);
|
|
68
|
+
private discoverMe;
|
|
69
|
+
collect(from: SourceState, opts?: {
|
|
70
|
+
exclude?: ReadonlySet<string>;
|
|
71
|
+
}): Promise<{
|
|
72
|
+
triggers: Trigger[];
|
|
73
|
+
next: SourceState;
|
|
74
|
+
}>;
|
|
75
|
+
}
|
|
76
|
+
export declare class TelegramReplyChannel implements ReplyChannel {
|
|
77
|
+
private readonly clientOf;
|
|
78
|
+
private readonly chatIdOf;
|
|
79
|
+
constructor(clientOf: () => TelegramClient, chatIdOf: (threadId: string) => number);
|
|
80
|
+
matches(threadId: string): boolean;
|
|
81
|
+
deliver(threadId: string, body: string): Promise<{
|
|
82
|
+
externalId: string;
|
|
83
|
+
}>;
|
|
84
|
+
}
|