@floomhq/signaldash 0.4.0 → 0.5.0

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.
Files changed (2) hide show
  1. package/bin/sd.mjs +64 -18
  2. package/package.json +8 -2
package/bin/sd.mjs CHANGED
@@ -8,6 +8,13 @@ import { homedir } from "node:os";
8
8
  import { createInterface } from "node:readline";
9
9
  import { fileURLToPath } from "node:url";
10
10
 
11
+ // Lazy-load presentation deps so `mcp` (stdio, machine-facing) stays clean/fast.
12
+ async function ui() {
13
+ const [{ default: chalk }, { default: prompts }, { default: ora }, { default: open }] =
14
+ await Promise.all([import("chalk"), import("prompts"), import("ora"), import("open")]);
15
+ return { chalk, prompts, ora, open };
16
+ }
17
+
11
18
  const DEFAULT_BACKEND = process.env.SIGNALDASH_BACKEND || "https://signaldash-api.floom.dev";
12
19
 
13
20
  function configPaths() {
@@ -77,8 +84,12 @@ export async function cmdConnect(provider, dependencies = {}) {
77
84
  process.exitCode = 1;
78
85
  return;
79
86
  }
80
- log(`Open this to connect your ${provider}:\n\n ${r.json.url}\n`);
81
- log("Waiting for authentication to complete...");
87
+ const { chalk, ora, open } = await ui();
88
+ log("\n " + chalk.dim("Opening your browser. If it does not open, use this link:"));
89
+ log(" " + chalk.cyan(r.json.url) + "\n");
90
+ try { await open(r.json.url); } catch {}
91
+ const spin = ora({ text: `Waiting for ${provider} authentication...`, indent: 2 }).start();
92
+ dependencies._spin = spin;
82
93
 
83
94
  const timeoutMs = positiveMilliseconds("SIGNALDASH_CONNECT_TIMEOUT_MS", 5 * 60 * 1000);
84
95
  const pollMs = positiveMilliseconds("SIGNALDASH_CONNECT_POLL_MS", 2000);
@@ -86,10 +97,12 @@ export async function cmdConnect(provider, dependencies = {}) {
86
97
  while (now() < deadline) {
87
98
  const status = await request(`/connect/${provider}/status`, undefined, { method: "GET" });
88
99
  if (status.status === 200 && status.json.connected) {
89
- log(`Connected ${provider}${status.json.name ? `: ${status.json.name}` : ""}`);
100
+ if (dependencies._spin) dependencies._spin.succeed(`Connected ${provider}${status.json.name ? `: ${status.json.name}` : ""}`);
101
+ else log(`Connected ${provider}`);
90
102
  return;
91
103
  }
92
104
  if (status.status !== 202) {
105
+ if (dependencies._spin) dependencies._spin.fail("connect failed");
93
106
  error("connect status failed:", status.json.error || status.status);
94
107
  process.exitCode = 1;
95
108
  return;
@@ -97,7 +110,8 @@ export async function cmdConnect(provider, dependencies = {}) {
97
110
  await sleep(Math.min(pollMs, Math.max(0, deadline - now())));
98
111
  }
99
112
 
100
- error(`Not connected yet. The link above stays valid — open it, finish authenticating, then run:\n npx @floomhq/signaldash connect ${provider}`);
113
+ if (dependencies._spin) dependencies._spin.stop();
114
+ error(`Not connected yet. The link above stays valid; open it, finish authenticating, then run:\n npx @floomhq/signaldash connect ${provider}\nManual fallback:\n signaldash connect ${provider} claim <account_id>`);
101
115
  process.exitCode = 1;
102
116
  }
103
117
  export async function cmdClaim(provider, accountId, dependencies = {}) {
@@ -161,23 +175,51 @@ export async function cmdSkill(dependencies = {}) {
161
175
  export async function cmdSetup(code, dependencies = {}) {
162
176
  const log = dependencies.log || console.log;
163
177
  const { execSync } = await import("node:child_process");
164
- log("SignalDash setup\n");
165
- await cmdLogin(code, undefined, dependencies);
178
+ const { chalk, prompts } = await ui();
179
+
180
+ log("");
181
+ log(" " + chalk.bold.cyan("SignalDash") + chalk.dim(" secure LinkedIn + WhatsApp access for your agent"));
182
+ log("");
183
+
184
+ await cmdLogin(code, undefined, { ...dependencies, log: () => {} });
166
185
  if (process.exitCode === 1) return;
167
- await cmdSkill(dependencies);
186
+ log(" " + chalk.green("+") + " logged in");
187
+
188
+ await cmdSkill({ ...dependencies, log: () => {} });
189
+ log(" " + chalk.green("+") + " agent skill installed");
190
+
168
191
  try {
169
192
  execSync("claude mcp add signaldash -- npx -y @floomhq/signaldash mcp", { stdio: "ignore" });
170
- log("Registered the MCP with Claude Code.");
193
+ log(" " + chalk.green("+") + " MCP registered with Claude Code");
171
194
  } catch {
172
- log("To finish in Cursor, add to .cursor/mcp.json:");
173
- log(' {"mcpServers":{"signaldash":{"command":"npx","args":["-y","@floomhq/signaldash","mcp"]}}}');
195
+ log(" " + chalk.yellow("!") + " Claude Code not found. For Cursor, add to .cursor/mcp.json:");
196
+ log(" " + chalk.dim('{"mcpServers":{"signaldash":{"command":"npx","args":["-y","@floomhq/signaldash","mcp"]}}}'));
174
197
  }
175
- for (const provider of ["linkedin", "whatsapp"]) {
176
- log(`\n--- connect ${provider} ---`);
198
+ log("");
199
+
200
+ const choice = dependencies.choice ?? (await prompts({
201
+ type: "select",
202
+ name: "value",
203
+ message: "Which accounts do you want to connect?",
204
+ choices: [
205
+ { title: "WhatsApp only", value: ["whatsapp"] },
206
+ { title: "LinkedIn only", value: ["linkedin"] },
207
+ { title: "Both", value: ["linkedin", "whatsapp"] },
208
+ { title: "Skip for now", value: [] },
209
+ ],
210
+ initial: 0,
211
+ })).value;
212
+
213
+ if (!choice || choice.length === 0) {
214
+ log("\n " + chalk.dim("Nothing connected. Run `signaldash connect whatsapp` or `connect linkedin` anytime."));
215
+ return;
216
+ }
217
+ for (const provider of choice) {
218
+ log("");
177
219
  await cmdConnect(provider, dependencies);
178
- process.exitCode = 0; // a skipped channel should not fail the whole setup
220
+ process.exitCode = 0;
179
221
  }
180
- log("\nDone. Ask your agent: \"list my recent LinkedIn chats and draft replies.\"");
222
+ log("\n " + chalk.dim("Try: ") + chalk.white('"list my recent whatsapp chats and draft replies"') + "\n");
181
223
  }
182
224
 
183
225
  export async function main(argv = process.argv.slice(2), dependencies = {}) {
@@ -193,9 +235,13 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
193
235
  else log(`SignalDash — secure LinkedIn + WhatsApp access for your agent.\n\n signaldash login <invite-code> [--backend URL]\n signaldash connect linkedin|whatsapp\n signaldash connect linkedin|whatsapp claim <account_id>\n signaldash mcp\n signaldash skill install the agent skill\n\nThe agent reaches channels only through SignalDash. No keys on your machine.`);
194
236
  }
195
237
 
196
- if (
197
- process.argv[1] &&
198
- realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)
199
- ) {
238
+ function isDirectRun() {
239
+ try {
240
+ return Boolean(process.argv[1]) && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
241
+ } catch {
242
+ return false;
243
+ }
244
+ }
245
+ if (isDirectRun()) {
200
246
  await main();
201
247
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/signaldash",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Secure LinkedIn and WhatsApp MCP access for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,5 +27,11 @@
27
27
  "unipile",
28
28
  "ai-agent"
29
29
  ],
30
- "license": "MIT"
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "chalk": "5.3.0",
33
+ "prompts": "2.4.2",
34
+ "ora": "8.0.1",
35
+ "open": "10.1.0"
36
+ }
31
37
  }