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 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 a Codex command plus vault memory while the command is running.
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
@@ -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
  }
@@ -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 either Ollama or a Codex command for reply generation. On startup it scans unread WhatsApp Web chats, then continues listening for new messages.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digital-brain",
3
- "version": "1.1.15",
3
+ "version": "1.1.18",
4
4
  "description": "Your private digital imprint for AI assistants.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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}${provider === "ollama" ? ` (${model})` : ""}`);
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
  }