@deckasoft/waify 0.3.5 → 0.3.6

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +60 -61
  2. package/package.json +3 -1
package/dist/cli/index.js CHANGED
@@ -212,7 +212,7 @@ var init_secrets = __esm({
212
212
  init_paths();
213
213
  SecretsSchema = z4.object({
214
214
  GEMINI_API_KEY: z4.string().min(1),
215
- OPENWA_API_KEY: z4.string().min(1)
215
+ OPENWA_API_KEY: z4.string().min(1).default("dev-admin-key")
216
216
  });
217
217
  parseEnvFile = (content) => content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).reduce((acc, line) => {
218
218
  const eq = line.indexOf("=");
@@ -963,6 +963,7 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as wr
963
963
  import { homedir as homedir2 } from "os";
964
964
  import { join as join2 } from "path";
965
965
  import { createInterface } from "readline";
966
+ import qrcode from "qrcode-terminal";
966
967
  import { z as z5 } from "zod";
967
968
  var SessionResponseSchema = z5.object({
968
969
  id: z5.string().optional(),
@@ -1009,17 +1010,50 @@ volumes:
1009
1010
  openwa-data:
1010
1011
  `;
1011
1012
  var promptLine = (rl, question) => new Promise((resolve2) => rl.question(question, resolve2));
1013
+ var renderQr = (qrString) => new Promise((resolve2) => qrcode.generate(qrString, { small: true }, () => resolve2()));
1012
1014
  var registerSetup = (program2) => {
1013
1015
  program2.command("setup").description("Guided first-run wizard: installs OpenWA, authenticates WhatsApp, and configures waify").action(async () => {
1016
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1014
1017
  try {
1015
1018
  console.warn("Checking Docker...");
1016
1019
  const dockerCheck = spawnSync("docker", ["info"], { stdio: "pipe" });
1017
1020
  if (dockerCheck.status !== 0) {
1018
1021
  console.error("Docker is not running or not installed. Please install Docker and start it before running setup.");
1019
- process.exit(1);
1022
+ process.exitCode = 1;
1023
+ return;
1020
1024
  }
1021
1025
  console.warn("Creating config directory...");
1022
1026
  mkdirSync4(join2(homedir2(), ".config", "waify"), { recursive: true });
1027
+ const baseUrl = loadConfig().openwaBaseUrl;
1028
+ const rawApiKey = await promptLine(
1029
+ rl,
1030
+ 'OpenWA API key (press Enter to use default "dev-admin-key"):\n> '
1031
+ );
1032
+ const openwaApiKey = rawApiKey.trim() || "dev-admin-key";
1033
+ let geminiKey = "";
1034
+ while (!geminiKey.trim()) {
1035
+ geminiKey = await promptLine(
1036
+ rl,
1037
+ "Enter your Gemini API key (get one free at https://aistudio.google.com/apikey):\n> "
1038
+ );
1039
+ if (!geminiKey.trim()) {
1040
+ console.warn("Gemini API key cannot be empty. Please try again.");
1041
+ }
1042
+ }
1043
+ let recipientNumber = "";
1044
+ const phoneRegex = /^\d{8,15}$/;
1045
+ while (!phoneRegex.test(recipientNumber.trim())) {
1046
+ recipientNumber = await promptLine(
1047
+ rl,
1048
+ "Enter your recipient's WhatsApp number (e.g. 5511999998888 \u2014 digits only, no + or spaces):\n> "
1049
+ );
1050
+ if (!phoneRegex.test(recipientNumber.trim())) {
1051
+ console.warn("Invalid number format. Use digits only, 8\u201315 characters. Please try again.");
1052
+ }
1053
+ }
1054
+ const chatId = `${recipientNumber.trim()}@c.us`;
1055
+ saveSecrets({ GEMINI_API_KEY: geminiKey.trim(), OPENWA_API_KEY: openwaApiKey });
1056
+ saveConfig({ ...loadConfig(), openwaApiKey, recipients: [{ chatId }] });
1023
1057
  console.warn("Writing docker-compose.yml...");
1024
1058
  writeFileSync5(composePath(), composeTemplate(), "utf-8");
1025
1059
  console.warn("Starting OpenWA containers (this may take a minute on first run)...");
@@ -1028,13 +1062,14 @@ var registerSetup = (program2) => {
1028
1062
  });
1029
1063
  if (upResult.status !== 0) {
1030
1064
  console.error("Failed to start OpenWA containers. Check docker compose logs for details.");
1031
- process.exit(1);
1065
+ process.exitCode = 1;
1066
+ return;
1032
1067
  }
1033
1068
  console.warn("Waiting for OpenWA API to start...");
1034
1069
  let apiReady = false;
1035
1070
  for (let attempt = 0; attempt < 30; attempt++) {
1036
1071
  try {
1037
- const res = await fetchWithTimeout("http://localhost:2785/api/health");
1072
+ const res = await fetchWithTimeout(`${baseUrl}/api/health`);
1038
1073
  if (res.status >= 200 && res.status < 300) {
1039
1074
  apiReady = true;
1040
1075
  break;
@@ -1047,24 +1082,11 @@ var registerSetup = (program2) => {
1047
1082
  console.error(
1048
1083
  "OpenWA API did not become ready in time. Check logs with: docker compose -f " + composePath() + " logs openwa-api"
1049
1084
  );
1050
- process.exit(1);
1051
- }
1052
- console.warn("Reading API credentials...");
1053
- const keyResult = spawnSync(
1054
- "docker",
1055
- ["compose", "-f", composePath(), "exec", "-T", "openwa-api", "cat", "/app/data/.api-key"],
1056
- { encoding: "utf-8", stdio: "pipe" }
1057
- );
1058
- const openwaApiKey = keyResult.stdout?.trim();
1059
- if (keyResult.status !== 0 || !openwaApiKey) {
1060
- console.error(
1061
- "Could not read API key from container. Check logs: docker compose -f " + composePath() + " logs openwa-api"
1062
- );
1063
- if (keyResult.stderr) console.error(keyResult.stderr);
1064
- process.exit(1);
1085
+ process.exitCode = 1;
1086
+ return;
1065
1087
  }
1066
1088
  console.warn("Creating WhatsApp session...");
1067
- const sessionRes = await fetchWithTimeout("http://localhost:2785/api/sessions", {
1089
+ const sessionRes = await fetchWithTimeout(`${baseUrl}/api/sessions`, {
1068
1090
  method: "POST",
1069
1091
  headers: {
1070
1092
  "X-API-Key": openwaApiKey,
@@ -1077,9 +1099,8 @@ var registerSetup = (program2) => {
1077
1099
  }
1078
1100
  const sessionData = SessionResponseSchema.parse(await sessionRes.json());
1079
1101
  const sessionId = sessionData.id ?? sessionData.name ?? "waify";
1080
- saveConfig({ ...loadConfig(), openwaApiKey, openwaSessionId: sessionId });
1081
1102
  console.warn("Starting WhatsApp engine...");
1082
- const startRes = await fetchWithTimeout(`http://localhost:2785/api/sessions/${sessionId}/start`, {
1103
+ const startRes = await fetchWithTimeout(`${baseUrl}/api/sessions/${sessionId}/start`, {
1083
1104
  method: "POST",
1084
1105
  headers: { "X-API-Key": openwaApiKey }
1085
1106
  }, 1e4);
@@ -1087,16 +1108,16 @@ var registerSetup = (program2) => {
1087
1108
  throw new Error(`Failed to start session: ${startRes.status} ${startRes.statusText}`);
1088
1109
  }
1089
1110
  console.warn("Waiting for QR code...");
1090
- let qrReady = false;
1111
+ let qrCode;
1091
1112
  for (let attempt = 0; attempt < 30; attempt++) {
1092
1113
  try {
1093
- const qrRes = await fetchWithTimeout(`http://localhost:2785/api/sessions/${sessionId}/qr`, {
1114
+ const qrRes = await fetchWithTimeout(`${baseUrl}/api/sessions/${sessionId}/qr`, {
1094
1115
  headers: { "X-API-Key": openwaApiKey }
1095
1116
  });
1096
1117
  if (qrRes.ok) {
1097
1118
  const qrData = QrResponseSchema.parse(await qrRes.json());
1098
1119
  if (qrData.qrCode) {
1099
- qrReady = true;
1120
+ qrCode = qrData.qrCode;
1100
1121
  break;
1101
1122
  }
1102
1123
  }
@@ -1105,16 +1126,19 @@ var registerSetup = (program2) => {
1105
1126
  await wait(2e3);
1106
1127
  }
1107
1128
  console.warn("\n\u{1F4F1} Scan the QR code with WhatsApp to link your device:");
1108
- console.warn(" Settings \u2192 Linked Devices \u2192 Link a Device");
1109
- console.warn(" View QR at: http://localhost:2785/api/docs (Sessions \u2192 GET /sessions/{id}/qr)");
1110
- if (!qrReady) {
1111
- console.warn(" (QR not yet ready \u2014 Chromium may still be initializing, check the Swagger link above)");
1129
+ console.warn(" Settings \u2192 Linked Devices \u2192 Link a Device\n");
1130
+ if (qrCode) {
1131
+ await renderQr(qrCode);
1132
+ console.warn("\n (QR expires in ~20s \u2014 re-run setup if it expires before you scan)");
1133
+ } else {
1134
+ console.warn(` QR not yet ready. Check: ${baseUrl}/api/sessions/${sessionId}/qr`);
1135
+ console.warn(` (Add header: X-API-Key: ${openwaApiKey})`);
1112
1136
  }
1113
1137
  console.warn(" Waiting up to 2 minutes for you to scan...\n");
1114
1138
  let connected = false;
1115
1139
  for (let attempt = 0; attempt < 60; attempt++) {
1116
1140
  try {
1117
- const statusRes = await fetchWithTimeout(`http://localhost:2785/api/sessions/${sessionId}`, {
1141
+ const statusRes = await fetchWithTimeout(`${baseUrl}/api/sessions/${sessionId}`, {
1118
1142
  headers: { "X-API-Key": openwaApiKey }
1119
1143
  });
1120
1144
  const parsed = StatusResponseSchema.safeParse(await statusRes.json());
@@ -1129,38 +1153,11 @@ var registerSetup = (program2) => {
1129
1153
  }
1130
1154
  if (!connected) {
1131
1155
  console.error("WhatsApp did not connect within 2 minutes. Please re-run `waify setup` to try again.");
1132
- process.exit(1);
1156
+ process.exitCode = 1;
1157
+ return;
1133
1158
  }
1134
1159
  console.warn("\u2713 WhatsApp connected!");
1135
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1136
- try {
1137
- let geminiKey = "";
1138
- while (!geminiKey.trim()) {
1139
- geminiKey = await promptLine(
1140
- rl,
1141
- "Enter your Gemini API key (get one free at https://aistudio.google.com/apikey):\n> "
1142
- );
1143
- if (!geminiKey.trim()) {
1144
- console.warn("Gemini API key cannot be empty. Please try again.");
1145
- }
1146
- }
1147
- saveSecrets({ GEMINI_API_KEY: geminiKey.trim(), OPENWA_API_KEY: openwaApiKey });
1148
- let recipientNumber = "";
1149
- const phoneRegex = /^\d{8,15}$/;
1150
- while (!phoneRegex.test(recipientNumber.trim())) {
1151
- recipientNumber = await promptLine(
1152
- rl,
1153
- "Enter your recipient's WhatsApp number (e.g. 5511999998888 \u2014 digits only, no + or spaces):\n> "
1154
- );
1155
- if (!phoneRegex.test(recipientNumber.trim())) {
1156
- console.warn("Invalid number format. Use digits only, 8\u201315 characters. Please try again.");
1157
- }
1158
- }
1159
- const chatId = `${recipientNumber.trim()}@c.us`;
1160
- saveConfig({ ...loadConfig(), recipients: [{ chatId }] });
1161
- } finally {
1162
- rl.close();
1163
- }
1160
+ saveConfig({ ...loadConfig(), openwaSessionId: sessionId });
1164
1161
  if (!existsSync6(promptPath())) {
1165
1162
  savePrompt(defaultPrompt);
1166
1163
  }
@@ -1170,7 +1167,9 @@ var registerSetup = (program2) => {
1170
1167
  console.warn("\n\u2713 All done! Run `waify send` to send your first message.");
1171
1168
  } catch (err) {
1172
1169
  console.error(err instanceof Error ? err.message : String(err));
1173
- process.exit(1);
1170
+ process.exitCode = 1;
1171
+ } finally {
1172
+ rl.close();
1174
1173
  }
1175
1174
  });
1176
1175
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deckasoft/waify",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "AI-powered daily message sender for WhatsApp, powered by OpenWA",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -41,6 +41,7 @@
41
41
  "ink-select-input": "^6.2.0",
42
42
  "ink-spinner": "^5.0.0",
43
43
  "ink-text-input": "^6.0.0",
44
+ "qrcode-terminal": "^0.12.0",
44
45
  "react": "^19.2.6",
45
46
  "zod": "^3.24.0"
46
47
  },
@@ -48,6 +49,7 @@
48
49
  "@semantic-release/changelog": "^6.0.3",
49
50
  "@semantic-release/git": "^10.0.1",
50
51
  "@types/node": "^22.0.0",
52
+ "@types/qrcode-terminal": "^0.12.2",
51
53
  "@types/react": "^19.2.15",
52
54
  "semantic-release": "^25.0.3",
53
55
  "tsup": "^8.5.1",