@flowapt/flowiq-cli 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.
package/README.md CHANGED
@@ -199,6 +199,10 @@ flowiq bc resume <org_id> --campaign july-referrals --commit [--retry-failed]
199
199
  `helpdesk_messages`. Built for the `--python` fire-and-forget engine (which
200
200
  returns a `broadcastId` but has no CLI status log), but works for any broadcast
201
201
  id (dashboard sends included). `flowiq bc status <org> <broadcastId>`.
202
+ - **Engine auto-routing (v0.3.6):** any `send --tag` of **more than 10** eligible
203
+ recipients **always uses the python engine** (a `>2000` tag routes there too).
204
+ Only a ≤10 send stays on the resumable per-row Node engine. `--python` forces
205
+ python at any size. (CSV per-row sends stay on Node — python takes uniform params.)
202
206
  - **`--python` engine (v0.3.4, `send --tag` only)**: hand the whole send to the
203
207
  **python `/meta-broadcast` endpoint** — the *same* sender the dashboard's "Python
204
208
  endpoint" toggle uses — via a staff-gated proxy (the master key stays server-side
package/TEAM-GUIDE.md CHANGED
@@ -92,7 +92,7 @@ When you see it, run `npm i -g @flowapt/flowiq-cli` — a stale version also mea
92
92
  | Split a big id-list cohort into send-safe batch tags | `flowiq seg plan <org_id> --tag-prefix … --ids-file …` → `flowiq seg apply … --commit` |
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
- | 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) |
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). **Any tag send over 10 recipients uses python automatically.** |
96
96
  | Check how a broadcast is landing (read/delivered/sent/failed) | `flowiq bc status <org_id> <broadcastId>` (the `broadcastId` a `--python` send prints) |
97
97
  | Read a contact's chat | `flowiq m pull <contact_id>` then open the JSON |
98
98
  | Export an org's full chat history | `flowiq export chats <org_id>` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowapt/flowiq-cli",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
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": {
@@ -812,17 +812,29 @@ async function runTagPipeline(orgId, opts, { commitStage }) {
812
812
  if (template.url_button?.present && !buttonLiteral) aborts.push("template has a dynamic URL button — pass --button param1=<code>");
813
813
  if (aborts.length) { for (const a of aborts) console.error(`ABORT — ${a}`); process.exit(1); }
814
814
 
815
- // --python: hand the whole send to the python /meta-broadcast engine (the same
816
- // sender the dashboard's "Python endpoint" toggle uses) via the staff-gated
817
- // proxy. Python resolves the tag + eligibility + sends fire-and-forget.
815
+ // ENGINE ROUTING (policy): any send of MORE THAN 10 recipients ALWAYS uses the
816
+ // python /meta-broadcast engine (the proven bulk sender). --python forces it at
817
+ // any size; only a ≤10 send stays on the resumable per-row Node engine.
818
+ const PYTHON_MIN = 10;
818
819
  if (opts.python) {
819
820
  return runPythonTagSend(orgId, { opts, tag, templateName, template, headerMedia, bodyLiterals, buttonLiteral, commitStage });
820
821
  }
821
822
 
822
- // resolve the tag server-side (broadcast-safe recipients only)
823
+ // resolve the tag server-side (broadcast-safe recipients only). A >2000 tag
824
+ // 413s the Node resolver — that's by definition >10, so route straight to python.
823
825
  let resolved;
824
826
  try { resolved = await http.post("broadcast", { action: "resolve-tag", organization_id: orgId, tag }); }
825
- catch (e) { console.error(`Tag resolution failed: ${e.message}`); if (e.body?.error) console.error(` ${e.body.error}`); process.exit(1); }
827
+ catch (e) {
828
+ if (e.status === 413) {
829
+ console.log(`Tag "${tag}" has >2000 contacts → PYTHON engine (any send >${PYTHON_MIN} uses python).`);
830
+ return runPythonTagSend(orgId, { opts, tag, templateName, template, headerMedia, bodyLiterals, buttonLiteral, commitStage });
831
+ }
832
+ console.error(`Tag resolution failed: ${e.message}`); if (e.body?.error) console.error(` ${e.body.error}`); process.exit(1);
833
+ }
834
+ if (resolved.recipients.length > PYTHON_MIN) {
835
+ console.log(`Tag "${tag}": ${resolved.recipients.length} sendable (>${PYTHON_MIN}) → PYTHON engine (any send >${PYTHON_MIN} uses python).`);
836
+ return runPythonTagSend(orgId, { opts, tag, templateName, template, headerMedia, bodyLiterals, buttonLiteral, commitStage });
837
+ }
826
838
  console.log(`Tag "${tag}" on ${resolved.organization_name}: ${resolved.tagged_total} contact(s), ${resolved.recipients.length} sendable, ${resolved.excluded.length} excluded (opt-out/archived/blocked).`);
827
839
  if (!resolved.recipients.length) { console.log("Nothing sendable under this tag."); return; }
828
840
 
package/src/index.js CHANGED
@@ -391,7 +391,7 @@ export function run(argv) {
391
391
  .option("--button <k=v>", "with --tag: dynamic URL button param, e.g. --button param1=<short-code>", broadcastCmd.collectKV, {})
392
392
  .option("--campaign <name>", "campaign id / config file slug (default: CSV filename / tag)")
393
393
  .option("--header-media <url>", "header image/video/doc URL for a media-header template (default: the template's own stored image)")
394
- .option("--python", "with --tag: send via the python /meta-broadcast engine (same as the dashboard's Python toggle; fire-and-forget, python-tracked)")
394
+ .option("--python", "with --tag: force the python /meta-broadcast engine (same as the dashboard's Python toggle). NOTE: any tag send >10 recipients ALWAYS uses python automatically")
395
395
  .option("--commit", "actually send (omit to dry-run)")
396
396
  .option("--yes", "skip the type-the-campaign-name confirm gate (CI)")
397
397
  .option("--force-remap", "ignore the saved mapping and rebuild interactively")