@delexec/ops 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.
Files changed (55) hide show
  1. package/README.md +3 -0
  2. package/README.zh-CN.md +6 -0
  3. package/node_modules/@delexec/caller-controller/README.md +3 -0
  4. package/node_modules/@delexec/caller-controller/README.zh-CN.md +6 -0
  5. package/node_modules/@delexec/caller-controller/package.json +53 -0
  6. package/node_modules/@delexec/caller-controller/src/server.js +127 -0
  7. package/node_modules/@delexec/caller-controller-core/README.md +3 -0
  8. package/node_modules/@delexec/caller-controller-core/README.zh-CN.md +6 -0
  9. package/node_modules/@delexec/caller-controller-core/package.json +26 -0
  10. package/node_modules/@delexec/caller-controller-core/src/index.js +1612 -0
  11. package/node_modules/@delexec/caller-skill-adapter/package.json +12 -0
  12. package/node_modules/@delexec/caller-skill-adapter/src/server.js +1042 -0
  13. package/node_modules/@delexec/caller-skill-mcp-adapter/README.md +65 -0
  14. package/node_modules/@delexec/caller-skill-mcp-adapter/package.json +16 -0
  15. package/node_modules/@delexec/caller-skill-mcp-adapter/src/server.js +527 -0
  16. package/node_modules/@delexec/responder-controller/README.md +3 -0
  17. package/node_modules/@delexec/responder-controller/README.zh-CN.md +6 -0
  18. package/node_modules/@delexec/responder-controller/package.json +53 -0
  19. package/node_modules/@delexec/responder-controller/src/server.js +254 -0
  20. package/node_modules/@delexec/responder-runtime-core/README.md +3 -0
  21. package/node_modules/@delexec/responder-runtime-core/README.zh-CN.md +6 -0
  22. package/node_modules/@delexec/responder-runtime-core/package.json +26 -0
  23. package/node_modules/@delexec/responder-runtime-core/src/executors.js +326 -0
  24. package/node_modules/@delexec/responder-runtime-core/src/index.js +1202 -0
  25. package/node_modules/@delexec/runtime-utils/README.md +3 -0
  26. package/node_modules/@delexec/runtime-utils/README.zh-CN.md +6 -0
  27. package/node_modules/@delexec/runtime-utils/package.json +23 -0
  28. package/node_modules/@delexec/runtime-utils/src/index.js +338 -0
  29. package/node_modules/@delexec/sqlite-store/README.md +3 -0
  30. package/node_modules/@delexec/sqlite-store/README.zh-CN.md +6 -0
  31. package/node_modules/@delexec/sqlite-store/package.json +26 -0
  32. package/node_modules/@delexec/sqlite-store/src/index.js +68 -0
  33. package/node_modules/@delexec/transport-email/README.md +3 -0
  34. package/node_modules/@delexec/transport-email/README.zh-CN.md +6 -0
  35. package/node_modules/@delexec/transport-email/package.json +23 -0
  36. package/node_modules/@delexec/transport-email/src/index.js +185 -0
  37. package/node_modules/@delexec/transport-emailengine/README.md +3 -0
  38. package/node_modules/@delexec/transport-emailengine/README.zh-CN.md +6 -0
  39. package/node_modules/@delexec/transport-emailengine/package.json +26 -0
  40. package/node_modules/@delexec/transport-emailengine/src/index.js +210 -0
  41. package/node_modules/@delexec/transport-gmail/README.md +3 -0
  42. package/node_modules/@delexec/transport-gmail/README.zh-CN.md +6 -0
  43. package/node_modules/@delexec/transport-gmail/package.json +26 -0
  44. package/node_modules/@delexec/transport-gmail/src/index.js +295 -0
  45. package/node_modules/@delexec/transport-relay-http/README.md +3 -0
  46. package/node_modules/@delexec/transport-relay-http/README.zh-CN.md +6 -0
  47. package/node_modules/@delexec/transport-relay-http/package.json +23 -0
  48. package/node_modules/@delexec/transport-relay-http/src/index.js +124 -0
  49. package/package.json +64 -0
  50. package/src/cli.js +1571 -0
  51. package/src/config.js +1180 -0
  52. package/src/example-hotline-worker.js +65 -0
  53. package/src/example-hotline.js +196 -0
  54. package/src/logging.js +56 -0
  55. package/src/supervisor.js +3070 -0
