@kittymi/openclaw-generic-http 0.1.3

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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/dist/channel/account.d.ts +7 -0
  4. package/dist/channel/account.js +18 -0
  5. package/dist/channel/capabilities.d.ts +10 -0
  6. package/dist/channel/capabilities.js +11 -0
  7. package/dist/channel/host-adapter.d.ts +28 -0
  8. package/dist/channel/host-adapter.js +36 -0
  9. package/dist/channel/lifecycle.d.ts +18 -0
  10. package/dist/channel/lifecycle.js +28 -0
  11. package/dist/channel/plugin.d.ts +46 -0
  12. package/dist/channel/plugin.js +120 -0
  13. package/dist/channel/probe.d.ts +19 -0
  14. package/dist/channel/probe.js +149 -0
  15. package/dist/channel/resolve.d.ts +30 -0
  16. package/dist/channel/resolve.js +98 -0
  17. package/dist/channel/stream.d.ts +35 -0
  18. package/dist/channel/stream.js +127 -0
  19. package/dist/config/host-config-schema.d.ts +21 -0
  20. package/dist/config/host-config-schema.js +80 -0
  21. package/dist/config/loader.d.ts +7 -0
  22. package/dist/config/loader.js +38 -0
  23. package/dist/config/schema.d.ts +48 -0
  24. package/dist/config/schema.js +1 -0
  25. package/dist/errors/codes.d.ts +11 -0
  26. package/dist/errors/codes.js +10 -0
  27. package/dist/errors/exceptions.d.ts +7 -0
  28. package/dist/errors/exceptions.js +12 -0
  29. package/dist/inbound/mapper.d.ts +21 -0
  30. package/dist/inbound/mapper.js +22 -0
  31. package/dist/inbound/validator.d.ts +4 -0
  32. package/dist/inbound/validator.js +114 -0
  33. package/dist/index.d.ts +33 -0
  34. package/dist/index.js +31 -0
  35. package/dist/mapping/conversation-mapper.d.ts +1 -0
  36. package/dist/mapping/conversation-mapper.js +3 -0
  37. package/dist/mapping/sender-mapper.d.ts +1 -0
  38. package/dist/mapping/sender-mapper.js +3 -0
  39. package/dist/mapping/thread-mapper.d.ts +1 -0
  40. package/dist/mapping/thread-mapper.js +7 -0
  41. package/dist/openclaw-entry.d.ts +276 -0
  42. package/dist/openclaw-entry.js +728 -0
  43. package/dist/outbound/client.d.ts +6 -0
  44. package/dist/outbound/client.js +1 -0
  45. package/dist/outbound/controller.d.ts +15 -0
  46. package/dist/outbound/controller.js +28 -0
  47. package/dist/outbound/http-client.d.ts +23 -0
  48. package/dist/outbound/http-client.js +150 -0
  49. package/dist/outbound/mapper.d.ts +29 -0
  50. package/dist/outbound/mapper.js +19 -0
  51. package/dist/outbound/mock-client.d.ts +18 -0
  52. package/dist/outbound/mock-client.js +26 -0
  53. package/dist/outbound/sender.d.ts +3 -0
  54. package/dist/outbound/sender.js +5 -0
  55. package/dist/protocol/attachments.d.ts +10 -0
  56. package/dist/protocol/attachments.js +56 -0
  57. package/dist/protocol/dto.d.ts +46 -0
  58. package/dist/protocol/dto.js +1 -0
  59. package/dist/protocol/serializer.d.ts +1 -0
  60. package/dist/protocol/serializer.js +3 -0
  61. package/dist/security/nonce-store.d.ts +30 -0
  62. package/dist/security/nonce-store.js +32 -0
  63. package/dist/security/signer.d.ts +10 -0
  64. package/dist/security/signer.js +20 -0
  65. package/dist/security/verifier.d.ts +2 -0
  66. package/dist/security/verifier.js +20 -0
  67. package/dist/setup-entry.d.ts +351 -0
  68. package/dist/setup-entry.js +73 -0
  69. package/dist/utils/json.d.ts +1 -0
  70. package/dist/utils/json.js +3 -0
  71. package/dist/utils/log.d.ts +1 -0
  72. package/dist/utils/log.js +3 -0
  73. package/dist/utils/time.d.ts +1 -0
  74. package/dist/utils/time.js +3 -0
  75. package/openclaw.config.schema.json +80 -0
  76. package/openclaw.plugin.json +175 -0
  77. package/package.json +72 -0
