@openbat/cli 0.2.1 → 0.2.3

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/dist/index.js CHANGED
@@ -163,7 +163,7 @@ function configPath() {
163
163
 
164
164
  // src/api-client.ts
165
165
  var import_node_url = require("url");
166
- var CLI_VERSION = "0.1.1";
166
+ var CLI_VERSION = "0.2.3";
167
167
  var KEY_REGEX = /ob_(?:live|read|admin|pat)_[0-9a-f]{32}/g;
168
168
  function redact(s) {
169
169
  return s.replace(KEY_REGEX, (k) => `${k.slice(0, 16)}\u2026<hidden>`);
@@ -213,7 +213,7 @@ var ApiClient = class {
213
213
  const msg = err instanceof Error ? err.message : String(err);
214
214
  throw new Error(`Request to ${redact(url)} failed: ${redact(msg)}`);
215
215
  }
216
- return parseResponse(res, url);
216
+ return parseResponse(res, url, "GET");
217
217
  }
218
218
  /** Issue a POST with a JSON body. */
219
219
  async post(path, body) {
@@ -275,12 +275,12 @@ mutate_fn = async function(method, path, body) {
275
275
  const msg = err instanceof Error ? err.message : String(err);
276
276
  throw new Error(`Request to ${redact(url)} failed: ${redact(msg)}`);
277
277
  }
278
- return parseResponse(res, url);
278
+ return parseResponse(res, url, method);
279
279
  };
280
- async function parseResponse(res, url) {
280
+ async function parseResponse(res, url, method) {
281
281
  if (res.status === 401) {
282
282
  throw new Error(
283
- "Unauthorized. The API key was rejected (invalid, wrong kind for this endpoint, expired, or revoked)."
283
+ "Unauthorized. The API key was rejected (invalid, wrong kind for this endpoint, expired, or revoked). Run `openbat login` to refresh credentials."
284
284
  );
285
285
  }
286
286
  if (res.status === 403) {
@@ -301,7 +301,7 @@ async function parseResponse(res, url) {
301
301
  } catch {
302
302
  }
303
303
  throw new Error(
304
- `GET ${redact(url)} \u2192 ${res.status} ${res.statusText}${errBody?.error ? `: ${redact(errBody.error)}` : ""}`
304
+ `${method} ${redact(url)} \u2192 ${res.status} ${res.statusText}${errBody?.error ? `: ${redact(errBody.error)}` : ""}`
305
305
  );
306
306
  }
307
307
  try {
@@ -388,7 +388,9 @@ function configCommand() {
388
388
  const cmd = new import_commander.Command("config").description(
389
389
  "Manage the CLI's ~/.openbatrc settings"
390
390
  );
391
- cmd.command("set-key").description("Store your Read API key in ~/.openbatrc").option("--from-stdin", "Read the key from stdin (default and recommended)", true).option(
391
+ cmd.command("set-key").description(
392
+ "Store your API key in ~/.openbatrc. Reads from stdin by default; use --value to pass inline (discouraged \u2014 leaks into shell history)."
393
+ ).option(
392
394
  "--value <key>",
393
395
  "Pass the key inline. Discouraged \u2014 leaks into shell history."
394
396
  ).action(async (opts) => {
@@ -404,13 +406,13 @@ function configCommand() {
404
406
  key = Buffer.concat(chunks).toString("utf8").trim();
405
407
  if (!key) {
406
408
  fatal(
407
- "No key on stdin. Try: echo 'ob_read_...' | openbat config set-key --from-stdin"
409
+ "No key on stdin. Try: echo 'ob_read_...' | openbat config set-key"
408
410
  );
409
411
  }
410
412
  }
411
413
  await setApiKey(key);
412
414
  process.stdout.write(
413
- `Saved Read key to ${configPath()} (mode 0600).
415
+ `Saved key to ${configPath()} (mode 0600).
414
416
  `
415
417
  );
416
418
  } catch (err) {
@@ -529,7 +531,9 @@ async function resolveChatbotId(api, chatbotFlag) {
529
531
  );
530
532
  const chatbots = list.chatbots ?? [];
531
533
  if (chatbots.length === 0) {
532
- fatal("This API key authorizes no chatbots.");
534
+ fatal(
535
+ "No chatbots yet. Run `openbat chatbots create --name <name>`, or ask an org owner to invite you."
536
+ );
533
537
  }
534
538
  if (chatbotFlag) {
535
539
  if (UUID_RE2.test(chatbotFlag)) {
@@ -714,18 +718,6 @@ function exportCommand() {
714
718
 
715
719
  // src/commands/auth.ts
716
720
  var import_commander3 = require("commander");
717
- async function client2(globals) {
718
- const cfg = await resolveConfig({
719
- apiKeyFlag: globals.apiKey ?? null,
720
- baseUrlFlag: globals.baseUrl ?? null
721
- });
722
- if (!cfg.apiKey) {
723
- fatal(
724
- "No API key configured. Run `openbat config set-key`, pass --api-key, or set OPENBAT_API_KEY."
725
- );
726
- }
727
- return new ApiClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl });
728
- }
729
721
  function detectKind(apiKey) {
730
722
  if (apiKey.startsWith("ob_read_")) return "read";
731
723
  if (apiKey.startsWith("ob_admin_")) return "admin";
@@ -777,17 +769,6 @@ function authCommand() {
777
769
  fatal(err instanceof Error ? err.message : String(err));
778
770
  }
779
771
  });
780
- cmd.command("audit-log").description("Show recent audit-log entries for the current user").option("--days <n>", "Look-back window in days", "7").action(async function() {
781
- try {
782
- process.stderr.write(
783
- "audit-log: endpoint not yet exposed via HTTP \u2014 query `select * from api_audit_log` in Supabase Studio for now.\n"
784
- );
785
- const _c = await client2(this.optsWithGlobals());
786
- void _c;
787
- } catch (err) {
788
- fatal(err instanceof Error ? err.message : String(err));
789
- }
790
- });
791
772
  return cmd;
792
773
  }
793
774
 
@@ -1074,7 +1055,15 @@ async function pickDefaultChatbot(token, baseUrl) {
1074
1055
  return;
1075
1056
  }
1076
1057
  if (chatbots.length === 0) {
1077
- process.stdout.write("\nNo chatbots reachable yet. Create one from the dashboard.\n");
1058
+ process.stdout.write(
1059
+ [
1060
+ "",
1061
+ "No chatbots yet. To get one:",
1062
+ " \u2022 Create one: openbat chatbots create --name <name>",
1063
+ " \u2022 Or ask an org owner to invite you, then run `openbat login` again",
1064
+ ""
1065
+ ].join("\n")
1066
+ );
1078
1067
  return;
1079
1068
  }
1080
1069
  if (chatbots.length === 1) {
@@ -1142,7 +1131,7 @@ function logoutCommand() {
1142
1131
  }
1143
1132
  } else {
1144
1133
  process.stderr.write(
1145
- "Note: read/admin keys can't self-revoke. Visit the dashboard to revoke; this command will just clear the local config.\n"
1134
+ "Note: read/admin keys can't self-revoke server-side. Revoke them in the dashboard if needed; clearing local config only.\n"
1146
1135
  );
1147
1136
  }
1148
1137
  await clearApiKey();
@@ -1155,7 +1144,7 @@ function logoutCommand() {
1155
1144
 
1156
1145
  // src/commands/org.ts
1157
1146
  var import_commander6 = require("commander");
1158
- async function client3(globals) {
1147
+ async function client2(globals) {
1159
1148
  const cfg = await resolveConfig({
1160
1149
  apiKeyFlag: globals.apiKey ?? null,
1161
1150
  baseUrlFlag: globals.baseUrl ?? null
@@ -1175,7 +1164,7 @@ function orgCommand() {
1175
1164
  cmd.command("list").description("List orgs the current PAT's user belongs to").action(async function() {
1176
1165
  try {
1177
1166
  const globals = this.optsWithGlobals();
1178
- const c = await client3(globals);
1167
+ const c = await client2(globals);
1179
1168
  const result = await c.get("/api/v1/orgs");
1180
1169
  emit(result.orgs, { json: !!globals.json });
1181
1170
  } catch (err) {
@@ -1185,7 +1174,7 @@ function orgCommand() {
1185
1174
  cmd.command("show").description("Show the active org (id, name, members)").action(async function() {
1186
1175
  try {
1187
1176
  const globals = this.optsWithGlobals();
1188
- const c = await client3(globals);
1177
+ const c = await client2(globals);
1189
1178
  const result = await c.get(
1190
1179
  "/api/v1/orgs/active"
1191
1180
  );
@@ -1200,7 +1189,7 @@ function orgCommand() {
1200
1189
  cmd.command("rename").description("Rename an org (owner only)").requiredOption("--id <orgId>", "Org id").requiredOption("--name <name>", "New name").action(async function(opts) {
1201
1190
  try {
1202
1191
  const globals = this.optsWithGlobals();
1203
- const c = await client3(globals);
1192
+ const c = await client2(globals);
1204
1193
  await c.patch(`/api/v1/orgs/${opts.id}`, { name: opts.name });
1205
1194
  emit({ ok: true, renamed: opts.id }, { json: !!globals.json });
1206
1195
  } catch (err) {
@@ -1211,7 +1200,7 @@ function orgCommand() {
1211
1200
  members.command("list").requiredOption("--id <orgId>", "Org id").action(async function(opts) {
1212
1201
  try {
1213
1202
  const globals = this.optsWithGlobals();
1214
- const c = await client3(globals);
1203
+ const c = await client2(globals);
1215
1204
  const result = await c.get(
1216
1205
  `/api/v1/orgs/${opts.id}/members`
1217
1206
  );
@@ -1223,7 +1212,7 @@ function orgCommand() {
1223
1212
  members.command("invite").requiredOption("--id <orgId>", "Org id").requiredOption("--email <email>", "Invitee email").option("--role <role>", "member | admin", "member").action(async function(opts) {
1224
1213
  try {
1225
1214
  const globals = this.optsWithGlobals();
1226
- const c = await client3(globals);
1215
+ const c = await client2(globals);
1227
1216
  const result = await c.post(
1228
1217
  `/api/v1/orgs/${opts.id}/members`,
1229
1218
  { email: opts.email, role: opts.role }
@@ -1241,7 +1230,7 @@ function orgCommand() {
1241
1230
  members.command("set-role").requiredOption("--id <orgId>", "Org id").requiredOption("--member <memberId>", "Member id").requiredOption("--role <role>", "admin | member").action(async function(opts) {
1242
1231
  try {
1243
1232
  const globals = this.optsWithGlobals();
1244
- const c = await client3(globals);
1233
+ const c = await client2(globals);
1245
1234
  await c.patch(`/api/v1/orgs/${opts.id}/members/${opts.member}`, {
1246
1235
  role: opts.role
1247
1236
  });
@@ -1253,7 +1242,7 @@ function orgCommand() {
1253
1242
  members.command("remove").requiredOption("--id <orgId>", "Org id").requiredOption("--member <memberId>", "Member id").action(async function(opts) {
1254
1243
  try {
1255
1244
  const globals = this.optsWithGlobals();
1256
- const c = await client3(globals);
1245
+ const c = await client2(globals);
1257
1246
  await c.delete(`/api/v1/orgs/${opts.id}/members/${opts.member}`);
1258
1247
  emit({ ok: true }, { json: !!globals.json });
1259
1248
  } catch (err) {
@@ -1264,7 +1253,7 @@ function orgCommand() {
1264
1253
  invitations.command("list").requiredOption("--id <orgId>", "Org id").action(async function(opts) {
1265
1254
  try {
1266
1255
  const globals = this.optsWithGlobals();
1267
- const c = await client3(globals);
1256
+ const c = await client2(globals);
1268
1257
  const result = await c.get(
1269
1258
  `/api/v1/orgs/${opts.id}/invitations`
1270
1259
  );
@@ -1278,7 +1267,7 @@ function orgCommand() {
1278
1267
 
1279
1268
  // src/commands/write.ts
1280
1269
  var import_commander7 = require("commander");
1281
- async function client4(globals) {
1270
+ async function client3(globals) {
1282
1271
  const cfg = await resolveConfig({
1283
1272
  apiKeyFlag: globals.apiKey ?? null,
1284
1273
  baseUrlFlag: globals.baseUrl ?? null
@@ -1288,6 +1277,15 @@ async function client4(globals) {
1288
1277
  }
1289
1278
  return new ApiClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl });
1290
1279
  }
1280
+ function requireChatbotId(cmd) {
1281
+ const globals = cmd.optsWithGlobals();
1282
+ if (!globals.chatbot) {
1283
+ fatal(
1284
+ "--chatbot <id> is required. Pass it inline or set a default with `openbat use <id>`."
1285
+ );
1286
+ }
1287
+ return globals.chatbot;
1288
+ }
1291
1289
  function surfacePlaintext(plaintext, label) {
1292
1290
  process.stderr.write(
1293
1291
  `
@@ -1307,7 +1305,7 @@ function chatbotsCommand() {
1307
1305
  cmd.command("list").description("List every chatbot the credential can reach").action(async function() {
1308
1306
  try {
1309
1307
  const globals = this.optsWithGlobals();
1310
- const c = await client4(globals);
1308
+ const c = await client3(globals);
1311
1309
  const result = await c.get(
1312
1310
  "/api/v1/chatbots"
1313
1311
  );
@@ -1319,7 +1317,7 @@ function chatbotsCommand() {
1319
1317
  cmd.command("create").description("Create a new chatbot (PAT scope required)").requiredOption("--name <name>", "Chatbot name").option("--website <url>").option("--docs-url <url>").option("--mcp-url <url>").option("--language <code>", "Primary language code (default: en)", "en").action(async function(opts) {
1320
1318
  try {
1321
1319
  const globals = this.optsWithGlobals();
1322
- const c = await client4(globals);
1320
+ const c = await client3(globals);
1323
1321
  const result = await c.post("/api/v1/chatbots", {
1324
1322
  name: opts.name,
1325
1323
  websiteUrl: opts.website,
@@ -1339,7 +1337,7 @@ function chatbotsCommand() {
1339
1337
  cmd.command("delete <chatbotId>").description("Delete a chatbot (irreversible \u2014 cascade-deletes everything)").action(async function(chatbotId) {
1340
1338
  try {
1341
1339
  const globals = this.optsWithGlobals();
1342
- const c = await client4(globals);
1340
+ const c = await client3(globals);
1343
1341
  await c.delete(`/api/v1/chatbots/${chatbotId}`);
1344
1342
  emit({ ok: true, deleted: chatbotId }, { json: !!globals.json });
1345
1343
  } catch (err) {
@@ -1350,23 +1348,25 @@ function chatbotsCommand() {
1350
1348
  }
1351
1349
  function webhooksCommand() {
1352
1350
  const cmd = new import_commander7.Command("webhooks").description("Manage webhooks for a chatbot");
1353
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1351
+ cmd.command("list").action(async function() {
1354
1352
  try {
1353
+ const chatbotId = requireChatbotId(this);
1355
1354
  const globals = this.optsWithGlobals();
1356
- const c = await client4(globals);
1355
+ const c = await client3(globals);
1357
1356
  const result = await c.get(
1358
- `/api/v1/chatbots/${opts.chatbot}/webhooks`
1357
+ `/api/v1/chatbots/${chatbotId}/webhooks`
1359
1358
  );
1360
1359
  emit(result.webhooks, { json: !!globals.json });
1361
1360
  } catch (err) {
1362
1361
  fatal(err instanceof Error ? err.message : String(err));
1363
1362
  }
1364
1363
  });
1365
- cmd.command("create").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--name <name>").requiredOption("--url <url>").option("--type <type>", "discord | slack | custom", "custom").action(async function(opts) {
1364
+ cmd.command("create").requiredOption("--name <name>").requiredOption("--url <url>").option("--type <type>", "discord | slack | custom", "custom").action(async function(opts) {
1366
1365
  try {
1366
+ const chatbotId = requireChatbotId(this);
1367
1367
  const globals = this.optsWithGlobals();
1368
- const c = await client4(globals);
1369
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/webhooks`, {
1368
+ const c = await client3(globals);
1369
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/webhooks`, {
1370
1370
  name: opts.name,
1371
1371
  url: opts.url,
1372
1372
  type: opts.type
@@ -1377,11 +1377,12 @@ function webhooksCommand() {
1377
1377
  fatal(err instanceof Error ? err.message : String(err));
1378
1378
  }
1379
1379
  });
1380
- cmd.command("delete").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--webhook <id>", "Webhook id").action(async function(opts) {
1380
+ cmd.command("delete").requiredOption("--webhook <id>", "Webhook id").action(async function(opts) {
1381
1381
  try {
1382
+ const chatbotId = requireChatbotId(this);
1382
1383
  const globals = this.optsWithGlobals();
1383
- const c = await client4(globals);
1384
- await c.delete(`/api/v1/chatbots/${opts.chatbot}/webhooks/${opts.webhook}`);
1384
+ const c = await client3(globals);
1385
+ await c.delete(`/api/v1/chatbots/${chatbotId}/webhooks/${opts.webhook}`);
1385
1386
  emit({ ok: true, deleted: opts.webhook }, { json: !!globals.json });
1386
1387
  } catch (err) {
1387
1388
  fatal(err instanceof Error ? err.message : String(err));
@@ -1394,12 +1395,13 @@ function settingsCommand() {
1394
1395
  "Manage chatbot settings + per-chatbot keys"
1395
1396
  );
1396
1397
  const keys = cmd.command("keys").description("Manage API keys for a chatbot");
1397
- keys.command("rotate-ingest").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1398
+ keys.command("rotate-ingest").action(async function() {
1398
1399
  try {
1400
+ const chatbotId = requireChatbotId(this);
1399
1401
  const globals = this.optsWithGlobals();
1400
- const c = await client4(globals);
1402
+ const c = await client3(globals);
1401
1403
  const result = await c.post(
1402
- `/api/v1/chatbots/${opts.chatbot}/keys/ingest/rotate`,
1404
+ `/api/v1/chatbots/${chatbotId}/keys/ingest/rotate`,
1403
1405
  {}
1404
1406
  );
1405
1407
  surfacePlaintext(result.plaintext, "New ingest key (ob_live_*)");
@@ -1408,12 +1410,13 @@ function settingsCommand() {
1408
1410
  fatal(err instanceof Error ? err.message : String(err));
1409
1411
  }
1410
1412
  });
1411
- keys.command("generate-read").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1413
+ keys.command("generate-read").action(async function() {
1412
1414
  try {
1415
+ const chatbotId = requireChatbotId(this);
1413
1416
  const globals = this.optsWithGlobals();
1414
- const c = await client4(globals);
1417
+ const c = await client3(globals);
1415
1418
  const result = await c.post(
1416
- `/api/v1/chatbots/${opts.chatbot}/keys/read`,
1419
+ `/api/v1/chatbots/${chatbotId}/keys/read`,
1417
1420
  {}
1418
1421
  );
1419
1422
  surfacePlaintext(result.plaintext, "New read key (ob_read_*)");
@@ -1422,11 +1425,12 @@ function settingsCommand() {
1422
1425
  fatal(err instanceof Error ? err.message : String(err));
1423
1426
  }
1424
1427
  });
1425
- keys.command("generate-admin").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--name <name>", "Human-friendly name (e.g. 'CI key')").option("--expires-in-days <n>", "Auto-expire after N days").action(async function(opts) {
1428
+ keys.command("generate-admin").requiredOption("--name <name>", "Human-friendly name (e.g. 'CI key')").option("--expires-in-days <n>", "Auto-expire after N days").action(async function(opts) {
1426
1429
  try {
1430
+ const chatbotId = requireChatbotId(this);
1427
1431
  const globals = this.optsWithGlobals();
1428
- const c = await client4(globals);
1429
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/admin-keys`, {
1432
+ const c = await client3(globals);
1433
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/admin-keys`, {
1430
1434
  name: opts.name,
1431
1435
  expiresInDays: opts.expiresInDays ? Number(opts.expiresInDays) : void 0
1432
1436
  });
@@ -1436,30 +1440,33 @@ function settingsCommand() {
1436
1440
  fatal(err instanceof Error ? err.message : String(err));
1437
1441
  }
1438
1442
  });
1439
- keys.command("list-admin").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1443
+ keys.command("list-admin").action(async function() {
1440
1444
  try {
1445
+ const chatbotId = requireChatbotId(this);
1441
1446
  const globals = this.optsWithGlobals();
1442
- const c = await client4(globals);
1447
+ const c = await client3(globals);
1443
1448
  const result = await c.get(
1444
- `/api/v1/chatbots/${opts.chatbot}/admin-keys`
1449
+ `/api/v1/chatbots/${chatbotId}/admin-keys`
1445
1450
  );
1446
1451
  emit(result.keys, { json: !!globals.json });
1447
1452
  } catch (err) {
1448
1453
  fatal(err instanceof Error ? err.message : String(err));
1449
1454
  }
1450
1455
  });
1451
- keys.command("revoke-admin").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--key <keyId>", "Admin key id").action(async function(opts) {
1456
+ keys.command("revoke-admin").requiredOption("--key <keyId>", "Admin key id").action(async function(opts) {
1452
1457
  try {
1458
+ const chatbotId = requireChatbotId(this);
1453
1459
  const globals = this.optsWithGlobals();
1454
- const c = await client4(globals);
1455
- await c.delete(`/api/v1/chatbots/${opts.chatbot}/admin-keys/${opts.key}`);
1460
+ const c = await client3(globals);
1461
+ await c.delete(`/api/v1/chatbots/${chatbotId}/admin-keys/${opts.key}`);
1456
1462
  emit({ ok: true, revoked: opts.key }, { json: !!globals.json });
1457
1463
  } catch (err) {
1458
1464
  fatal(err instanceof Error ? err.message : String(err));
1459
1465
  }
1460
1466
  });
1461
- cmd.command("update").description("Patch a chatbot's settings JSONB").requiredOption("--chatbot <id>", "Chatbot id").option("--description <text>").option("--website-url <url>").option("--language <code>").action(async function(opts) {
1467
+ cmd.command("update").description("Patch a chatbot's settings JSONB").option("--description <text>").option("--website-url <url>").option("--language <code>").action(async function(opts) {
1462
1468
  try {
1469
+ const chatbotId = requireChatbotId(this);
1463
1470
  const settings = {};
1464
1471
  if (opts.description) settings.description = opts.description;
1465
1472
  if (opts.websiteUrl) settings.website_url = opts.websiteUrl;
@@ -1468,8 +1475,8 @@ function settingsCommand() {
1468
1475
  fatal("Provide at least one setting to update.");
1469
1476
  }
1470
1477
  const globals = this.optsWithGlobals();
1471
- const c = await client4(globals);
1472
- await c.patch(`/api/v1/chatbots/${opts.chatbot}/settings`, { settings });
1478
+ const c = await client3(globals);
1479
+ await c.patch(`/api/v1/chatbots/${chatbotId}/settings`, { settings });
1473
1480
  emit({ ok: true }, { json: !!globals.json });
1474
1481
  } catch (err) {
1475
1482
  fatal(err instanceof Error ? err.message : String(err));
@@ -1479,19 +1486,20 @@ function settingsCommand() {
1479
1486
  }
1480
1487
  function workflowsCommand() {
1481
1488
  const cmd = new import_commander7.Command("workflows").description("Manage workflows");
1482
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1489
+ cmd.command("list").action(async function() {
1483
1490
  try {
1491
+ const chatbotId = requireChatbotId(this);
1484
1492
  const globals = this.optsWithGlobals();
1485
- const c = await client4(globals);
1493
+ const c = await client3(globals);
1486
1494
  const result = await c.get(
1487
- `/api/v1/chatbots/${opts.chatbot}/workflows`
1495
+ `/api/v1/chatbots/${chatbotId}/workflows`
1488
1496
  );
1489
1497
  emit(result.workflows, { json: !!globals.json });
1490
1498
  } catch (err) {
1491
1499
  fatal(err instanceof Error ? err.message : String(err));
1492
1500
  }
1493
1501
  });
1494
- cmd.command("create").description("Create a workflow from a built-in template").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--name <name>").requiredOption(
1502
+ cmd.command("create").description("Create a workflow from a built-in template").requiredOption("--name <name>").requiredOption(
1495
1503
  "--template <name>",
1496
1504
  "flag-to-webhook | outcome-to-webhook | sentiment-drop-to-webhook"
1497
1505
  ).requiredOption(
@@ -1499,9 +1507,10 @@ function workflowsCommand() {
1499
1507
  "Flag value / outcome value / sentiment threshold"
1500
1508
  ).requiredOption("--webhook <id>", "Webhook id to fire").option("--message <tpl>", "Message template (supports {{user.id}}, etc.)").action(async function(opts) {
1501
1509
  try {
1510
+ const chatbotId = requireChatbotId(this);
1502
1511
  const globals = this.optsWithGlobals();
1503
- const c = await client4(globals);
1504
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/workflows`, {
1512
+ const c = await client3(globals);
1513
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/workflows`, {
1505
1514
  name: opts.name,
1506
1515
  template: opts.template,
1507
1516
  triggerValue: opts.triggerValue,
@@ -1517,23 +1526,25 @@ function workflowsCommand() {
1517
1526
  }
1518
1527
  function reportsCommand() {
1519
1528
  const cmd = new import_commander7.Command("reports").description("Manage AI reports");
1520
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1529
+ cmd.command("list").action(async function() {
1521
1530
  try {
1531
+ const chatbotId = requireChatbotId(this);
1522
1532
  const globals = this.optsWithGlobals();
1523
- const c = await client4(globals);
1533
+ const c = await client3(globals);
1524
1534
  const result = await c.get(
1525
- `/api/v1/chatbots/${opts.chatbot}/reports`
1535
+ `/api/v1/chatbots/${chatbotId}/reports`
1526
1536
  );
1527
1537
  emit(result.reports, { json: !!globals.json });
1528
1538
  } catch (err) {
1529
1539
  fatal(err instanceof Error ? err.message : String(err));
1530
1540
  }
1531
1541
  });
1532
- cmd.command("create").description("Create a new AI report; returns the org-private dashboard URL").requiredOption("--chatbot <id>", "Chatbot id").option("--name <name>", "Report name", "Untitled Report").action(async function(opts) {
1542
+ cmd.command("create").description("Create a new AI report; returns the org-private dashboard URL").option("--name <name>", "Report name", "Untitled Report").action(async function(opts) {
1533
1543
  try {
1544
+ const chatbotId = requireChatbotId(this);
1534
1545
  const globals = this.optsWithGlobals();
1535
- const c = await client4(globals);
1536
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/reports`, { name: opts.name });
1546
+ const c = await client3(globals);
1547
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/reports`, { name: opts.name });
1537
1548
  process.stderr.write(
1538
1549
  `
1539
1550
  Created report. View it (org members only):
@@ -1553,30 +1564,32 @@ Created report. View it (org members only):
1553
1564
  }
1554
1565
  function analysisCommand() {
1555
1566
  const cmd = new import_commander7.Command("analysis").description("Manage analysis definitions");
1556
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").option("--type <t>", "intent | flag | assistant_outcome | assistant_issue").option("--pending", "Include pending suggestions").action(async function(opts) {
1567
+ cmd.command("list").option("--type <t>", "intent | flag | assistant_outcome | assistant_issue").option("--pending", "Include pending suggestions").action(async function(opts) {
1557
1568
  try {
1569
+ const chatbotId = requireChatbotId(this);
1558
1570
  const globals = this.optsWithGlobals();
1559
- const c = await client4(globals);
1571
+ const c = await client3(globals);
1560
1572
  const qs = new URLSearchParams();
1561
1573
  if (opts.type) qs.set("type", opts.type);
1562
1574
  if (opts.pending) qs.set("pending", "true");
1563
1575
  const result = await c.get(
1564
- `/api/v1/chatbots/${opts.chatbot}/analysis-definitions${qs.size ? `?${qs}` : ""}`
1576
+ `/api/v1/chatbots/${chatbotId}/analysis-definitions${qs.size ? `?${qs}` : ""}`
1565
1577
  );
1566
1578
  emit(result.definitions, { json: !!globals.json });
1567
1579
  } catch (err) {
1568
1580
  fatal(err instanceof Error ? err.message : String(err));
1569
1581
  }
1570
1582
  });
1571
- cmd.command("add").requiredOption("--chatbot <id>", "Chatbot id").requiredOption(
1583
+ cmd.command("add").requiredOption(
1572
1584
  "--type <t>",
1573
1585
  "intent | flag | assistant_outcome | assistant_issue"
1574
1586
  ).requiredOption("--name <slug>", "snake_case slug").requiredOption("--display-name <text>").requiredOption("--description <text>").action(async function(opts) {
1575
1587
  try {
1588
+ const chatbotId = requireChatbotId(this);
1576
1589
  const globals = this.optsWithGlobals();
1577
- const c = await client4(globals);
1590
+ const c = await client3(globals);
1578
1591
  const result = await c.post(
1579
- `/api/v1/chatbots/${opts.chatbot}/analysis-definitions`,
1592
+ `/api/v1/chatbots/${chatbotId}/analysis-definitions`,
1580
1593
  {
1581
1594
  analysisType: opts.type,
1582
1595
  name: opts.name,
@@ -1595,10 +1608,11 @@ function usersCommand() {
1595
1608
  const cmd = new import_commander7.Command("users").description(
1596
1609
  "List external users (chatbot customers) with health metrics"
1597
1610
  );
1598
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").option("--from <iso>").option("--to <iso>").option("--days <n>", "Convenience: last N days").option("--search <q>").option("--limit <n>", "Page size", "20").action(async function(opts) {
1611
+ cmd.command("list").option("--from <iso>").option("--to <iso>").option("--days <n>", "Convenience: last N days").option("--search <q>").option("--limit <n>", "Page size", "20").action(async function(opts) {
1599
1612
  try {
1613
+ const chatbotId = requireChatbotId(this);
1600
1614
  const globals = this.optsWithGlobals();
1601
- const c = await client4(globals);
1615
+ const c = await client3(globals);
1602
1616
  const qs = new URLSearchParams();
1603
1617
  let from = opts.from;
1604
1618
  if (!from && opts.days) {
@@ -1611,7 +1625,7 @@ function usersCommand() {
1611
1625
  if (opts.search) qs.set("search", opts.search);
1612
1626
  qs.set("limit", opts.limit);
1613
1627
  const result = await c.get(
1614
- `/api/v1/chatbots/${opts.chatbot}/external-users?${qs}`
1628
+ `/api/v1/chatbots/${chatbotId}/external-users?${qs}`
1615
1629
  );
1616
1630
  emit(result.users, { json: !!globals.json });
1617
1631
  process.stderr.write(`
@@ -1629,7 +1643,7 @@ function sdkCommand() {
1629
1643
  );
1630
1644
  cmd.command("install-instructions").description("Print markdown the calling agent can follow").option(
1631
1645
  "--framework <name>",
1632
- "next | node | vercel-ai-sdk (default: next)",
1646
+ "next | vercel-ai-sdk (default: next)",
1633
1647
  "next"
1634
1648
  ).option("--chatbot <id>", "Chatbot id (for the example snippet)").action(async function(opts) {
1635
1649
  const chatbotId = opts.chatbot ?? "<chatbotId>";
@@ -1686,27 +1700,43 @@ function sdkCommand() {
1686
1700
  const out = opts.framework === "vercel-ai-sdk" ? snippetWrapper : snippetNext;
1687
1701
  process.stdout.write(out);
1688
1702
  });
1689
- cmd.command("verify").description("Check whether any event has arrived for the chatbot yet").requiredOption("--chatbot <id>", "Chatbot id").option("--timeout <n>", "Seconds to wait (default: 60)", "60").action(async function(opts) {
1703
+ cmd.command("verify").description("Check whether any event has arrived for the chatbot yet").option("--timeout <n>", "Seconds to wait (default: 60)", "60").action(async function(opts) {
1690
1704
  try {
1691
1705
  const globals = this.optsWithGlobals();
1692
- const c = await client4(globals);
1693
- const deadline = Date.now() + Number(opts.timeout) * 1e3;
1706
+ const chatbotId = globals.chatbot;
1707
+ if (!chatbotId) {
1708
+ fatal("--chatbot <id> is required (also accepts the persisted active chatbot).");
1709
+ }
1710
+ const c = await client3(globals);
1711
+ const timeoutSec = Number(opts.timeout);
1712
+ const deadline = Date.now() + timeoutSec * 1e3;
1713
+ const params = new URLSearchParams({
1714
+ chatbotId,
1715
+ limit: "1"
1716
+ });
1717
+ process.stderr.write(
1718
+ `Waiting for first event on chatbot ${chatbotId} (timeout ${timeoutSec}s)\u2026
1719
+ `
1720
+ );
1721
+ let lastTick = Date.now();
1694
1722
  while (Date.now() < deadline) {
1695
- try {
1696
- const result = await c.get(`/api/v1/conversations?limit=1`);
1697
- if (result.total > 0) {
1698
- process.stderr.write(
1699
- `\u2713 First event detected. ${result.total} conversation(s) ingested.
1723
+ const result = await c.get(`/api/v1/conversations?${params}`);
1724
+ if (result.total > 0) {
1725
+ process.stderr.write(
1726
+ `First event detected. ${result.total} conversation(s) ingested.
1700
1727
  `
1701
- );
1702
- return;
1703
- }
1704
- } catch {
1728
+ );
1729
+ return;
1730
+ }
1731
+ if (Date.now() - lastTick >= 5e3) {
1732
+ process.stderr.write(".");
1733
+ lastTick = Date.now();
1705
1734
  }
1706
1735
  await new Promise((r) => setTimeout(r, 2e3));
1707
1736
  }
1708
1737
  process.stderr.write(
1709
- `Timed out after ${opts.timeout}s \u2014 no events yet. Confirm OPENBAT_API_KEY and that recordMessages is being called.
1738
+ `
1739
+ Timed out after ${timeoutSec}s \u2014 no events yet. Confirm OPENBAT_API_KEY and that recordMessages is being called.
1710
1740
  `
1711
1741
  );
1712
1742
  process.exit(2);
@@ -1721,7 +1751,7 @@ function sdkCommand() {
1721
1751
  var program = new import_commander8.Command();
1722
1752
  program.name("openbat").description(
1723
1753
  "Query OpenBat chatbot data \u2014 conversations, sentiment, analytics, exports."
1724
- ).version("0.2.1").option("--api-key <key>", "Override the stored Read API key (footgun \u2014 leaks into shell history)").option(
1754
+ ).version("0.2.3").option("--api-key <key>", "Override the stored Read API key (footgun \u2014 leaks into shell history)").option(
1725
1755
  "--base-url <url>",
1726
1756
  "Override the OpenBat API base URL (defaults to ~/.openbatrc or https://openbat.dev)"
1727
1757
  ).option(