botinabox 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +1 -0
- package/dist/{channel-m9f7MFD7.d.ts → channel-06G0vbIn.d.ts} +1 -0
- package/dist/channels/discord/index.d.ts +1 -1
- package/dist/channels/slack/index.d.ts +1 -1
- package/dist/channels/webhook/index.d.ts +1 -1
- package/dist/index.d.ts +97 -5
- package/package.json +3 -2
- package/dist/channels/discord/index.js +0 -98
- package/dist/channels/slack/index.js +0 -80
- package/dist/channels/webhook/index.js +0 -178
- package/dist/chunk-DLJKZD3Q.js +0 -22
- package/dist/chunk-QLA6YOFN.js +0 -22
- package/dist/inbound-AFOHYNUY.js +0 -6
- package/dist/inbound-SNEMBLGA.js +0 -6
- package/dist/index.js +0 -2965
- package/dist/providers/anthropic/index.js +0 -169
- package/dist/providers/ollama/index.js +0 -179
- package/dist/providers/openai/index.js +0 -212
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `botinabox` are documented here.
|
|
4
|
+
|
|
5
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [0.2.0] — 2026-04-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Users primitive** — `users` and `user_identities` core tables. Users are protected objects (never auto-rendered into other entities' context). `UserRegistry` class: `register()`, `getById()`, `getByEmail()`, `resolveByIdentity()`, `resolveOrCreate()`, `addIdentity()`.
|
|
14
|
+
- **Secrets primitive** — `secrets` core table for encrypted credential storage. Protected by default. `SecretStore` class: `set()`, `get()`, `getMeta()`, `list()`, `rotate()`, `delete()`.
|
|
15
|
+
- **Message pipeline user resolution** — `MessagePipeline` accepts optional `UserRegistry`. When provided, resolves `InboundMessage.from` to a user ID via `resolveOrCreate()` before task creation. `InboundMessage.userId` field added.
|
|
16
|
+
- **`user_id` on messages table** — Tracks resolved user alongside raw `peer_id`.
|
|
17
|
+
- **Protected/encrypted passthrough** — `EntityContextDef` now supports `protected` and `encrypted` fields, passed through to Lattice's entity context system.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Core table count: 15 → 18 (added `users`, `user_identities`, `secrets`).
|
|
22
|
+
- `messages` table gains `user_id` column.
|
|
23
|
+
|
|
24
|
+
## [0.1.1] — 2026-03-28
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Initial release bug fixes and stability improvements.
|
|
29
|
+
|
|
30
|
+
## [0.1.0] — 2026-03-25
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Initial release: DataStore, HookBus, AgentRegistry, TaskQueue, RunManager, WakeupQueue, BudgetController, WorkflowEngine, SessionManager, ChannelRegistry, MessagePipeline.
|
|
35
|
+
- 15 core tables, LLM provider routing, channel adapters (Slack, Discord, Webhook).
|
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A modular TypeScript framework for building multi-agent bots with LLM orchestrat
|
|
|
11
11
|
- **SQLite data layer** — Schema-driven tables, migrations, entity context rendering, and query builder. WAL mode for concurrent reads.
|
|
12
12
|
- **Event-driven hooks** — Priority-ordered, filter-based event bus for decoupled inter-layer communication.
|
|
13
13
|
- **Budget controls** — Per-agent and global cost tracking with warning thresholds and hard stops.
|
|
14
|
+
- **Protected primitives** — Users and secrets are first-class, privacy-isolated objects. User identity resolution across channels. Secrets with optional AES-256-GCM at-rest encryption.
|
|
14
15
|
- **Security** — Input sanitization, field length enforcement, audit logging, and HMAC webhook verification.
|
|
15
16
|
- **Self-updating** — Built-in update checker with configurable policies and maintenance windows.
|
|
16
17
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-
|
|
1
|
+
import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-06G0vbIn.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* DiscordAdapter — ChannelAdapter implementation for Discord.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-
|
|
1
|
+
import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-06G0vbIn.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* SlackAdapter — ChannelAdapter implementation for Slack.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-
|
|
1
|
+
import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-06G0vbIn.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* WebhookAdapter — ChannelAdapter implementation for webhook-based channels.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as ChannelAdapter, H as HealthStatus, I as InboundMessage } from './channel-
|
|
2
|
-
export { A as Attachment, a as ChannelCapabilities, b as ChannelConfig, c as ChannelMeta, d as ChatType, F as FormattingMode, O as OutboundPayload, S as SendResult } from './channel-
|
|
1
|
+
import { C as ChannelAdapter, H as HealthStatus, I as InboundMessage } from './channel-06G0vbIn.js';
|
|
2
|
+
export { A as Attachment, a as ChannelCapabilities, b as ChannelConfig, c as ChannelMeta, d as ChatType, F as FormattingMode, O as OutboundPayload, S as SendResult } from './channel-06G0vbIn.js';
|
|
3
3
|
import { T as TokenUsage, L as LLMProvider, M as ModelInfo, R as ResolvedModel, C as ChatMessage } from './provider-qqJYv9nv.js';
|
|
4
4
|
export { a as ChatParams, b as ChatResult, c as ContentBlock, d as ToolDefinition, e as ToolUse } from './provider-qqJYv9nv.js';
|
|
5
5
|
import * as better_sqlite3 from 'better-sqlite3';
|
|
@@ -518,6 +518,12 @@ interface EntityContextDef {
|
|
|
518
518
|
files: Record<string, EntityFileSpec>;
|
|
519
519
|
indexFile?: string;
|
|
520
520
|
protectedFiles?: string[];
|
|
521
|
+
/** When true, this entity's data is never rendered into other entities' context files. */
|
|
522
|
+
protected?: boolean;
|
|
523
|
+
/** Enable at-rest encryption. Requires encryptionKey in Lattice options. */
|
|
524
|
+
encrypted?: boolean | {
|
|
525
|
+
columns: string[];
|
|
526
|
+
};
|
|
521
527
|
}
|
|
522
528
|
interface EntityFileSpec {
|
|
523
529
|
source: EntitySource;
|
|
@@ -741,6 +747,52 @@ declare class TaskQueue {
|
|
|
741
747
|
private poll;
|
|
742
748
|
}
|
|
743
749
|
|
|
750
|
+
interface UserInput {
|
|
751
|
+
id?: string;
|
|
752
|
+
org_id?: string;
|
|
753
|
+
name: string;
|
|
754
|
+
email?: string;
|
|
755
|
+
role?: string;
|
|
756
|
+
title?: string;
|
|
757
|
+
external_id?: string;
|
|
758
|
+
channel?: string;
|
|
759
|
+
timezone?: string;
|
|
760
|
+
preferences?: string;
|
|
761
|
+
notes?: string;
|
|
762
|
+
}
|
|
763
|
+
interface User {
|
|
764
|
+
id: string;
|
|
765
|
+
org_id: string | null;
|
|
766
|
+
name: string;
|
|
767
|
+
email: string | null;
|
|
768
|
+
role: string | null;
|
|
769
|
+
title: string | null;
|
|
770
|
+
external_id: string | null;
|
|
771
|
+
channel: string | null;
|
|
772
|
+
timezone: string | null;
|
|
773
|
+
preferences: string;
|
|
774
|
+
notes: string | null;
|
|
775
|
+
created_at: string;
|
|
776
|
+
updated_at: string;
|
|
777
|
+
deleted_at: string | null;
|
|
778
|
+
}
|
|
779
|
+
declare class UserRegistry {
|
|
780
|
+
private readonly db;
|
|
781
|
+
private readonly hooks;
|
|
782
|
+
constructor(db: DataStore, hooks: HookBus);
|
|
783
|
+
register(input: UserInput): Promise<User>;
|
|
784
|
+
getById(id: string): Promise<User | null>;
|
|
785
|
+
getByEmail(email: string): Promise<User | null>;
|
|
786
|
+
resolveByIdentity(channel: string, externalId: string): Promise<User | null>;
|
|
787
|
+
resolveOrCreate(externalId: string, channel: string, defaults?: Partial<UserInput>): Promise<User>;
|
|
788
|
+
list(filter?: {
|
|
789
|
+
role?: string;
|
|
790
|
+
org_id?: string;
|
|
791
|
+
}): Promise<User[]>;
|
|
792
|
+
update(id: string, changes: Partial<UserInput>): Promise<void>;
|
|
793
|
+
addIdentity(userId: string, channel: string, externalId: string, displayName?: string): Promise<void>;
|
|
794
|
+
}
|
|
795
|
+
|
|
744
796
|
/**
|
|
745
797
|
* MessagePipeline — routes inbound messages to the task queue.
|
|
746
798
|
* Story 4.2
|
|
@@ -752,7 +804,8 @@ declare class MessagePipeline {
|
|
|
752
804
|
private readonly taskQueue;
|
|
753
805
|
private readonly config;
|
|
754
806
|
private readonly agentBindings;
|
|
755
|
-
|
|
807
|
+
private readonly userRegistry?;
|
|
808
|
+
constructor(hooks: HookBus, agentRegistry: AgentRegistry, taskQueue: TaskQueue, config: BotConfig, userRegistry?: UserRegistry);
|
|
756
809
|
/**
|
|
757
810
|
* Process an inbound message end-to-end.
|
|
758
811
|
* 1. Emit 'message.inbound'
|
|
@@ -906,7 +959,7 @@ declare function chunkText(text: string, maxLen: number): string[];
|
|
|
906
959
|
declare function formatText(text: string, mode: "mrkdwn" | "html" | "plain"): string;
|
|
907
960
|
|
|
908
961
|
/**
|
|
909
|
-
* Define all
|
|
962
|
+
* Define all 18 core tables on a DataStore instance.
|
|
910
963
|
* Call before db.init().
|
|
911
964
|
*/
|
|
912
965
|
declare function defineCoreTables(db: DataStore): void;
|
|
@@ -1265,4 +1318,43 @@ declare class CliExecutionAdapter {
|
|
|
1265
1318
|
}>;
|
|
1266
1319
|
}
|
|
1267
1320
|
|
|
1268
|
-
|
|
1321
|
+
interface SecretInput {
|
|
1322
|
+
name: string;
|
|
1323
|
+
type?: string;
|
|
1324
|
+
environment?: string;
|
|
1325
|
+
value?: string;
|
|
1326
|
+
location?: string;
|
|
1327
|
+
description?: string;
|
|
1328
|
+
rotation_schedule?: string;
|
|
1329
|
+
expires_at?: string;
|
|
1330
|
+
notes?: string;
|
|
1331
|
+
org_id?: string;
|
|
1332
|
+
}
|
|
1333
|
+
interface SecretMeta {
|
|
1334
|
+
id: string;
|
|
1335
|
+
org_id: string | null;
|
|
1336
|
+
name: string;
|
|
1337
|
+
type: string;
|
|
1338
|
+
environment: string;
|
|
1339
|
+
location: string | null;
|
|
1340
|
+
description: string | null;
|
|
1341
|
+
rotation_schedule: string | null;
|
|
1342
|
+
expires_at: string | null;
|
|
1343
|
+
notes: string | null;
|
|
1344
|
+
created_at: string;
|
|
1345
|
+
updated_at: string;
|
|
1346
|
+
}
|
|
1347
|
+
declare class SecretStore {
|
|
1348
|
+
private readonly db;
|
|
1349
|
+
private readonly hooks;
|
|
1350
|
+
constructor(db: DataStore, hooks: HookBus);
|
|
1351
|
+
set(input: SecretInput): Promise<SecretMeta>;
|
|
1352
|
+
get(name: string, environment?: string): Promise<string | null>;
|
|
1353
|
+
getMeta(name: string, environment?: string): Promise<SecretMeta | null>;
|
|
1354
|
+
list(): Promise<SecretMeta[]>;
|
|
1355
|
+
rotate(name: string, newValue: string, environment?: string): Promise<void>;
|
|
1356
|
+
delete(name: string, environment?: string): Promise<void>;
|
|
1357
|
+
private _toMeta;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
export { AGENT_STATUSES, type AgentConfig, type AgentDefinition, type AgentFilter, type AgentRecord, AgentRegistry, type AgentStatus, ApiExecutionAdapter, AuditEmitter, type AuditEvent, BackupManager, type BotConfig, type BudgetCheck, type BudgetConfig, BudgetController, CORE_MIGRATIONS, ChannelAdapter, ChannelRegistry, ChannelRegistryError, ChatMessage, ChatSessionManager, CliExecutionAdapter, type ColumnValidator, ColumnValidatorImpl, type ConfigLoadError, type ConfigLoadResult, DEFAULTS, DEFAULT_CONFIG, type DataConfig, DataStore, DataStoreError, EVENTS, type EntityColumnDef, type EntityConfig, type EntityContextDef, type EntityFileSpec, type EntitySource, type ExecutionAdapter, type Filter, HealthStatus, HeartbeatScheduler, HookBus, type HookHandler, type HookOptions, type HookRegistration, InboundMessage, LLMProvider, MAX_CHAIN_DEPTH, MessagePipeline, type ModelConfig, ModelInfo, ModelRouter, NdjsonLogger, NotificationQueue, type PackageMigration, type PackageUpdate, type PkLookup, ProviderRegistry, type QueryOptions, RUN_STATUSES, type RelationDef, type RenderConfig, ResolvedModel, type RetryPolicy, type Row, type RunContext, RunManager, type RunResult, type RunStatus, type SanitizerOptions, type SchemaError, type SecretInput, type SecretMeta, SecretStore, type SecurityConfig, type SeedItem, SessionKey, SessionManager, type SqliteAdapter, type StepRef, TASK_STATUSES, type TableDefinition, type TableInfoRow, type TaskDefinition, TaskQueue, type TaskRecord, type TaskStatus, TokenUsage, type Unsubscribe, UpdateChecker, type UpdateConfig, UpdateManager, type UpdateManifest, type User, type UserInput, UserRegistry, WakeupQueue, type WorkflowConfigEntry, type WorkflowDefinition$1 as WorkflowDefinition, WorkflowEngine, type WorkflowRunRecord, type WorkflowRunStatus, type WorkflowStep$1 as WorkflowStep, type WorkflowStepConfig, type WorkflowTrigger, _resetConfig, areDependenciesMet, buildAgentBindings, buildChainOrigin, checkAllowlist, checkChainDepth, checkMentionGate, chunkText, classifyUpdate, compareVersions, createConfigRevision, defineCoreTables, detectCycle, discoverChannels, discoverProviders, formatText, getConfig, initConfig, interpolate, interpolateEnv, loadConfig, parseVersion, runPackageMigrations, sanitize, topologicalSort, validateConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botinabox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Bot in a Box — framework for building multi-agent bots",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,8 +44,9 @@
|
|
|
44
44
|
"typecheck": "tsc --noEmit"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"@types/uuid": "^10.0.0",
|
|
47
48
|
"ajv": "^8.17.1",
|
|
48
|
-
"latticesql": "^0.
|
|
49
|
+
"latticesql": "^0.18.0",
|
|
49
50
|
"yaml": "^2.7.0"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseDiscordEvent
|
|
3
|
-
} from "../../chunk-DLJKZD3Q.js";
|
|
4
|
-
|
|
5
|
-
// src/channels/discord/outbound.ts
|
|
6
|
-
var DISCORD_MAX_LENGTH = 2e3;
|
|
7
|
-
function chunkForDiscord(text) {
|
|
8
|
-
if (text.length <= DISCORD_MAX_LENGTH) return [text];
|
|
9
|
-
const chunks = [];
|
|
10
|
-
let remaining = text;
|
|
11
|
-
while (remaining.length > DISCORD_MAX_LENGTH) {
|
|
12
|
-
const slice = remaining.slice(0, DISCORD_MAX_LENGTH);
|
|
13
|
-
const lastSpace = slice.lastIndexOf(" ");
|
|
14
|
-
if (lastSpace > 0) {
|
|
15
|
-
chunks.push(remaining.slice(0, lastSpace));
|
|
16
|
-
remaining = remaining.slice(lastSpace + 1);
|
|
17
|
-
} else {
|
|
18
|
-
chunks.push(remaining.slice(0, DISCORD_MAX_LENGTH));
|
|
19
|
-
remaining = remaining.slice(DISCORD_MAX_LENGTH);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
if (remaining.length > 0) chunks.push(remaining);
|
|
23
|
-
return chunks;
|
|
24
|
-
}
|
|
25
|
-
function formatForDiscord(text) {
|
|
26
|
-
return text;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// src/channels/discord/adapter.ts
|
|
30
|
-
var DiscordAdapter = class {
|
|
31
|
-
id = "discord";
|
|
32
|
-
meta = {
|
|
33
|
-
displayName: "Discord",
|
|
34
|
-
icon: "https://discord.com/favicon.ico",
|
|
35
|
-
homepage: "https://discord.com"
|
|
36
|
-
};
|
|
37
|
-
capabilities = {
|
|
38
|
-
chatTypes: ["direct", "group", "channel"],
|
|
39
|
-
threads: true,
|
|
40
|
-
reactions: true,
|
|
41
|
-
editing: true,
|
|
42
|
-
media: true,
|
|
43
|
-
polls: false,
|
|
44
|
-
maxTextLength: 2e3,
|
|
45
|
-
formattingMode: "markdown"
|
|
46
|
-
};
|
|
47
|
-
onMessage;
|
|
48
|
-
connected = false;
|
|
49
|
-
config = null;
|
|
50
|
-
client;
|
|
51
|
-
constructor(client) {
|
|
52
|
-
this.client = client ?? null;
|
|
53
|
-
}
|
|
54
|
-
async connect(config) {
|
|
55
|
-
this.config = config;
|
|
56
|
-
this.connected = true;
|
|
57
|
-
}
|
|
58
|
-
async disconnect() {
|
|
59
|
-
this.connected = false;
|
|
60
|
-
this.config = null;
|
|
61
|
-
}
|
|
62
|
-
async healthCheck() {
|
|
63
|
-
return { ok: this.connected };
|
|
64
|
-
}
|
|
65
|
-
async send(target, payload) {
|
|
66
|
-
if (!this.connected) {
|
|
67
|
-
return { success: false, error: "Not connected" };
|
|
68
|
-
}
|
|
69
|
-
const text = formatForDiscord(payload.text);
|
|
70
|
-
if (this.client) {
|
|
71
|
-
try {
|
|
72
|
-
const result = await this.client.sendMessage(target.peerId, text);
|
|
73
|
-
return { success: true, messageId: result.id };
|
|
74
|
-
} catch (err) {
|
|
75
|
-
return { success: false, error: String(err) };
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return { success: true };
|
|
79
|
-
}
|
|
80
|
-
/** Simulate receiving an inbound message (for testing/webhooks). */
|
|
81
|
-
async receive(event) {
|
|
82
|
-
if (this.onMessage) {
|
|
83
|
-
const { parseDiscordEvent: parseDiscordEvent2 } = await import("../../inbound-SNEMBLGA.js");
|
|
84
|
-
const msg = parseDiscordEvent2(event);
|
|
85
|
-
await this.onMessage(msg);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
function createDiscordAdapter(client) {
|
|
90
|
-
return new DiscordAdapter(client);
|
|
91
|
-
}
|
|
92
|
-
export {
|
|
93
|
-
DiscordAdapter,
|
|
94
|
-
chunkForDiscord,
|
|
95
|
-
createDiscordAdapter as default,
|
|
96
|
-
formatForDiscord,
|
|
97
|
-
parseDiscordEvent
|
|
98
|
-
};
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseSlackEvent
|
|
3
|
-
} from "../../chunk-QLA6YOFN.js";
|
|
4
|
-
|
|
5
|
-
// src/channels/slack/outbound.ts
|
|
6
|
-
function formatForSlack(text) {
|
|
7
|
-
let result = text.replace(/\*\*(.+?)\*\*/gs, "*$1*");
|
|
8
|
-
result = result.replace(/__(.+?)__/gs, "*$1*");
|
|
9
|
-
return result;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// src/channels/slack/adapter.ts
|
|
13
|
-
var SlackAdapter = class {
|
|
14
|
-
id = "slack";
|
|
15
|
-
meta = {
|
|
16
|
-
displayName: "Slack",
|
|
17
|
-
icon: "https://slack.com/favicon.ico",
|
|
18
|
-
homepage: "https://slack.com"
|
|
19
|
-
};
|
|
20
|
-
capabilities = {
|
|
21
|
-
chatTypes: ["direct", "group", "channel"],
|
|
22
|
-
threads: true,
|
|
23
|
-
reactions: true,
|
|
24
|
-
editing: true,
|
|
25
|
-
media: true,
|
|
26
|
-
polls: false,
|
|
27
|
-
maxTextLength: 4e4,
|
|
28
|
-
formattingMode: "mrkdwn"
|
|
29
|
-
};
|
|
30
|
-
onMessage;
|
|
31
|
-
connected = false;
|
|
32
|
-
config = null;
|
|
33
|
-
client;
|
|
34
|
-
constructor(client) {
|
|
35
|
-
this.client = client ?? null;
|
|
36
|
-
}
|
|
37
|
-
async connect(config) {
|
|
38
|
-
this.config = config;
|
|
39
|
-
this.connected = true;
|
|
40
|
-
}
|
|
41
|
-
async disconnect() {
|
|
42
|
-
this.connected = false;
|
|
43
|
-
this.config = null;
|
|
44
|
-
}
|
|
45
|
-
async healthCheck() {
|
|
46
|
-
return { ok: this.connected };
|
|
47
|
-
}
|
|
48
|
-
async send(target, payload) {
|
|
49
|
-
if (!this.connected) {
|
|
50
|
-
return { success: false, error: "Not connected" };
|
|
51
|
-
}
|
|
52
|
-
const text = formatForSlack(payload.text);
|
|
53
|
-
if (this.client) {
|
|
54
|
-
try {
|
|
55
|
-
const result = await this.client.postMessage(target.peerId, text, target.threadId);
|
|
56
|
-
return { success: result.ok, messageId: result.ts };
|
|
57
|
-
} catch (err) {
|
|
58
|
-
return { success: false, error: String(err) };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return { success: true };
|
|
62
|
-
}
|
|
63
|
-
/** Simulate receiving an inbound message (for testing/webhooks). */
|
|
64
|
-
async receive(event) {
|
|
65
|
-
if (this.onMessage) {
|
|
66
|
-
const { parseSlackEvent: parseSlackEvent2 } = await import("../../inbound-AFOHYNUY.js");
|
|
67
|
-
const msg = parseSlackEvent2(event);
|
|
68
|
-
await this.onMessage(msg);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
function createSlackAdapter(client) {
|
|
73
|
-
return new SlackAdapter(client);
|
|
74
|
-
}
|
|
75
|
-
export {
|
|
76
|
-
SlackAdapter,
|
|
77
|
-
createSlackAdapter as default,
|
|
78
|
-
formatForSlack,
|
|
79
|
-
parseSlackEvent
|
|
80
|
-
};
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
// src/channels/webhook/server.ts
|
|
2
|
-
import { createServer } from "http";
|
|
3
|
-
|
|
4
|
-
// src/channels/webhook/hmac.ts
|
|
5
|
-
import { createHmac, timingSafeEqual } from "crypto";
|
|
6
|
-
function verifyHmac(body, secret, signature) {
|
|
7
|
-
const expected = createHmac("sha256", secret).update(body, "utf8").digest("hex");
|
|
8
|
-
const provided = signature.startsWith("sha256=") ? signature.slice(7) : signature;
|
|
9
|
-
if (expected.length !== provided.length) return false;
|
|
10
|
-
try {
|
|
11
|
-
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(provided, "hex"));
|
|
12
|
-
} catch {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// src/channels/webhook/server.ts
|
|
18
|
-
var WebhookServer = class {
|
|
19
|
-
server = null;
|
|
20
|
-
port;
|
|
21
|
-
secret;
|
|
22
|
-
onMessage;
|
|
23
|
-
constructor(opts) {
|
|
24
|
-
this.port = opts.port ?? 3200;
|
|
25
|
-
this.secret = opts.secret;
|
|
26
|
-
this.onMessage = opts.onMessage;
|
|
27
|
-
}
|
|
28
|
-
start() {
|
|
29
|
-
return new Promise((resolve) => {
|
|
30
|
-
this.server = createServer((req, res) => {
|
|
31
|
-
void this.handleRequest(req, res);
|
|
32
|
-
});
|
|
33
|
-
this.server.listen(this.port, () => resolve());
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
stop() {
|
|
37
|
-
return new Promise((resolve, reject) => {
|
|
38
|
-
if (!this.server) {
|
|
39
|
-
resolve();
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
this.server.close((err) => {
|
|
43
|
-
if (err) reject(err);
|
|
44
|
-
else resolve();
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
async handleRequest(req, res) {
|
|
49
|
-
const url = req.url ?? "/";
|
|
50
|
-
const method = req.method ?? "GET";
|
|
51
|
-
if (method !== "POST" || url !== "/webhook/inbound") {
|
|
52
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
53
|
-
res.end(JSON.stringify({ error: "Not found" }));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
let body = "";
|
|
57
|
-
for await (const chunk of req) {
|
|
58
|
-
body += chunk;
|
|
59
|
-
}
|
|
60
|
-
if (this.secret) {
|
|
61
|
-
const sig = req.headers["x-webhook-signature"];
|
|
62
|
-
if (!sig || !verifyHmac(body, this.secret, sig)) {
|
|
63
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
64
|
-
res.end(JSON.stringify({ error: "Invalid signature" }));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
let parsed;
|
|
69
|
-
try {
|
|
70
|
-
parsed = JSON.parse(body);
|
|
71
|
-
} catch {
|
|
72
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
73
|
-
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const msg = {
|
|
77
|
-
id: parsed["id"] ?? `webhook-${Date.now()}`,
|
|
78
|
-
channel: "webhook",
|
|
79
|
-
from: parsed["from"] ?? "unknown",
|
|
80
|
-
body: parsed["text"] ?? "",
|
|
81
|
-
threadId: parsed["threadId"],
|
|
82
|
-
receivedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
83
|
-
raw: parsed
|
|
84
|
-
};
|
|
85
|
-
try {
|
|
86
|
-
await this.onMessage(msg);
|
|
87
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
88
|
-
res.end(JSON.stringify({ ok: true }));
|
|
89
|
-
} catch (err) {
|
|
90
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
91
|
-
res.end(JSON.stringify({ error: String(err) }));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// src/channels/webhook/adapter.ts
|
|
97
|
-
var WebhookAdapter = class {
|
|
98
|
-
id = "webhook";
|
|
99
|
-
meta = {
|
|
100
|
-
displayName: "Webhook",
|
|
101
|
-
homepage: "https://example.com"
|
|
102
|
-
};
|
|
103
|
-
capabilities = {
|
|
104
|
-
chatTypes: ["direct"],
|
|
105
|
-
threads: false,
|
|
106
|
-
reactions: false,
|
|
107
|
-
editing: false,
|
|
108
|
-
media: false,
|
|
109
|
-
polls: false,
|
|
110
|
-
maxTextLength: 65535,
|
|
111
|
-
formattingMode: "plain"
|
|
112
|
-
};
|
|
113
|
-
onMessage;
|
|
114
|
-
connected = false;
|
|
115
|
-
config = null;
|
|
116
|
-
webhookServer = null;
|
|
117
|
-
async connect(config) {
|
|
118
|
-
this.config = config;
|
|
119
|
-
this.connected = true;
|
|
120
|
-
if (this.config.port) {
|
|
121
|
-
this.webhookServer = new WebhookServer({
|
|
122
|
-
port: this.config.port,
|
|
123
|
-
secret: this.config.secret,
|
|
124
|
-
onMessage: async (msg) => {
|
|
125
|
-
if (this.onMessage) await this.onMessage(msg);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
await this.webhookServer.start();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
async disconnect() {
|
|
132
|
-
if (this.webhookServer) {
|
|
133
|
-
await this.webhookServer.stop();
|
|
134
|
-
this.webhookServer = null;
|
|
135
|
-
}
|
|
136
|
-
this.connected = false;
|
|
137
|
-
this.config = null;
|
|
138
|
-
}
|
|
139
|
-
async healthCheck() {
|
|
140
|
-
return { ok: this.connected };
|
|
141
|
-
}
|
|
142
|
-
async send(target, payload) {
|
|
143
|
-
if (!this.connected) {
|
|
144
|
-
return { success: false, error: "Not connected" };
|
|
145
|
-
}
|
|
146
|
-
const callbackUrl = this.config?.callbackUrl;
|
|
147
|
-
if (!callbackUrl) {
|
|
148
|
-
return { success: true };
|
|
149
|
-
}
|
|
150
|
-
try {
|
|
151
|
-
const body = JSON.stringify({
|
|
152
|
-
to: target.peerId,
|
|
153
|
-
threadId: target.threadId,
|
|
154
|
-
text: payload.text
|
|
155
|
-
});
|
|
156
|
-
const response = await fetch(callbackUrl, {
|
|
157
|
-
method: "POST",
|
|
158
|
-
headers: { "Content-Type": "application/json" },
|
|
159
|
-
body
|
|
160
|
-
});
|
|
161
|
-
if (response.ok) {
|
|
162
|
-
return { success: true };
|
|
163
|
-
}
|
|
164
|
-
return { success: false, error: `HTTP ${response.status}` };
|
|
165
|
-
} catch (err) {
|
|
166
|
-
return { success: false, error: String(err) };
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
function createWebhookAdapter() {
|
|
171
|
-
return new WebhookAdapter();
|
|
172
|
-
}
|
|
173
|
-
export {
|
|
174
|
-
WebhookAdapter,
|
|
175
|
-
WebhookServer,
|
|
176
|
-
createWebhookAdapter as default,
|
|
177
|
-
verifyHmac
|
|
178
|
-
};
|
package/dist/chunk-DLJKZD3Q.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// src/channels/discord/inbound.ts
|
|
2
|
-
function parseDiscordEvent(event) {
|
|
3
|
-
const id = event.id ?? `discord-${Date.now()}`;
|
|
4
|
-
const channel = event.channel_id ?? "unknown";
|
|
5
|
-
const from = event.author?.id ?? "unknown";
|
|
6
|
-
const body = event.content ?? "";
|
|
7
|
-
const replyToId = event.message_reference?.message_id;
|
|
8
|
-
const receivedAt = event.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9
|
-
return {
|
|
10
|
-
id,
|
|
11
|
-
channel,
|
|
12
|
-
from,
|
|
13
|
-
body,
|
|
14
|
-
replyToId,
|
|
15
|
-
receivedAt,
|
|
16
|
-
raw: event
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
parseDiscordEvent
|
|
22
|
-
};
|
package/dist/chunk-QLA6YOFN.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// src/channels/slack/inbound.ts
|
|
2
|
-
function parseSlackEvent(event) {
|
|
3
|
-
const id = event.client_msg_id ?? event.ts ?? event.event_ts ?? `slack-${Date.now()}`;
|
|
4
|
-
const channel = event.channel ?? "unknown";
|
|
5
|
-
const from = event.user ?? "unknown";
|
|
6
|
-
const body = event.text ?? "";
|
|
7
|
-
const threadId = event.thread_ts !== void 0 ? event.thread_ts : void 0;
|
|
8
|
-
const receivedAt = event.ts ? new Date(parseFloat(event.ts) * 1e3).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
9
|
-
return {
|
|
10
|
-
id,
|
|
11
|
-
channel,
|
|
12
|
-
from,
|
|
13
|
-
body,
|
|
14
|
-
threadId,
|
|
15
|
-
receivedAt,
|
|
16
|
-
raw: event
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
parseSlackEvent
|
|
22
|
-
};
|
package/dist/inbound-AFOHYNUY.js
DELETED