@flowapt/flowiq-cli 0.1.1 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowapt/flowiq-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Command-line tool for FlowIQ staff: round-trip agent prompts, questionnaires, webhooks, and more without ever touching service-role credentials.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,66 @@
1
+ // `flowiq messages pull <contact_id> [--count N]`.
2
+ // Read-only chat fetch (no push — chat history is the system of record).
3
+
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { http } from "../http.js";
7
+
8
+ const MSG_DIR = path.resolve(process.cwd(), ".flowiq", "messages");
9
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
+
11
+ function slugify(name, fallback) {
12
+ const s = String(name || "")
13
+ .toLowerCase()
14
+ .replace(/[^a-z0-9]+/g, "-")
15
+ .replace(/-+/g, "-")
16
+ .replace(/^-|-$/g, "");
17
+ return s || fallback;
18
+ }
19
+
20
+ async function fileExists(p) {
21
+ try { await fs.access(p); return true; } catch { return false; }
22
+ }
23
+
24
+ export async function pull(contactId, opts = {}) {
25
+ if (!UUID_RE.test(contactId)) {
26
+ console.error(`Error: "${contactId}" is not a valid contact UUID.`);
27
+ process.exit(1);
28
+ }
29
+ const query = { contact_id: contactId };
30
+ if (opts.count) query.count = String(opts.count);
31
+
32
+ let resp;
33
+ try {
34
+ resp = await http.get("messages", query);
35
+ } catch (e) {
36
+ console.error(`Pull failed: ${e.message}`);
37
+ process.exit(1);
38
+ }
39
+
40
+ await fs.mkdir(MSG_DIR, { recursive: true });
41
+ const nameSlug = slugify(
42
+ resp.contact_name || resp.contact_whatsapp_id || resp.contact_id,
43
+ resp.contact_id
44
+ );
45
+ const orgSlug = resp.organization_name ? slugify(resp.organization_name, "") : "";
46
+ const slug = orgSlug ? `${nameSlug}-${orgSlug}` : nameSlug;
47
+ const filePath = path.join(MSG_DIR, `${slug}.json`);
48
+ const overwriting = await fileExists(filePath);
49
+ await fs.writeFile(filePath, JSON.stringify(resp, null, 2) + "\n", "utf8");
50
+
51
+ console.log(`${overwriting ? "Overwrote" : "Wrote"} ${filePath}`);
52
+ console.log(` contact: ${resp.contact_name ?? "(no name)"} (${resp.contact_id})`);
53
+ if (resp.contact_whatsapp_id) {
54
+ console.log(` whatsapp_id: ${resp.contact_whatsapp_id}`);
55
+ }
56
+ if (resp.organization_name) {
57
+ console.log(` organization: ${resp.organization_name} (${resp.organization_id})`);
58
+ }
59
+ console.log(` user msgs: ${resp.user_messages_found}/${resp.user_messages_requested} requested`);
60
+ console.log(` anchor: ${resp.anchor_created_at}`);
61
+ console.log(` total rows: ${resp.total_rows}`);
62
+ console.log(` by sender:`);
63
+ for (const [k, v] of Object.entries(resp.sender_breakdown).sort()) {
64
+ console.log(` ${k.padEnd(16)} ${v}`);
65
+ }
66
+ }
@@ -65,6 +65,14 @@ export async function pull(orgId) {
65
65
  console.log(` agent: ${resp.agent_name} (${resp.agent_id})`);
66
66
  console.log(` sections: ${resp.prompt_sections.length}`);
67
67
  console.log(` content: ${totalChars.toLocaleString()} chars`);
68
+ if (resp.bootstrapped) {
69
+ console.log("");
70
+ console.log(" ⚠ this agent had no prompt_sections — seeded a single \"Main\"");
71
+ console.log(" section from settings.system_prompt. on push, system_prompt");
72
+ console.log(" will be regenerated with a \"## SECTION: Main\" header wrap.");
73
+ console.log(" split the JSON into proper sections before push if you want");
74
+ console.log(" different headers.");
75
+ }
68
76
  }
69
77
 
70
78
  export async function push(identifier) {
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ import { Command } from "commander";
5
5
  import * as authCmd from "./commands/auth.js";
6
6
  import * as promptsCmd from "./commands/prompts.js";
7
7
  import * as questionnairesCmd from "./commands/questionnaires.js";
8
+ import * as messagesCmd from "./commands/messages.js";
8
9
 
9
10
  export function run(argv) {
10
11
  const program = new Command();
@@ -43,5 +44,14 @@ export function run(argv) {
43
44
  .description("Apply a local JSON file's questionnaire to the agent")
44
45
  .action((id) => questionnairesCmd.push(id));
45
46
 
47
+ // messages (read-only: pull a contact's helpdesk_messages history)
48
+ const msgs = program.command("messages")
49
+ .alias("m")
50
+ .description("Pull a contact's helpdesk_messages history (read-only)");
51
+ msgs.command("pull <contact_id>")
52
+ .description("Pull last N user-message windows of conversation for a contact (default N=25, max 200)")
53
+ .option("--count <n>", "number of most-recent user-* messages to anchor on", "25")
54
+ .action((contactId, opts) => messagesCmd.pull(contactId, opts));
55
+
46
56
  program.parseAsync(argv);
47
57
  }