instar 0.12.18 → 0.12.19
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/.claude/skills/setup-wizard/skill.md +145 -4
- package/dashboard/index.html +155 -0
- package/dist/cli.js +51 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +116 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +20 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/whatsapp.d.ts +31 -0
- package/dist/commands/whatsapp.d.ts.map +1 -0
- package/dist/commands/whatsapp.js +386 -0
- package/dist/commands/whatsapp.js.map +1 -0
- package/dist/core/ClaudeCliIntelligenceProvider.d.ts.map +1 -1
- package/dist/core/ClaudeCliIntelligenceProvider.js +0 -4
- package/dist/core/ClaudeCliIntelligenceProvider.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +2 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +172 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/ResumeValidator.d.ts +58 -0
- package/dist/core/ResumeValidator.d.ts.map +1 -0
- package/dist/core/ResumeValidator.js +150 -0
- package/dist/core/ResumeValidator.js.map +1 -0
- package/dist/messaging/AdapterRegistry.d.ts +27 -0
- package/dist/messaging/AdapterRegistry.d.ts.map +1 -0
- package/dist/messaging/AdapterRegistry.js +40 -0
- package/dist/messaging/AdapterRegistry.js.map +1 -0
- package/dist/messaging/TelegramAdapter.d.ts +20 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +456 -20
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/WhatsAppAdapter.d.ts +163 -0
- package/dist/messaging/WhatsAppAdapter.d.ts.map +1 -0
- package/dist/messaging/WhatsAppAdapter.js +529 -0
- package/dist/messaging/WhatsAppAdapter.js.map +1 -0
- package/dist/messaging/backends/BaileysBackend.d.ts +54 -0
- package/dist/messaging/backends/BaileysBackend.d.ts.map +1 -0
- package/dist/messaging/backends/BaileysBackend.js +198 -0
- package/dist/messaging/backends/BaileysBackend.js.map +1 -0
- package/dist/messaging/backends/BusinessApiBackend.d.ts +173 -0
- package/dist/messaging/backends/BusinessApiBackend.d.ts.map +1 -0
- package/dist/messaging/backends/BusinessApiBackend.js +269 -0
- package/dist/messaging/backends/BusinessApiBackend.js.map +1 -0
- package/dist/messaging/backends/WhatsAppWebhookRoutes.d.ts +26 -0
- package/dist/messaging/backends/WhatsAppWebhookRoutes.d.ts.map +1 -0
- package/dist/messaging/backends/WhatsAppWebhookRoutes.js +50 -0
- package/dist/messaging/backends/WhatsAppWebhookRoutes.js.map +1 -0
- package/dist/messaging/shared/AuthGate.d.ts +97 -0
- package/dist/messaging/shared/AuthGate.d.ts.map +1 -0
- package/dist/messaging/shared/AuthGate.js +154 -0
- package/dist/messaging/shared/AuthGate.js.map +1 -0
- package/dist/messaging/shared/CommandRouter.d.ts +84 -0
- package/dist/messaging/shared/CommandRouter.d.ts.map +1 -0
- package/dist/messaging/shared/CommandRouter.js +145 -0
- package/dist/messaging/shared/CommandRouter.js.map +1 -0
- package/dist/messaging/shared/CrossPlatformAlerts.d.ts +67 -0
- package/dist/messaging/shared/CrossPlatformAlerts.d.ts.map +1 -0
- package/dist/messaging/shared/CrossPlatformAlerts.js +134 -0
- package/dist/messaging/shared/CrossPlatformAlerts.js.map +1 -0
- package/dist/messaging/shared/EncryptedAuthStore.d.ts +51 -0
- package/dist/messaging/shared/EncryptedAuthStore.d.ts.map +1 -0
- package/dist/messaging/shared/EncryptedAuthStore.js +190 -0
- package/dist/messaging/shared/EncryptedAuthStore.js.map +1 -0
- package/dist/messaging/shared/FeatureFlags.d.ts +21 -0
- package/dist/messaging/shared/FeatureFlags.d.ts.map +1 -0
- package/dist/messaging/shared/FeatureFlags.js +21 -0
- package/dist/messaging/shared/FeatureFlags.js.map +1 -0
- package/dist/messaging/shared/MessageBridge.d.ts +70 -0
- package/dist/messaging/shared/MessageBridge.d.ts.map +1 -0
- package/dist/messaging/shared/MessageBridge.js +178 -0
- package/dist/messaging/shared/MessageBridge.js.map +1 -0
- package/dist/messaging/shared/MessageLogger.d.ts +94 -0
- package/dist/messaging/shared/MessageLogger.d.ts.map +1 -0
- package/dist/messaging/shared/MessageLogger.js +173 -0
- package/dist/messaging/shared/MessageLogger.js.map +1 -0
- package/dist/messaging/shared/MessagingEventBus.d.ts +149 -0
- package/dist/messaging/shared/MessagingEventBus.d.ts.map +1 -0
- package/dist/messaging/shared/MessagingEventBus.js +111 -0
- package/dist/messaging/shared/MessagingEventBus.js.map +1 -0
- package/dist/messaging/shared/PhoneUtils.d.ts +39 -0
- package/dist/messaging/shared/PhoneUtils.d.ts.map +1 -0
- package/dist/messaging/shared/PhoneUtils.js +98 -0
- package/dist/messaging/shared/PhoneUtils.js.map +1 -0
- package/dist/messaging/shared/PrivacyConsent.d.ts +61 -0
- package/dist/messaging/shared/PrivacyConsent.d.ts.map +1 -0
- package/dist/messaging/shared/PrivacyConsent.js +132 -0
- package/dist/messaging/shared/PrivacyConsent.js.map +1 -0
- package/dist/messaging/shared/SessionChannelRegistry.d.ts +48 -0
- package/dist/messaging/shared/SessionChannelRegistry.d.ts.map +1 -0
- package/dist/messaging/shared/SessionChannelRegistry.js +144 -0
- package/dist/messaging/shared/SessionChannelRegistry.js.map +1 -0
- package/dist/messaging/shared/SmartChunker.d.ts +16 -0
- package/dist/messaging/shared/SmartChunker.d.ts.map +1 -0
- package/dist/messaging/shared/SmartChunker.js +100 -0
- package/dist/messaging/shared/SmartChunker.js.map +1 -0
- package/dist/messaging/shared/StallDetector.d.ts +85 -0
- package/dist/messaging/shared/StallDetector.d.ts.map +1 -0
- package/dist/messaging/shared/StallDetector.js +217 -0
- package/dist/messaging/shared/StallDetector.js.map +1 -0
- package/dist/messaging/shared/index.d.ts +22 -0
- package/dist/messaging/shared/index.d.ts.map +1 -0
- package/dist/messaging/shared/index.js +13 -0
- package/dist/messaging/shared/index.js.map +1 -0
- package/dist/scaffold/templates.d.ts +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +68 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +12 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +73 -16
- package/dist/server/routes.js.map +1 -1
- package/package.json +9 -1
- package/src/data/builtin-manifest.json +101 -93
- package/src/templates/hooks/compaction-recovery.sh +52 -25
- package/src/templates/hooks/settings-template.json +12 -0
- package/src/templates/hooks/telegram-topic-context.sh +117 -0
- package/upgrades/0.12.19.md +31 -0
|
@@ -179,10 +179,11 @@ Present:
|
|
|
179
179
|
> What would you like to change?
|
|
180
180
|
|
|
181
181
|
1. **"Update Telegram setup"** → Jump to Phase 3
|
|
182
|
-
2. **"
|
|
183
|
-
3. **"
|
|
184
|
-
4. **"
|
|
185
|
-
5. **"
|
|
182
|
+
2. **"Set up WhatsApp"** → Jump to Phase 4g
|
|
183
|
+
3. **"Change agent personality"** → Jump to Phase 2c
|
|
184
|
+
4. **"Add a user"** → New User Flow
|
|
185
|
+
5. **"View current config"** → Display scenario and settings
|
|
186
|
+
6. **"Something else"** → Free-form request
|
|
186
187
|
|
|
187
188
|
---
|
|
188
189
|
|
|
@@ -1110,6 +1111,20 @@ mkdir -p .instar/state/sessions .instar/state/jobs .instar/logs
|
|
|
1110
1111
|
"pollIntervalMs": 2000,
|
|
1111
1112
|
"stallTimeoutMinutes": 5
|
|
1112
1113
|
}
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
"type": "whatsapp",
|
|
1117
|
+
"enabled": true,
|
|
1118
|
+
"config": {
|
|
1119
|
+
"backend": "baileys | business-api",
|
|
1120
|
+
"authorizedNumbers": ["+1XXXXXXXXXX"],
|
|
1121
|
+
"requireConsent": false,
|
|
1122
|
+
"businessApi": {
|
|
1123
|
+
"phoneNumberId": "<from Meta Developer Console>",
|
|
1124
|
+
"accessToken": "<from Meta Developer Console>",
|
|
1125
|
+
"webhookVerifyToken": "<random string you choose>"
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1113
1128
|
}
|
|
1114
1129
|
],
|
|
1115
1130
|
"monitoring": {
|
|
@@ -1159,6 +1174,132 @@ Then make it executable:
|
|
|
1159
1174
|
chmod +x .claude/scripts/telegram-reply.sh
|
|
1160
1175
|
```
|
|
1161
1176
|
|
|
1177
|
+
### 4g. WhatsApp Setup (Optional)
|
|
1178
|
+
|
|
1179
|
+
**WhatsApp is optional.** Unlike Telegram, WhatsApp is an additional communication channel — not the primary interface. Only offer it after Telegram is configured.
|
|
1180
|
+
|
|
1181
|
+
Present:
|
|
1182
|
+
|
|
1183
|
+
> **Want to add WhatsApp as a second channel?**
|
|
1184
|
+
>
|
|
1185
|
+
> WhatsApp lets you talk to your agent from your phone's default messaging app. It works alongside Telegram — you'll get cross-platform alerts if either channel disconnects.
|
|
1186
|
+
>
|
|
1187
|
+
> 1. Yes, set up WhatsApp
|
|
1188
|
+
> 2. Skip for now
|
|
1189
|
+
>
|
|
1190
|
+
> Type a number.
|
|
1191
|
+
|
|
1192
|
+
If they choose to skip, move to Phase 4.5. If they want WhatsApp:
|
|
1193
|
+
|
|
1194
|
+
#### Step 4g-1: Choose Backend
|
|
1195
|
+
|
|
1196
|
+
Present:
|
|
1197
|
+
|
|
1198
|
+
> WhatsApp has two connection modes:
|
|
1199
|
+
>
|
|
1200
|
+
> 1. **Personal (Baileys)** — connects through WhatsApp Web. Works on any machine, no server setup needed. Best for personal use and local development.
|
|
1201
|
+
> 2. **Business API** — uses Meta's official Cloud API. Requires a Meta Developer account and a publicly accessible server for webhooks. Best for production deployments.
|
|
1202
|
+
>
|
|
1203
|
+
> Which one fits your setup?
|
|
1204
|
+
|
|
1205
|
+
**If they choose Baileys:**
|
|
1206
|
+
|
|
1207
|
+
> Great — Baileys connects through WhatsApp Web, just like scanning a QR code.
|
|
1208
|
+
>
|
|
1209
|
+
> I need your phone number to authorize messages. Only messages from this number will be processed — everything else is ignored.
|
|
1210
|
+
|
|
1211
|
+
Collect their phone number (plain text, NOT AskUserQuestion). Format it with country code (+1XXXXXXXXXX).
|
|
1212
|
+
|
|
1213
|
+
Write the config:
|
|
1214
|
+
```bash
|
|
1215
|
+
node -e "
|
|
1216
|
+
const fs = require('fs');
|
|
1217
|
+
const p = '<project_dir>/.instar/config.json';
|
|
1218
|
+
const c = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
1219
|
+
c.messaging = c.messaging || [];
|
|
1220
|
+
c.messaging.push({
|
|
1221
|
+
type: 'whatsapp',
|
|
1222
|
+
enabled: true,
|
|
1223
|
+
config: {
|
|
1224
|
+
backend: 'baileys',
|
|
1225
|
+
authorizedNumbers: ['<PHONE_NUMBER>'],
|
|
1226
|
+
requireConsent: false
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
fs.writeFileSync(p, JSON.stringify(c, null, 2));
|
|
1230
|
+
"
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
Then tell the user:
|
|
1234
|
+
|
|
1235
|
+
> WhatsApp is configured. When you start your agent, it will show a pairing code in the logs. Open WhatsApp on your phone → Settings → Linked Devices → Link a Device, and enter the code.
|
|
1236
|
+
>
|
|
1237
|
+
> You can also pair later with: `instar whatsapp connect`
|
|
1238
|
+
|
|
1239
|
+
**If they choose Business API:**
|
|
1240
|
+
|
|
1241
|
+
> The Business API needs three things from your Meta Developer Console:
|
|
1242
|
+
> 1. **Phone Number ID** — the ID of your WhatsApp Business phone number
|
|
1243
|
+
> 2. **Access Token** — a permanent token from System Users
|
|
1244
|
+
> 3. **Webhook Verify Token** — a random string you choose (I can generate one)
|
|
1245
|
+
>
|
|
1246
|
+
> Do you have a Meta Developer account set up? If not, I can walk you through it, or you can switch to Baileys for now.
|
|
1247
|
+
|
|
1248
|
+
If they want to proceed, collect:
|
|
1249
|
+
- Phone Number ID (plain text)
|
|
1250
|
+
- Access Token (plain text — will be stored in config)
|
|
1251
|
+
- Webhook Verify Token (offer to generate: `node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"`)
|
|
1252
|
+
- Their WhatsApp phone number for authorization
|
|
1253
|
+
|
|
1254
|
+
Check if the server has a public URL:
|
|
1255
|
+
|
|
1256
|
+
> Does your server have a public URL? The Business API sends webhooks to your server — it needs to be reachable from the internet.
|
|
1257
|
+
>
|
|
1258
|
+
> 1. Yes, my server is public (enter URL)
|
|
1259
|
+
> 2. No, I'm on a local machine
|
|
1260
|
+
>
|
|
1261
|
+
> If local: "For local development, I'd recommend switching to Baileys — it works without any server setup. The Business API is designed for cloud deployments. Want to switch to Baileys instead?"
|
|
1262
|
+
|
|
1263
|
+
Write the config:
|
|
1264
|
+
```bash
|
|
1265
|
+
node -e "
|
|
1266
|
+
const fs = require('fs');
|
|
1267
|
+
const p = '<project_dir>/.instar/config.json';
|
|
1268
|
+
const c = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
1269
|
+
c.messaging = c.messaging || [];
|
|
1270
|
+
c.messaging.push({
|
|
1271
|
+
type: 'whatsapp',
|
|
1272
|
+
enabled: true,
|
|
1273
|
+
config: {
|
|
1274
|
+
backend: 'business-api',
|
|
1275
|
+
authorizedNumbers: ['<PHONE_NUMBER>'],
|
|
1276
|
+
requireConsent: false,
|
|
1277
|
+
businessApi: {
|
|
1278
|
+
phoneNumberId: '<PHONE_NUMBER_ID>',
|
|
1279
|
+
accessToken: '<ACCESS_TOKEN>',
|
|
1280
|
+
webhookVerifyToken: '<VERIFY_TOKEN>'
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
});
|
|
1284
|
+
fs.writeFileSync(p, JSON.stringify(c, null, 2));
|
|
1285
|
+
"
|
|
1286
|
+
```
|
|
1287
|
+
|
|
1288
|
+
Tell the user the webhook URL to configure in Meta Developer Console:
|
|
1289
|
+
|
|
1290
|
+
> Configure your webhook in the Meta Developer Console:
|
|
1291
|
+
> - **URL**: `https://<your-server>/webhooks/whatsapp`
|
|
1292
|
+
> - **Verify Token**: `<VERIFY_TOKEN>`
|
|
1293
|
+
> - **Subscribe to**: `messages`
|
|
1294
|
+
|
|
1295
|
+
#### Step 4g-2: Cross-Platform Alerts
|
|
1296
|
+
|
|
1297
|
+
If both Telegram and WhatsApp are configured, mention:
|
|
1298
|
+
|
|
1299
|
+
> Since you have both Telegram and WhatsApp, your agent will automatically alert you on one platform if the other disconnects. If WhatsApp goes down, you'll get a message on Telegram (and vice versa).
|
|
1300
|
+
|
|
1301
|
+
No additional config needed — CrossPlatformAlerts wires automatically in `server.ts` when both adapters are present.
|
|
1302
|
+
|
|
1162
1303
|
## Phase 4.5: Cloud Backup (Recommended)
|
|
1163
1304
|
|
|
1164
1305
|
**SCENARIO GATE:** If `isMultiMachine=true`, cloud backup was already set up in the multi-machine section of Phase 2. **Skip this phase entirely for Scenarios 2, 4, 6, 7.**
|
package/dashboard/index.html
CHANGED
|
@@ -203,6 +203,52 @@
|
|
|
203
203
|
.empty-state .icon { font-size: 32px; margin-bottom: 12px; opacity: 0.5; }
|
|
204
204
|
.empty-state p { font-size: 13px; line-height: 1.5; }
|
|
205
205
|
|
|
206
|
+
/* WhatsApp QR panel */
|
|
207
|
+
.wa-qr-panel {
|
|
208
|
+
position: fixed;
|
|
209
|
+
top: 50%;
|
|
210
|
+
left: 50%;
|
|
211
|
+
transform: translate(-50%, -50%);
|
|
212
|
+
background: var(--bg-panel);
|
|
213
|
+
border: 1px solid var(--border);
|
|
214
|
+
border-radius: 12px;
|
|
215
|
+
padding: 24px;
|
|
216
|
+
z-index: 1000;
|
|
217
|
+
text-align: center;
|
|
218
|
+
min-width: 320px;
|
|
219
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
|
|
220
|
+
}
|
|
221
|
+
.wa-qr-panel h3 { color: var(--text-bright); margin-bottom: 8px; }
|
|
222
|
+
.wa-qr-panel p { color: var(--text-dim); font-size: 13px; margin-bottom: 16px; }
|
|
223
|
+
.wa-qr-panel canvas { border-radius: 8px; background: #fff; padding: 12px; }
|
|
224
|
+
.wa-qr-panel .wa-connected {
|
|
225
|
+
color: var(--accent);
|
|
226
|
+
font-size: 14px;
|
|
227
|
+
padding: 32px 0;
|
|
228
|
+
}
|
|
229
|
+
.wa-qr-panel .wa-close {
|
|
230
|
+
position: absolute; top: 8px; right: 12px;
|
|
231
|
+
background: none; border: none; color: var(--text-dim);
|
|
232
|
+
font-size: 18px; cursor: pointer;
|
|
233
|
+
}
|
|
234
|
+
.wa-qr-panel .wa-close:hover { color: var(--text-bright); }
|
|
235
|
+
.wa-qr-backdrop {
|
|
236
|
+
position: fixed; inset: 0;
|
|
237
|
+
background: rgba(0,0,0,0.5);
|
|
238
|
+
z-index: 999;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.wa-status-btn {
|
|
242
|
+
background: none; border: 1px solid var(--border);
|
|
243
|
+
color: var(--text-dim); padding: 4px 10px; border-radius: 6px;
|
|
244
|
+
cursor: pointer; font-size: 12px; margin-right: 8px;
|
|
245
|
+
display: none;
|
|
246
|
+
}
|
|
247
|
+
.wa-status-btn:hover { border-color: var(--accent-dim); color: var(--text); }
|
|
248
|
+
.wa-status-btn.needs-qr { border-color: var(--orange); color: var(--orange); animation: pulse 2s infinite; }
|
|
249
|
+
.wa-status-btn.connected { border-color: var(--accent-dim); color: var(--accent); }
|
|
250
|
+
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
251
|
+
|
|
206
252
|
/* Main panel */
|
|
207
253
|
.main {
|
|
208
254
|
display: flex;
|
|
@@ -578,6 +624,7 @@
|
|
|
578
624
|
<span class="logo">🦐</span>
|
|
579
625
|
<h1>Instar Dashboard</h1>
|
|
580
626
|
</div>
|
|
627
|
+
<button class="wa-status-btn" id="waStatusBtn" onclick="toggleQrPanel()">WhatsApp</button>
|
|
581
628
|
<div class="status-badge" id="connStatus">
|
|
582
629
|
<span class="dot"></span>
|
|
583
630
|
<span id="connText">Connected</span>
|
|
@@ -633,6 +680,16 @@
|
|
|
633
680
|
</main>
|
|
634
681
|
</div>
|
|
635
682
|
|
|
683
|
+
<!-- WhatsApp QR panel (hidden by default) -->
|
|
684
|
+
<div class="wa-qr-backdrop" id="waQrBackdrop" style="display:none" onclick="closeQrPanel()"></div>
|
|
685
|
+
<div class="wa-qr-panel" id="waQrPanel" style="display:none">
|
|
686
|
+
<button class="wa-close" onclick="closeQrPanel()">×</button>
|
|
687
|
+
<h3>WhatsApp Connection</h3>
|
|
688
|
+
<p id="waQrMsg">Scan this QR code with WhatsApp to connect</p>
|
|
689
|
+
<div id="waQrContainer"></div>
|
|
690
|
+
</div>
|
|
691
|
+
|
|
692
|
+
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.4/build/qrcode.min.js"></script>
|
|
636
693
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js"></script>
|
|
637
694
|
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.min.js"></script>
|
|
638
695
|
<script>
|
|
@@ -681,6 +738,7 @@
|
|
|
681
738
|
document.getElementById('authOverlay').style.display = 'none';
|
|
682
739
|
document.getElementById('app').style.display = 'grid';
|
|
683
740
|
connectWebSocket();
|
|
741
|
+
startWaPolling();
|
|
684
742
|
} else {
|
|
685
743
|
showAuthError('Incorrect PIN');
|
|
686
744
|
}
|
|
@@ -704,6 +762,7 @@
|
|
|
704
762
|
document.getElementById('authOverlay').style.display = 'none';
|
|
705
763
|
document.getElementById('app').style.display = 'grid';
|
|
706
764
|
connectWebSocket();
|
|
765
|
+
startWaPolling();
|
|
707
766
|
}
|
|
708
767
|
})
|
|
709
768
|
.catch(() => {});
|
|
@@ -1021,6 +1080,102 @@
|
|
|
1021
1080
|
return div.innerHTML;
|
|
1022
1081
|
}
|
|
1023
1082
|
|
|
1083
|
+
// ── WhatsApp QR ────────────────────────────────────────────
|
|
1084
|
+
let waQrInterval = null;
|
|
1085
|
+
let waQrVisible = false;
|
|
1086
|
+
let waConfigured = false;
|
|
1087
|
+
|
|
1088
|
+
function startWaPolling() {
|
|
1089
|
+
if (waQrInterval) return;
|
|
1090
|
+
pollWaQr(); // Immediate first poll
|
|
1091
|
+
waQrInterval = setInterval(pollWaQr, 3000);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
function stopWaPolling() {
|
|
1095
|
+
if (waQrInterval) {
|
|
1096
|
+
clearInterval(waQrInterval);
|
|
1097
|
+
waQrInterval = null;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
function pollWaQr() {
|
|
1102
|
+
if (!token) return;
|
|
1103
|
+
fetch('/whatsapp/qr', { headers: { 'Authorization': `Bearer ${token}` } })
|
|
1104
|
+
.then(r => {
|
|
1105
|
+
if (r.status === 503) { waConfigured = false; updateWaButton(); return null; }
|
|
1106
|
+
if (!r.ok) return null;
|
|
1107
|
+
waConfigured = true;
|
|
1108
|
+
return r.json();
|
|
1109
|
+
})
|
|
1110
|
+
.then(data => {
|
|
1111
|
+
if (!data) return;
|
|
1112
|
+
updateWaButton(data);
|
|
1113
|
+
if (waQrVisible) renderQrPanel(data);
|
|
1114
|
+
})
|
|
1115
|
+
.catch(() => {});
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
function updateWaButton(data) {
|
|
1119
|
+
const btn = document.getElementById('waStatusBtn');
|
|
1120
|
+
if (!waConfigured) { btn.style.display = 'none'; return; }
|
|
1121
|
+
btn.style.display = 'inline-block';
|
|
1122
|
+
if (data && data.qr) {
|
|
1123
|
+
btn.className = 'wa-status-btn needs-qr';
|
|
1124
|
+
btn.textContent = 'WhatsApp: Scan QR';
|
|
1125
|
+
} else if (data && data.state === 'connected') {
|
|
1126
|
+
btn.className = 'wa-status-btn connected';
|
|
1127
|
+
btn.textContent = 'WhatsApp: ' + (data.phoneNumber || 'Connected');
|
|
1128
|
+
} else if (data) {
|
|
1129
|
+
btn.className = 'wa-status-btn';
|
|
1130
|
+
btn.textContent = 'WhatsApp: ' + (data.state || 'Unknown');
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
function toggleQrPanel() {
|
|
1135
|
+
waQrVisible ? closeQrPanel() : openQrPanel();
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
function openQrPanel() {
|
|
1139
|
+
waQrVisible = true;
|
|
1140
|
+
document.getElementById('waQrBackdrop').style.display = 'block';
|
|
1141
|
+
document.getElementById('waQrPanel').style.display = 'block';
|
|
1142
|
+
pollWaQr(); // Fresh data
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
function closeQrPanel() {
|
|
1146
|
+
waQrVisible = false;
|
|
1147
|
+
document.getElementById('waQrBackdrop').style.display = 'none';
|
|
1148
|
+
document.getElementById('waQrPanel').style.display = 'none';
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
function renderQrPanel(data) {
|
|
1152
|
+
const container = document.getElementById('waQrContainer');
|
|
1153
|
+
const msg = document.getElementById('waQrMsg');
|
|
1154
|
+
|
|
1155
|
+
if (data.state === 'connected') {
|
|
1156
|
+
msg.textContent = 'Connected to ' + (data.phoneNumber || 'WhatsApp');
|
|
1157
|
+
container.innerHTML = '<div class="wa-connected">Connected</div>';
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (data.qr) {
|
|
1162
|
+
msg.textContent = 'Scan this QR code with WhatsApp to connect';
|
|
1163
|
+
container.innerHTML = '';
|
|
1164
|
+
if (typeof QRCode !== 'undefined') {
|
|
1165
|
+
const canvas = document.createElement('canvas');
|
|
1166
|
+
container.appendChild(canvas);
|
|
1167
|
+
QRCode.toCanvas(canvas, data.qr, { width: 256, margin: 1 }, function(err) {
|
|
1168
|
+
if (err) container.innerHTML = '<p style="color:var(--red)">Failed to render QR</p>';
|
|
1169
|
+
});
|
|
1170
|
+
} else {
|
|
1171
|
+
container.innerHTML = '<p style="color:var(--text-dim)">QR library not loaded</p>';
|
|
1172
|
+
}
|
|
1173
|
+
} else {
|
|
1174
|
+
msg.textContent = 'WhatsApp status: ' + (data.state || 'disconnected');
|
|
1175
|
+
container.innerHTML = '<p style="color:var(--text-dim)">Waiting for QR code...</p>';
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1024
1179
|
// ── Keyboard shortcuts ───────────────────────────────────
|
|
1025
1180
|
document.addEventListener('keydown', (e) => {
|
|
1026
1181
|
// Ctrl+C when terminal focused but not input
|
package/dist/cli.js
CHANGED
|
@@ -277,6 +277,11 @@ program
|
|
|
277
277
|
.option('--user <user>', 'User name (non-interactive)')
|
|
278
278
|
.option('--telegram-token <token>', 'Telegram bot token (non-interactive)')
|
|
279
279
|
.option('--telegram-group <group>', 'Telegram group/chat ID (non-interactive)')
|
|
280
|
+
.option('--whatsapp-backend <backend>', 'WhatsApp backend: baileys or business-api (non-interactive)')
|
|
281
|
+
.option('--whatsapp-phone <phone>', 'WhatsApp authorized phone number (non-interactive)')
|
|
282
|
+
.option('--whatsapp-phone-number-id <id>', 'Business API phone number ID (non-interactive)')
|
|
283
|
+
.option('--whatsapp-access-token <token>', 'Business API access token (non-interactive)')
|
|
284
|
+
.option('--whatsapp-verify-token <token>', 'Business API webhook verify token (non-interactive)')
|
|
280
285
|
.option('--scenario <number>', 'Scenario number 1-8 (non-interactive)')
|
|
281
286
|
.action(async (opts) => {
|
|
282
287
|
const [major, minor] = process.versions.node.split('.').map(Number);
|
|
@@ -318,6 +323,22 @@ addCmd
|
|
|
318
323
|
.option('--credentials-file <path>', 'Path to Google OAuth credentials JSON file')
|
|
319
324
|
.option('--token-file <path>', 'Path to store Gmail auth token')
|
|
320
325
|
.action((opts) => addEmail(opts));
|
|
326
|
+
addCmd
|
|
327
|
+
.command('whatsapp')
|
|
328
|
+
.description('Add WhatsApp messaging adapter')
|
|
329
|
+
.option('--backend <backend>', 'Backend: baileys (free, QR auth) or business-api (paid, Meta API)', 'baileys')
|
|
330
|
+
.option('--auth-method <method>', 'Auth method: qr (scan code) or pairing-code (8-digit code)', 'qr')
|
|
331
|
+
.option('--phone <number>', 'Phone number for pairing code auth (E.164 format: +1234567890)')
|
|
332
|
+
.option('--authorized <numbers>', 'Comma-separated authorized phone numbers (E.164). Empty = allow all.')
|
|
333
|
+
.option('--encrypt', 'Encrypt auth credentials at rest (recommended)')
|
|
334
|
+
.option('--phone-number-id <id>', 'Meta Phone Number ID (Business API)')
|
|
335
|
+
.option('--access-token <token>', 'Meta access token (Business API)')
|
|
336
|
+
.option('--webhook-verify-token <token>', 'Webhook verification token (Business API)')
|
|
337
|
+
.option('--webhook-port <port>', 'Webhook port if different from server port (Business API)', parseInt)
|
|
338
|
+
.action(async (opts) => {
|
|
339
|
+
const { addWhatsApp } = await import('./commands/whatsapp.js');
|
|
340
|
+
return addWhatsApp(opts);
|
|
341
|
+
});
|
|
321
342
|
addCmd
|
|
322
343
|
.command('sentry')
|
|
323
344
|
.description('Add Sentry error monitoring')
|
|
@@ -1140,6 +1161,36 @@ program
|
|
|
1140
1161
|
.description('Diagnose multi-machine health and connectivity')
|
|
1141
1162
|
.option('-d, --dir <path>', 'Project directory')
|
|
1142
1163
|
.action(doctor);
|
|
1164
|
+
// ── Channels ─────────────────────────────────────────────────────
|
|
1165
|
+
const channelsCmd = program
|
|
1166
|
+
.command('channels')
|
|
1167
|
+
.description('Manage messaging channel adapters');
|
|
1168
|
+
channelsCmd
|
|
1169
|
+
.command('login <adapter>')
|
|
1170
|
+
.description('Authenticate a messaging adapter (e.g., whatsapp)')
|
|
1171
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
1172
|
+
.option('--method <method>', 'Auth method: qr or pairing-code', 'qr')
|
|
1173
|
+
.option('--phone <number>', 'Phone number for pairing code auth')
|
|
1174
|
+
.action(async (adapter, opts) => {
|
|
1175
|
+
const { channelLogin } = await import('./commands/whatsapp.js');
|
|
1176
|
+
return channelLogin(adapter, opts);
|
|
1177
|
+
});
|
|
1178
|
+
channelsCmd
|
|
1179
|
+
.command('doctor [adapter]')
|
|
1180
|
+
.description('Diagnose messaging adapter health')
|
|
1181
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
1182
|
+
.action(async (adapter, opts) => {
|
|
1183
|
+
const { channelDoctor } = await import('./commands/whatsapp.js');
|
|
1184
|
+
return channelDoctor(adapter, opts);
|
|
1185
|
+
});
|
|
1186
|
+
channelsCmd
|
|
1187
|
+
.command('status')
|
|
1188
|
+
.description('Show status of all configured messaging adapters')
|
|
1189
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
1190
|
+
.action(async (opts) => {
|
|
1191
|
+
const { channelStatus } = await import('./commands/whatsapp.js');
|
|
1192
|
+
return channelStatus(opts);
|
|
1193
|
+
});
|
|
1143
1194
|
// ── System Review ────────────────────────────────────────────────
|
|
1144
1195
|
program
|
|
1145
1196
|
.command('review')
|