@@ -0,0 +1,30 @@
1
+ import type { GenericHttpAccountConfig } from "../config/schema.js";
2
+ export interface ResolveRequest {
3
+ accountId?: string | null;
4
+ kind: "conversation" | "sender";
5
+ query: string;
6
+ }
7
+ export interface ResolveResult {
8
+ id: string;
9
+ name: string;
10
+ kind: ResolveRequest["kind"];
11
+ }
12
+ export interface ResolveResponse {
13
+ success: true;
14
+ results: ResolveResult[];
15
+ }
16
+ export interface ResolveAccountOptions {
17
+ fetchImpl?: typeof fetch;
18
+ nowEpochSeconds?: () => number;
19
+ nonceFactory?: () => string;
20
+ requestIdFactory?: () => string;
21
+ }
22
+ /**
23
+ * Minimal local resolve fallback.
24
+ *
25
+ * The first runtime loop has no external lookup client yet, so if callers
26
+ * already have a stable ID they can pass it through this method and still get a
27
+ * protocol-shaped resolve response.
28
+ */
29
+ export declare function resolveLocally(request: ResolveRequest): ResolveResponse;
30
+ export declare function resolveRemotely(accountConfig: GenericHttpAccountConfig, request: ResolveRequest, options?: ResolveAccountOptions): Promise<ResolveResponse>;
@@ -0,0 +1,98 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { serializeProtocolObject } from "../protocol/serializer.js";
3
+ import { signPayload } from "../security/signer.js";
4
+ function buildSignedHeaders(accountConfig, method, path, rawBody, timestamp, nonce, requestId) {
5
+ const signingSecret = accountConfig.outboundSecret ?? accountConfig.signingSecret ?? "";
6
+ const signature = signPayload(signingSecret, {
7
+ method,
8
+ path,
9
+ timestamp,
10
+ nonce,
11
+ rawBody
12
+ });
13
+ return {
14
+ accept: "application/json",
15
+ "content-type": "application/json",
16
+ "x-request-id": requestId,
17
+ "x-generic-http-version": "1",
18
+ "x-timestamp": timestamp,
19
+ "x-nonce": nonce,
20
+ "x-signature": signature,
21
+ "x-api-key": accountConfig.apiKey ?? ""
22
+ };
23
+ }
24
+ /**
25
+ * Minimal local resolve fallback.
26
+ *
27
+ * The first runtime loop has no external lookup client yet, so if callers
28
+ * already have a stable ID they can pass it through this method and still get a
29
+ * protocol-shaped resolve response.
30
+ */
31
+ export function resolveLocally(request) {
32
+ const query = request.query.trim();
33
+ if (query === "") {
34
+ return {
35
+ success: true,
36
+ results: []
37
+ };
38
+ }
39
+ return {
40
+ success: true,
41
+ results: [
42
+ {
43
+ id: query,
44
+ name: query,
45
+ kind: request.kind
46
+ }
47
+ ]
48
+ };
49
+ }
50
+ export async function resolveRemotely(accountConfig, request, options = {}) {
51
+ const query = request.query.trim();
52
+ if (query === "") {
53
+ return {
54
+ success: true,
55
+ results: []
56
+ };
57
+ }
58
+ const resolvedOptions = {
59
+ fetchImpl: options.fetchImpl ?? fetch,
60
+ nowEpochSeconds: options.nowEpochSeconds ?? (() => Math.floor(Date.now() / 1000)),
61
+ nonceFactory: options.nonceFactory ?? (() => randomUUID()),
62
+ requestIdFactory: options.requestIdFactory ?? (() => randomUUID())
63
+ };
64
+ const endpoint = new URL("/resolve", accountConfig.baseUrl);
65
+ const rawBody = serializeProtocolObject({
66
+ accountId: request.accountId ?? undefined,
67
+ kind: request.kind,
68
+ query
69
+ });
70
+ const timestamp = String(resolvedOptions.nowEpochSeconds());
71
+ const nonce = resolvedOptions.nonceFactory();
72
+ const requestId = resolvedOptions.requestIdFactory();
73
+ const headers = buildSignedHeaders(accountConfig, "POST", endpoint.pathname, rawBody, timestamp, nonce, requestId);
74
+ const response = await resolvedOptions.fetchImpl(endpoint.toString(), {
75
+ method: "POST",
76
+ headers,
77
+ body: rawBody
78
+ });
79
+ if (!response.ok) {
80
+ throw new Error(`POST /resolve failed with ${response.status} ${response.statusText}`);
81
+ }
82
+ const payload = (await response.json());
83
+ if (payload.success !== true || !Array.isArray(payload.results)) {
84
+ throw new Error("POST /resolve returned an invalid response payload");
85
+ }
86
+ return {
87
+ success: true,
88
+ results: payload.results
89
+ .filter((item) => typeof item.id === "string" &&
90
+ typeof item.name === "string" &&
91
+ (item.kind === "conversation" || item.kind === "sender"))
92
+ .map((item) => ({
93
+ id: item.id,
94
+ name: item.name,
95
+ kind: item.kind
96
+ }))
97
+ };
98
+ }
@@ -0,0 +1,35 @@
1
+ import type { InboundMessageRequestDto } from "../protocol/dto.js";
2
+ import { type NormalizedInboundMessageEvent } from "../inbound/mapper.js";
3
+ import type { GenericHttpAccountConfig } from "../config/schema.js";
4
+ export interface StreamPullOptions {
5
+ fetchImpl?: typeof fetch;
6
+ nowEpochSeconds?: () => number;
7
+ nonceFactory?: () => string;
8
+ requestIdFactory?: () => string;
9
+ limit?: number;
10
+ }
11
+ export interface StreamAckOptions {
12
+ fetchImpl?: typeof fetch;
13
+ nowEpochSeconds?: () => number;
14
+ nonceFactory?: () => string;
15
+ requestIdFactory?: () => string;
16
+ }
17
+ export interface PulledInboundMessage {
18
+ eventId: string;
19
+ accountId: string;
20
+ receivedAt: string;
21
+ request: InboundMessageRequestDto;
22
+ normalizedEvent: NormalizedInboundMessageEvent;
23
+ }
24
+ export interface PullInboundMessagesResult {
25
+ success: true;
26
+ accountId: string;
27
+ items: PulledInboundMessage[];
28
+ }
29
+ export interface AckInboundMessagesResult {
30
+ success: true;
31
+ accountId: string;
32
+ ackedEventIds: string[];
33
+ }
34
+ export declare function pullInboundMessages(accountId: string, accountConfig: GenericHttpAccountConfig, options?: StreamPullOptions): Promise<PullInboundMessagesResult>;
35
+ export declare function ackInboundMessages(accountId: string, eventIds: string[], accountConfig: GenericHttpAccountConfig, options?: StreamAckOptions): Promise<AckInboundMessagesResult>;
@@ -0,0 +1,127 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { serializeProtocolObject } from "../protocol/serializer.js";
3
+ import { signPayload } from "../security/signer.js";
4
+ import { mapInboundMessage } from "../inbound/mapper.js";
5
+ import { validateInboundMessage } from "../inbound/validator.js";
6
+ function buildSignedHeaders(accountConfig, method, path, rawBody, timestamp, nonce, requestId) {
7
+ const signingSecret = accountConfig.outboundSecret ?? accountConfig.signingSecret ?? "";
8
+ const signature = signPayload(signingSecret, {
9
+ method,
10
+ path,
11
+ timestamp,
12
+ nonce,
13
+ rawBody
14
+ });
15
+ return {
16
+ accept: "application/json, text/event-stream",
17
+ "content-type": "application/json",
18
+ "x-request-id": requestId,
19
+ "x-generic-http-version": "1",
20
+ "x-timestamp": timestamp,
21
+ "x-nonce": nonce,
22
+ "x-signature": signature,
23
+ "x-api-key": accountConfig.apiKey ?? ""
24
+ };
25
+ }
26
+ function resolveRequiredOptions(options) {
27
+ return {
28
+ fetchImpl: options.fetchImpl ?? fetch,
29
+ nowEpochSeconds: options.nowEpochSeconds ?? (() => Math.floor(Date.now() / 1000)),
30
+ nonceFactory: options.nonceFactory ?? (() => randomUUID()),
31
+ requestIdFactory: options.requestIdFactory ?? (() => randomUUID())
32
+ };
33
+ }
34
+ function parseSseEvents(raw) {
35
+ const chunks = raw
36
+ .split(/\r?\n\r?\n/)
37
+ .map((chunk) => chunk.trim())
38
+ .filter((chunk) => chunk !== "");
39
+ return chunks.map((chunk) => {
40
+ const lines = chunk.split(/\r?\n/);
41
+ let eventName = "message";
42
+ const dataLines = [];
43
+ for (const line of lines) {
44
+ if (line.startsWith("event:")) {
45
+ eventName = line.slice("event:".length).trim();
46
+ continue;
47
+ }
48
+ if (line.startsWith("data:")) {
49
+ dataLines.push(line.slice("data:".length).trim());
50
+ }
51
+ }
52
+ return {
53
+ event: eventName,
54
+ data: dataLines.join("\n")
55
+ };
56
+ });
57
+ }
58
+ export async function pullInboundMessages(accountId, accountConfig, options = {}) {
59
+ const resolvedOptions = resolveRequiredOptions(options);
60
+ const endpoint = new URL("/stream/inbound", accountConfig.baseUrl);
61
+ endpoint.searchParams.set("accountId", accountId);
62
+ endpoint.searchParams.set("limit", String(options.limit ?? 10));
63
+ const timestamp = String(resolvedOptions.nowEpochSeconds());
64
+ const nonce = resolvedOptions.nonceFactory();
65
+ const requestId = resolvedOptions.requestIdFactory();
66
+ const headers = buildSignedHeaders(accountConfig, "GET", endpoint.pathname, "", timestamp, nonce, requestId);
67
+ const response = await resolvedOptions.fetchImpl(endpoint.toString(), {
68
+ method: "GET",
69
+ headers
70
+ });
71
+ if (!response.ok) {
72
+ throw new Error(`GET /stream/inbound failed with ${response.status} ${response.statusText}`);
73
+ }
74
+ const rawSse = await response.text();
75
+ const items = parseSseEvents(rawSse)
76
+ .filter((entry) => entry.event === "inbound-message")
77
+ .map((entry) => JSON.parse(entry.data))
78
+ .map((entry) => {
79
+ validateInboundMessage(entry.request);
80
+ return {
81
+ eventId: entry.eventId,
82
+ accountId: entry.accountId,
83
+ receivedAt: entry.receivedAt,
84
+ request: entry.request,
85
+ normalizedEvent: mapInboundMessage(entry.request)
86
+ };
87
+ });
88
+ return {
89
+ success: true,
90
+ accountId,
91
+ items
92
+ };
93
+ }
94
+ export async function ackInboundMessages(accountId, eventIds, accountConfig, options = {}) {
95
+ const normalizedEventIds = eventIds
96
+ .map((eventId) => eventId.trim())
97
+ .filter((eventId) => eventId !== "");
98
+ const resolvedOptions = resolveRequiredOptions(options);
99
+ const endpoint = new URL("/stream/acks", accountConfig.baseUrl);
100
+ const rawBody = serializeProtocolObject({
101
+ accountId,
102
+ eventIds: normalizedEventIds
103
+ });
104
+ const timestamp = String(resolvedOptions.nowEpochSeconds());
105
+ const nonce = resolvedOptions.nonceFactory();
106
+ const requestId = resolvedOptions.requestIdFactory();
107
+ const headers = buildSignedHeaders(accountConfig, "POST", endpoint.pathname, rawBody, timestamp, nonce, requestId);
108
+ const response = await resolvedOptions.fetchImpl(endpoint.toString(), {
109
+ method: "POST",
110
+ headers,
111
+ body: rawBody
112
+ });
113
+ if (!response.ok) {
114
+ throw new Error(`POST /stream/acks failed with ${response.status} ${response.statusText}`);
115
+ }
116
+ const payload = (await response.json());
117
+ if (payload.success !== true ||
118
+ payload.accountId !== accountId ||
119
+ !Array.isArray(payload.ackedEventIds)) {
120
+ throw new Error("POST /stream/acks returned an invalid response payload");
121
+ }
122
+ return {
123
+ success: true,
124
+ accountId,
125
+ ackedEventIds: payload.ackedEventIds.filter((eventId) => typeof eventId === "string")
126
+ };
127
+ }
@@ -0,0 +1,21 @@
1
+ export interface GenericHttpHostConfigSchemaProperty {
2
+ type?: string;
3
+ title?: string;
4
+ description?: string;
5
+ format?: string;
6
+ minimum?: number;
7
+ default?: unknown;
8
+ properties?: Record<string, GenericHttpHostConfigSchemaProperty>;
9
+ required?: string[];
10
+ additionalProperties?: boolean | GenericHttpHostConfigSchemaProperty;
11
+ }
12
+ export interface GenericHttpHostConfigSchema {
13
+ $schema: string;
14
+ type: "object";
15
+ title: string;
16
+ description: string;
17
+ properties: Record<string, GenericHttpHostConfigSchemaProperty>;
18
+ required: string[];
19
+ additionalProperties: boolean;
20
+ }
21
+ export declare const genericHttpHostConfigSchema: GenericHttpHostConfigSchema;
@@ -0,0 +1,80 @@
1
+ export const genericHttpHostConfigSchema = {
2
+ $schema: "http://json-schema.org/draft-07/schema#",
3
+ type: "object",
4
+ title: "Generic HTTP Channel Config",
5
+ description: "Configuration for the OpenClaw generic HTTP channel plugin using bridge/relay webhook and stream ingress.",
6
+ properties: {
7
+ enabled: {
8
+ type: "boolean",
9
+ title: "Enabled",
10
+ description: "Whether the generic HTTP channel is enabled.",
11
+ default: false
12
+ },
13
+ defaultAccount: {
14
+ type: "string",
15
+ title: "Default Account",
16
+ description: "Default account ID used when the host does not specify one."
17
+ },
18
+ accounts: {
19
+ type: "object",
20
+ title: "Accounts",
21
+ description: "Per-account bridge and outbound transport settings.",
22
+ additionalProperties: {
23
+ type: "object",
24
+ properties: {
25
+ baseUrl: {
26
+ type: "string",
27
+ format: "uri",
28
+ title: "Bridge Base URL",
29
+ description: "Bridge or relay base URL used for probe, resolve, stream ingress, and outbound delivery."
30
+ },
31
+ apiKey: {
32
+ type: "string",
33
+ title: "API Key",
34
+ description: "Optional shared credential for bridge authentication."
35
+ },
36
+ signingSecret: {
37
+ type: "string",
38
+ title: "Signing Secret",
39
+ description: "Shared secret used for signing outbound and stream transport requests."
40
+ },
41
+ inboundSecret: {
42
+ type: "string",
43
+ title: "Inbound Secret",
44
+ description: "Optional dedicated secret for inbound webhook validation on the bridge side."
45
+ },
46
+ outboundSecret: {
47
+ type: "string",
48
+ title: "Outbound Secret",
49
+ description: "Optional dedicated secret for outbound signing when outbound trust is split from inbound."
50
+ },
51
+ connectTimeoutMillis: {
52
+ type: "number",
53
+ minimum: 0,
54
+ title: "Connect Timeout (ms)",
55
+ description: "HTTP connect timeout in milliseconds.",
56
+ default: 5000
57
+ },
58
+ readTimeoutMillis: {
59
+ type: "number",
60
+ minimum: 0,
61
+ title: "Read Timeout (ms)",
62
+ description: "HTTP read timeout in milliseconds.",
63
+ default: 10000
64
+ },
65
+ maxRetries: {
66
+ type: "number",
67
+ minimum: 0,
68
+ title: "Max Retries",
69
+ description: "Maximum retry count for retryable outbound HTTP failures.",
70
+ default: 0
71
+ }
72
+ },
73
+ required: ["baseUrl"],
74
+ additionalProperties: false
75
+ }
76
+ }
77
+ },
78
+ required: ["enabled", "defaultAccount", "accounts"],
79
+ additionalProperties: false
80
+ };
@@ -0,0 +1,7 @@
1
+ import type { GenericHttpPluginConfig } from "./schema.js";
2
+ /**
3
+ * Normalize partially-hydrated config input into the runtime shape expected by
4
+ * the plugin. The plugin can then rely on explicit defaults instead of
5
+ * scattering fallback behavior across transport, validation, and routing code.
6
+ */
7
+ export declare function loadConfig(rawConfig?: Partial<GenericHttpPluginConfig>): GenericHttpPluginConfig;
@@ -0,0 +1,38 @@
1
+ const DEFAULT_ACCOUNT_ID = "default";
2
+ const DEFAULT_CONNECT_TIMEOUT_MILLIS = 5000;
3
+ const DEFAULT_READ_TIMEOUT_MILLIS = 10000;
4
+ function normalizeAccountConfig(account) {
5
+ return {
6
+ baseUrl: account.baseUrl ?? "",
7
+ apiKey: account.apiKey,
8
+ signingSecret: account.signingSecret,
9
+ inboundSecret: account.inboundSecret,
10
+ outboundSecret: account.outboundSecret,
11
+ connectTimeoutMillis: account.connectTimeoutMillis ?? DEFAULT_CONNECT_TIMEOUT_MILLIS,
12
+ readTimeoutMillis: account.readTimeoutMillis ?? DEFAULT_READ_TIMEOUT_MILLIS,
13
+ maxRetries: account.maxRetries ?? 0
14
+ };
15
+ }
16
+ /**
17
+ * Normalize partially-hydrated config input into the runtime shape expected by
18
+ * the plugin. The plugin can then rely on explicit defaults instead of
19
+ * scattering fallback behavior across transport, validation, and routing code.
20
+ */
21
+ export function loadConfig(rawConfig = {}) {
22
+ const defaultAccount = rawConfig.defaultAccount ?? DEFAULT_ACCOUNT_ID;
23
+ const rawAccounts = rawConfig.accounts ?? {};
24
+ const normalizedAccounts = {};
25
+ Object.entries(rawAccounts).forEach(([accountId, accountConfig]) => {
26
+ normalizedAccounts[accountId] = normalizeAccountConfig(accountConfig);
27
+ });
28
+ // Always materialize the declared default account so downstream routing code
29
+ // does not need a separate existence fallback.
30
+ if (normalizedAccounts[defaultAccount] === undefined) {
31
+ normalizedAccounts[defaultAccount] = normalizeAccountConfig({});
32
+ }
33
+ return {
34
+ enabled: rawConfig.enabled ?? false,
35
+ defaultAccount,
36
+ accounts: normalizedAccounts
37
+ };
38
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Per-account runtime configuration for the generic HTTP channel.
3
+ *
4
+ * Keep this shape close to the public docs so operators can map config files,
5
+ * CLI setup, and runtime behavior without translating between models.
6
+ */
7
+ export interface GenericHttpAccountConfig {
8
+ /**
9
+ * Base URL of the third-party bridge service that receives outbound calls
10
+ * and exposes health/probe endpoints.
11
+ */
12
+ baseUrl: string;
13
+ /**
14
+ * Shared API credential used for basic transport authentication.
15
+ */
16
+ apiKey?: string;
17
+ /**
18
+ * Shared secret used to sign outbound requests and verify inbound requests.
19
+ */
20
+ signingSecret?: string;
21
+ /**
22
+ * Optional dedicated secret for inbound validation when callers should not
23
+ * share the same secret used for outbound plugin requests.
24
+ */
25
+ inboundSecret?: string;
26
+ /**
27
+ * Optional dedicated secret for outbound signing when rotation or separation
28
+ * of trust domains is required.
29
+ */
30
+ outboundSecret?: string;
31
+ /**
32
+ * Connection timeout for outbound HTTP calls, in milliseconds.
33
+ */
34
+ connectTimeoutMillis?: number;
35
+ /**
36
+ * Read timeout for outbound HTTP calls, in milliseconds.
37
+ */
38
+ readTimeoutMillis?: number;
39
+ /**
40
+ * Maximum retry attempts for retryable outbound transport failures.
41
+ */
42
+ maxRetries?: number;
43
+ }
44
+ export interface GenericHttpPluginConfig {
45
+ enabled: boolean;
46
+ defaultAccount: string;
47
+ accounts: Record<string, GenericHttpAccountConfig>;
48
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ export declare const ERROR_CODES: {
2
+ readonly INVALID_REQUEST: "INVALID_REQUEST";
3
+ readonly MISSING_REQUIRED_FIELD: "MISSING_REQUIRED_FIELD";
4
+ readonly INVALID_FIELD_FORMAT: "INVALID_FIELD_FORMAT";
5
+ readonly INVALID_CREDENTIAL: "INVALID_CREDENTIAL";
6
+ readonly INVALID_SIGNATURE: "INVALID_SIGNATURE";
7
+ readonly TIMESTAMP_EXPIRED: "TIMESTAMP_EXPIRED";
8
+ readonly NONCE_REPLAYED: "NONCE_REPLAYED";
9
+ readonly INTERNAL_ERROR: "INTERNAL_ERROR";
10
+ };
11
+ export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
@@ -0,0 +1,10 @@
1
+ export const ERROR_CODES = {
2
+ INVALID_REQUEST: "INVALID_REQUEST",
3
+ MISSING_REQUIRED_FIELD: "MISSING_REQUIRED_FIELD",
4
+ INVALID_FIELD_FORMAT: "INVALID_FIELD_FORMAT",
5
+ INVALID_CREDENTIAL: "INVALID_CREDENTIAL",
6
+ INVALID_SIGNATURE: "INVALID_SIGNATURE",
7
+ TIMESTAMP_EXPIRED: "TIMESTAMP_EXPIRED",
8
+ NONCE_REPLAYED: "NONCE_REPLAYED",
9
+ INTERNAL_ERROR: "INTERNAL_ERROR"
10
+ };
@@ -0,0 +1,7 @@
1
+ import type { ErrorCode } from "./codes.js";
2
+ export declare class GenericHttpPluginError extends Error {
3
+ readonly code: ErrorCode;
4
+ readonly details?: Record<string, unknown>;
5
+ readonly retryable: boolean;
6
+ constructor(code: ErrorCode, message: string, details?: Record<string, unknown>, retryable?: boolean);
7
+ }
@@ -0,0 +1,12 @@
1
+ export class GenericHttpPluginError extends Error {
2
+ code;
3
+ details;
4
+ retryable;
5
+ constructor(code, message, details, retryable = false) {
6
+ super(message);
7
+ this.name = "GenericHttpPluginError";
8
+ this.code = code;
9
+ this.details = details;
10
+ this.retryable = retryable;
11
+ }
12
+ }
@@ -0,0 +1,21 @@
1
+ import type { AttachmentDto, InboundMessageRequestDto } from "../protocol/dto.js";
2
+ export interface NormalizedInboundMessageEvent {
3
+ eventId: string;
4
+ eventType: "message.created";
5
+ accountId: string;
6
+ conversationId: string;
7
+ conversationType: string;
8
+ conversationTitle: string | null;
9
+ threadId: string | null;
10
+ senderId: string;
11
+ senderName: string | null;
12
+ senderType: string;
13
+ messageId: string;
14
+ text: string | null;
15
+ attachments: AttachmentDto[];
16
+ replyToMessageId: string | null;
17
+ occurredAt: string | null;
18
+ idempotencyKey: string | null;
19
+ metadata: Record<string, unknown>;
20
+ }
21
+ export declare function mapInboundMessage(request: InboundMessageRequestDto): NormalizedInboundMessageEvent;
@@ -0,0 +1,22 @@
1
+ export function mapInboundMessage(request) {
2
+ return {
3
+ eventId: request.eventId,
4
+ eventType: "message.created",
5
+ accountId: request.accountId,
6
+ conversationId: request.conversation.conversationId,
7
+ conversationType: request.conversation.type,
8
+ conversationTitle: request.conversation.title ?? null,
9
+ threadId: request.threadId ?? null,
10
+ senderId: request.sender.id,
11
+ senderName: request.sender.name ?? null,
12
+ senderType: request.sender.type,
13
+ messageId: request.message.messageId,
14
+ text: request.message.text ?? null,
15
+ attachments: request.message.attachments ?? [],
16
+ replyToMessageId: request.message.replyToMessageId ?? null,
17
+ occurredAt: request.occurredAt ?? null,
18
+ idempotencyKey: request.idempotencyKey ?? null,
19
+ // Keep metadata object-shaped for downstream handlers even when omitted.
20
+ metadata: request.metadata ?? {}
21
+ };
22
+ }
@@ -0,0 +1,4 @@
1
+ import type { InboundMessageRequestDto } from "../protocol/dto.js";
2
+ type InboundMessageRequest = InboundMessageRequestDto;
3
+ export declare function validateInboundMessage(request: InboundMessageRequest): asserts request is InboundMessageRequestDto;
4
+ export {};