owpenwork 0.1.7 → 0.1.9
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 -3
- package/dist/cli.js +311 -120
- package/dist/config.js +3 -2
- package/dist/whatsapp.js +10 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Quick run without install:
|
|
|
22
22
|
npx owpenwork
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Then follow the
|
|
25
|
+
Then follow the guided setup (choose what to configure, link WhatsApp, start).
|
|
26
26
|
|
|
27
27
|
1) One-command setup (installs deps, builds, creates `.env` if missing):
|
|
28
28
|
|
|
@@ -79,17 +79,20 @@ Use a separate WhatsApp number as the bot account so it stays independent from y
|
|
|
79
79
|
## Telegram (Untested)
|
|
80
80
|
|
|
81
81
|
Telegram support is wired but not E2E tested yet. To try it:
|
|
82
|
-
-
|
|
83
|
-
-
|
|
82
|
+
- Run `owpenwork login telegram --token <token>`.
|
|
83
|
+
- Or set `TELEGRAM_BOT_TOKEN` and `TELEGRAM_ENABLED=true`.
|
|
84
84
|
|
|
85
85
|
## Commands
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
88
|
owpenwork
|
|
89
89
|
owpenwork --non-interactive
|
|
90
|
+
owpenwork login whatsapp
|
|
91
|
+
owpenwork login telegram --token <token>
|
|
90
92
|
owpenwork pairing list
|
|
91
93
|
owpenwork pairing approve <code>
|
|
92
94
|
owpenwork status
|
|
95
|
+
owpenwork doctor --reset
|
|
93
96
|
```
|
|
94
97
|
|
|
95
98
|
## Defaults
|
package/dist/cli.js
CHANGED
|
@@ -1,50 +1,86 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { cancel, confirm, intro, isCancel, log, multiselect, outro, select, spinner, text, } from "@clack/prompts";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { startBridge } from "./bridge.js";
|
|
7
7
|
import { loadConfig, normalizeWhatsAppId, readConfigFile, writeConfigFile, } from "./config.js";
|
|
8
8
|
import { BridgeStore } from "./db.js";
|
|
9
9
|
import { createLogger } from "./logger.js";
|
|
10
|
+
import { createClient } from "./opencode.js";
|
|
10
11
|
import { loginWhatsApp, unpairWhatsApp } from "./whatsapp.js";
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
12
|
+
const VERSION = "0.1.9";
|
|
13
|
+
const STEP_OPTIONS = [
|
|
14
|
+
{
|
|
15
|
+
value: "config",
|
|
16
|
+
label: "Setup configuration",
|
|
17
|
+
hint: "DM policy + allowlist",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: "whatsapp",
|
|
21
|
+
label: "Link WhatsApp",
|
|
22
|
+
hint: "Scan QR",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: "telegram",
|
|
26
|
+
label: "Link Telegram",
|
|
27
|
+
hint: "Optional",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: "start",
|
|
31
|
+
label: "Start bridge",
|
|
32
|
+
hint: "Listen for messages",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
const STEP_SET = new Set(["config", "whatsapp", "telegram", "start"]);
|
|
36
|
+
function isInteractive() {
|
|
37
|
+
return Boolean(process.stdin.isTTY || process.env.OWPENWORK_FORCE_TUI === "1");
|
|
38
|
+
}
|
|
39
|
+
function unwrap(value) {
|
|
40
|
+
if (isCancel(value)) {
|
|
41
|
+
cancel("Setup cancelled");
|
|
33
42
|
process.exit(0);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
function parseSelections(raw) {
|
|
47
|
+
if (!raw)
|
|
48
|
+
return null;
|
|
49
|
+
const selections = raw
|
|
50
|
+
.split(",")
|
|
51
|
+
.map((item) => item.trim())
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.filter((item) => STEP_SET.has(item));
|
|
54
|
+
return selections.length ? selections : [];
|
|
55
|
+
}
|
|
56
|
+
function defaultSelections(configExists, whatsappLinked) {
|
|
57
|
+
const selections = [];
|
|
58
|
+
if (!configExists)
|
|
59
|
+
selections.push("config");
|
|
60
|
+
if (!whatsappLinked)
|
|
61
|
+
selections.push("whatsapp");
|
|
62
|
+
selections.push("start");
|
|
63
|
+
return selections;
|
|
64
|
+
}
|
|
65
|
+
function updateConfig(configPath, updater) {
|
|
66
|
+
const { config } = readConfigFile(configPath);
|
|
67
|
+
const base = config ?? { version: 1 };
|
|
68
|
+
const next = updater(base);
|
|
69
|
+
next.version = next.version ?? 1;
|
|
70
|
+
writeConfigFile(configPath, next);
|
|
71
|
+
return next;
|
|
72
|
+
}
|
|
73
|
+
async function runSetupWizard(config, options) {
|
|
74
|
+
const testMode = process.env.OWPENWORK_TEST_SETUP;
|
|
75
|
+
const next = updateConfig(config.configPath, (cfg) => ({ ...cfg }));
|
|
76
|
+
if (options.nonInteractive || testMode) {
|
|
43
77
|
next.channels = next.channels ?? {};
|
|
78
|
+
const mode = testMode === "dedicated" ? "dedicated" : "personal";
|
|
79
|
+
const allowFrom = mode === "personal" ? ["+15551234567"] : [];
|
|
44
80
|
next.channels.whatsapp = {
|
|
45
|
-
dmPolicy: "pairing",
|
|
46
|
-
allowFrom
|
|
47
|
-
selfChatMode:
|
|
81
|
+
dmPolicy: mode === "personal" ? "allowlist" : "pairing",
|
|
82
|
+
allowFrom,
|
|
83
|
+
selfChatMode: mode === "personal",
|
|
48
84
|
accounts: {
|
|
49
85
|
[config.whatsappAccountId]: {
|
|
50
86
|
authDir: config.whatsappAuthDir,
|
|
@@ -52,67 +88,126 @@ async function runSetupWizard(config, nonInteractive) {
|
|
|
52
88
|
},
|
|
53
89
|
};
|
|
54
90
|
writeConfigFile(config.configPath, next);
|
|
55
|
-
|
|
91
|
+
log.success(`Wrote ${config.configPath}`);
|
|
56
92
|
return;
|
|
57
93
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
94
|
+
if (options.useTui) {
|
|
95
|
+
const mode = unwrap(await select({
|
|
96
|
+
message: "WhatsApp number",
|
|
97
|
+
options: [
|
|
98
|
+
{
|
|
99
|
+
value: "personal",
|
|
100
|
+
label: "Personal number (no pairing)",
|
|
101
|
+
hint: "Allowlist your own number",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
value: "dedicated",
|
|
105
|
+
label: "Dedicated number",
|
|
106
|
+
hint: "Pairing or allowlist",
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
initialValue: "personal",
|
|
110
|
+
}));
|
|
111
|
+
let dmPolicy = "pairing";
|
|
112
|
+
let allowFrom = [];
|
|
113
|
+
let selfChatMode = false;
|
|
114
|
+
if (mode === "personal") {
|
|
115
|
+
const number = unwrap(await text({
|
|
116
|
+
message: "Your WhatsApp number (E.164)",
|
|
117
|
+
placeholder: "+15551234567",
|
|
118
|
+
validate: (value) => {
|
|
119
|
+
const normalized = normalizeWhatsAppId(String(value ?? ""));
|
|
120
|
+
return /^\+\d+$/.test(normalized) ? undefined : "Use E.164 format (+123...)";
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
allowFrom = [normalizeWhatsAppId(number)];
|
|
124
|
+
dmPolicy = "allowlist";
|
|
125
|
+
selfChatMode = true;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
dmPolicy = unwrap(await select({
|
|
129
|
+
message: "Direct messages",
|
|
130
|
+
options: [
|
|
131
|
+
{ value: "pairing", label: "Pairing (recommended)" },
|
|
132
|
+
{ value: "allowlist", label: "Allowlist only" },
|
|
133
|
+
{ value: "open", label: "Open (public)" },
|
|
134
|
+
{ value: "disabled", label: "Disabled" },
|
|
135
|
+
],
|
|
136
|
+
initialValue: "pairing",
|
|
137
|
+
}));
|
|
138
|
+
const listInput = unwrap(await text({
|
|
139
|
+
message: "Allowlist numbers (comma-separated, optional)",
|
|
140
|
+
placeholder: "+15551234567, +15551234568",
|
|
141
|
+
}));
|
|
142
|
+
if (listInput.trim()) {
|
|
143
|
+
allowFrom = listInput
|
|
144
|
+
.split(",")
|
|
145
|
+
.map((item) => normalizeWhatsAppId(item))
|
|
146
|
+
.filter(Boolean);
|
|
147
|
+
}
|
|
148
|
+
if (dmPolicy === "open") {
|
|
149
|
+
allowFrom = allowFrom.length ? allowFrom : ["*"];
|
|
72
150
|
}
|
|
73
|
-
normalized = candidate;
|
|
74
151
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
152
|
+
next.channels = next.channels ?? {};
|
|
153
|
+
next.channels.whatsapp = {
|
|
154
|
+
dmPolicy,
|
|
155
|
+
allowFrom,
|
|
156
|
+
selfChatMode,
|
|
157
|
+
accounts: {
|
|
158
|
+
[config.whatsappAccountId]: {
|
|
159
|
+
authDir: config.whatsappAuthDir,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
writeConfigFile(config.configPath, next);
|
|
164
|
+
log.success(`Wrote ${config.configPath}`);
|
|
165
|
+
return;
|
|
78
166
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
dmPolicy = "disabled";
|
|
88
|
-
else
|
|
89
|
-
dmPolicy = "pairing";
|
|
90
|
-
const listInput = await rl.question("Allowlist numbers (comma-separated, optional): ");
|
|
91
|
-
if (listInput.trim()) {
|
|
92
|
-
allowFrom = listInput
|
|
93
|
-
.split(",")
|
|
94
|
-
.map((item) => normalizeWhatsAppId(item))
|
|
95
|
-
.filter(Boolean);
|
|
96
|
-
}
|
|
97
|
-
if (dmPolicy === "open") {
|
|
98
|
-
allowFrom = allowFrom.length ? allowFrom : ["*"];
|
|
167
|
+
}
|
|
168
|
+
async function runTelegramSetup(config, tokenOverride) {
|
|
169
|
+
const tokenFromEnv = tokenOverride ?? process.env.OWPENWORK_TEST_TELEGRAM_TOKEN;
|
|
170
|
+
let token = tokenFromEnv || config.telegramToken;
|
|
171
|
+
if (!token) {
|
|
172
|
+
if (!isInteractive()) {
|
|
173
|
+
log.warn("Telegram token missing. Provide TELEGRAM_BOT_TOKEN or use login command.");
|
|
174
|
+
return;
|
|
99
175
|
}
|
|
176
|
+
token = unwrap(await text({
|
|
177
|
+
message: "Telegram bot token",
|
|
178
|
+
placeholder: "123456:ABCDEF...",
|
|
179
|
+
validate: (value) => (String(value ?? "").trim() ? undefined : "Token required"),
|
|
180
|
+
}));
|
|
100
181
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
182
|
+
updateConfig(config.configPath, (cfg) => {
|
|
183
|
+
const next = { ...cfg };
|
|
184
|
+
next.channels = next.channels ?? {};
|
|
185
|
+
next.channels.telegram = {
|
|
186
|
+
token,
|
|
187
|
+
enabled: true,
|
|
188
|
+
};
|
|
189
|
+
return next;
|
|
190
|
+
});
|
|
191
|
+
log.success("Telegram token saved.");
|
|
192
|
+
}
|
|
193
|
+
async function runStart(pathOverride) {
|
|
194
|
+
if (pathOverride?.trim()) {
|
|
195
|
+
process.env.OPENCODE_DIRECTORY = pathOverride.trim();
|
|
196
|
+
}
|
|
197
|
+
const config = loadConfig();
|
|
198
|
+
const logger = createLogger(config.logLevel);
|
|
199
|
+
if (!process.env.OPENCODE_DIRECTORY) {
|
|
200
|
+
process.env.OPENCODE_DIRECTORY = config.opencodeDirectory;
|
|
201
|
+
}
|
|
202
|
+
const bridge = await startBridge(config, logger);
|
|
203
|
+
logger.info("Commands: owpenwork whatsapp login, owpenwork pairing list, owpenwork status");
|
|
204
|
+
const shutdown = async () => {
|
|
205
|
+
logger.info("shutting down");
|
|
206
|
+
await bridge.stop();
|
|
207
|
+
process.exit(0);
|
|
113
208
|
};
|
|
114
|
-
|
|
115
|
-
|
|
209
|
+
process.on("SIGINT", shutdown);
|
|
210
|
+
process.on("SIGTERM", shutdown);
|
|
116
211
|
}
|
|
117
212
|
async function runGuidedFlow(pathArg, opts) {
|
|
118
213
|
if (pathArg?.trim()) {
|
|
@@ -122,30 +217,95 @@ async function runGuidedFlow(pathArg, opts) {
|
|
|
122
217
|
if (!process.env.OPENCODE_DIRECTORY) {
|
|
123
218
|
process.env.OPENCODE_DIRECTORY = config.opencodeDirectory;
|
|
124
219
|
}
|
|
220
|
+
const authPath = path.join(config.whatsappAuthDir, "creds.json");
|
|
221
|
+
const whatsappLinked = fs.existsSync(authPath);
|
|
222
|
+
const configExists = fs.existsSync(config.configPath);
|
|
125
223
|
if (opts.check) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
console.log(`WhatsApp linked: ${linked ? "yes" : "no"}`);
|
|
131
|
-
process.exit(linked && cfgExists ? 0 : 1);
|
|
224
|
+
console.log(`Config: ${configExists ? "yes" : "no"}`);
|
|
225
|
+
console.log(`WhatsApp linked: ${whatsappLinked ? "yes" : "no"}`);
|
|
226
|
+
console.log(`Telegram configured: ${config.telegramToken ? "yes" : "no"}`);
|
|
227
|
+
process.exit(configExists && whatsappLinked ? 0 : 1);
|
|
132
228
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
229
|
+
if (opts.nonInteractive) {
|
|
230
|
+
await runSetupWizard(config, { nonInteractive: true, useTui: false });
|
|
231
|
+
console.log("Next: owpenwork whatsapp login");
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!isInteractive()) {
|
|
235
|
+
console.log("Non-interactive shell. Run `owpenwork start` or `owpenwork --non-interactive`.");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
intro("Owpenwork Setup");
|
|
239
|
+
const selectedFromEnv = parseSelections(process.env.OWPENWORK_TEST_SELECTIONS);
|
|
240
|
+
const selections = selectedFromEnv ??
|
|
241
|
+
unwrap(await multiselect({
|
|
242
|
+
message: "Select what to set up",
|
|
243
|
+
options: STEP_OPTIONS,
|
|
244
|
+
initialValues: defaultSelections(configExists, whatsappLinked),
|
|
245
|
+
required: false,
|
|
246
|
+
}));
|
|
247
|
+
const selectedSteps = Array.isArray(selections) ? selections : [];
|
|
248
|
+
if (!selectedSteps.length) {
|
|
249
|
+
cancel("No steps selected");
|
|
250
|
+
process.exit(0);
|
|
251
|
+
}
|
|
252
|
+
const total = selectedSteps.length;
|
|
253
|
+
let index = 1;
|
|
254
|
+
const runStep = async (label, task) => {
|
|
255
|
+
const spin = spinner();
|
|
256
|
+
spin.start(`Step ${index}/${total}: ${label}`);
|
|
257
|
+
await task();
|
|
258
|
+
spin.stop(`Step ${index}/${total}: ${label} done`);
|
|
259
|
+
index += 1;
|
|
260
|
+
};
|
|
261
|
+
for (const step of selectedSteps) {
|
|
262
|
+
if (step === "config") {
|
|
263
|
+
await runStep("Setup configuration", async () => {
|
|
264
|
+
await runSetupWizard(config, { nonInteractive: false, useTui: true });
|
|
265
|
+
config = loadConfig(process.env, { requireOpencode: false });
|
|
266
|
+
});
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (step === "whatsapp") {
|
|
270
|
+
await runStep("Link WhatsApp", async () => {
|
|
271
|
+
const alreadyLinked = fs.existsSync(authPath);
|
|
272
|
+
if (alreadyLinked) {
|
|
273
|
+
const relink = unwrap(await confirm({
|
|
274
|
+
message: "WhatsApp already linked. Relink?",
|
|
275
|
+
initialValue: false,
|
|
276
|
+
}));
|
|
277
|
+
if (!relink)
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
await loginWhatsApp(config, createLogger(config.logLevel));
|
|
281
|
+
});
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (step === "telegram") {
|
|
285
|
+
await runStep("Link Telegram", async () => {
|
|
286
|
+
await runTelegramSetup(config);
|
|
287
|
+
config = loadConfig(process.env, { requireOpencode: false });
|
|
288
|
+
});
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (step === "start") {
|
|
292
|
+
log.info("Starting bridge...");
|
|
293
|
+
outro("Owpenwork is running.");
|
|
294
|
+
await runStart(pathArg);
|
|
140
295
|
}
|
|
141
296
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!fs.existsSync(authPath)) {
|
|
145
|
-
await loginWhatsApp(config, createLogger(config.logLevel));
|
|
297
|
+
if (!selectedSteps.includes("start")) {
|
|
298
|
+
outro("Setup complete.");
|
|
146
299
|
}
|
|
147
|
-
await runStart(pathArg);
|
|
148
300
|
}
|
|
301
|
+
const program = new Command();
|
|
302
|
+
program
|
|
303
|
+
.name("owpenbot")
|
|
304
|
+
.version(VERSION)
|
|
305
|
+
.description("OpenCode WhatsApp + Telegram bridge")
|
|
306
|
+
.argument("[path]")
|
|
307
|
+
.option("--non-interactive", "Run setup defaults and exit", false)
|
|
308
|
+
.option("--check", "Validate config/auth and exit", false);
|
|
149
309
|
program
|
|
150
310
|
.command("start")
|
|
151
311
|
.description("Start the bridge")
|
|
@@ -164,7 +324,29 @@ program
|
|
|
164
324
|
.option("--non-interactive", "Write defaults without prompts", false)
|
|
165
325
|
.action(async (opts) => {
|
|
166
326
|
const config = loadConfig(process.env, { requireOpencode: false });
|
|
167
|
-
|
|
327
|
+
if (!isInteractive() && !opts.nonInteractive) {
|
|
328
|
+
console.log("Non-interactive shell. Use --non-interactive.");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
intro("Owpenwork Setup");
|
|
332
|
+
await runSetupWizard(config, { nonInteractive: Boolean(opts.nonInteractive), useTui: true });
|
|
333
|
+
outro("Setup complete.");
|
|
334
|
+
});
|
|
335
|
+
const login = program.command("login").description("Link channels");
|
|
336
|
+
login
|
|
337
|
+
.command("whatsapp")
|
|
338
|
+
.description("Login to WhatsApp via QR code")
|
|
339
|
+
.action(async () => {
|
|
340
|
+
const config = loadConfig(process.env, { requireOpencode: false });
|
|
341
|
+
await loginWhatsApp(config, createLogger(config.logLevel));
|
|
342
|
+
});
|
|
343
|
+
login
|
|
344
|
+
.command("telegram")
|
|
345
|
+
.option("--token <token>", "Telegram bot token")
|
|
346
|
+
.description("Save Telegram bot token")
|
|
347
|
+
.action(async (opts) => {
|
|
348
|
+
const config = loadConfig(process.env, { requireOpencode: false });
|
|
349
|
+
await runTelegramSetup(config, opts.token);
|
|
168
350
|
});
|
|
169
351
|
program
|
|
170
352
|
.command("pairing-code")
|
|
@@ -190,32 +372,28 @@ whatsapp
|
|
|
190
372
|
.description("Login to WhatsApp via QR code")
|
|
191
373
|
.action(async () => {
|
|
192
374
|
const config = loadConfig(process.env, { requireOpencode: false });
|
|
193
|
-
|
|
194
|
-
await loginWhatsApp(config, logger);
|
|
375
|
+
await loginWhatsApp(config, createLogger(config.logLevel));
|
|
195
376
|
});
|
|
196
377
|
whatsapp
|
|
197
378
|
.command("logout")
|
|
198
379
|
.description("Logout of WhatsApp and clear auth state")
|
|
199
380
|
.action(() => {
|
|
200
381
|
const config = loadConfig(process.env, { requireOpencode: false });
|
|
201
|
-
|
|
202
|
-
unpairWhatsApp(config, logger);
|
|
382
|
+
unpairWhatsApp(config, createLogger(config.logLevel));
|
|
203
383
|
});
|
|
204
384
|
program
|
|
205
385
|
.command("qr")
|
|
206
386
|
.description("Print a WhatsApp QR code to pair")
|
|
207
387
|
.action(async () => {
|
|
208
388
|
const config = loadConfig(process.env, { requireOpencode: false });
|
|
209
|
-
|
|
210
|
-
await loginWhatsApp(config, logger);
|
|
389
|
+
await loginWhatsApp(config, createLogger(config.logLevel));
|
|
211
390
|
});
|
|
212
391
|
program
|
|
213
392
|
.command("unpair")
|
|
214
393
|
.description("Clear WhatsApp pairing data")
|
|
215
394
|
.action(() => {
|
|
216
395
|
const config = loadConfig(process.env, { requireOpencode: false });
|
|
217
|
-
|
|
218
|
-
unpairWhatsApp(config, logger);
|
|
396
|
+
unpairWhatsApp(config, createLogger(config.logLevel));
|
|
219
397
|
});
|
|
220
398
|
const pairing = program.command("pairing").description("Pairing requests");
|
|
221
399
|
pairing
|
|
@@ -273,6 +451,7 @@ program
|
|
|
273
451
|
const linked = fs.existsSync(authPath);
|
|
274
452
|
console.log(`Config: ${config.configPath}`);
|
|
275
453
|
console.log(`WhatsApp linked: ${linked ? "yes" : "no"}`);
|
|
454
|
+
console.log(`Telegram configured: ${config.telegramToken ? "yes" : "no"}`);
|
|
276
455
|
console.log(`Auth dir: ${config.whatsappAuthDir}`);
|
|
277
456
|
console.log(`OpenCode URL: ${config.opencodeUrl}`);
|
|
278
457
|
});
|
|
@@ -290,16 +469,28 @@ program
|
|
|
290
469
|
console.log("WhatsApp linked.");
|
|
291
470
|
}
|
|
292
471
|
console.log(`OpenCode URL: ${config.opencodeUrl}`);
|
|
472
|
+
try {
|
|
473
|
+
const client = createClient(config);
|
|
474
|
+
const health = await client.global.health();
|
|
475
|
+
const healthy = Boolean(health.healthy);
|
|
476
|
+
console.log(`OpenCode reachable: ${healthy ? "yes" : "no"}`);
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
console.log("OpenCode reachable: no");
|
|
480
|
+
console.log(`Error: ${String(error)}`);
|
|
481
|
+
}
|
|
293
482
|
console.log("If replies fail, ensure the server is running.");
|
|
294
483
|
const resetRequested = Boolean(opts.reset);
|
|
295
484
|
if (!resetRequested && !process.stdin.isTTY)
|
|
296
485
|
return;
|
|
297
486
|
let confirmed = resetRequested;
|
|
298
487
|
if (!resetRequested) {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
488
|
+
const answer = await confirm({ message: "Reset owpenbot state?", initialValue: false });
|
|
489
|
+
if (isCancel(answer)) {
|
|
490
|
+
cancel("Reset cancelled");
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
confirmed = Boolean(answer);
|
|
303
494
|
}
|
|
304
495
|
if (!confirmed)
|
|
305
496
|
return;
|
|
@@ -309,7 +500,7 @@ program
|
|
|
309
500
|
fs.rmSync(target, { recursive: true, force: true });
|
|
310
501
|
}
|
|
311
502
|
}
|
|
312
|
-
console.log("Reset complete. Run: owpenwork
|
|
503
|
+
console.log("Reset complete. Run: owpenwork");
|
|
313
504
|
});
|
|
314
505
|
program.parseAsync(process.argv).catch((error) => {
|
|
315
506
|
console.error(error);
|
package/dist/config.js
CHANGED
|
@@ -141,6 +141,7 @@ export function loadConfig(env = process.env, options = {}) {
|
|
|
141
141
|
const whatsappAllowFrom = new Set([...fileAllowFrom, ...envAllowFrom]);
|
|
142
142
|
const toolOutputLimit = parseInteger(env.TOOL_OUTPUT_LIMIT) ?? 1200;
|
|
143
143
|
const permissionMode = env.PERMISSION_MODE?.toLowerCase() === "deny" ? "deny" : "allow";
|
|
144
|
+
const telegramToken = env.TELEGRAM_BOT_TOKEN?.trim() || configFile.channels?.telegram?.token || undefined;
|
|
144
145
|
return {
|
|
145
146
|
configPath,
|
|
146
147
|
configFile,
|
|
@@ -148,8 +149,8 @@ export function loadConfig(env = process.env, options = {}) {
|
|
|
148
149
|
opencodeDirectory: resolvedDirectory,
|
|
149
150
|
opencodeUsername: env.OPENCODE_SERVER_USERNAME?.trim() || undefined,
|
|
150
151
|
opencodePassword: env.OPENCODE_SERVER_PASSWORD?.trim() || undefined,
|
|
151
|
-
telegramToken
|
|
152
|
-
telegramEnabled: parseBoolean(env.TELEGRAM_ENABLED,
|
|
152
|
+
telegramToken,
|
|
153
|
+
telegramEnabled: parseBoolean(env.TELEGRAM_ENABLED, configFile.channels?.telegram?.enabled ?? Boolean(telegramToken)),
|
|
153
154
|
whatsappAuthDir,
|
|
154
155
|
whatsappAccountId,
|
|
155
156
|
whatsappDmPolicy: dmPolicy,
|
package/dist/whatsapp.js
CHANGED
|
@@ -156,8 +156,16 @@ export async function loginWhatsApp(config, logger) {
|
|
|
156
156
|
if (update.connection === "open") {
|
|
157
157
|
finish("connection.open");
|
|
158
158
|
}
|
|
159
|
-
if (update.connection === "close"
|
|
160
|
-
|
|
159
|
+
if (update.connection === "close") {
|
|
160
|
+
const lastDisconnect = update.lastDisconnect;
|
|
161
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
162
|
+
if (statusCode === 515 && state.creds?.registered) {
|
|
163
|
+
finish("connection.close.registered");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (state.creds?.registered) {
|
|
167
|
+
finish("connection.close.registered");
|
|
168
|
+
}
|
|
161
169
|
}
|
|
162
170
|
});
|
|
163
171
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "owpenwork",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "WhatsApp bridge for a running OpenCode server",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"test:npx": "node scripts/test-npx.mjs"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"@clack/prompts": "^0.11.0",
|
|
43
44
|
"@opencode-ai/sdk": "^1.1.19",
|
|
44
45
|
"@whiskeysockets/baileys": "7.0.0-rc.9",
|
|
45
46
|
"better-sqlite3": "^11.7.0",
|