@flowapt/flowiq-cli 0.8.1 → 0.9.2

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/src/index.js CHANGED
@@ -11,6 +11,7 @@ import * as questionnairesCmd from "./commands/questionnaires.js";
11
11
  import * as messagesCmd from "./commands/messages.js";
12
12
  import * as fineTuningCmd from "./commands/fine-tuning.js";
13
13
  import * as messagingWebhooksCmd from "./commands/messaging-webhooks.js";
14
+ import * as popupsCmd from "./commands/popups.js";
14
15
  import * as webhooksCmd from "./commands/webhooks.js";
15
16
  import * as flowmodCmd from "./commands/flowmod.js";
16
17
  import * as groupsCmd from "./commands/groups.js";
@@ -137,6 +138,62 @@ export function run(argv) {
137
138
  .option("--dry-run", "preview the diff without writing anything")
138
139
  .action((id, opts) => messagingWebhooksCmd.push(id, opts));
139
140
 
141
+ // popups — the popup service's /api/popup-settings is the ONE home for popup
142
+ // writes (editor + CLI). This CLI holds no popup logic: `schema` reads what a
143
+ // popup may contain from the server, so new popup features need no release.
144
+ const popups = program.command("popups")
145
+ .alias("pp")
146
+ .description("Popups: list / pull / push / create / duplicate / activate / live / delete / upload — one JSON document per popup, validated by the popup service");
147
+ popups.command("schema")
148
+ .description("What a popup document may contain TODAY (content, rules, design, webhooks) — served by the popup service, never stale")
149
+ .option("--json", "print the full schema as JSON (enums + limits included)")
150
+ .action((opts) => popupsCmd.schema(opts));
151
+ popups.command("list <organization_id>")
152
+ .description("Every popup of an org: id, state, views, signups, and what a signup triggers")
153
+ .option("--json", "print as JSON")
154
+ .action((orgId, opts) => popupsCmd.list(orgId, opts));
155
+ popups.command("pull <organization_id> [popup_id]")
156
+ .description("Write a popup's full document (popup + webhooks + etag) to ./.flowiq/popups/<org>/; no popup_id = every popup")
157
+ .action((orgId, popupId) => popupsCmd.pull(orgId, popupId));
158
+ popups.command("push <file>")
159
+ .description("Apply a pulled document back: id-keyed webhook upsert, integration configs MERGED, refused if someone else edited it since the pull")
160
+ .option("--dry-run", "show what would change without writing")
161
+ .option("--replace-webhooks", "webhooks not in the file are REMOVED (needs --confirm-delete-webhooks)")
162
+ .option("--confirm-delete-webhooks", "allow the push to remove webhooks")
163
+ .option("--force", "overwrite even if the popup changed since the pull")
164
+ .action((file, opts) => popupsCmd.push(file, opts));
165
+ popups.command("create <organization_id> <file>")
166
+ .description("Create a popup from a JSON document (a pulled file from ANY org works — ids are stripped); created INACTIVE")
167
+ .option("--dry-run", "validate only")
168
+ .action((orgId, file, opts) => popupsCmd.create(orgId, file, opts));
169
+ popups.command("duplicate <organization_id> <popup_id>")
170
+ .description("Copy a popup (design, content, behaviour AND webhooks) — inactive, no link, zero stats")
171
+ .option("--name <name>", "name for the copy (default: \"<name> (copy)\")")
172
+ .option("--from-org <organization_id>", "copy a popup that lives in ANOTHER org (webhooks are not copied across orgs)")
173
+ .option("--no-webhooks", "do not copy the webhooks")
174
+ .action((orgId, popupId, opts) => popupsCmd.duplicate(orgId, popupId, opts));
175
+ popups.command("activate <organization_id> <popup_id>")
176
+ .description("Switch a popup ON (is_active) — the real on/off switch")
177
+ .action((orgId, popupId) => popupsCmd.activate(orgId, popupId));
178
+ popups.command("deactivate <organization_id> <popup_id>")
179
+ .description("Switch a popup OFF everywhere (store, pinned id and its link)")
180
+ .action((orgId, popupId) => popupsCmd.deactivate(orgId, popupId));
181
+ popups.command("live <organization_id> <popup_id>")
182
+ .description("Make this the popup the org's store embed shows (one per org; others are demoted)")
183
+ .option("--off", "unselect it (NOT a kill switch — use deactivate for that)")
184
+ .action((orgId, popupId, opts) => popupsCmd.live(orgId, popupId, opts));
185
+ popups.command("delete <organization_id> <popup_id>")
186
+ .description("Delete a popup and its signups; a live popup or one with signups needs --confirm")
187
+ .option("--confirm", "delete even if live or it has signups")
188
+ .action((orgId, popupId, opts) => popupsCmd.remove(orgId, popupId, opts));
189
+ popups.command("upload <organization_id> <popup_id> <file>")
190
+ .description("Upload an image (--slot) or a custom font (--font) for a popup and print the URL to put in its design")
191
+ .option("--slot <name>", "image slot: main, main-mobile, textimage, textimage-mobile, success-main, success-mobile, intro-logo, deck-<key>…")
192
+ .option("--font <family>", "font family name (uploads to the public popup_fonts bucket)")
193
+ .option("--weight <n>", "font weight (default 400)")
194
+ .option("--italic", "italic face")
195
+ .action((orgId, popupId, file, opts) => popupsCmd.upload(orgId, popupId, file, opts));
196
+
140
197
  // webhooks (Shopify/WooCommerce platform webhooks; auto-detects platform + ip_whitelist proxy)
141
198
  const wh = program.command("webhooks")
142
199
  .alias("wh")
@@ -166,13 +223,15 @@ export function run(argv) {
166
223
  // groups (read-only: pull WhatsApp GROUP chat history from the FlowMod Evolution DB)
167
224
  const groups = program.command("groups")
168
225
  .alias("grp")
169
- .description("Read WhatsApp group chat history from the FlowMod Evolution DB (set FLOWMOD_EVO_DB_URL env). Read-only.");
226
+ .description("DEPRECATED — Evolution is retired and frozen (nothing since 29 Aug 2026). Read group chats with the whatsapp-flowapt / whatsapp-flowmod MCP instead.");
170
227
  groups.command("list")
171
- .description("List an Evolution instance's group chats with message counts + last activity")
228
+ .description("[archive only] List the frozen Evolution instance's group chats — needs --archive")
172
229
  .option("--instance <name>", "Evolution instance name", "flowapt")
230
+ .option("--archive", "read the FROZEN pre-29-Aug-2026 archive anyway")
173
231
  .action((opts) => groupsCmd.list(opts));
174
232
  groups.command("pull <name-or-jid>")
175
- .description("Pull a group's messages in a time window into a local JSON file + print a timeline")
233
+ .description("[archive only] Pull a group's FROZEN message history into a local JSON file — needs --archive")
234
+ .option("--archive", "read the FROZEN pre-29-Aug-2026 archive anyway")
176
235
  .option("--instance <name>", "Evolution instance name", "flowapt")
177
236
  .option("--last <window>", "rolling window back from now, e.g. 48h, 7d, 2w, 90m", "48h")
178
237
  .option("--since <date>", "window start (SAST), YYYY-MM-DD or \"YYYY-MM-DD HH:MM\" (overrides --last)")
@@ -198,9 +257,9 @@ export function run(argv) {
198
257
  .description("List every task in the DB (optionally filter: open | in_progress | done) to find an id")
199
258
  .action((status) => pinboardCmd.listRemote(status));
200
259
 
201
- // hours (client-hours ledger — every org's package = 10h Flowapt work/month)
260
+ // hours (client-hours ledger + each org's monthly hours package, v0.9.2)
202
261
  const hours = program.command("hours")
203
- .description("Client-hours ledger: log + report Flowapt work against each org's 10h/month package");
262
+ .description("Client-hours ledger: log + report Flowapt work against each org's monthly hours package");
204
263
  hours.command("log <organization_id>")
205
264
  .description("Log work done for a client org (exactly one of --minutes / --hours)")
206
265
  .option("--minutes <n>", "duration in whole minutes")
@@ -216,10 +275,27 @@ export function run(argv) {
216
275
  .option("--json", "raw JSON output")
217
276
  .action((orgId, opts) => hoursCmd.list(orgId, opts));
218
277
  hours.command("summary")
219
- .description("Cross-org month totals — every org with logged work, vs the 10h package")
278
+ .description("Cross-org month totals: every org with logged work or a package, vs its package")
220
279
  .option("--month <YYYY-MM>", "which month to report")
221
280
  .option("--json", "raw JSON output")
222
281
  .action((opts) => hoursCmd.summary(opts));
282
+ const hoursPkg = hours.command("package")
283
+ .description("Each org's monthly hours package (one org, or several sharing one). Changes: Matt + Gidon only");
284
+ hoursPkg.command("show [organization_id]")
285
+ .description("Every package, or the one an org is on")
286
+ .option("--json", "raw JSON output")
287
+ .action((orgId, opts) => hoursCmd.packageShow(orgId, opts));
288
+ hoursPkg.command("set <organization_ids...>")
289
+ .description("Put these orgs on ONE package of --hours a month (several orgs = a shared package)")
290
+ .requiredOption("--hours <h>", "hours included per month (0 = none included, e.g. a self-service plan)")
291
+ .option("--name <text>", "package name (default: the org's name, or the names joined)")
292
+ .option("--notes <text>", "where the figure comes from, e.g. \"Order Form 12 Aug 2026\"")
293
+ .option("--dry-run", "show what would change, write nothing")
294
+ .action((orgIds, opts) => hoursCmd.packageSet(orgIds, opts));
295
+ hoursPkg.command("clear <organization_id>")
296
+ .description("Take an org off its package (back to 'no package on file')")
297
+ .option("--dry-run", "show what would change, write nothing")
298
+ .action((orgId, opts) => hoursCmd.packageClear(orgId, opts));
223
299
 
224
300
  // report deck — the monthly client deck (build / copy / approve / send)
225
301
  const report = program.command("report").description("Client reporting: the monthly 10-slide client deck");
@@ -417,7 +493,7 @@ export function run(argv) {
417
493
  .option("--agent <id>", "target a specific agent instead of the org's active one")
418
494
  .option("--use-settings-prompt [bool]", "settings.use_settings_prompt (true if bare)")
419
495
  .option("--model <model>", "settings.model (e.g. gpt-5.4-mini)")
420
- .option("--reasoning-effort <level>", "settings.reasoning_effort — low|medium|high (\"\" clears it); pairs with reasoning models (e.g. gpt-5.6-luna + high)")
496
+ .option("--reasoning-effort <level>", "settings.reasoning_effort — low|medium|high|xhigh|max (\"\" clears it; max = Claude only); pairs with reasoning models (e.g. gpt-5.6-luna + high, claude-sonnet-5 + medium)")
421
497
  .option("--rename <name>", "rename the agent")
422
498
  .option("--tool <flag=bool>", "toggle a tool flag (repeatable): woo_order_build/woo_tip_field/woo_order_note_field/view_cart_tool/restock_tool/block_tool_status/postal_code_tool_status/shopify_products_web_chat/ticket_tool_status/product_lookup/email_request_tool/collapse_product_variants", agentConfigCmd.collectTool, [])
423
499
  .option("--discount [bool]", "agent.discount.enabled (true if bare)")
@@ -460,7 +536,9 @@ export function run(argv) {
460
536
  plans.command("list [organization_id]")
461
537
  .description("List plans, default open (draft, pending_review, approved, scheduled), across all active orgs or one org")
462
538
  .option("--status <status>", "open (default) | all | pending_review | draft | approved | rejected | scheduled | sent | cancelled (comma-separated allowed)")
463
- .option("--since <date>", "only plans scheduled on or after YYYY-MM-DD")
539
+ .option("--since <date>", "only plans whose SEND date is on or after YYYY-MM-DD")
540
+ .option("--created-since <date>", "only plans CREATED on or after YYYY-MM-DD (SAST) — what a client just submitted")
541
+ .option("--touched-since <date>", "only plans UPDATED on or after YYYY-MM-DD (SAST)")
464
542
  .option("--include-inactive", "include plans from inactive orgs (cross-org listing)")
465
543
  .option("--limit <n>", "max plans returned (default 100, max 500)")
466
544
  .option("--json", "print the raw response")
@@ -679,6 +757,18 @@ export function run(argv) {
679
757
  .option("--confirm", "actually cancel")
680
758
  .action((orgId, queueId, opts) => broadcastCmd.scheduledUpdate(orgId, queueId, "cancel", opts));
681
759
 
760
+ const drafts = broadcast.command("drafts")
761
+ .description("Saved SEND DRAFTS (the send dialog's stored configurations) — read-only");
762
+ drafts.command("list <organization_id>", { isDefault: true })
763
+ .description("List the org's saved send drafts, newest first")
764
+ .option("--limit <n>", "max rows (default 25, max 200)")
765
+ .option("--json", "raw JSON")
766
+ .action((orgId, opts) => broadcastCmd.draftsList(orgId, opts));
767
+ drafts.command("show <organization_id> <draft_id>")
768
+ .description("One saved send draft in full (the whole stored config)")
769
+ .option("--json", "raw JSON")
770
+ .action((orgId, draftId, opts) => broadcastCmd.draftsShow(orgId, draftId, opts));
771
+
682
772
  broadcast.command("list-remote <organization_id>")
683
773
  .description("List the org's broadcasts newest-first (full broadcastId + template + status + recipients) — the discovery step for `bc status` / `bc retry`")
684
774
  .option("--limit <n>", "how many to show (default 25, max 200)")