@flowapt/flowiq-cli 0.3.4 → 0.3.5

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
@@ -160,7 +160,7 @@ flowiq ct list
160
160
  - **Warnings (non-blocking):** unknown keys (likely typos the runtime would silently ignore) and unknown `{{placeholders}}` (they will NOT be substituted at runtime — known: `organization_id`, `contact_id`, `agent_id`, `contact_whatsapp_id`, `contact_full_name`, `supabase_anon_key`, `openai_api_key`, …).
161
161
  - `--agent` + filenames behave like `prompts`/`knowledge`; the file carries `agent_id`, so `push` targets the agent it was pulled from.
162
162
 
163
- ### Broadcast — `flowiq broadcast map|preview|send|resume|list` (alias `bc`)
163
+ ### Broadcast — `flowiq broadcast map|preview|send|resume|status|list` (alias `bc`)
164
164
 
165
165
  Send an **APPROVED** WhatsApp template to every row of a CSV, filling the
166
166
  template's variables **per row** from the CSV's own columns. The
@@ -194,6 +194,11 @@ flowiq bc resume <org_id> --campaign july-referrals --commit [--retry-failed]
194
194
  `/api/send-template` as `headerMedia`, the same field the dashboard uses. A
195
195
  media-header template with no resolvable image is refused (pass
196
196
  `--header-media`).
197
+ - **`status <org> <broadcastId>` (v0.3.5)**: live delivery counts for a broadcast
198
+ by its id — `read` / `delivered` / `sent` / `failed` (+ % reached) from
199
+ `helpdesk_messages`. Built for the `--python` fire-and-forget engine (which
200
+ returns a `broadcastId` but has no CLI status log), but works for any broadcast
201
+ id (dashboard sends included). `flowiq bc status <org> <broadcastId>`.
197
202
  - **`--python` engine (v0.3.4, `send --tag` only)**: hand the whole send to the
198
203
  **python `/meta-broadcast` endpoint** — the *same* sender the dashboard's "Python
199
204
  endpoint" toggle uses — via a staff-gated proxy (the master key stays server-side
package/TEAM-GUIDE.md CHANGED
@@ -93,6 +93,7 @@ When you see it, run `npm i -g @flowapt/flowiq-cli` — a stale version also mea
93
93
  | Send to one batch tag | `flowiq bc send <org_id> --tag <batch-tag> --template … --body param1="Hi {{first_name}}" --commit` |
94
94
  | Send a broadcast whose template has an IMAGE header | Same as above — the image is automatic (the template's own header image). Override with `--header-media <public-image-url>` if needed. |
95
95
  | Send via the SAME engine as the dashboard's "Python" toggle | add `--python` to a `bc send --tag …` (fire-and-forget; python resolves the tag + sends + tracks; no CLI resume for this engine) |
96
+ | Check how a broadcast is landing (read/delivered/sent/failed) | `flowiq bc status <org_id> <broadcastId>` (the `broadcastId` a `--python` send prints) |
96
97
  | Read a contact's chat | `flowiq m pull <contact_id>` then open the JSON |
97
98
  | Export an org's full chat history | `flowiq export chats <org_id>` |
98
99
  | Check / create WhatsApp templates | `flowiq tpl pull <org_id>` / `flowiq tpl create <org_id> --request-file req.json` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowapt/flowiq-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Command-line tool for FlowIQ staff: round-trip agent prompts, questionnaires, fine-tuning, pin-board tasks, webhooks, templates, agent-updates, chat exports, and live agent testing without ever touching service-role credentials.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -943,6 +943,29 @@ export async function resume(orgId, opts = {}) {
943
943
  await runPipeline(orgId, opts, { commitStage: !!opts.commit, isResume: true });
944
944
  }
945
945
 
946
+ /** Live delivery status for a broadcast by its broadcastId (e.g. from a --python
947
+ * fire-and-forget send). Read-only — reads the `broadcasts` row + message_status
948
+ * breakdown. This is the visibility the fire-and-forget engine otherwise loses. */
949
+ export async function status(orgId, broadcastId, opts = {}) {
950
+ if (!UUID_RE.test(orgId)) { console.error(`Error: "${orgId}" is not a valid organization UUID.`); process.exit(1); }
951
+ if (!UUID_RE.test(broadcastId)) { console.error("Error: broadcast id must be a UUID (the broadcastId a --python send printed)."); process.exit(1); }
952
+ let resp;
953
+ try { resp = await http.post("broadcast", { action: "status", organization_id: orgId, broadcast_id: broadcastId }); }
954
+ catch (e) { console.error(`Status failed: ${e.message}`); if (e.body?.error) console.error(` ${e.body.error}`); process.exit(1); }
955
+ if (opts.json) { console.log(JSON.stringify(resp, null, 2)); return; }
956
+ const b = resp.broadcast, d = resp.delivery;
957
+ const reached = d.read + d.delivered + d.sent;
958
+ console.log(`Broadcast ${b.id} — ${resp.organization_name}`);
959
+ console.log(` template: ${b.template_name}${b.broadcast_name ? ` (${b.broadcast_name})` : ""}${b.media_url ? " · media header" : ""}`);
960
+ console.log(` status: ${b.status ?? "?"} · created ${b.created_at}`);
961
+ console.log(` recipients: ${b.total_recipients ?? "?"} · ${d.linked} message(s) linked`);
962
+ console.log(` ✅ reached ${reached} (read ${d.read} · delivered ${d.delivered} · sent ${d.sent})`);
963
+ console.log(` ❌ failed ${d.failed}${d.other ? ` · other/pending ${d.other}` : ""}`);
964
+ if (b.total_recipients) {
965
+ console.log(` ${((reached / b.total_recipients) * 100).toFixed(1)}% of ${b.total_recipients} recipients reached${d.linked < b.total_recipients ? " (still sending?)" : ""}`);
966
+ }
967
+ }
968
+
946
969
  export async function list() {
947
970
  let files;
948
971
  try { files = (await fs.readdir(CAMPAIGN_DIR)).filter((f) => f.endsWith(".json") && !f.endsWith(".report.json")); }
package/src/index.js CHANGED
@@ -411,6 +411,10 @@ export function run(argv) {
411
411
  .option("--rate <n>", "max messages per second (hard cap 10)", "8")
412
412
  .option("--retry-failed", "also re-attempt rows previously marked failed (confirmed failures only)")
413
413
  .action((orgId, opts) => broadcastCmd.resume(orgId, opts));
414
+ broadcast.command("status <organization_id> <broadcast_id>")
415
+ .description("Live delivery counts (read/delivered/sent/failed) for a broadcast by its broadcastId — e.g. from a --python fire-and-forget send")
416
+ .option("--json", "raw JSON")
417
+ .action((orgId, broadcastId, opts) => broadcastCmd.status(orgId, broadcastId, opts));
414
418
  broadcast.command("list")
415
419
  .description("List local campaigns + sent counts")
416
420
  .action(() => broadcastCmd.list());