@arjun-shah/agentbar-cli 0.1.10 → 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 (2) hide show
  1. package/bin/agentbar.js +132 -34
  2. package/package.json +1 -1
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,
@@ -90,26 +93,6 @@ const saveConfig = (config) => {
90
93
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
91
94
  };
92
95
 
93
- const syncConfig = async (config) => {
94
- const apiBase = (config.apiBase || DEFAULT_CONFIG.apiBase).replace(/\/$/, "");
95
- const siteKey = resolveSiteKey(config);
96
- if (!siteKey) {
97
- return;
98
- }
99
- try {
100
- await fetch(`${apiBase}/api/config`, {
101
- method: "POST",
102
- headers: { "Content-Type": "application/json" },
103
- body: JSON.stringify({
104
- siteKey,
105
- config: { ...config, siteKey },
106
- }),
107
- });
108
- } catch (_error) {
109
- console.warn("Could not sync settings to the hosted dashboard.");
110
- }
111
- };
112
-
113
96
  const normalizeUrl = (value) => {
114
97
  if (!value || typeof value !== "string") {
115
98
  return "";
@@ -139,15 +122,72 @@ const resolveSiteKey = (config) => {
139
122
  }
140
123
  };
141
124
 
125
+ const authHeaders = (config) =>
126
+ config.authToken
127
+ ? {
128
+ Authorization: `Bearer ${config.authToken}`,
129
+ }
130
+ : {};
131
+
142
132
  const renderSnippet = (config) => {
143
133
  const apiBase = (config.apiBase || DEFAULT_CONFIG.apiBase).replace(/\/$/, "");
144
134
  const siteKey = resolveSiteKey(config) || "your-site-key";
145
135
  return `<script src="${apiBase}/agentbar.js" data-site-key="${siteKey}"></script>`;
146
136
  };
147
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;
154
+ }
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;
165
+ }
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.");
184
+ }
185
+ };
186
+
148
187
  const printHelp = () => {
149
188
  console.log("Agent Plugin Bar CLI\n");
150
189
  console.log("Commands:");
190
+ console.log(" agentbar login Open the web login flow and save an auth token");
151
191
  console.log(" agentbar init Interactive setup and snippet output");
152
192
  console.log(" agentbar snippet Print current embed snippet");
153
193
  console.log(" agentbar set <key> <v> Update config value");
@@ -156,7 +196,7 @@ const printHelp = () => {
156
196
  console.log(" agentbar help Show help\n");
157
197
  console.log("Config keys:");
158
198
  console.log(
159
- " siteUrl, apiBase, depth, maxPages, siteKey, themeColor, position, title, subtitle,"
199
+ " siteUrl, apiBase, authToken, accountEmail, depth, maxPages, siteKey, themeColor, position, title, subtitle,"
160
200
  );
161
201
  console.log(
162
202
  " buttonLabel, fontFamily, panelBackground, textColor, mutedTextColor, borderColor,"
@@ -198,8 +238,60 @@ const ask = (rl, prompt, fallback) =>
198
238
  });
199
239
  });
200
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
+
201
288
  const init = async () => {
202
- 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
+
203
295
  const rl = readline.createInterface({
204
296
  input: process.stdin,
205
297
  output: process.stdout,
@@ -234,8 +326,15 @@ const printStats = async () => {
234
326
  process.exit(1);
235
327
  }
236
328
 
329
+ if (!config.authToken) {
330
+ console.error("Missing auth token. Run `agentbar login` first.");
331
+ process.exit(1);
332
+ }
333
+
237
334
  try {
238
- const response = await fetch(`${apiBase}/api/status`);
335
+ const response = await fetch(`${apiBase}/api/status`, {
336
+ headers: authHeaders(config),
337
+ });
239
338
  if (!response.ok) {
240
339
  const data = await response.json().catch(() => ({}));
241
340
  throw new Error(data?.error || `Status request failed (${response.status})`);
@@ -245,7 +344,7 @@ const printStats = async () => {
245
344
  const matched = items.filter((item) => item.key === siteKey);
246
345
  if (!matched.length) {
247
346
  console.log("No indexed content found for", siteKey);
248
- 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.");
249
348
  return;
250
349
  }
251
350
  matched.forEach((item) => {
@@ -283,7 +382,7 @@ const setValue = async (key, value) => {
283
382
  process.exit(1);
284
383
  }
285
384
  config[key] = parsed;
286
- } else if (key === "offsetX" || key === "offsetY") {
385
+ } else if (key === "offsetX" || key === "offsetY" || key === "dragOffset") {
287
386
  const parsed = Number(value);
288
387
  if (!Number.isFinite(parsed)) {
289
388
  console.error(`${key} must be a number.`);
@@ -307,13 +406,6 @@ const setValue = async (key, value) => {
307
406
  key === "autoScroll"
308
407
  ) {
309
408
  config[key] = value === "true" || value === true;
310
- } else if (key === "dragOffset") {
311
- const parsed = Number(value);
312
- if (!Number.isFinite(parsed)) {
313
- console.error(`${key} must be a number.`);
314
- process.exit(1);
315
- }
316
- config[key] = parsed;
317
409
  } else if (key === "suggestions") {
318
410
  config[key] = String(value)
319
411
  .split(/[|,]/)
@@ -335,6 +427,9 @@ const main = async () => {
335
427
  const [command, arg1, arg2] = process.argv.slice(2);
336
428
 
337
429
  switch (command) {
430
+ case "login":
431
+ await login();
432
+ return;
338
433
  case "init":
339
434
  await init();
340
435
  return;
@@ -364,4 +459,7 @@ const main = async () => {
364
459
  }
365
460
  };
366
461
 
367
- 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.10",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "description": "Agent Plugin Bar CLI helper.",
6
6
  "bin": {