digital-brain 1.1.15 → 1.1.18
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 +11 -3
- package/bin/digital-brain.js +31 -1
- package/docs/AUTOMATIONS.md +7 -1
- package/package.json +1 -1
- package/whatsapp-web/auto-reply.mjs +60 -4
package/README.md
CHANGED
|
@@ -83,8 +83,8 @@ node ./bin/digital-brain.js init ./Digital Brain\ Vault
|
|
|
83
83
|
- Generate reply-ready person context that keeps WhatsApp, iMessage, Slack, and LinkedIn evidence separate under the same person.
|
|
84
84
|
- Create AI-readable memory files for future prompts.
|
|
85
85
|
- Draft WhatsApp sends by default, send with explicit `--yes`, or configure auto-send mode during init.
|
|
86
|
-
- Run an explicit WhatsApp auto-responder that uses Ollama or
|
|
87
|
-
- Choose the WhatsApp auto-reply provider during init: Ollama, Codex app bridge, or Codex CLI.
|
|
86
|
+
- Run an explicit WhatsApp auto-responder that uses Ollama, OpenAI, or Codex plus vault memory while the command is running.
|
|
87
|
+
- Choose the WhatsApp auto-reply provider during init: Ollama, OpenAI API, Codex app bridge, or Codex CLI.
|
|
88
88
|
- Enforce an AI-disclosure guard after repeated AI-assisted sends.
|
|
89
89
|
|
|
90
90
|
## Core Commands
|
|
@@ -99,6 +99,7 @@ digital-brain import-linkedin --input ./linkedin-archive.zip
|
|
|
99
99
|
digital-brain send-whatsapp --to "Name" --message "text"
|
|
100
100
|
digital-brain auto-whatsapp --allow "Name" --model llama3.1
|
|
101
101
|
digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1
|
|
102
|
+
OPENAI_API_KEY="sk-..." digital-brain auto-whatsapp --allow-all --provider openai --model gpt-4.1-mini --yes
|
|
102
103
|
digital-brain auto-whatsapp --allow-all --provider codex --yes
|
|
103
104
|
digital-brain auto-whatsapp --allow-all --provider codex-app --yes
|
|
104
105
|
```
|
|
@@ -127,6 +128,7 @@ digital-brain auto-whatsapp --allow "Mom" --model llama3.1
|
|
|
127
128
|
digital-brain auto-whatsapp --allow "Mom" --model llama3.1 --yes
|
|
128
129
|
digital-brain auto-whatsapp --allow "Mom" --model llama3.1 --yes --no-process-unread
|
|
129
130
|
digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1 --yes
|
|
131
|
+
OPENAI_API_KEY="sk-..." digital-brain auto-whatsapp --allow-all --provider openai --model gpt-4.1-mini --yes
|
|
130
132
|
digital-brain auto-whatsapp --allow-all --provider codex --yes
|
|
131
133
|
digital-brain auto-whatsapp --allow-all --provider codex-app --yes
|
|
132
134
|
```
|
|
@@ -135,7 +137,13 @@ If you start without `--allow`, `--contact`, or `--allow-all` in an interactive
|
|
|
135
137
|
|
|
136
138
|
Even with `--allow-all`, likely business, notification, OTP, delivery, bank, and support chats are skipped by default. Use explicit `--allow "Name"` or `--contact "+15551234567"` for trusted personal chats. Pass `--include-businesses` only if you intentionally want those chats included.
|
|
137
139
|
|
|
138
|
-
The default provider is local Ollama. `--provider codex` uses the Codex CLI. `--provider codex-app` uses a file bridge for the Codex desktop app.
|
|
140
|
+
The default provider is local Ollama. `--provider openai` uses the OpenAI API with the same vault context prompt. `--provider codex` uses the Codex CLI. `--provider codex-app` uses a file bridge for the Codex desktop app.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
OPENAI_API_KEY="sk-..." digital-brain auto-whatsapp --allow-all --provider openai --model gpt-4.1-mini --yes
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If you select `OpenAI API` during `digital-brain init`, you can either paste an API key to store it in the local vault config or leave it blank and set `OPENAI_API_KEY` before running `auto-whatsapp`.
|
|
139
147
|
|
|
140
148
|
```bash
|
|
141
149
|
digital-brain auto-whatsapp --allow-all --provider codex --yes
|
package/bin/digital-brain.js
CHANGED
|
@@ -50,6 +50,8 @@ async function init(argv, args) {
|
|
|
50
50
|
let timezone = args.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || "local";
|
|
51
51
|
let outboundMode = args["outbound-mode"] || "draft";
|
|
52
52
|
let autoReplyProvider = args["auto-reply-provider"] || args.provider || "ollama";
|
|
53
|
+
let openaiApiKey = args["openai-api-key"] || "";
|
|
54
|
+
let openaiModel = args["openai-model"] || "gpt-4.1-mini";
|
|
53
55
|
let privacyMode = args["privacy-mode"] || "standard";
|
|
54
56
|
let sourceMarkdownMode = args["source-markdown-mode"] || "none";
|
|
55
57
|
let selectedSources = parseList(args.sources || "whatsapp");
|
|
@@ -109,9 +111,17 @@ async function init(argv, args) {
|
|
|
109
111
|
if (outboundMode !== "disabled") {
|
|
110
112
|
autoReplyProvider = await select(rl, "WhatsApp auto-reply brain", [
|
|
111
113
|
["ollama", "Ollama local model", "Runs fully local if Ollama and the model are installed.", "🦙"],
|
|
114
|
+
["openai", "OpenAI API", "Fast hosted replies using the same Digital Brain context prompt.", "⚡"],
|
|
112
115
|
["codex-app", "Codex app bridge", "Uses request/response files for a Codex desktop automation or thread.", "🧠"],
|
|
113
116
|
["codex", "Codex CLI", "Uses a local codex command; only choose this if the CLI works.", "⌨️"],
|
|
114
117
|
], autoReplyProvider);
|
|
118
|
+
if (autoReplyProvider === "openai") {
|
|
119
|
+
openaiModel = await ask(rl, "🤖 OpenAI model", openaiModel);
|
|
120
|
+
openaiApiKey = await askSecret(rl, "🔑 OpenAI API key", {
|
|
121
|
+
fallbackLabel: process.env.OPENAI_API_KEY ? "OPENAI_API_KEY env found" : "use OPENAI_API_KEY at runtime",
|
|
122
|
+
helpText: "Paste a key to store it locally in this vault config. Leave blank to use OPENAI_API_KEY at runtime.",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
115
125
|
}
|
|
116
126
|
connectAi = await confirm(rl, "🔗 Add global AI pointers for Codex/Claude/Gemini?", true);
|
|
117
127
|
responsibilityAccepted = await responsibilityGate(rl, { schedule, outboundMode });
|
|
@@ -141,6 +151,8 @@ async function init(argv, args) {
|
|
|
141
151
|
timezone,
|
|
142
152
|
outboundMode,
|
|
143
153
|
autoReplyProvider,
|
|
154
|
+
autoReplyModel: autoReplyProvider === "openai" ? openaiModel : args.model || undefined,
|
|
155
|
+
openaiApiKey: autoReplyProvider === "openai" && openaiApiKey ? openaiApiKey : undefined,
|
|
144
156
|
privacyMode,
|
|
145
157
|
sourceMarkdownMode,
|
|
146
158
|
selectedSources,
|
|
@@ -184,6 +196,9 @@ async function init(argv, args) {
|
|
|
184
196
|
console.log("Codex app config was not detected. The bridge guide was still generated for later use.");
|
|
185
197
|
}
|
|
186
198
|
}
|
|
199
|
+
if (autoReplyProvider === "openai" && !openaiApiKey && !process.env.OPENAI_API_KEY) {
|
|
200
|
+
console.log("OpenAI provider selected. Set OPENAI_API_KEY before running auto-whatsapp, or add openaiApiKey to the vault config.");
|
|
201
|
+
}
|
|
187
202
|
console.log("Next:");
|
|
188
203
|
console.log(" digital-brain run");
|
|
189
204
|
if (schedule === "always-on") console.log(` "${path.join(vault, "Tools", "digital-brain-watch.sh")}"`);
|
|
@@ -310,6 +325,14 @@ function printSetupCheck(vault, options = {}) {
|
|
|
310
325
|
optional: true,
|
|
311
326
|
},
|
|
312
327
|
];
|
|
328
|
+
if (config.autoReplyProvider === "openai") {
|
|
329
|
+
checks.push({
|
|
330
|
+
label: "OpenAI API key",
|
|
331
|
+
ok: Boolean(process.env.OPENAI_API_KEY || config.openaiApiKey),
|
|
332
|
+
value: process.env.OPENAI_API_KEY ? "found in OPENAI_API_KEY" : config.openaiApiKey ? "stored in vault config" : "not found",
|
|
333
|
+
hint: "Set OPENAI_API_KEY or re-run init and choose OpenAI API.",
|
|
334
|
+
});
|
|
335
|
+
}
|
|
313
336
|
if (selectedSources.includes("whatsapp")) {
|
|
314
337
|
checks.push({
|
|
315
338
|
label: "WhatsApp Mac database",
|
|
@@ -507,6 +530,13 @@ async function ask(rl, label, fallback, helpText = "") {
|
|
|
507
530
|
return answer.trim() || fallback;
|
|
508
531
|
}
|
|
509
532
|
|
|
533
|
+
async function askSecret(rl, label, options = {}) {
|
|
534
|
+
if (options.helpText) console.log(` ${options.helpText}`);
|
|
535
|
+
const suffix = options.fallbackLabel ? ` [${options.fallbackLabel}]` : "";
|
|
536
|
+
const answer = await rl.question(`${label}${suffix}: `);
|
|
537
|
+
return answer.trim();
|
|
538
|
+
}
|
|
539
|
+
|
|
510
540
|
async function askNumber(rl, label, fallback, options = {}) {
|
|
511
541
|
const suffix = options.suffix ? ` ${options.suffix}` : "";
|
|
512
542
|
const answer = await ask(rl, `${label}${suffix}`, String(fallback));
|
|
@@ -657,6 +687,6 @@ Usage:
|
|
|
657
687
|
digital-brain extract --days 30
|
|
658
688
|
digital-brain interpret --days 30
|
|
659
689
|
digital-brain send-whatsapp --to "Name" --message "Text" [--yes]
|
|
660
|
-
digital-brain auto-whatsapp --allow "Name" --contact "+15551234567" --provider ollama|codex --model llama3.1 [--yes] [--no-process-unread]
|
|
690
|
+
digital-brain auto-whatsapp --allow "Name" --contact "+15551234567" --provider ollama|openai|codex|codex-app --model llama3.1 [--yes] [--no-process-unread]
|
|
661
691
|
`);
|
|
662
692
|
}
|
package/docs/AUTOMATIONS.md
CHANGED
|
@@ -98,13 +98,14 @@ The generated script loops forever and sleeps for `refreshIntervalMinutes`. The
|
|
|
98
98
|
|
|
99
99
|
## WhatsApp Auto-Reply
|
|
100
100
|
|
|
101
|
-
`digital-brain auto-whatsapp` is separate from refresh automation. It uses WhatsApp Web for live incoming messages and
|
|
101
|
+
`digital-brain auto-whatsapp` is separate from refresh automation. It uses WhatsApp Web for live incoming messages and Ollama, OpenAI, or Codex for reply generation. On startup it scans unread WhatsApp Web chats, then continues listening for new messages.
|
|
102
102
|
|
|
103
103
|
Draft-only:
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
digital-brain auto-whatsapp --allow "Mom" --model llama3.1
|
|
107
107
|
digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1
|
|
108
|
+
OPENAI_API_KEY="sk-..." digital-brain auto-whatsapp --allow-all --provider openai --model gpt-4.1-mini
|
|
108
109
|
digital-brain auto-whatsapp --allow-all --provider codex
|
|
109
110
|
digital-brain auto-whatsapp --allow-all --provider codex-app
|
|
110
111
|
```
|
|
@@ -114,6 +115,7 @@ Auto-send while the command is running:
|
|
|
114
115
|
```bash
|
|
115
116
|
digital-brain auto-whatsapp --allow "Mom" --model llama3.1 --yes
|
|
116
117
|
digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1 --yes
|
|
118
|
+
OPENAI_API_KEY="sk-..." digital-brain auto-whatsapp --allow-all --provider openai --model gpt-4.1-mini --yes
|
|
117
119
|
digital-brain auto-whatsapp --allow-all --provider codex --yes
|
|
118
120
|
digital-brain auto-whatsapp --allow-all --provider codex-app --yes
|
|
119
121
|
```
|
|
@@ -132,11 +134,14 @@ Provider options:
|
|
|
132
134
|
|
|
133
135
|
```bash
|
|
134
136
|
digital-brain auto-whatsapp --allow "Mom" --provider ollama --model llama3.1 --yes
|
|
137
|
+
OPENAI_API_KEY="sk-..." digital-brain auto-whatsapp --allow "Mom" --provider openai --model gpt-4.1-mini --yes
|
|
135
138
|
digital-brain auto-whatsapp --allow "Mom" --provider codex --yes
|
|
136
139
|
digital-brain auto-whatsapp --allow "Mom" --provider codex-app --yes
|
|
137
140
|
digital-brain auto-whatsapp --allow "Mom" --provider codex --codex-command "codex exec --skip-git-repo-check" --yes
|
|
138
141
|
```
|
|
139
142
|
|
|
143
|
+
`--provider openai` sends the same Digital Brain prompt and relevant vault context to the OpenAI API. Set `OPENAI_API_KEY`, pass `--openai-api-key`, or choose OpenAI during `digital-brain init` and paste the key into the local vault config.
|
|
144
|
+
|
|
140
145
|
`--provider codex` runs a local Codex command. If `--codex-command` contains `{promptFile}`, Digital Brain writes the prompt to a temp file and substitutes the path; otherwise it pipes the prompt to stdin.
|
|
141
146
|
|
|
142
147
|
`--provider codex-app` does not use the Codex CLI. It writes request JSON files to `08 Sources/WhatsApp/Outbound/Codex App Bridge/requests` and waits for response JSON files in `responses`. A Codex desktop automation or live Codex thread must process those request files and write `{"reply":"..."}` to the provided `responsePath`.
|
|
@@ -153,6 +158,7 @@ Guardrails:
|
|
|
153
158
|
|
|
154
159
|
- with `--provider ollama`, requires Ollama running locally
|
|
155
160
|
- with `--provider ollama`, requires the selected model, for example `ollama pull llama3.1`
|
|
161
|
+
- with `--provider openai`, requires `OPENAI_API_KEY`, `--openai-api-key`, or `openaiApiKey` in the vault config
|
|
156
162
|
- with `--provider codex`, requires a working local Codex command
|
|
157
163
|
- with `--provider codex-app`, requires a Codex desktop bridge automation/thread that writes response files
|
|
158
164
|
- requires `--allow "Name"` or `--contact "+15551234567"` unless `--allow-all` is explicitly passed
|
package/package.json
CHANGED
|
@@ -23,8 +23,9 @@ const whitelistPath = path.join(outboundDir, "auto-reply-whitelist.json");
|
|
|
23
23
|
const codexAppBridgeDir = path.join(outboundDir, "Codex App Bridge");
|
|
24
24
|
const config = readConfig(vault);
|
|
25
25
|
const provider = args.provider || config.autoReplyProvider || "ollama";
|
|
26
|
-
const model = args.model || config.autoReplyModel || "llama3.1";
|
|
26
|
+
const model = args.model || config.autoReplyModel || (provider === "openai" ? "gpt-4.1-mini" : "llama3.1");
|
|
27
27
|
const codexCommand = args["codex-command"] || process.env.DIGITAL_BRAIN_CODEX_COMMAND || config.codexCommand || "codex exec --skip-git-repo-check";
|
|
28
|
+
const openaiApiKey = args["openai-api-key"] || process.env.OPENAI_API_KEY || config.openaiApiKey || "";
|
|
28
29
|
const allow = parseList(args.allow || "");
|
|
29
30
|
const deny = parseList(args.deny || "");
|
|
30
31
|
const contactNumbers = parseList([args.contact, args.phone, args["contact-number"]].filter(Boolean).join(","))
|
|
@@ -63,8 +64,10 @@ if (!hasInitialScope && !interactiveTerminal) {
|
|
|
63
64
|
|
|
64
65
|
if (provider === "ollama") {
|
|
65
66
|
await assertOllamaModel(model);
|
|
67
|
+
} else if (provider === "openai") {
|
|
68
|
+
assertOpenAiConfig();
|
|
66
69
|
} else if (!["codex", "codex-app"].includes(provider)) {
|
|
67
|
-
throw new Error(`Unsupported auto-reply provider "${provider}". Use "ollama", "codex", or "codex-app".`);
|
|
70
|
+
throw new Error(`Unsupported auto-reply provider "${provider}". Use "ollama", "openai", "codex", or "codex-app".`);
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
const client = new Client({
|
|
@@ -78,7 +81,7 @@ client.on("qr", (qr) => {
|
|
|
78
81
|
});
|
|
79
82
|
|
|
80
83
|
client.on("ready", async () => {
|
|
81
|
-
console.log(`Digital Brain WhatsApp auto-reply running with provider: ${provider}${
|
|
84
|
+
console.log(`Digital Brain WhatsApp auto-reply running with provider: ${provider}${["ollama", "openai"].includes(provider) ? ` (${model})` : ""}`);
|
|
82
85
|
console.log(sendEnabled ? "Auto-send is enabled." : "Draft mode. Replies will be logged but not sent. Add --yes or set outboundMode=auto-send to send.");
|
|
83
86
|
if (!hasInitialScope) await configureInteractiveScope();
|
|
84
87
|
console.log(runtimeAllowAll ? "Allowlist: all chats, with first-send approval per new chat." : allowlistSummary());
|
|
@@ -347,9 +350,44 @@ function readMemoryContext(chatName) {
|
|
|
347
350
|
async function generateReply(prompt) {
|
|
348
351
|
if (provider === "codex") return generateCodexReply(prompt);
|
|
349
352
|
if (provider === "codex-app") return generateCodexAppReply(prompt);
|
|
353
|
+
if (provider === "openai") return generateOpenAiReply(prompt);
|
|
350
354
|
return generateOllamaReply(prompt);
|
|
351
355
|
}
|
|
352
356
|
|
|
357
|
+
async function generateOpenAiReply(prompt) {
|
|
358
|
+
const timeoutMs = numberArg("provider-timeout-ms", 45000);
|
|
359
|
+
const controller = new AbortController();
|
|
360
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
361
|
+
let response;
|
|
362
|
+
try {
|
|
363
|
+
response = await fetch("https://api.openai.com/v1/responses", {
|
|
364
|
+
method: "POST",
|
|
365
|
+
signal: controller.signal,
|
|
366
|
+
headers: {
|
|
367
|
+
"authorization": `Bearer ${openaiApiKey}`,
|
|
368
|
+
"content-type": "application/json",
|
|
369
|
+
},
|
|
370
|
+
body: JSON.stringify({
|
|
371
|
+
model,
|
|
372
|
+
input: prompt,
|
|
373
|
+
max_output_tokens: 160,
|
|
374
|
+
temperature: 0.35,
|
|
375
|
+
}),
|
|
376
|
+
});
|
|
377
|
+
} catch (error) {
|
|
378
|
+
if (error.name === "AbortError") throw new Error(`OpenAI reply timed out after ${timeoutMs}ms`);
|
|
379
|
+
throw error;
|
|
380
|
+
} finally {
|
|
381
|
+
clearTimeout(timer);
|
|
382
|
+
}
|
|
383
|
+
const text = await response.text();
|
|
384
|
+
if (!response.ok) throw new Error(`OpenAI reply failed: ${response.status} ${summarize(text)}`);
|
|
385
|
+
const body = parseJsonLine(text);
|
|
386
|
+
const reply = extractOpenAiText(body);
|
|
387
|
+
if (!reply) throw new Error("OpenAI returned an empty reply.");
|
|
388
|
+
return cleanReply(reply);
|
|
389
|
+
}
|
|
390
|
+
|
|
353
391
|
async function generateOllamaReply(prompt) {
|
|
354
392
|
const response = await fetch("http://127.0.0.1:11434/api/generate", {
|
|
355
393
|
method: "POST",
|
|
@@ -465,6 +503,24 @@ async function assertOllamaModel(modelName) {
|
|
|
465
503
|
}
|
|
466
504
|
}
|
|
467
505
|
|
|
506
|
+
function assertOpenAiConfig() {
|
|
507
|
+
if (!openaiApiKey) {
|
|
508
|
+
throw new Error("OpenAI provider requires an API key. Set OPENAI_API_KEY, pass --openai-api-key, or re-run init and enter the key.");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function extractOpenAiText(body) {
|
|
513
|
+
if (!body) return "";
|
|
514
|
+
if (typeof body.output_text === "string") return body.output_text;
|
|
515
|
+
const chunks = [];
|
|
516
|
+
for (const item of body.output || []) {
|
|
517
|
+
for (const content of item.content || []) {
|
|
518
|
+
if (typeof content.text === "string") chunks.push(content.text);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return chunks.join(" ");
|
|
522
|
+
}
|
|
523
|
+
|
|
468
524
|
function cleanReply(value) {
|
|
469
525
|
return String(value)
|
|
470
526
|
.replace(/^["'\s]+|["'\s]+$/g, "")
|
|
@@ -805,6 +861,6 @@ function parseArgs(argv) {
|
|
|
805
861
|
}
|
|
806
862
|
|
|
807
863
|
function usage() {
|
|
808
|
-
console.error('Usage: digital-brain auto-whatsapp --allow "Name" --contact "+15551234567" --provider ollama|codex|codex-app --model llama3.1 [--yes] [--allow-all] [--include-groups] [--include-businesses]');
|
|
864
|
+
console.error('Usage: digital-brain auto-whatsapp --allow "Name" --contact "+15551234567" --provider ollama|openai|codex|codex-app --model llama3.1 [--yes] [--allow-all] [--include-groups] [--include-businesses]');
|
|
809
865
|
process.exit(1);
|
|
810
866
|
}
|