@arjun-shah/agentbar-cli 0.1.9 → 0.1.11

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 (3) hide show
  1. package/README.md +7 -1
  2. package/bin/agentbar.js +140 -203
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -24,7 +24,13 @@ agentbar stats
24
24
  ```
25
25
 
26
26
  The CLI writes `agentbar.config.json` in your project directory. `agentbar init` only asks
27
- for your site URL.
27
+ for your site URL and syncs settings to the hosted dashboard so your embed is a single line:
28
+
29
+ ```html
30
+ <script src="https://agent-pug.vercel.app/agentbar.js" data-site-key="your-site-key"></script>
31
+ ```
32
+
33
+ Open the deployed dashboard to edit settings and copy the snippet again.
28
34
 
29
35
  ## Customization
30
36
 
package/bin/agentbar.js CHANGED
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
- import readline from "node:readline";
5
5
  import process from "node:process";
6
+ import readline from "node:readline";
6
7
 
7
8
  const CONFIG_FILE = "agentbar.config.json";
8
9
  const DEFAULT_CONFIG = {
9
10
  apiBase: "https://agent-pug.vercel.app",
11
+ authToken: "",
12
+ accountEmail: "",
10
13
  siteUrl: "",
11
14
  siteKey: "",
12
15
  depth: 1,
@@ -119,202 +122,72 @@ const resolveSiteKey = (config) => {
119
122
  }
120
123
  };
121
124
 
125
+ const authHeaders = (config) =>
126
+ config.authToken
127
+ ? {
128
+ Authorization: `Bearer ${config.authToken}`,
129
+ }
130
+ : {};
131
+
122
132
  const renderSnippet = (config) => {
123
- const lines = [
124
- "<script",
125
- ` src=\"${config.apiBase.replace(/\/$/, "")}/agentbar.js\"`,
126
- ` data-site=\"${config.siteUrl || "https://your-site.com"}\"`,
127
- ` data-api=\"${config.apiBase.replace(/\/$/, "")}\"`,
128
- ` data-depth=\"${config.depth}\"`,
129
- ` data-max-pages=\"${config.maxPages}\"`,
130
- ];
131
- if (config.siteKey) {
132
- lines.push(` data-site-key=\"${config.siteKey}\"`);
133
- }
134
- if (config.themeColor) {
135
- lines.push(` data-theme-color=\"${config.themeColor}\"`);
136
- }
137
- if (config.position) {
138
- lines.push(` data-position=\"${config.position}\"`);
139
- }
140
- if (config.title) {
141
- lines.push(` data-title=\"${config.title}\"`);
142
- }
143
- if (config.subtitle) {
144
- lines.push(` data-subtitle=\"${config.subtitle}\"`);
145
- }
146
- if (config.buttonLabel) {
147
- lines.push(` data-button-label=\"${config.buttonLabel}\"`);
148
- }
149
- if (config.fontFamily) {
150
- lines.push(` data-font-family=\"${config.fontFamily}\"`);
151
- }
152
- if (config.panelBackground) {
153
- lines.push(` data-panel-background=\"${config.panelBackground}\"`);
154
- }
155
- if (config.textColor) {
156
- lines.push(` data-text-color=\"${config.textColor}\"`);
157
- }
158
- if (config.mutedTextColor) {
159
- lines.push(` data-muted-text-color=\"${config.mutedTextColor}\"`);
160
- }
161
- if (config.borderColor) {
162
- lines.push(` data-border-color=\"${config.borderColor}\"`);
163
- }
164
- if (config.buttonBackground) {
165
- lines.push(` data-button-background=\"${config.buttonBackground}\"`);
166
- }
167
- if (config.buttonTextColor) {
168
- lines.push(` data-button-text-color=\"${config.buttonTextColor}\"`);
169
- }
170
- if (config.accentTextColor) {
171
- lines.push(` data-accent-text-color=\"${config.accentTextColor}\"`);
172
- }
173
- if (config.buttonShadow) {
174
- lines.push(` data-button-shadow=\"${config.buttonShadow}\"`);
175
- }
176
- if (config.panelShadow) {
177
- lines.push(` data-panel-shadow=\"${config.panelShadow}\"`);
178
- }
179
- if (config.badgeLabel) {
180
- lines.push(` data-badge-label=\"${config.badgeLabel}\"`);
181
- }
182
- if (config.badgeBackground) {
183
- lines.push(` data-badge-background=\"${config.badgeBackground}\"`);
184
- }
185
- if (config.badgeTextColor) {
186
- lines.push(` data-badge-text-color=\"${config.badgeTextColor}\"`);
187
- }
188
- if (config.userBubbleBackground) {
189
- lines.push(` data-user-bubble-background=\"${config.userBubbleBackground}\"`);
190
- }
191
- if (config.userBubbleText) {
192
- lines.push(` data-user-bubble-text=\"${config.userBubbleText}\"`);
193
- }
194
- if (config.userBubbleBorder) {
195
- lines.push(` data-user-bubble-border=\"${config.userBubbleBorder}\"`);
196
- }
197
- if (config.assistantBubbleBackground) {
198
- lines.push(` data-assistant-bubble-background=\"${config.assistantBubbleBackground}\"`);
199
- }
200
- if (config.assistantBubbleText) {
201
- lines.push(` data-assistant-bubble-text=\"${config.assistantBubbleText}\"`);
202
- }
203
- if (config.assistantBubbleBorder) {
204
- lines.push(` data-assistant-bubble-border=\"${config.assistantBubbleBorder}\"`);
205
- }
206
- if (config.panelWidth) {
207
- lines.push(` data-panel-width=\"${config.panelWidth}\"`);
208
- }
209
- if (config.panelMaxHeight) {
210
- lines.push(` data-panel-max-height=\"${config.panelMaxHeight}\"`);
211
- }
212
- if (config.panelRadius) {
213
- lines.push(` data-panel-radius=\"${config.panelRadius}\"`);
214
- }
215
- if (config.buttonRadius) {
216
- lines.push(` data-button-radius=\"${config.buttonRadius}\"`);
217
- }
218
- if (typeof config.offsetX === "number") {
219
- lines.push(` data-offset-x=\"${config.offsetX}\"`);
220
- }
221
- if (typeof config.offsetY === "number") {
222
- lines.push(` data-offset-y=\"${config.offsetY}\"`);
223
- }
224
- if (config.inputPlaceholder) {
225
- lines.push(` data-input-placeholder=\"${config.inputPlaceholder}\"`);
226
- }
227
- if (config.sendLabel) {
228
- lines.push(` data-send-label=\"${config.sendLabel}\"`);
229
- }
230
- if (config.suggestions?.length) {
231
- lines.push(` data-suggestions=\"${config.suggestions.join(" | ")}\"`);
232
- }
233
- if (config.greeting) {
234
- lines.push(` data-greeting=\"${config.greeting}\"`);
235
- }
236
- if (typeof config.draggable === "boolean") {
237
- lines.push(` data-draggable=\"${config.draggable}\"`);
238
- }
239
- if (typeof config.dragOffset === "number" && config.dragOffset !== 0) {
240
- lines.push(` data-drag-offset=\"${config.dragOffset}\"`);
241
- }
242
- if (typeof config.persistPosition === "boolean") {
243
- lines.push(` data-persist-position=\"${config.persistPosition}\"`);
244
- }
245
- if (config.positionKey) {
246
- lines.push(` data-position-key=\"${config.positionKey}\"`);
247
- }
248
- if (config.openOnLoad) {
249
- lines.push(` data-open=\"${config.openOnLoad}\"`);
250
- }
251
- if (config.showReset) {
252
- lines.push(` data-show-reset=\"${config.showReset}\"`);
253
- }
254
- if (config.persist) {
255
- lines.push(` data-persist=\"${config.persist}\"`);
256
- }
257
- if (config.storageKey) {
258
- lines.push(` data-storage-key=\"${config.storageKey}\"`);
259
- }
260
- if (typeof config.showTypingIndicator === "boolean") {
261
- lines.push(` data-show-typing-indicator=\"${config.showTypingIndicator}\"`);
262
- }
263
- if (typeof config.showExport === "boolean") {
264
- lines.push(` data-show-export=\"${config.showExport}\"`);
265
- }
266
- if (config.exportLabel) {
267
- lines.push(` data-export-label=\"${config.exportLabel}\"`);
268
- }
269
- if (typeof config.showScrollButton === "boolean") {
270
- lines.push(` data-show-scroll-button=\"${config.showScrollButton}\"`);
271
- }
272
- if (config.scrollLabel) {
273
- lines.push(` data-scroll-label=\"${config.scrollLabel}\"`);
274
- }
275
- if (typeof config.showMinimize === "boolean") {
276
- lines.push(` data-show-minimize=\"${config.showMinimize}\"`);
277
- }
278
- if (typeof config.minimizedOnLoad === "boolean") {
279
- lines.push(` data-minimized-on-load=\"${config.minimizedOnLoad}\"`);
280
- }
281
- if (config.minimizeLabel) {
282
- lines.push(` data-minimize-label=\"${config.minimizeLabel}\"`);
283
- }
284
- if (config.expandLabel) {
285
- lines.push(` data-expand-label=\"${config.expandLabel}\"`);
286
- }
287
- if (typeof config.showTimestamps === "boolean") {
288
- lines.push(` data-show-timestamps=\"${config.showTimestamps}\"`);
289
- }
290
- if (config.timestampLocale) {
291
- lines.push(` data-timestamp-locale=\"${config.timestampLocale}\"`);
292
- }
293
- if (typeof config.autoScroll === "boolean") {
294
- lines.push(` data-auto-scroll=\"${config.autoScroll}\"`);
295
- }
296
- if (typeof config.autoScrollThreshold === "number") {
297
- lines.push(` data-auto-scroll-threshold=\"${config.autoScrollThreshold}\"`);
298
- }
299
- if (config.messageMaxWidth) {
300
- lines.push(` data-message-max-width=\"${config.messageMaxWidth}\"`);
301
- }
302
- if (config.launcherTooltip) {
303
- lines.push(` data-launcher-tooltip=\"${config.launcherTooltip}\"`);
133
+ const apiBase = (config.apiBase || DEFAULT_CONFIG.apiBase).replace(/\/$/, "");
134
+ const siteKey = resolveSiteKey(config) || "your-site-key";
135
+ return `<script src="${apiBase}/agentbar.js" data-site-key="${siteKey}"></script>`;
136
+ };
137
+
138
+ const openBrowser = (url) => {
139
+ try {
140
+ if (process.platform === "darwin") {
141
+ spawn("open", [url], { stdio: "ignore", detached: true }).unref();
142
+ return true;
143
+ }
144
+ if (process.platform === "win32") {
145
+ spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
146
+ return true;
147
+ }
148
+ if (process.platform === "linux") {
149
+ spawn("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
150
+ return true;
151
+ }
152
+ } catch {
153
+ return false;
304
154
  }
305
- if (typeof config.autoIngest === "boolean") {
306
- lines.push(` data-auto-ingest=\"${config.autoIngest}\"`);
155
+ return false;
156
+ };
157
+
158
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
159
+
160
+ const syncConfig = async (config) => {
161
+ const apiBase = (config.apiBase || DEFAULT_CONFIG.apiBase).replace(/\/$/, "");
162
+ const siteKey = resolveSiteKey(config);
163
+ if (!siteKey || !config.authToken) {
164
+ return;
307
165
  }
308
- if (typeof config.closeOnOutsideClick === "boolean") {
309
- lines.push(` data-close-on-outside-click=\"${config.closeOnOutsideClick}\"`);
166
+ try {
167
+ const response = await fetch(`${apiBase}/api/config`, {
168
+ method: "POST",
169
+ headers: {
170
+ "Content-Type": "application/json",
171
+ ...authHeaders(config),
172
+ },
173
+ body: JSON.stringify({
174
+ siteKey,
175
+ config: { ...config, siteKey },
176
+ }),
177
+ });
178
+ if (!response.ok) {
179
+ const data = await response.json().catch(() => ({}));
180
+ throw new Error(data?.error || "Could not sync hosted settings.");
181
+ }
182
+ } catch (_error) {
183
+ console.warn("Could not sync settings to the hosted dashboard.");
310
184
  }
311
- lines.push("></script>");
312
- return lines.join("\n");
313
185
  };
314
186
 
315
187
  const printHelp = () => {
316
188
  console.log("Agent Plugin Bar CLI\n");
317
189
  console.log("Commands:");
190
+ console.log(" agentbar login Open the web login flow and save an auth token");
318
191
  console.log(" agentbar init Interactive setup and snippet output");
319
192
  console.log(" agentbar snippet Print current embed snippet");
320
193
  console.log(" agentbar set <key> <v> Update config value");
@@ -323,7 +196,7 @@ const printHelp = () => {
323
196
  console.log(" agentbar help Show help\n");
324
197
  console.log("Config keys:");
325
198
  console.log(
326
- " siteUrl, apiBase, depth, maxPages, siteKey, themeColor, position, title, subtitle,"
199
+ " siteUrl, apiBase, authToken, accountEmail, depth, maxPages, siteKey, themeColor, position, title, subtitle,"
327
200
  );
328
201
  console.log(
329
202
  " buttonLabel, fontFamily, panelBackground, textColor, mutedTextColor, borderColor,"
@@ -365,8 +238,60 @@ const ask = (rl, prompt, fallback) =>
365
238
  });
366
239
  });
367
240
 
241
+ const login = async (existingConfig = loadConfig()) => {
242
+ const config = { ...existingConfig };
243
+ const apiBase = (config.apiBase || DEFAULT_CONFIG.apiBase).replace(/\/$/, "");
244
+
245
+ const response = await fetch(`${apiBase}/api/auth/device/start`, {
246
+ method: "POST",
247
+ headers: { "Content-Type": "application/json" },
248
+ });
249
+ const data = await response.json().catch(() => ({}));
250
+ if (!response.ok) {
251
+ throw new Error(data?.error || "Failed to start login.");
252
+ }
253
+
254
+ console.log(`User code: ${data.userCode}`);
255
+ console.log(`Verification URL: ${data.verificationUrl}`);
256
+ if (!openBrowser(data.verificationUrl)) {
257
+ console.log("Open that URL in your browser to continue.");
258
+ }
259
+ console.log("Waiting for approval...\n");
260
+
261
+ const intervalMs = Math.max(1000, Number(data.interval || 2) * 1000);
262
+ const expiresAt = Number(data.expiresAt || 0);
263
+
264
+ while (!expiresAt || Date.now() < expiresAt) {
265
+ await sleep(intervalMs);
266
+ const pollResponse = await fetch(`${apiBase}/api/auth/device/poll`, {
267
+ method: "POST",
268
+ headers: { "Content-Type": "application/json" },
269
+ body: JSON.stringify({ deviceCode: data.deviceCode }),
270
+ });
271
+ const pollData = await pollResponse.json().catch(() => ({}));
272
+ if (!pollResponse.ok) {
273
+ throw new Error(pollData?.error || "Login polling failed.");
274
+ }
275
+ if (!pollData.approved) {
276
+ continue;
277
+ }
278
+ config.authToken = pollData.accessToken || "";
279
+ config.accountEmail = pollData.user?.email || "";
280
+ saveConfig(config);
281
+ console.log(`Logged in as ${config.accountEmail || "your account"}.`);
282
+ return config;
283
+ }
284
+
285
+ throw new Error("Login request expired before approval.");
286
+ };
287
+
368
288
  const init = async () => {
369
- const config = loadConfig();
289
+ let config = loadConfig();
290
+ if (!config.authToken) {
291
+ console.log("No saved login found. Opening the web login flow...\n");
292
+ config = await login(config);
293
+ }
294
+
370
295
  const rl = readline.createInterface({
371
296
  input: process.stdin,
372
297
  output: process.stdout,
@@ -384,9 +309,11 @@ const init = async () => {
384
309
  }
385
310
 
386
311
  saveConfig(config);
312
+ await syncConfig(config);
387
313
  console.log("\nSaved config to", configPath);
388
314
  console.log("\nEmbed snippet:\n");
389
315
  console.log(renderSnippet(config));
316
+ console.log(`\nDashboard: ${(config.apiBase || DEFAULT_CONFIG.apiBase).replace(/\/$/, "")}\n`);
390
317
  };
391
318
 
392
319
  const printStats = async () => {
@@ -399,8 +326,15 @@ const printStats = async () => {
399
326
  process.exit(1);
400
327
  }
401
328
 
329
+ if (!config.authToken) {
330
+ console.error("Missing auth token. Run `agentbar login` first.");
331
+ process.exit(1);
332
+ }
333
+
402
334
  try {
403
- const response = await fetch(`${apiBase}/api/status`);
335
+ const response = await fetch(`${apiBase}/api/status`, {
336
+ headers: authHeaders(config),
337
+ });
404
338
  if (!response.ok) {
405
339
  const data = await response.json().catch(() => ({}));
406
340
  throw new Error(data?.error || `Status request failed (${response.status})`);
@@ -410,7 +344,7 @@ const printStats = async () => {
410
344
  const matched = items.filter((item) => item.key === siteKey);
411
345
  if (!matched.length) {
412
346
  console.log("No indexed content found for", siteKey);
413
- console.log("Send a message in the widget to trigger ingest.");
347
+ console.log("Save the site config and run reindex from the console first.");
414
348
  return;
415
349
  }
416
350
  matched.forEach((item) => {
@@ -427,7 +361,7 @@ const printStats = async () => {
427
361
  }
428
362
  };
429
363
 
430
- const setValue = (key, value) => {
364
+ const setValue = async (key, value) => {
431
365
  if (!key || typeof value === "undefined") {
432
366
  console.error("Usage: agentbar set <key> <value>");
433
367
  process.exit(1);
@@ -448,7 +382,7 @@ const setValue = (key, value) => {
448
382
  process.exit(1);
449
383
  }
450
384
  config[key] = parsed;
451
- } else if (key === "offsetX" || key === "offsetY") {
385
+ } else if (key === "offsetX" || key === "offsetY" || key === "dragOffset") {
452
386
  const parsed = Number(value);
453
387
  if (!Number.isFinite(parsed)) {
454
388
  console.error(`${key} must be a number.`);
@@ -472,23 +406,20 @@ const setValue = (key, value) => {
472
406
  key === "autoScroll"
473
407
  ) {
474
408
  config[key] = value === "true" || value === true;
475
- } else if (key === "dragOffset") {
476
- const parsed = Number(value);
477
- if (!Number.isFinite(parsed)) {
478
- console.error(`${key} must be a number.`);
479
- process.exit(1);
480
- }
481
- config[key] = parsed;
482
409
  } else if (key === "suggestions") {
483
410
  config[key] = String(value)
484
411
  .split(/[|,]/)
485
412
  .map((item) => item.trim())
486
413
  .filter(Boolean);
414
+ } else if (key === "siteUrl") {
415
+ config[key] = normalizeUrl(value);
416
+ config.siteKey = resolveSiteKey(config);
487
417
  } else {
488
418
  config[key] = value;
489
419
  }
490
420
 
491
421
  saveConfig(config);
422
+ await syncConfig(config);
492
423
  console.log("Updated", key, "in", configPath);
493
424
  };
494
425
 
@@ -496,6 +427,9 @@ const main = async () => {
496
427
  const [command, arg1, arg2] = process.argv.slice(2);
497
428
 
498
429
  switch (command) {
430
+ case "login":
431
+ await login();
432
+ return;
499
433
  case "init":
500
434
  await init();
501
435
  return;
@@ -508,7 +442,7 @@ const main = async () => {
508
442
  await printStats();
509
443
  return;
510
444
  case "set":
511
- setValue(arg1, arg2);
445
+ await setValue(arg1, arg2);
512
446
  return;
513
447
  case "config":
514
448
  console.log(JSON.stringify(loadConfig(), null, 2));
@@ -525,4 +459,7 @@ const main = async () => {
525
459
  }
526
460
  };
527
461
 
528
- main();
462
+ main().catch((error) => {
463
+ console.error(error instanceof Error ? error.message : "Unknown error");
464
+ process.exit(1);
465
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arjun-shah/agentbar-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "description": "Agent Plugin Bar CLI helper.",
6
6
  "bin": {