botinabox 0.5.1 → 0.5.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.
- package/README.md +6 -0
- package/dist/chunk-3X3YKI4T.js +357 -0
- package/dist/chunk-D47AIFOD.js +351 -0
- package/dist/chunk-DSNJKNEW.js +328 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +220 -0
- package/dist/connector-B4Mj0P1b.d.ts +79 -0
- package/dist/connectors/google/index.d.ts +40 -18
- package/dist/connectors/google/index.js +69 -309
- package/dist/gmail-connector-2FVYTQJH.js +6 -0
- package/dist/gmail-connector-MNUBRNFM.js +6 -0
- package/dist/gmail-connector-URRFX6A3.js +6 -0
- package/dist/index.d.ts +2 -2
- package/package.json +10 -2
- package/CHANGELOG.md +0 -68
- package/CONTRIBUTING.md +0 -92
|
@@ -1,296 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return _google;
|
|
15
|
-
}
|
|
16
|
-
async function createOAuth2Client(config) {
|
|
17
|
-
const google = await getGoogle();
|
|
18
|
-
return new google.auth.OAuth2(
|
|
19
|
-
config.clientId,
|
|
20
|
-
config.clientSecret,
|
|
21
|
-
config.redirectUri
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
function getAuthUrl(client, scopes) {
|
|
25
|
-
return client.generateAuthUrl({
|
|
26
|
-
access_type: "offline",
|
|
27
|
-
prompt: "consent",
|
|
28
|
-
scope: scopes
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
async function exchangeCode(client, code) {
|
|
32
|
-
const { tokens } = await client.getToken(code);
|
|
33
|
-
return tokens;
|
|
34
|
-
}
|
|
35
|
-
async function loadTokens(getter, accountKey) {
|
|
36
|
-
const raw = await getter(`google_tokens:${accountKey}`);
|
|
37
|
-
if (!raw) return null;
|
|
38
|
-
try {
|
|
39
|
-
return JSON.parse(raw);
|
|
40
|
-
} catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async function saveTokens(setter, accountKey, tokens) {
|
|
45
|
-
await setter(`google_tokens:${accountKey}`, JSON.stringify(tokens));
|
|
46
|
-
}
|
|
47
|
-
async function refreshIfNeeded(client, tokens, saver) {
|
|
48
|
-
const buffer = 6e4;
|
|
49
|
-
const isExpired = tokens.expiry_date != null && Date.now() >= tokens.expiry_date - buffer;
|
|
50
|
-
if (!isExpired) return tokens;
|
|
51
|
-
client.setCredentials(tokens);
|
|
52
|
-
const { credentials } = await client.refreshAccessToken();
|
|
53
|
-
const refreshed = {
|
|
54
|
-
access_token: credentials.access_token,
|
|
55
|
-
refresh_token: credentials.refresh_token ?? tokens.refresh_token,
|
|
56
|
-
expiry_date: credentials.expiry_date ?? void 0,
|
|
57
|
-
token_type: credentials.token_type ?? "Bearer"
|
|
58
|
-
};
|
|
59
|
-
if (saver) {
|
|
60
|
-
await saver(refreshed);
|
|
61
|
-
}
|
|
62
|
-
return refreshed;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// src/connectors/google/gmail-connector.ts
|
|
66
|
-
var GoogleGmailConnector = class {
|
|
67
|
-
id = "google-gmail";
|
|
68
|
-
meta = {
|
|
69
|
-
displayName: "Google Gmail",
|
|
70
|
-
provider: "google",
|
|
71
|
-
dataType: "email"
|
|
72
|
-
};
|
|
73
|
-
tokenLoader;
|
|
74
|
-
tokenSaver;
|
|
75
|
-
client = null;
|
|
76
|
-
config = null;
|
|
77
|
-
tokens = null;
|
|
78
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
-
gmail = null;
|
|
80
|
-
constructor(opts) {
|
|
81
|
-
this.tokenLoader = opts.tokenLoader;
|
|
82
|
-
this.tokenSaver = opts.tokenSaver;
|
|
83
|
-
}
|
|
84
|
-
// ── Lifecycle ──────────────────────────────────────────────────
|
|
85
|
-
async connect(config) {
|
|
86
|
-
this.config = config;
|
|
87
|
-
this.client = await createOAuth2Client(config.oauth);
|
|
88
|
-
this.tokens = await loadTokens(this.tokenLoader, config.account);
|
|
89
|
-
if (!this.tokens) {
|
|
90
|
-
throw new Error(
|
|
91
|
-
`No stored tokens for account ${config.account}. Complete the OAuth flow first.`
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
this.tokens = await refreshIfNeeded(
|
|
95
|
-
this.client,
|
|
96
|
-
this.tokens,
|
|
97
|
-
async (t) => saveTokens(this.tokenSaver, config.account, t)
|
|
98
|
-
);
|
|
99
|
-
this.client.setCredentials(this.tokens);
|
|
100
|
-
const { google } = await import("googleapis");
|
|
101
|
-
this.gmail = google.gmail({ version: "v1", auth: this.client });
|
|
102
|
-
}
|
|
103
|
-
async disconnect() {
|
|
104
|
-
this.client = null;
|
|
105
|
-
this.gmail = null;
|
|
106
|
-
this.tokens = null;
|
|
107
|
-
this.config = null;
|
|
108
|
-
}
|
|
109
|
-
async healthCheck() {
|
|
110
|
-
try {
|
|
111
|
-
this.ensureConnected();
|
|
112
|
-
const res = await this.gmail.users.getProfile({ userId: "me" });
|
|
113
|
-
return { ok: true, account: res.data.emailAddress };
|
|
114
|
-
} catch (err) {
|
|
115
|
-
return { ok: false, error: errorMessage(err) };
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// ── Sync ───────────────────────────────────────────────────────
|
|
119
|
-
async sync(options) {
|
|
120
|
-
this.ensureConnected();
|
|
121
|
-
if (options?.cursor) {
|
|
122
|
-
return this.syncIncremental(options.cursor, options.limit);
|
|
123
|
-
}
|
|
124
|
-
return this.syncFull(options);
|
|
125
|
-
}
|
|
126
|
-
/** Incremental sync using Gmail history API. */
|
|
127
|
-
async syncIncremental(startHistoryId, limit) {
|
|
128
|
-
const records = [];
|
|
129
|
-
const errors = [];
|
|
130
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
131
|
-
let pageToken;
|
|
132
|
-
let latestHistoryId = startHistoryId;
|
|
133
|
-
do {
|
|
134
|
-
const res = await this.gmail.users.history.list({
|
|
135
|
-
userId: "me",
|
|
136
|
-
startHistoryId,
|
|
137
|
-
historyTypes: ["messageAdded"],
|
|
138
|
-
...pageToken ? { pageToken } : {}
|
|
139
|
-
});
|
|
140
|
-
latestHistoryId = res.data.historyId ?? latestHistoryId;
|
|
141
|
-
const histories = res.data.history ?? [];
|
|
142
|
-
for (const h of histories) {
|
|
143
|
-
for (const added of h.messagesAdded ?? []) {
|
|
144
|
-
const msgId = added.message?.id;
|
|
145
|
-
if (!msgId || seenIds.has(msgId)) continue;
|
|
146
|
-
seenIds.add(msgId);
|
|
147
|
-
try {
|
|
148
|
-
const record = await this.fetchMessage(msgId);
|
|
149
|
-
records.push(record);
|
|
150
|
-
} catch (err) {
|
|
151
|
-
errors.push({ id: msgId, error: errorMessage(err) });
|
|
152
|
-
}
|
|
153
|
-
if (limit && records.length >= limit) {
|
|
154
|
-
return { records, cursor: latestHistoryId, hasMore: true, errors };
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
pageToken = res.data.nextPageToken ?? void 0;
|
|
159
|
-
} while (pageToken);
|
|
160
|
-
return { records, cursor: latestHistoryId, hasMore: false, errors };
|
|
161
|
-
}
|
|
162
|
-
/** Full sync — list messages and fetch each one. */
|
|
163
|
-
async syncFull(options) {
|
|
164
|
-
const records = [];
|
|
165
|
-
const errors = [];
|
|
166
|
-
const maxResults = options?.limit ?? 100;
|
|
167
|
-
let query = "";
|
|
168
|
-
if (options?.since) {
|
|
169
|
-
const epoch = Math.floor(new Date(options.since).getTime() / 1e3);
|
|
170
|
-
query = `after:${epoch}`;
|
|
171
|
-
}
|
|
172
|
-
if (options?.filters?.q) {
|
|
173
|
-
query = query ? `${query} ${options.filters.q}` : String(options.filters.q);
|
|
174
|
-
}
|
|
175
|
-
let pageToken;
|
|
176
|
-
let collected = 0;
|
|
177
|
-
do {
|
|
178
|
-
const res = await this.gmail.users.messages.list({
|
|
179
|
-
userId: "me",
|
|
180
|
-
maxResults: Math.min(maxResults - collected, 100),
|
|
181
|
-
...query ? { q: query } : {},
|
|
182
|
-
...pageToken ? { pageToken } : {}
|
|
183
|
-
});
|
|
184
|
-
const messages = res.data.messages ?? [];
|
|
185
|
-
for (const msg of messages) {
|
|
186
|
-
try {
|
|
187
|
-
const record = await this.fetchMessage(msg.id);
|
|
188
|
-
records.push(record);
|
|
189
|
-
} catch (err) {
|
|
190
|
-
errors.push({ id: msg.id, error: errorMessage(err) });
|
|
191
|
-
}
|
|
192
|
-
collected++;
|
|
193
|
-
if (collected >= maxResults) break;
|
|
194
|
-
}
|
|
195
|
-
pageToken = res.data.nextPageToken ?? void 0;
|
|
196
|
-
} while (pageToken && collected < maxResults);
|
|
197
|
-
const profile = await this.gmail.users.getProfile({ userId: "me" });
|
|
198
|
-
const cursor = profile.data.historyId ?? void 0;
|
|
199
|
-
return {
|
|
200
|
-
records,
|
|
201
|
-
cursor,
|
|
202
|
-
hasMore: !!pageToken,
|
|
203
|
-
errors
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
// ── Push (send email) ─────────────────────────────────────────
|
|
207
|
-
async push(payload) {
|
|
208
|
-
this.ensureConnected();
|
|
209
|
-
try {
|
|
210
|
-
const toHeader = payload.to.map(formatAddress).join(", ");
|
|
211
|
-
const ccHeader = payload.cc.length ? `Cc: ${payload.cc.map(formatAddress).join(", ")}\r
|
|
212
|
-
` : "";
|
|
213
|
-
const bccHeader = payload.bcc.length ? `Bcc: ${payload.bcc.map(formatAddress).join(", ")}\r
|
|
214
|
-
` : "";
|
|
215
|
-
const mime = [
|
|
216
|
-
`To: ${toHeader}\r
|
|
217
|
-
`,
|
|
218
|
-
ccHeader,
|
|
219
|
-
bccHeader,
|
|
220
|
-
`Subject: ${payload.subject}\r
|
|
221
|
-
`,
|
|
222
|
-
`Content-Type: text/plain; charset="UTF-8"\r
|
|
223
|
-
`,
|
|
224
|
-
`\r
|
|
225
|
-
`,
|
|
226
|
-
payload.body ?? ""
|
|
227
|
-
].join("");
|
|
228
|
-
const encoded = Buffer.from(mime).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
229
|
-
const res = await this.gmail.users.messages.send({
|
|
230
|
-
userId: "me",
|
|
231
|
-
requestBody: { raw: encoded }
|
|
232
|
-
});
|
|
233
|
-
return { success: true, externalId: res.data.id };
|
|
234
|
-
} catch (err) {
|
|
235
|
-
return { success: false, error: errorMessage(err) };
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
// ── Internals ─────────────────────────────────────────────────
|
|
239
|
-
ensureConnected() {
|
|
240
|
-
if (!this.gmail || !this.config) {
|
|
241
|
-
throw new Error("GoogleGmailConnector is not connected. Call connect() first.");
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
/** Fetch a single message by ID and parse into an EmailRecord. */
|
|
245
|
-
async fetchMessage(messageId) {
|
|
246
|
-
const res = await this.gmail.users.messages.get({
|
|
247
|
-
userId: "me",
|
|
248
|
-
id: messageId,
|
|
249
|
-
format: "metadata",
|
|
250
|
-
metadataHeaders: ["From", "To", "Cc", "Bcc", "Subject", "Date"]
|
|
251
|
-
});
|
|
252
|
-
const msg = res.data;
|
|
253
|
-
const headers = msg.payload?.headers ?? [];
|
|
254
|
-
const getHeader = (name) => headers.find((h) => h.name.toLowerCase() === name.toLowerCase())?.value ?? "";
|
|
255
|
-
return {
|
|
256
|
-
gmailId: msg.id,
|
|
257
|
-
threadId: msg.threadId,
|
|
258
|
-
account: this.config.account,
|
|
259
|
-
subject: getHeader("Subject"),
|
|
260
|
-
from: parseAddress(getHeader("From")),
|
|
261
|
-
to: parseAddressList(getHeader("To")),
|
|
262
|
-
cc: parseAddressList(getHeader("Cc")),
|
|
263
|
-
bcc: parseAddressList(getHeader("Bcc")),
|
|
264
|
-
date: new Date(getHeader("Date")).toISOString(),
|
|
265
|
-
snippet: msg.snippet ?? "",
|
|
266
|
-
labels: msg.labelIds ?? [],
|
|
267
|
-
isRead: !(msg.labelIds ?? []).includes("UNREAD")
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
function parseAddress(raw) {
|
|
272
|
-
const match = raw.match(/^(.+?)\s*<([^>]+)>$/);
|
|
273
|
-
if (match) {
|
|
274
|
-
return { name: match[1].replace(/^["']|["']$/g, "").trim(), email: match[2] };
|
|
275
|
-
}
|
|
276
|
-
return { email: raw.trim() };
|
|
277
|
-
}
|
|
278
|
-
function parseAddressList(raw) {
|
|
279
|
-
if (!raw.trim()) return [];
|
|
280
|
-
const results = [];
|
|
281
|
-
const parts = raw.split(/,(?=(?:[^<]*<[^>]*>)*[^>]*$)/);
|
|
282
|
-
for (const part of parts) {
|
|
283
|
-
const trimmed = part.trim();
|
|
284
|
-
if (trimmed) results.push(parseAddress(trimmed));
|
|
285
|
-
}
|
|
286
|
-
return results;
|
|
287
|
-
}
|
|
288
|
-
function formatAddress(addr) {
|
|
289
|
-
return addr.name ? `${addr.name} <${addr.email}>` : addr.email;
|
|
290
|
-
}
|
|
291
|
-
function errorMessage(err) {
|
|
292
|
-
return err instanceof Error ? err.message : String(err);
|
|
293
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
GoogleGmailConnector,
|
|
3
|
+
createOAuth2Client,
|
|
4
|
+
createServiceAccountClient,
|
|
5
|
+
exchangeCode,
|
|
6
|
+
getAuthUrl,
|
|
7
|
+
loadTokens,
|
|
8
|
+
refreshIfNeeded,
|
|
9
|
+
saveTokens
|
|
10
|
+
} from "../../chunk-3X3YKI4T.js";
|
|
294
11
|
|
|
295
12
|
// src/connectors/google/calendar-connector.ts
|
|
296
13
|
var GoogleCalendarConnector = class {
|
|
@@ -307,26 +24,38 @@ var GoogleCalendarConnector = class {
|
|
|
307
24
|
tokens = null;
|
|
308
25
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
309
26
|
calendar = null;
|
|
310
|
-
constructor(opts) {
|
|
27
|
+
constructor(opts = {}) {
|
|
311
28
|
this.tokenLoader = opts.tokenLoader;
|
|
312
29
|
this.tokenSaver = opts.tokenSaver;
|
|
313
30
|
}
|
|
314
31
|
// ── Lifecycle ──────────────────────────────────────────────────
|
|
315
32
|
async connect(config) {
|
|
316
33
|
this.config = config;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
34
|
+
const scopes = config.scopes ?? [
|
|
35
|
+
"https://www.googleapis.com/auth/calendar.readonly"
|
|
36
|
+
];
|
|
37
|
+
if (config.serviceAccount) {
|
|
38
|
+
this.client = await createServiceAccountClient(config.serviceAccount, scopes);
|
|
39
|
+
} else if (config.oauth) {
|
|
40
|
+
this.client = await createOAuth2Client(config.oauth);
|
|
41
|
+
if (!this.tokenLoader) {
|
|
42
|
+
throw new Error("tokenLoader required for OAuth2 flow");
|
|
43
|
+
}
|
|
44
|
+
this.tokens = await loadTokens(this.tokenLoader, config.account);
|
|
45
|
+
if (!this.tokens) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`No stored tokens for account ${config.account}. Complete the OAuth flow first.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
this.tokens = await refreshIfNeeded(
|
|
51
|
+
this.client,
|
|
52
|
+
this.tokens,
|
|
53
|
+
this.tokenSaver ? async (t) => saveTokens(this.tokenSaver, config.account, t) : void 0
|
|
322
54
|
);
|
|
55
|
+
this.client.setCredentials(this.tokens);
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error("Either serviceAccount or oauth config is required");
|
|
323
58
|
}
|
|
324
|
-
this.tokens = await refreshIfNeeded(
|
|
325
|
-
this.client,
|
|
326
|
-
this.tokens,
|
|
327
|
-
async (t) => saveTokens(this.tokenSaver, config.account, t)
|
|
328
|
-
);
|
|
329
|
-
this.client.setCredentials(this.tokens);
|
|
330
59
|
const { google } = await import("googleapis");
|
|
331
60
|
this.calendar = google.calendar({ version: "v3", auth: this.client });
|
|
332
61
|
}
|
|
@@ -346,7 +75,37 @@ var GoogleCalendarConnector = class {
|
|
|
346
75
|
);
|
|
347
76
|
return { ok: true, account: primary?.id ?? this.config.account };
|
|
348
77
|
} catch (err) {
|
|
349
|
-
return { ok: false, error:
|
|
78
|
+
return { ok: false, error: errorMessage(err) };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// ── Auth ───────────────────────────────────────────────────────
|
|
82
|
+
async authenticate(codeProvider) {
|
|
83
|
+
if (!this.config) {
|
|
84
|
+
return { success: false, error: "Call connect() first to set config, or pass config and call authenticate() before connect()." };
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
if (!this.config.oauth) {
|
|
88
|
+
return { success: false, error: "OAuth config required for browser-based authenticate(). Use serviceAccount for headless auth." };
|
|
89
|
+
}
|
|
90
|
+
if (!this.tokenSaver) {
|
|
91
|
+
return { success: false, error: "tokenSaver required for authenticate() flow." };
|
|
92
|
+
}
|
|
93
|
+
const client = await createOAuth2Client(this.config.oauth);
|
|
94
|
+
const scopes = this.config.scopes ?? [
|
|
95
|
+
"https://www.googleapis.com/auth/calendar.readonly"
|
|
96
|
+
];
|
|
97
|
+
const authUrl = getAuthUrl(client, scopes);
|
|
98
|
+
const code = await codeProvider(authUrl);
|
|
99
|
+
const tokens = await exchangeCode(client, code);
|
|
100
|
+
await saveTokens(this.tokenSaver, this.config.account, tokens);
|
|
101
|
+
this.tokens = tokens;
|
|
102
|
+
this.client = client;
|
|
103
|
+
this.client.setCredentials(tokens);
|
|
104
|
+
const { google } = await import("googleapis");
|
|
105
|
+
this.calendar = google.calendar({ version: "v3", auth: this.client });
|
|
106
|
+
return { success: true, account: this.config.account };
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return { success: false, error: errorMessage(err) };
|
|
350
109
|
}
|
|
351
110
|
}
|
|
352
111
|
// ── Sync ───────────────────────────────────────────────────────
|
|
@@ -376,7 +135,7 @@ var GoogleCalendarConnector = class {
|
|
|
376
135
|
try {
|
|
377
136
|
records.push(this.mapEvent(event, calendarId));
|
|
378
137
|
} catch (err) {
|
|
379
|
-
errors.push({ id: event.id, error:
|
|
138
|
+
errors.push({ id: event.id, error: errorMessage(err) });
|
|
380
139
|
}
|
|
381
140
|
if (options?.limit && records.length >= options.limit) break;
|
|
382
141
|
}
|
|
@@ -418,7 +177,7 @@ var GoogleCalendarConnector = class {
|
|
|
418
177
|
try {
|
|
419
178
|
records.push(this.mapEvent(event, calendarId));
|
|
420
179
|
} catch (err) {
|
|
421
|
-
errors.push({ id: event.id, error:
|
|
180
|
+
errors.push({ id: event.id, error: errorMessage(err) });
|
|
422
181
|
}
|
|
423
182
|
if (records.length >= maxResults) break;
|
|
424
183
|
}
|
|
@@ -472,13 +231,14 @@ var GoogleCalendarConnector = class {
|
|
|
472
231
|
};
|
|
473
232
|
}
|
|
474
233
|
};
|
|
475
|
-
function
|
|
234
|
+
function errorMessage(err) {
|
|
476
235
|
return err instanceof Error ? err.message : String(err);
|
|
477
236
|
}
|
|
478
237
|
export {
|
|
479
238
|
GoogleCalendarConnector,
|
|
480
239
|
GoogleGmailConnector,
|
|
481
240
|
createOAuth2Client,
|
|
241
|
+
createServiceAccountClient,
|
|
482
242
|
exchangeCode,
|
|
483
243
|
getAuthUrl,
|
|
484
244
|
loadTokens,
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { C as ChannelAdapter, H as HealthStatus, I as InboundMessage } from './c
|
|
|
2
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
|
-
import { C as ConnectorConfig } from './connector-
|
|
6
|
-
export { a as Connector, b as ConnectorMeta, P as PushResult, S as SyncOptions, c as SyncResult } from './connector-
|
|
5
|
+
import { C as ConnectorConfig } from './connector-B4Mj0P1b.js';
|
|
6
|
+
export { A as AuthResult, a as Connector, b as ConnectorMeta, P as PushResult, S as SyncOptions, c as SyncResult } from './connector-B4Mj0P1b.js';
|
|
7
7
|
import * as better_sqlite3 from 'better-sqlite3';
|
|
8
8
|
|
|
9
9
|
/** Execution adapter types — Story 1.5 / 3.4 / 3.5 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botinabox",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Bot in a Box — framework for building multi-agent bots",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,10 +42,18 @@
|
|
|
42
42
|
"bin": {
|
|
43
43
|
"botinabox": "./bin/botinabox.mjs"
|
|
44
44
|
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"bin"
|
|
48
|
+
],
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
45
52
|
"scripts": {
|
|
46
53
|
"build": "tsup",
|
|
47
54
|
"test": "vitest run",
|
|
48
|
-
"typecheck": "tsc --noEmit"
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"prepublishOnly": "npm run build && npm run typecheck && npm test"
|
|
49
57
|
},
|
|
50
58
|
"dependencies": {
|
|
51
59
|
"@types/uuid": "^10.0.0",
|
package/CHANGELOG.md
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
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.5.1] — 2026-04-04
|
|
10
|
-
|
|
11
|
-
### Fixed
|
|
12
|
-
|
|
13
|
-
- **cron-parser ESM import** — cron-parser v4 is CommonJS-only; fixed named import to default import (`import cronParser from "cron-parser"`).
|
|
14
|
-
|
|
15
|
-
## [0.5.0] — 2026-04-04
|
|
16
|
-
|
|
17
|
-
### Added
|
|
18
|
-
|
|
19
|
-
- **Connector interface** — Generic `Connector<T>` abstraction for external service integrations (Gmail, Calendar, Trello, Jira, Salesforce, etc.). Pull-based `sync()` returns typed records; optional `push()` writes back. Connectors produce data — consumers decide where to store it. New `connectors` config key in `BotConfig`.
|
|
20
|
-
- **Google connectors** — `GoogleGmailConnector` and `GoogleCalendarConnector` implementing `Connector<EmailRecord>` and `Connector<CalendarEventRecord>`. Incremental sync (Gmail historyId, Calendar syncToken), full sync with pagination, email sending via `push()`. OAuth2 helpers with callback-based token persistence. Exported from `botinabox/google`. `googleapis` as optional peer dependency.
|
|
21
|
-
- **Scheduler** — Database-backed `Scheduler` class with `schedules` core table. Supports recurring (cron expressions via `cron-parser`) and one-time schedules. Hook-based actions: when a schedule fires, it emits the configured action as a hook event. Methods: `register()`, `update()`, `unregister()`, `list()`, `tick()`.
|
|
22
|
-
- **`schedules` core table** — id, name, type (recurring/one_time), cron, run_at, timezone, enabled, action, action_config, last_fired_at, next_fire_at.
|
|
23
|
-
|
|
24
|
-
### Deprecated
|
|
25
|
-
|
|
26
|
-
- **HeartbeatScheduler** — Replaced by `Scheduler`. HeartbeatScheduler uses in-memory `setInterval` which loses state on restart. Kept for backward compatibility but marked `@deprecated`.
|
|
27
|
-
|
|
28
|
-
### Dependencies
|
|
29
|
-
|
|
30
|
-
- Added `cron-parser` ^4.9.0.
|
|
31
|
-
- Added `googleapis` >=140.0.0 as optional peer dependency.
|
|
32
|
-
|
|
33
|
-
## [0.3.0] — 2026-04-03
|
|
34
|
-
|
|
35
|
-
### Added
|
|
36
|
-
|
|
37
|
-
- **Domain tables** — `defineDomainTables(db, options?)` creates standard multi-agent app tables: org, project, client, invoice, repository, file, channel, rule, event + junction tables. Configurable: disable clients, repos, files, channels, rules, or events.
|
|
38
|
-
- **Domain entity contexts** — `defineDomainEntityContexts(db, options?)` renders per-entity context directories for all domain tables. Projects get REPOS.md + RULES.md. Clients get REPOS.md + AGENTS.md + INVOICES.md.
|
|
39
|
-
- **Claude stream parser** — `parseClaudeStream(stdout)` parses Claude CLI NDJSON output into structured results (session, model, cost, tokens, text, errors). Plus `isMaxTurns()`, `isLoginRequired()`, `deactivateLocalImagePaths()`.
|
|
40
|
-
- **Process env builder** — `buildProcessEnv(allowedKeys?, inject?)` creates a clean subprocess environment with only safe variables. Strips all secrets.
|
|
41
|
-
|
|
42
|
-
## [0.2.0] — 2026-04-03
|
|
43
|
-
|
|
44
|
-
### Added
|
|
45
|
-
|
|
46
|
-
- **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()`.
|
|
47
|
-
- **Secrets primitive** — `secrets` core table for encrypted credential storage. Protected by default. `SecretStore` class: `set()`, `get()`, `getMeta()`, `list()`, `rotate()`, `delete()`.
|
|
48
|
-
- **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.
|
|
49
|
-
- **`user_id` on messages table** — Tracks resolved user alongside raw `peer_id`.
|
|
50
|
-
- **Protected/encrypted passthrough** — `EntityContextDef` now supports `protected` and `encrypted` fields, passed through to Lattice's entity context system.
|
|
51
|
-
|
|
52
|
-
### Changed
|
|
53
|
-
|
|
54
|
-
- Core table count: 15 → 18 (added `users`, `user_identities`, `secrets`).
|
|
55
|
-
- `messages` table gains `user_id` column.
|
|
56
|
-
|
|
57
|
-
## [0.1.1] — 2026-03-28
|
|
58
|
-
|
|
59
|
-
### Fixed
|
|
60
|
-
|
|
61
|
-
- Initial release bug fixes and stability improvements.
|
|
62
|
-
|
|
63
|
-
## [0.1.0] — 2026-03-25
|
|
64
|
-
|
|
65
|
-
### Added
|
|
66
|
-
|
|
67
|
-
- Initial release: DataStore, HookBus, AgentRegistry, TaskQueue, RunManager, WakeupQueue, BudgetController, WorkflowEngine, SessionManager, ChannelRegistry, MessagePipeline.
|
|
68
|
-
- 15 core tables, LLM provider routing, channel adapters (Slack, Discord, Webhook).
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# Contributing
|
|
2
|
-
|
|
3
|
-
Thanks for your interest in contributing to Bot in a Box.
|
|
4
|
-
|
|
5
|
-
## Development Setup
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
git clone https://github.com/automated-industries/botinabox.git
|
|
9
|
-
cd botinabox
|
|
10
|
-
pnpm install
|
|
11
|
-
pnpm build
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Project Structure
|
|
15
|
-
|
|
16
|
-
This is a pnpm monorepo. Packages are in `packages/`:
|
|
17
|
-
|
|
18
|
-
- `shared/` — Types and constants (zero dependencies)
|
|
19
|
-
- `core/` — Core framework
|
|
20
|
-
- `cli/` — CLI scaffolding tool
|
|
21
|
-
- `providers/` — LLM provider adapters
|
|
22
|
-
- `channels/` — Messaging channel adapters
|
|
23
|
-
|
|
24
|
-
## Running Tests
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
# All packages
|
|
28
|
-
pnpm test:run
|
|
29
|
-
|
|
30
|
-
# Single package
|
|
31
|
-
cd packages/core && pnpm test
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Tests use [Vitest](https://vitest.dev/). Each package has its own `vitest.config.ts`.
|
|
35
|
-
|
|
36
|
-
## Building
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
# All packages
|
|
40
|
-
pnpm build
|
|
41
|
-
|
|
42
|
-
# Single package
|
|
43
|
-
cd packages/core && pnpm build
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Build uses [tsup](https://tsup.egoist.dev/) targeting ESM with declaration files.
|
|
47
|
-
|
|
48
|
-
## Type Checking
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
pnpm typecheck
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Code Style
|
|
55
|
-
|
|
56
|
-
- TypeScript with strict mode
|
|
57
|
-
- ESM modules (`"type": "module"`)
|
|
58
|
-
- ES2022 target
|
|
59
|
-
- No default exports except for provider/channel factory functions
|
|
60
|
-
|
|
61
|
-
## Making Changes
|
|
62
|
-
|
|
63
|
-
1. Create a branch from `main`
|
|
64
|
-
2. Make your changes
|
|
65
|
-
3. Add or update tests
|
|
66
|
-
4. Run `pnpm test:run` and `pnpm typecheck`
|
|
67
|
-
5. Open a pull request
|
|
68
|
-
|
|
69
|
-
## Adding a Provider
|
|
70
|
-
|
|
71
|
-
1. Create `packages/providers/your-provider/`
|
|
72
|
-
2. Implement the `LLMProvider` interface from `@botinabox/shared`
|
|
73
|
-
3. Export a default factory function
|
|
74
|
-
4. Add `"botinabox": { "type": "provider" }` to `package.json`
|
|
75
|
-
5. Add tests
|
|
76
|
-
|
|
77
|
-
## Adding a Channel Adapter
|
|
78
|
-
|
|
79
|
-
1. Create `packages/channels/your-channel/`
|
|
80
|
-
2. Implement the `ChannelAdapter` interface from `@botinabox/shared`
|
|
81
|
-
3. Export a default factory function
|
|
82
|
-
4. Add `"botinabox": { "type": "channel" }` to `package.json`
|
|
83
|
-
5. Add tests
|
|
84
|
-
|
|
85
|
-
## Reporting Issues
|
|
86
|
-
|
|
87
|
-
Open an issue on GitHub with:
|
|
88
|
-
|
|
89
|
-
- Steps to reproduce
|
|
90
|
-
- Expected behavior
|
|
91
|
-
- Actual behavior
|
|
92
|
-
- Node.js and pnpm versions
|