@@ -0,0 +1,185 @@
1
+ import crypto from "node:crypto";
2
+
3
+ export const RSP_EMAIL_SUBJECT_PREFIX = "[RSP]";
4
+
5
+ function nowIso() {
6
+ return new Date().toISOString();
7
+ }
8
+
9
+ function randomInt(maxInclusive) {
10
+ if (maxInclusive <= 0) {
11
+ return 0;
12
+ }
13
+ return Math.floor(Math.random() * (maxInclusive + 1));
14
+ }
15
+
16
+ function deepClone(value) {
17
+ return value == null ? value : JSON.parse(JSON.stringify(value));
18
+ }
19
+
20
+ export function normalizeAttachments(attachments = []) {
21
+ return (Array.isArray(attachments) ? attachments : []).map((attachment, index) => {
22
+ const contentBase64 = Buffer.isBuffer(attachment?.content)
23
+ ? attachment.content.toString("base64")
24
+ : attachment?.content_base64 || Buffer.from(String(attachment?.content || ""), "utf8").toString("base64");
25
+ const byteSize = Buffer.from(contentBase64 || "", "base64").length;
26
+ return {
27
+ attachment_id: attachment?.attachment_id || `att_${index}_${crypto.randomUUID()}`,
28
+ name: attachment?.name || `attachment-${index + 1}.bin`,
29
+ media_type: attachment?.media_type || "application/octet-stream",
30
+ content_base64: contentBase64,
31
+ byte_size: Number.isFinite(Number(attachment?.byte_size)) ? Number(attachment.byte_size) : byteSize
32
+ };
33
+ });
34
+ }
35
+
36
+ export function normalizeEnvelope(envelope = {}) {
37
+ const base = deepClone(envelope);
38
+ const bodyText =
39
+ typeof envelope.body_text === "string"
40
+ ? envelope.body_text
41
+ : typeof envelope.result_package === "object"
42
+ ? JSON.stringify(envelope.result_package)
43
+ : typeof envelope.payload === "string"
44
+ ? envelope.payload
45
+ : envelope.payload?.body_text || "";
46
+
47
+ const to =
48
+ envelope.to ||
49
+ envelope.address ||
50
+ envelope.receiver ||
51
+ (envelope.direction === "responder_to_caller" ? "caller-controller" : "responder-controller");
52
+ const from =
53
+ envelope.from ||
54
+ envelope.sender ||
55
+ (envelope.direction === "responder_to_caller" ? "responder-controller" : "caller-controller");
56
+
57
+ const payload =
58
+ envelope.payload && typeof envelope.payload === "object" && !Array.isArray(envelope.payload)
59
+ ? deepClone(envelope.payload)
60
+ : envelope.payload;
61
+
62
+ return {
63
+ ...base,
64
+ message_id: envelope.message_id || `mail_${crypto.randomUUID()}`,
65
+ request_id: envelope.request_id || payload?.request_id || envelope.result_package?.request_id || null,
66
+ thread_id: envelope.thread_id || envelope.thread_hint || envelope.request_id || `thread_${crypto.randomUUID()}`,
67
+ thread_hint: envelope.thread_hint || envelope.thread_id || envelope.request_id || null,
68
+ from,
69
+ to,
70
+ type: envelope.type || payload?.type || "email.message",
71
+ direction: envelope.direction || null,
72
+ body_text: bodyText,
73
+ payload,
74
+ result_package: envelope.result_package ? deepClone(envelope.result_package) : null,
75
+ attachments: normalizeAttachments(envelope.attachments || payload?.attachments || []),
76
+ sent_at: nowIso()
77
+ };
78
+ }
79
+
80
+ export function buildEmailSubject(envelope = {}) {
81
+ const normalized = normalizeEnvelope(envelope);
82
+ const type = normalized.type || "message";
83
+ const requestId = normalized.request_id || normalized.thread_id || normalized.message_id;
84
+ return `${RSP_EMAIL_SUBJECT_PREFIX} ${type} ${requestId}`;
85
+ }
86
+
87
+ export function isRspEmailSubject(subject) {
88
+ return typeof subject === "string" && subject.startsWith(`${RSP_EMAIL_SUBJECT_PREFIX} `);
89
+ }
90
+
91
+ export function buildRspHeaders(envelope = {}) {
92
+ const normalized = normalizeEnvelope(envelope);
93
+ return {
94
+ "X-RSP-Transport": "1",
95
+ "X-RSP-Request-Id": normalized.request_id || "",
96
+ "X-RSP-Thread-Id": normalized.thread_id || "",
97
+ "X-RSP-Message-Id": normalized.message_id || "",
98
+ "X-RSP-Type": normalized.type || "",
99
+ "X-RSP-From": normalized.from || "",
100
+ "X-RSP-To": normalized.to || ""
101
+ };
102
+ }
103
+
104
+ export class InMemoryEmailTransport {
105
+ constructor(options = {}) {
106
+ this.messages = [];
107
+ this.options = {
108
+ duplicateRate: Number(options.duplicateRate || 0),
109
+ outOfOrderRate: Number(options.outOfOrderRate || 0),
110
+ minDelayMs: Number(options.minDelayMs || 0),
111
+ maxDelayMs: Number(options.maxDelayMs || 0)
112
+ };
113
+ }
114
+
115
+ async send(envelope) {
116
+ const delayRange = Math.max(this.options.maxDelayMs - this.options.minDelayMs, 0);
117
+ const delayMs = this.options.minDelayMs + randomInt(delayRange);
118
+ if (delayMs > 0) {
119
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
120
+ }
121
+
122
+ const record = normalizeEnvelope(envelope);
123
+ this.messages.push(record);
124
+
125
+ if (Math.random() < this.options.duplicateRate) {
126
+ this.messages.push({ ...deepClone(record), message_id: `dup_${crypto.randomUUID()}` });
127
+ }
128
+
129
+ if (Math.random() < this.options.outOfOrderRate && this.messages.length >= 2) {
130
+ const last = this.messages.length - 1;
131
+ const swapWith = randomInt(last - 1);
132
+ const temp = this.messages[last];
133
+ this.messages[last] = this.messages[swapWith];
134
+ this.messages[swapWith] = temp;
135
+ }
136
+
137
+ return deepClone(record);
138
+ }
139
+
140
+ async poll({ receiver, limit = 50 } = {}) {
141
+ const items = this.messages
142
+ .filter((item) => !receiver || item.to === receiver)
143
+ .slice(0, limit)
144
+ .map((item) => deepClone(item));
145
+ return { items };
146
+ }
147
+
148
+ async ack(messageId, { receiver } = {}) {
149
+ const index = this.messages.findIndex((item) => item.message_id === messageId && (!receiver || item.to === receiver));
150
+ if (index === -1) {
151
+ return { acked: false };
152
+ }
153
+ this.messages.splice(index, 1);
154
+ return { acked: true };
155
+ }
156
+
157
+ async sendTaskEmail(message) {
158
+ return this.send(message);
159
+ }
160
+
161
+ async pollThreadReplies({ request_id, direction = "responder_to_caller", limit = 50 } = {}) {
162
+ return this.messages
163
+ .filter((item) => (!request_id || item.request_id === request_id) && item.direction === direction)
164
+ .slice(0, limit)
165
+ .map((item) => deepClone(item));
166
+ }
167
+
168
+ clear() {
169
+ this.messages.length = 0;
170
+ }
171
+ }
172
+
173
+ export async function sendTaskEmail(transport, message) {
174
+ if (!transport || typeof transport.sendTaskEmail !== "function") {
175
+ throw new Error("TRANSPORT_SEND_NOT_AVAILABLE");
176
+ }
177
+ return transport.sendTaskEmail(message);
178
+ }
179
+
180
+ export async function pollThreadReplies(transport, query) {
181
+ if (!transport || typeof transport.pollThreadReplies !== "function") {
182
+ throw new Error("TRANSPORT_POLL_NOT_AVAILABLE");
183
+ }
184
+ return transport.pollThreadReplies(query);
185
+ }
@@ -0,0 +1,3 @@
1
+ # @delexec/transport-emailengine
2
+
3
+ EmailEngine transport adapter for delegated execution.
@@ -0,0 +1,6 @@
1
+ # @delexec/transport-emailengine
2
+
3
+ > 英文版:README.md
4
+ > 说明:中文文档为准。
5
+
6
+ 用于委托执行的 EmailEngine 传输适配器。
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@delexec/transport-emailengine",
3
+ "version": "0.1.0",
4
+ "description": "EmailEngine transport adapter for delegated execution",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./package.json": "./package.json"
10
+ },
11
+ "files": [
12
+ "src",
13
+ "README.md"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "keywords": [
19
+ "delegated-execution",
20
+ "transport",
21
+ "emailengine"
22
+ ],
23
+ "dependencies": {
24
+ "@delexec/transport-email": "0.1.0"
25
+ }
26
+ }
@@ -0,0 +1,210 @@
1
+ import {
2
+ buildEmailSubject,
3
+ buildRspHeaders,
4
+ isRspEmailSubject,
5
+ normalizeEnvelope
6
+ } from "@delexec/transport-email";
7
+
8
+ function base64ToUtf8(value) {
9
+ return Buffer.from(value || "", "base64").toString("utf8");
10
+ }
11
+
12
+ async function request(baseUrl, pathname, { method = "GET", headers = {}, body, parse = "json" } = {}) {
13
+ const response = await fetch(new URL(pathname, baseUrl), {
14
+ method,
15
+ headers,
16
+ body
17
+ });
18
+ if (parse === "buffer") {
19
+ return {
20
+ status: response.status,
21
+ headers: response.headers,
22
+ body: Buffer.from(await response.arrayBuffer())
23
+ };
24
+ }
25
+ const text = await response.text();
26
+ return {
27
+ status: response.status,
28
+ headers: response.headers,
29
+ body: text ? JSON.parse(text) : null
30
+ };
31
+ }
32
+
33
+ function authHeaders(accessToken, extra = {}) {
34
+ return {
35
+ Authorization: `Bearer ${accessToken}`,
36
+ ...extra
37
+ };
38
+ }
39
+
40
+ async function fetchMessageAttachments(baseUrl, account, accessToken, attachments = []) {
41
+ const results = [];
42
+ for (const attachment of attachments) {
43
+ const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/attachment/${encodeURIComponent(attachment.id)}`, {
44
+ headers: authHeaders(accessToken),
45
+ parse: "buffer"
46
+ });
47
+ if (response.status !== 200) {
48
+ throw new Error(`emailengine_attachment_fetch_failed:${response.status}`);
49
+ }
50
+ const content = response.body;
51
+ results.push({
52
+ attachment_id: attachment.id,
53
+ name: attachment.filename || attachment.name || `attachment-${results.length + 1}.bin`,
54
+ media_type: attachment.contentType || attachment.mimeType || "application/octet-stream",
55
+ content_base64: content.toString("base64"),
56
+ byte_size: content.length
57
+ });
58
+ }
59
+ return results;
60
+ }
61
+
62
+ async function loadMessage(baseUrl, account, accessToken, message) {
63
+ const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/message/${encodeURIComponent(message.id)}?textType=*`, {
64
+ headers: authHeaders(accessToken)
65
+ });
66
+ if (response.status !== 200) {
67
+ throw new Error(`emailengine_message_fetch_failed:${response.status}`);
68
+ }
69
+ const body = response.body || {};
70
+ const attachments = await fetchMessageAttachments(baseUrl, account, accessToken, body.attachments || []);
71
+ const textBody = Array.isArray(body.text?.plain)
72
+ ? body.text.plain.join("\n")
73
+ : typeof body.text?.plain === "string"
74
+ ? body.text.plain
75
+ : "";
76
+ let payload = null;
77
+ try {
78
+ payload = textBody ? JSON.parse(textBody) : null;
79
+ } catch {
80
+ payload = null;
81
+ }
82
+ return normalizeEnvelope({
83
+ message_id: body.messageId || message.messageId || message.id || null,
84
+ thread_id: body.threadId || message.threadId || null,
85
+ thread_hint: body.threadId || message.threadId || null,
86
+ request_id: body.headers?.["x-rsp-request-id"] || body.headers?.["X-RSP-Request-Id"] || payload?.request_id || null,
87
+ from: body.from?.address || body.from?.[0]?.address || null,
88
+ to: body.to?.[0]?.address || body.to?.address || null,
89
+ type: body.headers?.["x-rsp-type"] || body.headers?.["X-RSP-Type"] || payload?.type || "email.message",
90
+ body_text: textBody,
91
+ payload,
92
+ attachments
93
+ });
94
+ }
95
+
96
+ export function createEmailEngineTransportAdapter({
97
+ baseUrl,
98
+ account,
99
+ accessToken,
100
+ receiver = account,
101
+ sender = account,
102
+ pageSize = 20
103
+ }) {
104
+ if (!baseUrl) {
105
+ throw new Error("emailengine_base_url_required");
106
+ }
107
+ if (!account) {
108
+ throw new Error("emailengine_account_required");
109
+ }
110
+ if (!accessToken) {
111
+ throw new Error("emailengine_access_token_required");
112
+ }
113
+
114
+ return {
115
+ async send(envelope) {
116
+ const normalized = normalizeEnvelope({
117
+ ...envelope,
118
+ from: envelope.from || sender,
119
+ to: envelope.to || receiver
120
+ });
121
+ const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/submit`, {
122
+ method: "POST",
123
+ headers: authHeaders(accessToken, {
124
+ "content-type": "application/json; charset=utf-8"
125
+ }),
126
+ body: JSON.stringify({
127
+ from: normalized.from,
128
+ to: [normalized.to],
129
+ subject: buildEmailSubject(normalized),
130
+ text: normalized.body_text,
131
+ headers: buildRspHeaders(normalized),
132
+ attachments: normalized.attachments.map((attachment) => ({
133
+ filename: attachment.name,
134
+ contentType: attachment.media_type,
135
+ content: attachment.content_base64,
136
+ encoding: "base64"
137
+ }))
138
+ })
139
+ });
140
+ if (response.status !== 200 && response.status !== 202) {
141
+ throw new Error(`emailengine_send_failed:${response.status}`);
142
+ }
143
+ return normalized;
144
+ },
145
+
146
+ async poll({ limit = 10 } = {}) {
147
+ const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/search?path=INBOX`, {
148
+ method: "POST",
149
+ headers: authHeaders(accessToken, {
150
+ "content-type": "application/json; charset=utf-8"
151
+ }),
152
+ body: JSON.stringify({
153
+ page: 0,
154
+ pageSize: Math.max(limit, pageSize),
155
+ search: {
156
+ unseen: true
157
+ }
158
+ })
159
+ });
160
+ if (response.status !== 200) {
161
+ throw new Error(`emailengine_poll_failed:${response.status}`);
162
+ }
163
+ const messages = Array.isArray(response.body?.messages) ? response.body.messages : [];
164
+ const items = [];
165
+ for (const message of messages) {
166
+ if (!isRspEmailSubject(message.subject || "")) {
167
+ continue;
168
+ }
169
+ items.push(await loadMessage(baseUrl, account, accessToken, message));
170
+ if (items.length >= limit) {
171
+ break;
172
+ }
173
+ }
174
+ return { items };
175
+ },
176
+
177
+ async ack(messageId) {
178
+ const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/message/${encodeURIComponent(messageId)}`, {
179
+ method: "PUT",
180
+ headers: authHeaders(accessToken, {
181
+ "content-type": "application/json; charset=utf-8"
182
+ }),
183
+ body: JSON.stringify({
184
+ flags: {
185
+ add: ["\\Seen"]
186
+ }
187
+ })
188
+ });
189
+ if (response.status !== 200) {
190
+ throw new Error(`emailengine_ack_failed:${response.status}`);
191
+ }
192
+ return { acked: true };
193
+ },
194
+
195
+ async health() {
196
+ const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}`, {
197
+ headers: authHeaders(accessToken)
198
+ });
199
+ if (response.status !== 200) {
200
+ throw new Error(`emailengine_health_failed:${response.status}`);
201
+ }
202
+ return {
203
+ ok: true,
204
+ provider: "emailengine",
205
+ version: "API v1",
206
+ account
207
+ };
208
+ }
209
+ };
210
+ }
@@ -0,0 +1,3 @@
1
+ # @delexec/transport-gmail
2
+
3
+ Gmail transport adapter for delegated execution.
@@ -0,0 +1,6 @@
1
+ # @delexec/transport-gmail
2
+
3
+ > 英文版:README.md
4
+ > 说明:中文文档为准。
5
+
6
+ 用于委托执行的 Gmail 传输适配器。
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@delexec/transport-gmail",
3
+ "version": "0.1.0",
4
+ "description": "Gmail transport adapter for delegated execution",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./package.json": "./package.json"
10
+ },
11
+ "files": [
12
+ "src",
13
+ "README.md"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "keywords": [
19
+ "delegated-execution",
20
+ "transport",
21
+ "gmail"
22
+ ],
23
+ "dependencies": {
24
+ "@delexec/transport-email": "0.1.0"
25
+ }
26
+ }