@botschat/botschat 0.1.4
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/bin/botschat-setup.mjs +312 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/src/accounts.d.ts +12 -0
- package/dist/src/accounts.d.ts.map +1 -0
- package/dist/src/accounts.js +103 -0
- package/dist/src/accounts.js.map +1 -0
- package/dist/src/channel.d.ts +206 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/dist/src/channel.js +1250 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/runtime.d.ts +3 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +18 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/types.d.ts +179 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +6 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/ws-client.d.ts +51 -0
- package/dist/src/ws-client.d.ts.map +1 -0
- package/dist/src/ws-client.js +170 -0
- package/dist/src/ws-client.js.map +1 -0
- package/openclaw.plugin.json +11 -0
- package/package.json +51 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* botschat-setup — Interactive CLI to connect your OpenClaw to BotsChat.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* botschat-setup # Interactive mode
|
|
8
|
+
* botschat-setup --url https://console.botschat.app --token bc_pat_xxx # Non-interactive
|
|
9
|
+
* botschat-setup --url https://console.botschat.app --email me@x.com --password xxx
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execFileSync } from "node:child_process";
|
|
13
|
+
import { createInterface } from "node:readline";
|
|
14
|
+
|
|
15
|
+
const DEFAULT_URL = "https://console.botschat.app";
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Helpers
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
function parseArgs(argv) {
|
|
22
|
+
const args = {};
|
|
23
|
+
for (let i = 2; i < argv.length; i++) {
|
|
24
|
+
const arg = argv[i];
|
|
25
|
+
if (arg.startsWith("--")) {
|
|
26
|
+
const key = arg.slice(2);
|
|
27
|
+
const next = argv[i + 1];
|
|
28
|
+
if (next && !next.startsWith("--")) {
|
|
29
|
+
args[key] = next;
|
|
30
|
+
i++;
|
|
31
|
+
} else {
|
|
32
|
+
args[key] = true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return args;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Check if URL is loopback (localhost / 127.x) */
|
|
40
|
+
function isLoopbackUrl(url) {
|
|
41
|
+
try {
|
|
42
|
+
const u = new URL(url);
|
|
43
|
+
return u.hostname === "localhost" || u.hostname.startsWith("127.");
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function prompt(rl, question, defaultValue) {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
52
|
+
rl.question(` ${question}${suffix}: `, (answer) => {
|
|
53
|
+
resolve(answer.trim() || defaultValue || "");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function promptPassword(rl, question) {
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
// Use raw mode to hide password input
|
|
61
|
+
process.stdout.write(` ${question}: `);
|
|
62
|
+
const stdin = process.stdin;
|
|
63
|
+
const wasRaw = stdin.isRaw;
|
|
64
|
+
if (stdin.setRawMode) stdin.setRawMode(true);
|
|
65
|
+
stdin.resume();
|
|
66
|
+
|
|
67
|
+
let password = "";
|
|
68
|
+
const onData = (ch) => {
|
|
69
|
+
const c = ch.toString("utf8");
|
|
70
|
+
if (c === "\n" || c === "\r" || c === "\u0004") {
|
|
71
|
+
if (stdin.setRawMode) stdin.setRawMode(wasRaw);
|
|
72
|
+
stdin.removeListener("data", onData);
|
|
73
|
+
process.stdout.write("\n");
|
|
74
|
+
resolve(password);
|
|
75
|
+
} else if (c === "\u0003") {
|
|
76
|
+
// Ctrl+C
|
|
77
|
+
process.exit(1);
|
|
78
|
+
} else if (c === "\u007f" || c === "\b") {
|
|
79
|
+
// Backspace
|
|
80
|
+
if (password.length > 0) {
|
|
81
|
+
password = password.slice(0, -1);
|
|
82
|
+
process.stdout.write("\b \b");
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
password += c;
|
|
86
|
+
process.stdout.write("*");
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
stdin.on("data", onData);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function runOpenclaw(args) {
|
|
94
|
+
const PATH = process.env.PATH || "";
|
|
95
|
+
const env = { ...process.env, PATH: `/opt/homebrew/bin:${PATH}` };
|
|
96
|
+
try {
|
|
97
|
+
execFileSync("openclaw", args, { env, stdio: "pipe" });
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function checkConnection(baseUrl, token, maxWait = 15000) {
|
|
105
|
+
const start = Date.now();
|
|
106
|
+
while (Date.now() - start < maxWait) {
|
|
107
|
+
try {
|
|
108
|
+
const resp = await fetch(`${baseUrl}/api/setup/status`, {
|
|
109
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
110
|
+
});
|
|
111
|
+
if (resp.ok) {
|
|
112
|
+
const data = await resp.json();
|
|
113
|
+
if (data.connected) return true;
|
|
114
|
+
}
|
|
115
|
+
} catch { /* retry */ }
|
|
116
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Main
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
async function main() {
|
|
126
|
+
const args = parseArgs(process.argv);
|
|
127
|
+
|
|
128
|
+
if (args.help || args.h) {
|
|
129
|
+
console.log(`
|
|
130
|
+
botschat-setup — Connect your OpenClaw to BotsChat
|
|
131
|
+
|
|
132
|
+
Usage:
|
|
133
|
+
botschat-setup Interactive setup
|
|
134
|
+
botschat-setup --token bc_pat_xxx Use existing token
|
|
135
|
+
botschat-setup --email me@x.com --password xxx Login and setup
|
|
136
|
+
botschat-setup --url https://console.botschat.app --token ... Custom server
|
|
137
|
+
|
|
138
|
+
Options:
|
|
139
|
+
--url BotsChat server URL (default: ${DEFAULT_URL})
|
|
140
|
+
--token Pairing token (bc_pat_xxx)
|
|
141
|
+
--email Email for login (creates pairing token automatically)
|
|
142
|
+
--password Password for login
|
|
143
|
+
--help Show this help
|
|
144
|
+
`);
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log();
|
|
149
|
+
console.log(" BotsChat Setup");
|
|
150
|
+
console.log(" ─────────────────────────────────────────");
|
|
151
|
+
console.log();
|
|
152
|
+
|
|
153
|
+
let cloudUrl = args.url || "";
|
|
154
|
+
let pairingToken = args.token || "";
|
|
155
|
+
let jwtToken = "";
|
|
156
|
+
|
|
157
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// ---- Step 1: Get cloud URL ----
|
|
161
|
+
if (!cloudUrl) {
|
|
162
|
+
cloudUrl = await prompt(rl, "BotsChat URL", DEFAULT_URL);
|
|
163
|
+
}
|
|
164
|
+
// Normalize: strip trailing slash
|
|
165
|
+
cloudUrl = cloudUrl.replace(/\/+$/, "");
|
|
166
|
+
|
|
167
|
+
// ---- Step 2: Get pairing token (direct or via login) ----
|
|
168
|
+
if (!pairingToken) {
|
|
169
|
+
if (args.email && args.password) {
|
|
170
|
+
// Non-interactive login
|
|
171
|
+
console.log(` Logging in as ${args.email}...`);
|
|
172
|
+
const resp = await fetch(`${cloudUrl}/api/setup/init`, {
|
|
173
|
+
method: "POST",
|
|
174
|
+
headers: { "Content-Type": "application/json" },
|
|
175
|
+
body: JSON.stringify({ email: args.email, password: args.password }),
|
|
176
|
+
});
|
|
177
|
+
if (!resp.ok) {
|
|
178
|
+
const err = await resp.json().catch(() => ({}));
|
|
179
|
+
console.error(` Error: ${err.error || `HTTP ${resp.status}`}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
const data = await resp.json();
|
|
183
|
+
pairingToken = data.pairingToken;
|
|
184
|
+
jwtToken = data.token;
|
|
185
|
+
console.log(` Authenticated as ${data.email}`);
|
|
186
|
+
} else {
|
|
187
|
+
// Ask: do you have a token, or login?
|
|
188
|
+
console.log(" How would you like to authenticate?");
|
|
189
|
+
console.log(" 1) I have a pairing token (from the BotsChat web UI)");
|
|
190
|
+
console.log(" 2) Login with email and password");
|
|
191
|
+
console.log();
|
|
192
|
+
const choice = await prompt(rl, "Choice", "1");
|
|
193
|
+
|
|
194
|
+
if (choice === "2") {
|
|
195
|
+
const email = await prompt(rl, "Email", "");
|
|
196
|
+
const password = await promptPassword(rl, "Password");
|
|
197
|
+
|
|
198
|
+
console.log(" Logging in...");
|
|
199
|
+
const resp = await fetch(`${cloudUrl}/api/setup/init`, {
|
|
200
|
+
method: "POST",
|
|
201
|
+
headers: { "Content-Type": "application/json" },
|
|
202
|
+
body: JSON.stringify({ email, password }),
|
|
203
|
+
});
|
|
204
|
+
if (!resp.ok) {
|
|
205
|
+
const err = await resp.json().catch(() => ({}));
|
|
206
|
+
console.error(` Error: ${err.error || `HTTP ${resp.status}`}`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
const data = await resp.json();
|
|
210
|
+
pairingToken = data.pairingToken;
|
|
211
|
+
jwtToken = data.token;
|
|
212
|
+
console.log(` Authenticated as ${data.email}`);
|
|
213
|
+
} else {
|
|
214
|
+
pairingToken = await prompt(rl, "Pairing token (bc_pat_...)", "");
|
|
215
|
+
if (!pairingToken) {
|
|
216
|
+
console.error(" Error: Pairing token is required");
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---- Step 2.5: Smart URL resolution — prefer the API's recommendation ----
|
|
224
|
+
// The /api/setup/init response's cloudUrl is resolved by the backend.
|
|
225
|
+
// But if we logged in, also check the cloudUrlWarning.
|
|
226
|
+
// If the URL is loopback and user hasn't explicitly chosen it, warn.
|
|
227
|
+
if (isLoopbackUrl(cloudUrl)) {
|
|
228
|
+
console.log();
|
|
229
|
+
console.log(" \x1b[33m⚠ Warning:\x1b[0m The cloud URL is \x1b[1m" + cloudUrl + "\x1b[0m (localhost).");
|
|
230
|
+
console.log(" This only works if OpenClaw is on the same machine.");
|
|
231
|
+
console.log(" If OpenClaw is on another host, use a LAN IP instead,");
|
|
232
|
+
console.log(" e.g. http://192.168.x.x:8787 or http://10.x.x.x:8787");
|
|
233
|
+
console.log();
|
|
234
|
+
const override = await prompt(rl, "Cloud URL (press Enter to keep, or type new URL)", cloudUrl);
|
|
235
|
+
if (override && override !== cloudUrl) {
|
|
236
|
+
cloudUrl = override.replace(/\/+$/, "");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// If the API returned a cloudUrlWarning (e.g. via login), the URL was
|
|
241
|
+
// already chosen in step 2. Try to resolve a better URL via /api/setup/cloud-url.
|
|
242
|
+
try {
|
|
243
|
+
const cuResp = await fetch(`${cloudUrl}/api/setup/cloud-url`);
|
|
244
|
+
if (cuResp.ok) {
|
|
245
|
+
const cuData = await cuResp.json();
|
|
246
|
+
if (cuData.cloudUrl && cuData.cloudUrl !== cloudUrl && !isLoopbackUrl(cuData.cloudUrl)) {
|
|
247
|
+
console.log(` Server recommends: ${cuData.cloudUrl}`);
|
|
248
|
+
const useRecommended = await prompt(rl, `Use ${cuData.cloudUrl} instead? [Y/n]`, "Y");
|
|
249
|
+
if (!useRecommended || useRecommended.toLowerCase() !== "n") {
|
|
250
|
+
cloudUrl = cuData.cloudUrl;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} catch { /* ignore — we'll use what we have */ }
|
|
255
|
+
|
|
256
|
+
console.log();
|
|
257
|
+
console.log(" Configuring OpenClaw...");
|
|
258
|
+
|
|
259
|
+
// ---- Step 3: Write config via openclaw CLI ----
|
|
260
|
+
const steps = [
|
|
261
|
+
{ label: "Setting cloud URL", args: ["config", "set", "channels.botschat.cloudUrl", cloudUrl] },
|
|
262
|
+
{ label: "Setting pairing token", args: ["config", "set", "channels.botschat.pairingToken", pairingToken] },
|
|
263
|
+
{ label: "Enabling channel", args: ["config", "set", "channels.botschat.enabled", "true"] },
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
for (const step of steps) {
|
|
267
|
+
process.stdout.write(` ${step.label}...`);
|
|
268
|
+
const ok = runOpenclaw(step.args);
|
|
269
|
+
console.log(ok ? " done" : " FAILED");
|
|
270
|
+
if (!ok) {
|
|
271
|
+
console.error(" Error: Failed to run openclaw CLI. Is openclaw installed and in PATH?");
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ---- Step 4: Restart gateway ----
|
|
277
|
+
console.log();
|
|
278
|
+
process.stdout.write(" Restarting gateway...");
|
|
279
|
+
const restarted = runOpenclaw(["gateway", "restart"]);
|
|
280
|
+
console.log(restarted ? " done" : " FAILED (you may need to restart manually)");
|
|
281
|
+
|
|
282
|
+
// ---- Step 5: Verify connection ----
|
|
283
|
+
if (jwtToken) {
|
|
284
|
+
console.log();
|
|
285
|
+
process.stdout.write(" Verifying connection...");
|
|
286
|
+
const connected = await checkConnection(cloudUrl, jwtToken);
|
|
287
|
+
if (connected) {
|
|
288
|
+
console.log(" connected!");
|
|
289
|
+
console.log();
|
|
290
|
+
console.log(" Setup complete! Open your BotsChat web UI to start chatting.");
|
|
291
|
+
} else {
|
|
292
|
+
console.log(" not yet connected.");
|
|
293
|
+
console.log(" Check your gateway logs: openclaw gateway logs");
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
console.log();
|
|
297
|
+
console.log(" Configuration saved! Check connection with:");
|
|
298
|
+
console.log(" openclaw gateway logs");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log();
|
|
302
|
+
console.log(" ─────────────────────────────────────────");
|
|
303
|
+
console.log();
|
|
304
|
+
} finally {
|
|
305
|
+
rl.close();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
main().catch((err) => {
|
|
310
|
+
console.error(" Error:", err.message || err);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { botschatPlugin } from "./src/channel.js";
|
|
2
|
+
declare const plugin: {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
configSchema: {
|
|
7
|
+
safeParse: () => {
|
|
8
|
+
success: boolean;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
register(api: {
|
|
12
|
+
runtime: unknown;
|
|
13
|
+
registerChannel: (reg: {
|
|
14
|
+
plugin: typeof botschatPlugin;
|
|
15
|
+
}) => void;
|
|
16
|
+
}): void;
|
|
17
|
+
};
|
|
18
|
+
export default plugin;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAMlD,QAAA,MAAM,MAAM;;;;;;;;;kBAKI;QACZ,OAAO,EAAE,OAAO,CAAC;QACjB,eAAe,EAAE,CAAC,GAAG,EAAE;YAAE,MAAM,EAAE,OAAO,cAAc,CAAA;SAAE,KAAK,IAAI,CAAC;KACnE;CAIF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { botschatPlugin } from "./src/channel.js";
|
|
2
|
+
import { setBotsChatRuntime } from "./src/runtime.js";
|
|
3
|
+
// OpenClaw Plugin Definition
|
|
4
|
+
// This is the entry point loaded by OpenClaw's plugin system.
|
|
5
|
+
// It registers the BotsChat channel plugin.
|
|
6
|
+
const plugin = {
|
|
7
|
+
id: "botschat",
|
|
8
|
+
name: "BotsChat",
|
|
9
|
+
description: "Connect to BotsChat cloud chat platform",
|
|
10
|
+
configSchema: { safeParse: () => ({ success: true }) },
|
|
11
|
+
register(api) {
|
|
12
|
+
setBotsChatRuntime(api.runtime);
|
|
13
|
+
api.registerChannel({ plugin: botschatPlugin });
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
export default plugin;
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,6BAA6B;AAC7B,8DAA8D;AAC9D,4CAA4C;AAC5C,MAAM,MAAM,GAAG;IACb,EAAE,EAAE,UAAU;IACd,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,yCAAyC;IACtD,YAAY,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE;IACtD,QAAQ,CAAC,GAGR;QACC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IAClD,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ResolvedBotsChatAccount } from "./types.js";
|
|
2
|
+
/** List all configured account IDs. */
|
|
3
|
+
export declare function listBotsChatAccountIds(cfg: unknown): string[];
|
|
4
|
+
/** Resolve default account ID. */
|
|
5
|
+
export declare function resolveDefaultBotsChatAccountId(cfg: unknown): string;
|
|
6
|
+
/** Resolve a single account by ID. */
|
|
7
|
+
export declare function resolveBotsChatAccount(cfg: unknown, accountId?: string | null): ResolvedBotsChatAccount;
|
|
8
|
+
/** Delete an account from config, returning the updated config. */
|
|
9
|
+
export declare function deleteBotsChatAccount(cfg: unknown, accountId: string): unknown;
|
|
10
|
+
/** Enable or disable an account, returning updated config. */
|
|
11
|
+
export declare function setBotsChatAccountEnabled(cfg: unknown, accountId: string, enabled: boolean): unknown;
|
|
12
|
+
//# sourceMappingURL=accounts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../src/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAUpB,uCAAuC;AACvC,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CAgB7D;AAED,kCAAkC;AAClC,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAGpE;AAED,sCAAsC;AACtC,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,uBAAuB,CA6BzB;AAED,mEAAmE;AACnE,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAsBT;AAED,8DAA8D;AAC9D,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,GACf,OAAO,CAoBT"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const DEFAULT_ACCOUNT_ID = "default";
|
|
2
|
+
/** Extract the botschat section from the full config. */
|
|
3
|
+
function section(cfg) {
|
|
4
|
+
const c = cfg;
|
|
5
|
+
return c?.channels?.botschat ?? {};
|
|
6
|
+
}
|
|
7
|
+
/** List all configured account IDs. */
|
|
8
|
+
export function listBotsChatAccountIds(cfg) {
|
|
9
|
+
const s = section(cfg);
|
|
10
|
+
const ids = [];
|
|
11
|
+
// Top-level counts as "default" if it has cloudUrl
|
|
12
|
+
if (s.cloudUrl) {
|
|
13
|
+
ids.push(DEFAULT_ACCOUNT_ID);
|
|
14
|
+
}
|
|
15
|
+
// Named accounts
|
|
16
|
+
if (s.accounts) {
|
|
17
|
+
for (const id of Object.keys(s.accounts)) {
|
|
18
|
+
if (id !== DEFAULT_ACCOUNT_ID || !s.cloudUrl) {
|
|
19
|
+
ids.push(id);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return ids.length > 0 ? ids : [DEFAULT_ACCOUNT_ID];
|
|
24
|
+
}
|
|
25
|
+
/** Resolve default account ID. */
|
|
26
|
+
export function resolveDefaultBotsChatAccountId(cfg) {
|
|
27
|
+
const ids = listBotsChatAccountIds(cfg);
|
|
28
|
+
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
29
|
+
}
|
|
30
|
+
/** Resolve a single account by ID. */
|
|
31
|
+
export function resolveBotsChatAccount(cfg, accountId) {
|
|
32
|
+
const s = section(cfg);
|
|
33
|
+
const id = accountId ?? resolveDefaultBotsChatAccountId(cfg);
|
|
34
|
+
let acct;
|
|
35
|
+
if (id === DEFAULT_ACCOUNT_ID || !s.accounts?.[id]) {
|
|
36
|
+
// Use top-level config
|
|
37
|
+
acct = {
|
|
38
|
+
enabled: s.enabled,
|
|
39
|
+
name: s.name,
|
|
40
|
+
cloudUrl: s.cloudUrl,
|
|
41
|
+
pairingToken: s.pairingToken,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
acct = s.accounts[id];
|
|
46
|
+
}
|
|
47
|
+
const cloudUrl = acct.cloudUrl ?? "";
|
|
48
|
+
const pairingToken = acct.pairingToken ?? "";
|
|
49
|
+
return {
|
|
50
|
+
accountId: id,
|
|
51
|
+
name: acct.name,
|
|
52
|
+
enabled: acct.enabled !== false,
|
|
53
|
+
configured: !!cloudUrl && !!pairingToken,
|
|
54
|
+
cloudUrl,
|
|
55
|
+
pairingToken,
|
|
56
|
+
config: acct,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Delete an account from config, returning the updated config. */
|
|
60
|
+
export function deleteBotsChatAccount(cfg, accountId) {
|
|
61
|
+
const c = cfg;
|
|
62
|
+
const botschat = { ...c?.channels?.botschat };
|
|
63
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
64
|
+
delete botschat.cloudUrl;
|
|
65
|
+
delete botschat.pairingToken;
|
|
66
|
+
delete botschat.name;
|
|
67
|
+
botschat.enabled = false;
|
|
68
|
+
}
|
|
69
|
+
else if (botschat.accounts) {
|
|
70
|
+
const accounts = { ...botschat.accounts };
|
|
71
|
+
delete accounts[accountId];
|
|
72
|
+
botschat.accounts = accounts;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
...c,
|
|
76
|
+
channels: {
|
|
77
|
+
...c.channels,
|
|
78
|
+
botschat,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Enable or disable an account, returning updated config. */
|
|
83
|
+
export function setBotsChatAccountEnabled(cfg, accountId, enabled) {
|
|
84
|
+
const c = cfg;
|
|
85
|
+
const botschat = { ...c?.channels?.botschat };
|
|
86
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
87
|
+
botschat.enabled = enabled;
|
|
88
|
+
}
|
|
89
|
+
else if (botschat.accounts) {
|
|
90
|
+
botschat.accounts = {
|
|
91
|
+
...botschat.accounts,
|
|
92
|
+
[accountId]: { ...botschat.accounts[accountId], enabled },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
...c,
|
|
97
|
+
channels: {
|
|
98
|
+
...c.channels,
|
|
99
|
+
botschat,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import type { ResolvedBotsChatAccount } from "./types.js";
|
|
2
|
+
import { BotsChatCloudClient } from "./ws-client.js";
|
|
3
|
+
export declare const botschatPlugin: {
|
|
4
|
+
readonly id: "botschat";
|
|
5
|
+
readonly meta: {
|
|
6
|
+
readonly id: "botschat";
|
|
7
|
+
readonly label: "BotsChat";
|
|
8
|
+
readonly selectionLabel: "BotsChat (cloud)";
|
|
9
|
+
readonly docsPath: "/channels/botschat";
|
|
10
|
+
readonly docsLabel: "botschat";
|
|
11
|
+
readonly blurb: "Cloud-based multi-channel chat interface";
|
|
12
|
+
readonly order: 80;
|
|
13
|
+
readonly quickstartAllowFrom: false;
|
|
14
|
+
};
|
|
15
|
+
readonly capabilities: {
|
|
16
|
+
readonly chatTypes: string[];
|
|
17
|
+
readonly polls: false;
|
|
18
|
+
readonly reactions: false;
|
|
19
|
+
readonly threads: true;
|
|
20
|
+
readonly media: true;
|
|
21
|
+
};
|
|
22
|
+
readonly agentPrompt: {
|
|
23
|
+
readonly messageToolHints: () => string[];
|
|
24
|
+
};
|
|
25
|
+
readonly reload: {
|
|
26
|
+
readonly configPrefixes: readonly ["channels.botschat"];
|
|
27
|
+
};
|
|
28
|
+
readonly config: {
|
|
29
|
+
readonly listAccountIds: (cfg: unknown) => string[];
|
|
30
|
+
readonly resolveAccount: (cfg: unknown, accountId?: string | null) => ResolvedBotsChatAccount;
|
|
31
|
+
readonly defaultAccountId: (cfg: unknown) => string;
|
|
32
|
+
readonly setAccountEnabled: ({ cfg, accountId, enabled }: {
|
|
33
|
+
cfg: unknown;
|
|
34
|
+
accountId: string;
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
}) => unknown;
|
|
37
|
+
readonly deleteAccount: ({ cfg, accountId }: {
|
|
38
|
+
cfg: unknown;
|
|
39
|
+
accountId: string;
|
|
40
|
+
}) => unknown;
|
|
41
|
+
readonly isConfigured: (account: ResolvedBotsChatAccount) => boolean;
|
|
42
|
+
readonly isEnabled: (account: ResolvedBotsChatAccount) => boolean;
|
|
43
|
+
readonly describeAccount: (account: ResolvedBotsChatAccount) => {
|
|
44
|
+
accountId: string;
|
|
45
|
+
name: string | undefined;
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
configured: boolean;
|
|
48
|
+
baseUrl: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
readonly outbound: {
|
|
52
|
+
readonly deliveryMode: "direct";
|
|
53
|
+
readonly sendText: (ctx: {
|
|
54
|
+
to: string;
|
|
55
|
+
text: string;
|
|
56
|
+
replyToId?: string | null;
|
|
57
|
+
threadId?: string | number | null;
|
|
58
|
+
accountId?: string | null;
|
|
59
|
+
}) => Promise<{
|
|
60
|
+
ok: boolean;
|
|
61
|
+
error: Error;
|
|
62
|
+
} | {
|
|
63
|
+
ok: boolean;
|
|
64
|
+
error?: undefined;
|
|
65
|
+
}>;
|
|
66
|
+
readonly sendMedia: (ctx: {
|
|
67
|
+
to: string;
|
|
68
|
+
text: string;
|
|
69
|
+
mediaUrl?: string;
|
|
70
|
+
accountId?: string | null;
|
|
71
|
+
}) => Promise<{
|
|
72
|
+
ok: boolean;
|
|
73
|
+
error: Error;
|
|
74
|
+
} | {
|
|
75
|
+
ok: boolean;
|
|
76
|
+
error?: undefined;
|
|
77
|
+
}>;
|
|
78
|
+
};
|
|
79
|
+
readonly gateway: {
|
|
80
|
+
readonly startAccount: (ctx: {
|
|
81
|
+
cfg: unknown;
|
|
82
|
+
accountId: string;
|
|
83
|
+
account: ResolvedBotsChatAccount;
|
|
84
|
+
runtime: unknown;
|
|
85
|
+
abortSignal: AbortSignal;
|
|
86
|
+
log?: {
|
|
87
|
+
info: (m: string) => void;
|
|
88
|
+
warn: (m: string) => void;
|
|
89
|
+
error: (m: string) => void;
|
|
90
|
+
};
|
|
91
|
+
getStatus: () => Record<string, unknown>;
|
|
92
|
+
setStatus: (s: Record<string, unknown>) => void;
|
|
93
|
+
}) => Promise<BotsChatCloudClient | undefined>;
|
|
94
|
+
readonly stopAccount: (ctx: {
|
|
95
|
+
accountId: string;
|
|
96
|
+
getStatus: () => Record<string, unknown>;
|
|
97
|
+
setStatus: (s: Record<string, unknown>) => void;
|
|
98
|
+
}) => Promise<void>;
|
|
99
|
+
};
|
|
100
|
+
readonly threading: {
|
|
101
|
+
readonly resolveReplyToMode: () => "all";
|
|
102
|
+
readonly buildToolContext: ({ context, hasRepliedRef }: {
|
|
103
|
+
context: {
|
|
104
|
+
To?: string;
|
|
105
|
+
MessageThreadId?: string | number;
|
|
106
|
+
ReplyToId?: string;
|
|
107
|
+
};
|
|
108
|
+
hasRepliedRef?: {
|
|
109
|
+
value: boolean;
|
|
110
|
+
};
|
|
111
|
+
}) => {
|
|
112
|
+
currentChannelId: string | undefined;
|
|
113
|
+
currentChannelProvider: string;
|
|
114
|
+
currentThreadTs: string | undefined;
|
|
115
|
+
hasRepliedRef: {
|
|
116
|
+
value: boolean;
|
|
117
|
+
} | undefined;
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
readonly pairing: {
|
|
121
|
+
readonly idLabel: "botsChatUserId";
|
|
122
|
+
readonly normalizeAllowEntry: (entry: string) => string;
|
|
123
|
+
};
|
|
124
|
+
readonly security: {
|
|
125
|
+
readonly resolveDmPolicy: (_ctx: {
|
|
126
|
+
account: ResolvedBotsChatAccount;
|
|
127
|
+
}) => {
|
|
128
|
+
policy: string;
|
|
129
|
+
allowFrom: string[];
|
|
130
|
+
policyPath: string;
|
|
131
|
+
allowFromPath: string;
|
|
132
|
+
approveHint: string;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
readonly setup: {
|
|
136
|
+
readonly applyAccountConfig: ({ cfg, input }: {
|
|
137
|
+
cfg: unknown;
|
|
138
|
+
accountId: string;
|
|
139
|
+
input: {
|
|
140
|
+
url?: string;
|
|
141
|
+
token?: string;
|
|
142
|
+
name?: string;
|
|
143
|
+
useEnv?: boolean;
|
|
144
|
+
};
|
|
145
|
+
}) => {
|
|
146
|
+
channels: {
|
|
147
|
+
botschat: {
|
|
148
|
+
name?: string;
|
|
149
|
+
enabled: boolean;
|
|
150
|
+
cloudUrl: string;
|
|
151
|
+
pairingToken: string;
|
|
152
|
+
accounts?: Record<string, import("./types.js").BotsChatAccountConfig>;
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
readonly validateInput: ({ input }: {
|
|
157
|
+
cfg: unknown;
|
|
158
|
+
accountId: string;
|
|
159
|
+
input: {
|
|
160
|
+
url?: string;
|
|
161
|
+
token?: string;
|
|
162
|
+
useEnv?: boolean;
|
|
163
|
+
};
|
|
164
|
+
}) => "BotsChat requires --url (e.g., --url console.botschat.app)" | "BotsChat requires --token (pairing token from console.botschat.app)" | null;
|
|
165
|
+
};
|
|
166
|
+
readonly status: {
|
|
167
|
+
readonly defaultRuntime: {
|
|
168
|
+
readonly accountId: "default";
|
|
169
|
+
readonly running: false;
|
|
170
|
+
readonly connected: false;
|
|
171
|
+
readonly lastStartAt: null;
|
|
172
|
+
readonly lastStopAt: null;
|
|
173
|
+
readonly lastError: null;
|
|
174
|
+
};
|
|
175
|
+
readonly buildAccountSnapshot: ({ account, runtime }: {
|
|
176
|
+
account: ResolvedBotsChatAccount;
|
|
177
|
+
cfg: unknown;
|
|
178
|
+
runtime?: Record<string, unknown>;
|
|
179
|
+
}) => {
|
|
180
|
+
accountId: string;
|
|
181
|
+
name: string | undefined;
|
|
182
|
+
enabled: boolean;
|
|
183
|
+
configured: boolean;
|
|
184
|
+
baseUrl: string;
|
|
185
|
+
running: boolean;
|
|
186
|
+
connected: boolean;
|
|
187
|
+
lastStartAt: number;
|
|
188
|
+
lastStopAt: number;
|
|
189
|
+
lastError: string;
|
|
190
|
+
lastConnectedAt: number;
|
|
191
|
+
lastDisconnect: {} | null;
|
|
192
|
+
};
|
|
193
|
+
readonly collectStatusIssues: (accounts: Array<{
|
|
194
|
+
accountId: string;
|
|
195
|
+
lastError?: string | null;
|
|
196
|
+
connected?: boolean;
|
|
197
|
+
configured?: boolean;
|
|
198
|
+
}>) => {
|
|
199
|
+
channel: string;
|
|
200
|
+
accountId: string;
|
|
201
|
+
kind: string;
|
|
202
|
+
message: string;
|
|
203
|
+
}[];
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
//# sourceMappingURL=channel.d.ts.map
|