@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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ApiClient
4
- } from "./chunk-NYKJTHHK.mjs";
4
+ } from "./chunk-HBSCN3HV.mjs";
5
5
 
6
6
  // src/index.ts
7
7
  import { Command as Command8 } from "commander";
@@ -210,7 +210,9 @@ function configCommand() {
210
210
  const cmd = new Command("config").description(
211
211
  "Manage the CLI's ~/.openbatrc settings"
212
212
  );
213
- 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(
213
+ cmd.command("set-key").description(
214
+ "Store your API key in ~/.openbatrc. Reads from stdin by default; use --value to pass inline (discouraged \u2014 leaks into shell history)."
215
+ ).option(
214
216
  "--value <key>",
215
217
  "Pass the key inline. Discouraged \u2014 leaks into shell history."
216
218
  ).action(async (opts) => {
@@ -226,13 +228,13 @@ function configCommand() {
226
228
  key = Buffer.concat(chunks).toString("utf8").trim();
227
229
  if (!key) {
228
230
  fatal(
229
- "No key on stdin. Try: echo 'ob_read_...' | openbat config set-key --from-stdin"
231
+ "No key on stdin. Try: echo 'ob_read_...' | openbat config set-key"
230
232
  );
231
233
  }
232
234
  }
233
235
  await setApiKey(key);
234
236
  process.stdout.write(
235
- `Saved Read key to ${configPath()} (mode 0600).
237
+ `Saved key to ${configPath()} (mode 0600).
236
238
  `
237
239
  );
238
240
  } catch (err) {
@@ -351,7 +353,9 @@ async function resolveChatbotId(api, chatbotFlag) {
351
353
  );
352
354
  const chatbots = list.chatbots ?? [];
353
355
  if (chatbots.length === 0) {
354
- fatal("This API key authorizes no chatbots.");
356
+ fatal(
357
+ "No chatbots yet. Run `openbat chatbots create --name <name>`, or ask an org owner to invite you."
358
+ );
355
359
  }
356
360
  if (chatbotFlag) {
357
361
  if (UUID_RE2.test(chatbotFlag)) {
@@ -536,18 +540,6 @@ function exportCommand() {
536
540
 
537
541
  // src/commands/auth.ts
538
542
  import { Command as Command3 } from "commander";
539
- async function client2(globals) {
540
- const cfg = await resolveConfig({
541
- apiKeyFlag: globals.apiKey ?? null,
542
- baseUrlFlag: globals.baseUrl ?? null
543
- });
544
- if (!cfg.apiKey) {
545
- fatal(
546
- "No API key configured. Run `openbat config set-key`, pass --api-key, or set OPENBAT_API_KEY."
547
- );
548
- }
549
- return new ApiClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl });
550
- }
551
543
  function detectKind(apiKey) {
552
544
  if (apiKey.startsWith("ob_read_")) return "read";
553
545
  if (apiKey.startsWith("ob_admin_")) return "admin";
@@ -599,17 +591,6 @@ function authCommand() {
599
591
  fatal(err instanceof Error ? err.message : String(err));
600
592
  }
601
593
  });
602
- 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() {
603
- try {
604
- process.stderr.write(
605
- "audit-log: endpoint not yet exposed via HTTP \u2014 query `select * from api_audit_log` in Supabase Studio for now.\n"
606
- );
607
- const _c = await client2(this.optsWithGlobals());
608
- void _c;
609
- } catch (err) {
610
- fatal(err instanceof Error ? err.message : String(err));
611
- }
612
- });
613
594
  return cmd;
614
595
  }
615
596
 
@@ -896,7 +877,15 @@ async function pickDefaultChatbot(token, baseUrl) {
896
877
  return;
897
878
  }
898
879
  if (chatbots.length === 0) {
899
- process.stdout.write("\nNo chatbots reachable yet. Create one from the dashboard.\n");
880
+ process.stdout.write(
881
+ [
882
+ "",
883
+ "No chatbots yet. To get one:",
884
+ " \u2022 Create one: openbat chatbots create --name <name>",
885
+ " \u2022 Or ask an org owner to invite you, then run `openbat login` again",
886
+ ""
887
+ ].join("\n")
888
+ );
900
889
  return;
901
890
  }
902
891
  if (chatbots.length === 1) {
@@ -964,7 +953,7 @@ function logoutCommand() {
964
953
  }
965
954
  } else {
966
955
  process.stderr.write(
967
- "Note: read/admin keys can't self-revoke. Visit the dashboard to revoke; this command will just clear the local config.\n"
956
+ "Note: read/admin keys can't self-revoke server-side. Revoke them in the dashboard if needed; clearing local config only.\n"
968
957
  );
969
958
  }
970
959
  await clearApiKey();
@@ -977,7 +966,7 @@ function logoutCommand() {
977
966
 
978
967
  // src/commands/org.ts
979
968
  import { Command as Command6 } from "commander";
980
- async function client3(globals) {
969
+ async function client2(globals) {
981
970
  const cfg = await resolveConfig({
982
971
  apiKeyFlag: globals.apiKey ?? null,
983
972
  baseUrlFlag: globals.baseUrl ?? null
@@ -997,7 +986,7 @@ function orgCommand() {
997
986
  cmd.command("list").description("List orgs the current PAT's user belongs to").action(async function() {
998
987
  try {
999
988
  const globals = this.optsWithGlobals();
1000
- const c = await client3(globals);
989
+ const c = await client2(globals);
1001
990
  const result = await c.get("/api/v1/orgs");
1002
991
  emit(result.orgs, { json: !!globals.json });
1003
992
  } catch (err) {
@@ -1007,7 +996,7 @@ function orgCommand() {
1007
996
  cmd.command("show").description("Show the active org (id, name, members)").action(async function() {
1008
997
  try {
1009
998
  const globals = this.optsWithGlobals();
1010
- const c = await client3(globals);
999
+ const c = await client2(globals);
1011
1000
  const result = await c.get(
1012
1001
  "/api/v1/orgs/active"
1013
1002
  );
@@ -1022,7 +1011,7 @@ function orgCommand() {
1022
1011
  cmd.command("rename").description("Rename an org (owner only)").requiredOption("--id <orgId>", "Org id").requiredOption("--name <name>", "New name").action(async function(opts) {
1023
1012
  try {
1024
1013
  const globals = this.optsWithGlobals();
1025
- const c = await client3(globals);
1014
+ const c = await client2(globals);
1026
1015
  await c.patch(`/api/v1/orgs/${opts.id}`, { name: opts.name });
1027
1016
  emit({ ok: true, renamed: opts.id }, { json: !!globals.json });
1028
1017
  } catch (err) {
@@ -1033,7 +1022,7 @@ function orgCommand() {
1033
1022
  members.command("list").requiredOption("--id <orgId>", "Org id").action(async function(opts) {
1034
1023
  try {
1035
1024
  const globals = this.optsWithGlobals();
1036
- const c = await client3(globals);
1025
+ const c = await client2(globals);
1037
1026
  const result = await c.get(
1038
1027
  `/api/v1/orgs/${opts.id}/members`
1039
1028
  );
@@ -1045,7 +1034,7 @@ function orgCommand() {
1045
1034
  members.command("invite").requiredOption("--id <orgId>", "Org id").requiredOption("--email <email>", "Invitee email").option("--role <role>", "member | admin", "member").action(async function(opts) {
1046
1035
  try {
1047
1036
  const globals = this.optsWithGlobals();
1048
- const c = await client3(globals);
1037
+ const c = await client2(globals);
1049
1038
  const result = await c.post(
1050
1039
  `/api/v1/orgs/${opts.id}/members`,
1051
1040
  { email: opts.email, role: opts.role }
@@ -1063,7 +1052,7 @@ function orgCommand() {
1063
1052
  members.command("set-role").requiredOption("--id <orgId>", "Org id").requiredOption("--member <memberId>", "Member id").requiredOption("--role <role>", "admin | member").action(async function(opts) {
1064
1053
  try {
1065
1054
  const globals = this.optsWithGlobals();
1066
- const c = await client3(globals);
1055
+ const c = await client2(globals);
1067
1056
  await c.patch(`/api/v1/orgs/${opts.id}/members/${opts.member}`, {
1068
1057
  role: opts.role
1069
1058
  });
@@ -1075,7 +1064,7 @@ function orgCommand() {
1075
1064
  members.command("remove").requiredOption("--id <orgId>", "Org id").requiredOption("--member <memberId>", "Member id").action(async function(opts) {
1076
1065
  try {
1077
1066
  const globals = this.optsWithGlobals();
1078
- const c = await client3(globals);
1067
+ const c = await client2(globals);
1079
1068
  await c.delete(`/api/v1/orgs/${opts.id}/members/${opts.member}`);
1080
1069
  emit({ ok: true }, { json: !!globals.json });
1081
1070
  } catch (err) {
@@ -1086,7 +1075,7 @@ function orgCommand() {
1086
1075
  invitations.command("list").requiredOption("--id <orgId>", "Org id").action(async function(opts) {
1087
1076
  try {
1088
1077
  const globals = this.optsWithGlobals();
1089
- const c = await client3(globals);
1078
+ const c = await client2(globals);
1090
1079
  const result = await c.get(
1091
1080
  `/api/v1/orgs/${opts.id}/invitations`
1092
1081
  );
@@ -1100,7 +1089,7 @@ function orgCommand() {
1100
1089
 
1101
1090
  // src/commands/write.ts
1102
1091
  import { Command as Command7 } from "commander";
1103
- async function client4(globals) {
1092
+ async function client3(globals) {
1104
1093
  const cfg = await resolveConfig({
1105
1094
  apiKeyFlag: globals.apiKey ?? null,
1106
1095
  baseUrlFlag: globals.baseUrl ?? null
@@ -1110,6 +1099,15 @@ async function client4(globals) {
1110
1099
  }
1111
1100
  return new ApiClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl });
1112
1101
  }
1102
+ function requireChatbotId(cmd) {
1103
+ const globals = cmd.optsWithGlobals();
1104
+ if (!globals.chatbot) {
1105
+ fatal(
1106
+ "--chatbot <id> is required. Pass it inline or set a default with `openbat use <id>`."
1107
+ );
1108
+ }
1109
+ return globals.chatbot;
1110
+ }
1113
1111
  function surfacePlaintext(plaintext, label) {
1114
1112
  process.stderr.write(
1115
1113
  `
@@ -1129,7 +1127,7 @@ function chatbotsCommand() {
1129
1127
  cmd.command("list").description("List every chatbot the credential can reach").action(async function() {
1130
1128
  try {
1131
1129
  const globals = this.optsWithGlobals();
1132
- const c = await client4(globals);
1130
+ const c = await client3(globals);
1133
1131
  const result = await c.get(
1134
1132
  "/api/v1/chatbots"
1135
1133
  );
@@ -1141,7 +1139,7 @@ function chatbotsCommand() {
1141
1139
  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) {
1142
1140
  try {
1143
1141
  const globals = this.optsWithGlobals();
1144
- const c = await client4(globals);
1142
+ const c = await client3(globals);
1145
1143
  const result = await c.post("/api/v1/chatbots", {
1146
1144
  name: opts.name,
1147
1145
  websiteUrl: opts.website,
@@ -1161,7 +1159,7 @@ function chatbotsCommand() {
1161
1159
  cmd.command("delete <chatbotId>").description("Delete a chatbot (irreversible \u2014 cascade-deletes everything)").action(async function(chatbotId) {
1162
1160
  try {
1163
1161
  const globals = this.optsWithGlobals();
1164
- const c = await client4(globals);
1162
+ const c = await client3(globals);
1165
1163
  await c.delete(`/api/v1/chatbots/${chatbotId}`);
1166
1164
  emit({ ok: true, deleted: chatbotId }, { json: !!globals.json });
1167
1165
  } catch (err) {
@@ -1172,23 +1170,25 @@ function chatbotsCommand() {
1172
1170
  }
1173
1171
  function webhooksCommand() {
1174
1172
  const cmd = new Command7("webhooks").description("Manage webhooks for a chatbot");
1175
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1173
+ cmd.command("list").action(async function() {
1176
1174
  try {
1175
+ const chatbotId = requireChatbotId(this);
1177
1176
  const globals = this.optsWithGlobals();
1178
- const c = await client4(globals);
1177
+ const c = await client3(globals);
1179
1178
  const result = await c.get(
1180
- `/api/v1/chatbots/${opts.chatbot}/webhooks`
1179
+ `/api/v1/chatbots/${chatbotId}/webhooks`
1181
1180
  );
1182
1181
  emit(result.webhooks, { json: !!globals.json });
1183
1182
  } catch (err) {
1184
1183
  fatal(err instanceof Error ? err.message : String(err));
1185
1184
  }
1186
1185
  });
1187
- 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) {
1186
+ cmd.command("create").requiredOption("--name <name>").requiredOption("--url <url>").option("--type <type>", "discord | slack | custom", "custom").action(async function(opts) {
1188
1187
  try {
1188
+ const chatbotId = requireChatbotId(this);
1189
1189
  const globals = this.optsWithGlobals();
1190
- const c = await client4(globals);
1191
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/webhooks`, {
1190
+ const c = await client3(globals);
1191
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/webhooks`, {
1192
1192
  name: opts.name,
1193
1193
  url: opts.url,
1194
1194
  type: opts.type
@@ -1199,11 +1199,12 @@ function webhooksCommand() {
1199
1199
  fatal(err instanceof Error ? err.message : String(err));
1200
1200
  }
1201
1201
  });
1202
- cmd.command("delete").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--webhook <id>", "Webhook id").action(async function(opts) {
1202
+ cmd.command("delete").requiredOption("--webhook <id>", "Webhook id").action(async function(opts) {
1203
1203
  try {
1204
+ const chatbotId = requireChatbotId(this);
1204
1205
  const globals = this.optsWithGlobals();
1205
- const c = await client4(globals);
1206
- await c.delete(`/api/v1/chatbots/${opts.chatbot}/webhooks/${opts.webhook}`);
1206
+ const c = await client3(globals);
1207
+ await c.delete(`/api/v1/chatbots/${chatbotId}/webhooks/${opts.webhook}`);
1207
1208
  emit({ ok: true, deleted: opts.webhook }, { json: !!globals.json });
1208
1209
  } catch (err) {
1209
1210
  fatal(err instanceof Error ? err.message : String(err));
@@ -1216,12 +1217,13 @@ function settingsCommand() {
1216
1217
  "Manage chatbot settings + per-chatbot keys"
1217
1218
  );
1218
1219
  const keys = cmd.command("keys").description("Manage API keys for a chatbot");
1219
- keys.command("rotate-ingest").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1220
+ keys.command("rotate-ingest").action(async function() {
1220
1221
  try {
1222
+ const chatbotId = requireChatbotId(this);
1221
1223
  const globals = this.optsWithGlobals();
1222
- const c = await client4(globals);
1224
+ const c = await client3(globals);
1223
1225
  const result = await c.post(
1224
- `/api/v1/chatbots/${opts.chatbot}/keys/ingest/rotate`,
1226
+ `/api/v1/chatbots/${chatbotId}/keys/ingest/rotate`,
1225
1227
  {}
1226
1228
  );
1227
1229
  surfacePlaintext(result.plaintext, "New ingest key (ob_live_*)");
@@ -1230,12 +1232,13 @@ function settingsCommand() {
1230
1232
  fatal(err instanceof Error ? err.message : String(err));
1231
1233
  }
1232
1234
  });
1233
- keys.command("generate-read").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1235
+ keys.command("generate-read").action(async function() {
1234
1236
  try {
1237
+ const chatbotId = requireChatbotId(this);
1235
1238
  const globals = this.optsWithGlobals();
1236
- const c = await client4(globals);
1239
+ const c = await client3(globals);
1237
1240
  const result = await c.post(
1238
- `/api/v1/chatbots/${opts.chatbot}/keys/read`,
1241
+ `/api/v1/chatbots/${chatbotId}/keys/read`,
1239
1242
  {}
1240
1243
  );
1241
1244
  surfacePlaintext(result.plaintext, "New read key (ob_read_*)");
@@ -1244,11 +1247,12 @@ function settingsCommand() {
1244
1247
  fatal(err instanceof Error ? err.message : String(err));
1245
1248
  }
1246
1249
  });
1247
- 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) {
1250
+ 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) {
1248
1251
  try {
1252
+ const chatbotId = requireChatbotId(this);
1249
1253
  const globals = this.optsWithGlobals();
1250
- const c = await client4(globals);
1251
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/admin-keys`, {
1254
+ const c = await client3(globals);
1255
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/admin-keys`, {
1252
1256
  name: opts.name,
1253
1257
  expiresInDays: opts.expiresInDays ? Number(opts.expiresInDays) : void 0
1254
1258
  });
@@ -1258,30 +1262,33 @@ function settingsCommand() {
1258
1262
  fatal(err instanceof Error ? err.message : String(err));
1259
1263
  }
1260
1264
  });
1261
- keys.command("list-admin").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1265
+ keys.command("list-admin").action(async function() {
1262
1266
  try {
1267
+ const chatbotId = requireChatbotId(this);
1263
1268
  const globals = this.optsWithGlobals();
1264
- const c = await client4(globals);
1269
+ const c = await client3(globals);
1265
1270
  const result = await c.get(
1266
- `/api/v1/chatbots/${opts.chatbot}/admin-keys`
1271
+ `/api/v1/chatbots/${chatbotId}/admin-keys`
1267
1272
  );
1268
1273
  emit(result.keys, { json: !!globals.json });
1269
1274
  } catch (err) {
1270
1275
  fatal(err instanceof Error ? err.message : String(err));
1271
1276
  }
1272
1277
  });
1273
- keys.command("revoke-admin").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--key <keyId>", "Admin key id").action(async function(opts) {
1278
+ keys.command("revoke-admin").requiredOption("--key <keyId>", "Admin key id").action(async function(opts) {
1274
1279
  try {
1280
+ const chatbotId = requireChatbotId(this);
1275
1281
  const globals = this.optsWithGlobals();
1276
- const c = await client4(globals);
1277
- await c.delete(`/api/v1/chatbots/${opts.chatbot}/admin-keys/${opts.key}`);
1282
+ const c = await client3(globals);
1283
+ await c.delete(`/api/v1/chatbots/${chatbotId}/admin-keys/${opts.key}`);
1278
1284
  emit({ ok: true, revoked: opts.key }, { json: !!globals.json });
1279
1285
  } catch (err) {
1280
1286
  fatal(err instanceof Error ? err.message : String(err));
1281
1287
  }
1282
1288
  });
1283
- 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) {
1289
+ cmd.command("update").description("Patch a chatbot's settings JSONB").option("--description <text>").option("--website-url <url>").option("--language <code>").action(async function(opts) {
1284
1290
  try {
1291
+ const chatbotId = requireChatbotId(this);
1285
1292
  const settings = {};
1286
1293
  if (opts.description) settings.description = opts.description;
1287
1294
  if (opts.websiteUrl) settings.website_url = opts.websiteUrl;
@@ -1290,8 +1297,8 @@ function settingsCommand() {
1290
1297
  fatal("Provide at least one setting to update.");
1291
1298
  }
1292
1299
  const globals = this.optsWithGlobals();
1293
- const c = await client4(globals);
1294
- await c.patch(`/api/v1/chatbots/${opts.chatbot}/settings`, { settings });
1300
+ const c = await client3(globals);
1301
+ await c.patch(`/api/v1/chatbots/${chatbotId}/settings`, { settings });
1295
1302
  emit({ ok: true }, { json: !!globals.json });
1296
1303
  } catch (err) {
1297
1304
  fatal(err instanceof Error ? err.message : String(err));
@@ -1301,19 +1308,20 @@ function settingsCommand() {
1301
1308
  }
1302
1309
  function workflowsCommand() {
1303
1310
  const cmd = new Command7("workflows").description("Manage workflows");
1304
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1311
+ cmd.command("list").action(async function() {
1305
1312
  try {
1313
+ const chatbotId = requireChatbotId(this);
1306
1314
  const globals = this.optsWithGlobals();
1307
- const c = await client4(globals);
1315
+ const c = await client3(globals);
1308
1316
  const result = await c.get(
1309
- `/api/v1/chatbots/${opts.chatbot}/workflows`
1317
+ `/api/v1/chatbots/${chatbotId}/workflows`
1310
1318
  );
1311
1319
  emit(result.workflows, { json: !!globals.json });
1312
1320
  } catch (err) {
1313
1321
  fatal(err instanceof Error ? err.message : String(err));
1314
1322
  }
1315
1323
  });
1316
- cmd.command("create").description("Create a workflow from a built-in template").requiredOption("--chatbot <id>", "Chatbot id").requiredOption("--name <name>").requiredOption(
1324
+ cmd.command("create").description("Create a workflow from a built-in template").requiredOption("--name <name>").requiredOption(
1317
1325
  "--template <name>",
1318
1326
  "flag-to-webhook | outcome-to-webhook | sentiment-drop-to-webhook"
1319
1327
  ).requiredOption(
@@ -1321,9 +1329,10 @@ function workflowsCommand() {
1321
1329
  "Flag value / outcome value / sentiment threshold"
1322
1330
  ).requiredOption("--webhook <id>", "Webhook id to fire").option("--message <tpl>", "Message template (supports {{user.id}}, etc.)").action(async function(opts) {
1323
1331
  try {
1332
+ const chatbotId = requireChatbotId(this);
1324
1333
  const globals = this.optsWithGlobals();
1325
- const c = await client4(globals);
1326
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/workflows`, {
1334
+ const c = await client3(globals);
1335
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/workflows`, {
1327
1336
  name: opts.name,
1328
1337
  template: opts.template,
1329
1338
  triggerValue: opts.triggerValue,
@@ -1339,23 +1348,25 @@ function workflowsCommand() {
1339
1348
  }
1340
1349
  function reportsCommand() {
1341
1350
  const cmd = new Command7("reports").description("Manage AI reports");
1342
- cmd.command("list").requiredOption("--chatbot <id>", "Chatbot id").action(async function(opts) {
1351
+ cmd.command("list").action(async function() {
1343
1352
  try {
1353
+ const chatbotId = requireChatbotId(this);
1344
1354
  const globals = this.optsWithGlobals();
1345
- const c = await client4(globals);
1355
+ const c = await client3(globals);
1346
1356
  const result = await c.get(
1347
- `/api/v1/chatbots/${opts.chatbot}/reports`
1357
+ `/api/v1/chatbots/${chatbotId}/reports`
1348
1358
  );
1349
1359
  emit(result.reports, { json: !!globals.json });
1350
1360
  } catch (err) {
1351
1361
  fatal(err instanceof Error ? err.message : String(err));
1352
1362
  }
1353
1363
  });
1354
- 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) {
1364
+ 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) {
1355
1365
  try {
1366
+ const chatbotId = requireChatbotId(this);
1356
1367
  const globals = this.optsWithGlobals();
1357
- const c = await client4(globals);
1358
- const result = await c.post(`/api/v1/chatbots/${opts.chatbot}/reports`, { name: opts.name });
1368
+ const c = await client3(globals);
1369
+ const result = await c.post(`/api/v1/chatbots/${chatbotId}/reports`, { name: opts.name });
1359
1370
  process.stderr.write(
1360
1371
  `
1361
1372
  Created report. View it (org members only):
@@ -1375,30 +1386,32 @@ Created report. View it (org members only):
1375
1386
  }
1376
1387
  function analysisCommand() {
1377
1388
  const cmd = new Command7("analysis").description("Manage analysis definitions");
1378
- 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) {
1389
+ cmd.command("list").option("--type <t>", "intent | flag | assistant_outcome | assistant_issue").option("--pending", "Include pending suggestions").action(async function(opts) {
1379
1390
  try {
1391
+ const chatbotId = requireChatbotId(this);
1380
1392
  const globals = this.optsWithGlobals();
1381
- const c = await client4(globals);
1393
+ const c = await client3(globals);
1382
1394
  const qs = new URLSearchParams();
1383
1395
  if (opts.type) qs.set("type", opts.type);
1384
1396
  if (opts.pending) qs.set("pending", "true");
1385
1397
  const result = await c.get(
1386
- `/api/v1/chatbots/${opts.chatbot}/analysis-definitions${qs.size ? `?${qs}` : ""}`
1398
+ `/api/v1/chatbots/${chatbotId}/analysis-definitions${qs.size ? `?${qs}` : ""}`
1387
1399
  );
1388
1400
  emit(result.definitions, { json: !!globals.json });
1389
1401
  } catch (err) {
1390
1402
  fatal(err instanceof Error ? err.message : String(err));
1391
1403
  }
1392
1404
  });
1393
- cmd.command("add").requiredOption("--chatbot <id>", "Chatbot id").requiredOption(
1405
+ cmd.command("add").requiredOption(
1394
1406
  "--type <t>",
1395
1407
  "intent | flag | assistant_outcome | assistant_issue"
1396
1408
  ).requiredOption("--name <slug>", "snake_case slug").requiredOption("--display-name <text>").requiredOption("--description <text>").action(async function(opts) {
1397
1409
  try {
1410
+ const chatbotId = requireChatbotId(this);
1398
1411
  const globals = this.optsWithGlobals();
1399
- const c = await client4(globals);
1412
+ const c = await client3(globals);
1400
1413
  const result = await c.post(
1401
- `/api/v1/chatbots/${opts.chatbot}/analysis-definitions`,
1414
+ `/api/v1/chatbots/${chatbotId}/analysis-definitions`,
1402
1415
  {
1403
1416
  analysisType: opts.type,
1404
1417
  name: opts.name,
@@ -1417,10 +1430,11 @@ function usersCommand() {
1417
1430
  const cmd = new Command7("users").description(
1418
1431
  "List external users (chatbot customers) with health metrics"
1419
1432
  );
1420
- 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) {
1433
+ 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) {
1421
1434
  try {
1435
+ const chatbotId = requireChatbotId(this);
1422
1436
  const globals = this.optsWithGlobals();
1423
- const c = await client4(globals);
1437
+ const c = await client3(globals);
1424
1438
  const qs = new URLSearchParams();
1425
1439
  let from = opts.from;
1426
1440
  if (!from && opts.days) {
@@ -1433,7 +1447,7 @@ function usersCommand() {
1433
1447
  if (opts.search) qs.set("search", opts.search);
1434
1448
  qs.set("limit", opts.limit);
1435
1449
  const result = await c.get(
1436
- `/api/v1/chatbots/${opts.chatbot}/external-users?${qs}`
1450
+ `/api/v1/chatbots/${chatbotId}/external-users?${qs}`
1437
1451
  );
1438
1452
  emit(result.users, { json: !!globals.json });
1439
1453
  process.stderr.write(`
@@ -1451,7 +1465,7 @@ function sdkCommand() {
1451
1465
  );
1452
1466
  cmd.command("install-instructions").description("Print markdown the calling agent can follow").option(
1453
1467
  "--framework <name>",
1454
- "next | node | vercel-ai-sdk (default: next)",
1468
+ "next | vercel-ai-sdk (default: next)",
1455
1469
  "next"
1456
1470
  ).option("--chatbot <id>", "Chatbot id (for the example snippet)").action(async function(opts) {
1457
1471
  const chatbotId = opts.chatbot ?? "<chatbotId>";
@@ -1508,27 +1522,43 @@ function sdkCommand() {
1508
1522
  const out = opts.framework === "vercel-ai-sdk" ? snippetWrapper : snippetNext;
1509
1523
  process.stdout.write(out);
1510
1524
  });
1511
- 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) {
1525
+ 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) {
1512
1526
  try {
1513
1527
  const globals = this.optsWithGlobals();
1514
- const c = await client4(globals);
1515
- const deadline = Date.now() + Number(opts.timeout) * 1e3;
1528
+ const chatbotId = globals.chatbot;
1529
+ if (!chatbotId) {
1530
+ fatal("--chatbot <id> is required (also accepts the persisted active chatbot).");
1531
+ }
1532
+ const c = await client3(globals);
1533
+ const timeoutSec = Number(opts.timeout);
1534
+ const deadline = Date.now() + timeoutSec * 1e3;
1535
+ const params = new URLSearchParams({
1536
+ chatbotId,
1537
+ limit: "1"
1538
+ });
1539
+ process.stderr.write(
1540
+ `Waiting for first event on chatbot ${chatbotId} (timeout ${timeoutSec}s)\u2026
1541
+ `
1542
+ );
1543
+ let lastTick = Date.now();
1516
1544
  while (Date.now() < deadline) {
1517
- try {
1518
- const result = await c.get(`/api/v1/conversations?limit=1`);
1519
- if (result.total > 0) {
1520
- process.stderr.write(
1521
- `\u2713 First event detected. ${result.total} conversation(s) ingested.
1545
+ const result = await c.get(`/api/v1/conversations?${params}`);
1546
+ if (result.total > 0) {
1547
+ process.stderr.write(
1548
+ `First event detected. ${result.total} conversation(s) ingested.
1522
1549
  `
1523
- );
1524
- return;
1525
- }
1526
- } catch {
1550
+ );
1551
+ return;
1552
+ }
1553
+ if (Date.now() - lastTick >= 5e3) {
1554
+ process.stderr.write(".");
1555
+ lastTick = Date.now();
1527
1556
  }
1528
1557
  await new Promise((r) => setTimeout(r, 2e3));
1529
1558
  }
1530
1559
  process.stderr.write(
1531
- `Timed out after ${opts.timeout}s \u2014 no events yet. Confirm OPENBAT_API_KEY and that recordMessages is being called.
1560
+ `
1561
+ Timed out after ${timeoutSec}s \u2014 no events yet. Confirm OPENBAT_API_KEY and that recordMessages is being called.
1532
1562
  `
1533
1563
  );
1534
1564
  process.exit(2);
@@ -1543,7 +1573,7 @@ function sdkCommand() {
1543
1573
  var program = new Command8();
1544
1574
  program.name("openbat").description(
1545
1575
  "Query OpenBat chatbot data \u2014 conversations, sentiment, analytics, exports."
1546
- ).version("0.2.1").option("--api-key <key>", "Override the stored Read API key (footgun \u2014 leaks into shell history)").option(
1576
+ ).version("0.2.3").option("--api-key <key>", "Override the stored Read API key (footgun \u2014 leaks into shell history)").option(
1547
1577
  "--base-url <url>",
1548
1578
  "Override the OpenBat API base URL (defaults to ~/.openbatrc or https://openbat.dev)"
1549
1579
  ).option(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openbat/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Command-line tool for querying OpenBat chatbot data — conversations, sentiment, analytics, exports",
5
5
  "bin": {
6
6
  "openbat": "bin/openbat"
@@ -37,7 +37,10 @@
37
37
  "scripts": {
38
38
  "build": "tsup",
39
39
  "dev": "tsup --watch",
40
- "prepublishOnly": "npm run build"
40
+ "prepublishOnly": "npm run build",
41
+ "pretest": "npm run build",
42
+ "test": "node --test --experimental-strip-types --no-warnings 'tests/**/*.test.ts'",
43
+ "test:integration": "node --test --experimental-strip-types --no-warnings 'tests/integration/**/*.test.ts'"
41
44
  },
42
45
  "keywords": [
43
46
  "openbat",