owpenwork 0.1.13 → 0.1.14
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/dist/bridge.js +116 -0
- package/dist/cli.js +1 -1
- package/dist/whatsapp.js +10 -0
- package/package.json +1 -1
package/dist/bridge.js
CHANGED
|
@@ -19,6 +19,11 @@ const TOOL_LABELS = {
|
|
|
19
19
|
task: "agent",
|
|
20
20
|
webfetch: "webfetch",
|
|
21
21
|
};
|
|
22
|
+
const CHANNEL_LABELS = {
|
|
23
|
+
whatsapp: "WhatsApp",
|
|
24
|
+
telegram: "Telegram",
|
|
25
|
+
};
|
|
26
|
+
const TYPING_INTERVAL_MS = 6000;
|
|
22
27
|
export async function startBridge(config, logger, reporter) {
|
|
23
28
|
const reportStatus = reporter?.onStatus;
|
|
24
29
|
const client = createClient(config);
|
|
@@ -43,6 +48,67 @@ export async function startBridge(config, logger, reporter) {
|
|
|
43
48
|
}
|
|
44
49
|
const sessionQueue = new Map();
|
|
45
50
|
const activeRuns = new Map();
|
|
51
|
+
const sessionModels = new Map();
|
|
52
|
+
const typingLoops = new Map();
|
|
53
|
+
const formatPeer = (channel, peerId) => channel === "whatsapp" ? normalizeWhatsAppId(peerId) : peerId;
|
|
54
|
+
const formatModelLabel = (model) => model ? `${model.providerID}/${model.modelID}` : null;
|
|
55
|
+
const extractModelRef = (info) => {
|
|
56
|
+
if (!info || typeof info !== "object")
|
|
57
|
+
return null;
|
|
58
|
+
const record = info;
|
|
59
|
+
if (record.role !== "user")
|
|
60
|
+
return null;
|
|
61
|
+
if (!record.model || typeof record.model !== "object")
|
|
62
|
+
return null;
|
|
63
|
+
const model = record.model;
|
|
64
|
+
if (typeof model.providerID !== "string" || typeof model.modelID !== "string")
|
|
65
|
+
return null;
|
|
66
|
+
return { providerID: model.providerID, modelID: model.modelID };
|
|
67
|
+
};
|
|
68
|
+
const reportThinking = (run) => {
|
|
69
|
+
if (!reportStatus)
|
|
70
|
+
return;
|
|
71
|
+
const modelLabel = formatModelLabel(sessionModels.get(run.sessionID));
|
|
72
|
+
const nextLabel = modelLabel ? `Thinking (${modelLabel})` : "Thinking...";
|
|
73
|
+
if (run.thinkingLabel === nextLabel && run.thinkingActive)
|
|
74
|
+
return;
|
|
75
|
+
run.thinkingLabel = nextLabel;
|
|
76
|
+
run.thinkingActive = true;
|
|
77
|
+
reportStatus(`[${CHANNEL_LABELS[run.channel]}] ${formatPeer(run.channel, run.peerId)} ${nextLabel}`);
|
|
78
|
+
};
|
|
79
|
+
const reportDone = (run) => {
|
|
80
|
+
if (!reportStatus || !run.thinkingActive)
|
|
81
|
+
return;
|
|
82
|
+
const modelLabel = formatModelLabel(sessionModels.get(run.sessionID));
|
|
83
|
+
const suffix = modelLabel ? ` (${modelLabel})` : "";
|
|
84
|
+
reportStatus(`[${CHANNEL_LABELS[run.channel]}] ${formatPeer(run.channel, run.peerId)} Done${suffix}`);
|
|
85
|
+
run.thinkingActive = false;
|
|
86
|
+
};
|
|
87
|
+
const startTyping = (run) => {
|
|
88
|
+
const adapter = adapters.get(run.channel);
|
|
89
|
+
if (!adapter?.sendTyping)
|
|
90
|
+
return;
|
|
91
|
+
if (typingLoops.has(run.sessionID))
|
|
92
|
+
return;
|
|
93
|
+
const sendTyping = async () => {
|
|
94
|
+
try {
|
|
95
|
+
await adapter.sendTyping?.(run.peerId);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
logger.warn({ error, channel: run.channel }, "typing update failed");
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
void sendTyping();
|
|
102
|
+
const timer = setInterval(sendTyping, TYPING_INTERVAL_MS);
|
|
103
|
+
typingLoops.set(run.sessionID, timer);
|
|
104
|
+
};
|
|
105
|
+
const stopTyping = (sessionID) => {
|
|
106
|
+
const timer = typingLoops.get(sessionID);
|
|
107
|
+
if (!timer)
|
|
108
|
+
return;
|
|
109
|
+
clearInterval(timer);
|
|
110
|
+
typingLoops.delete(sessionID);
|
|
111
|
+
};
|
|
46
112
|
let opencodeHealthy = false;
|
|
47
113
|
let opencodeVersion;
|
|
48
114
|
async function refreshHealth() {
|
|
@@ -80,6 +146,46 @@ export async function startBridge(config, logger, reporter) {
|
|
|
80
146
|
const event = normalizeEvent(raw);
|
|
81
147
|
if (!event)
|
|
82
148
|
continue;
|
|
149
|
+
if (event.type === "message.updated") {
|
|
150
|
+
if (event.properties && typeof event.properties === "object") {
|
|
151
|
+
const record = event.properties;
|
|
152
|
+
const info = record.info;
|
|
153
|
+
const sessionID = typeof info?.sessionID === "string" ? info.sessionID : null;
|
|
154
|
+
const model = extractModelRef(info);
|
|
155
|
+
if (sessionID && model) {
|
|
156
|
+
sessionModels.set(sessionID, model);
|
|
157
|
+
const run = activeRuns.get(sessionID);
|
|
158
|
+
if (run)
|
|
159
|
+
reportThinking(run);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (event.type === "session.status") {
|
|
164
|
+
if (event.properties && typeof event.properties === "object") {
|
|
165
|
+
const record = event.properties;
|
|
166
|
+
const sessionID = typeof record.sessionID === "string" ? record.sessionID : null;
|
|
167
|
+
const status = record.status;
|
|
168
|
+
if (sessionID && (status?.type === "busy" || status?.type === "retry")) {
|
|
169
|
+
const run = activeRuns.get(sessionID);
|
|
170
|
+
if (run) {
|
|
171
|
+
reportThinking(run);
|
|
172
|
+
startTyping(run);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (event.type === "session.idle") {
|
|
178
|
+
if (event.properties && typeof event.properties === "object") {
|
|
179
|
+
const record = event.properties;
|
|
180
|
+
const sessionID = typeof record.sessionID === "string" ? record.sessionID : null;
|
|
181
|
+
if (sessionID) {
|
|
182
|
+
stopTyping(sessionID);
|
|
183
|
+
const run = activeRuns.get(sessionID);
|
|
184
|
+
if (run)
|
|
185
|
+
reportDone(run);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
83
189
|
if (event.type === "message.part.updated") {
|
|
84
190
|
const part = event.properties?.part;
|
|
85
191
|
if (!part?.sessionID)
|
|
@@ -201,6 +307,8 @@ export async function startBridge(config, logger, reporter) {
|
|
|
201
307
|
seenToolStates: new Map(),
|
|
202
308
|
};
|
|
203
309
|
activeRuns.set(sessionID, runState);
|
|
310
|
+
reportThinking(runState);
|
|
311
|
+
startTyping(runState);
|
|
204
312
|
try {
|
|
205
313
|
const response = await client.session.prompt({
|
|
206
314
|
sessionID,
|
|
@@ -228,6 +336,8 @@ export async function startBridge(config, logger, reporter) {
|
|
|
228
336
|
});
|
|
229
337
|
}
|
|
230
338
|
finally {
|
|
339
|
+
stopTyping(sessionID);
|
|
340
|
+
reportDone(runState);
|
|
231
341
|
activeRuns.delete(sessionID);
|
|
232
342
|
}
|
|
233
343
|
});
|
|
@@ -243,6 +353,8 @@ export async function startBridge(config, logger, reporter) {
|
|
|
243
353
|
throw new Error("Failed to create session");
|
|
244
354
|
store.upsertSession(message.channel, message.peerId, sessionID);
|
|
245
355
|
logger.info({ sessionID, channel: message.channel, peerId: message.peerId }, "session created");
|
|
356
|
+
reportStatus?.(`${CHANNEL_LABELS[message.channel]} session created for ${formatPeer(message.channel, message.peerId)} (ID: ${sessionID}).`);
|
|
357
|
+
await sendText(message.channel, message.peerId, "🧭 Session started.", { kind: "system" });
|
|
246
358
|
return sessionID;
|
|
247
359
|
}
|
|
248
360
|
function enqueue(sessionID, task) {
|
|
@@ -271,6 +383,10 @@ export async function startBridge(config, logger, reporter) {
|
|
|
271
383
|
clearInterval(healthTimer);
|
|
272
384
|
if (stopHealthServer)
|
|
273
385
|
stopHealthServer();
|
|
386
|
+
for (const timer of typingLoops.values()) {
|
|
387
|
+
clearInterval(timer);
|
|
388
|
+
}
|
|
389
|
+
typingLoops.clear();
|
|
274
390
|
for (const adapter of adapters.values()) {
|
|
275
391
|
await adapter.stop();
|
|
276
392
|
}
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import { createLogger } from "./logger.js";
|
|
|
10
10
|
import { createClient } from "./opencode.js";
|
|
11
11
|
import { truncateText } from "./text.js";
|
|
12
12
|
import { loginWhatsApp, unpairWhatsApp } from "./whatsapp.js";
|
|
13
|
-
const VERSION = "0.1.
|
|
13
|
+
const VERSION = "0.1.14";
|
|
14
14
|
const STEP_OPTIONS = [
|
|
15
15
|
{
|
|
16
16
|
value: "config",
|
package/dist/whatsapp.js
CHANGED
|
@@ -171,6 +171,16 @@ export function createWhatsAppAdapter(config, logger, onMessage, opts = {}) {
|
|
|
171
171
|
const sent = await socket.sendMessage(peerId, { text });
|
|
172
172
|
recordSentMessage(sent?.key?.id);
|
|
173
173
|
},
|
|
174
|
+
async sendTyping(peerId) {
|
|
175
|
+
if (!socket)
|
|
176
|
+
return;
|
|
177
|
+
try {
|
|
178
|
+
await socket.sendPresenceUpdate("composing", peerId);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
log.warn({ error, peerId }, "whatsapp typing update failed");
|
|
182
|
+
}
|
|
183
|
+
},
|
|
174
184
|
};
|
|
175
185
|
}
|
|
176
186
|
export async function loginWhatsApp(config, logger, options = {}) {
|