@hookstream/cli 0.1.0 → 0.2.1

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 +262 -0
  2. package/dist/index.js +953 -105
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command50 } from "commander";
4
+ import { Command as Command77 } from "commander";
5
5
 
6
6
  // src/output.ts
7
7
  import chalk from "chalk";
@@ -57,6 +57,9 @@ function formatBytes(bytes) {
57
57
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
58
58
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
59
59
  }
60
+ function truncate(str, len) {
61
+ return str.length > len ? str.slice(0, len - 1) + "\u2026" : str;
62
+ }
60
63
  function statusColor(status) {
61
64
  switch (status) {
62
65
  case "active":
@@ -85,7 +88,9 @@ function statusColor(status) {
85
88
  // src/commands/login.ts
86
89
  import { createInterface } from "readline/promises";
87
90
  import { stdin, stdout } from "process";
91
+ import { exec } from "child_process";
88
92
  import { Command } from "commander";
93
+ import chalk2 from "chalk";
89
94
 
90
95
  // src/config.ts
91
96
  import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
@@ -121,8 +126,8 @@ var hookstreamClient = class {
121
126
  baseUrl;
122
127
  constructor(opts = {}) {
123
128
  const config = loadConfig();
124
- this.apiKey = opts.apiKey || process.env.HOOKSTREAM_API_KEY || config.api_key || "";
125
- this.baseUrl = (opts.baseUrl || process.env.HOOKSTREAM_BASE_URL || config.base_url || DEFAULT_BASE_URL).replace(/\/$/, "");
129
+ this.apiKey = opts.apiKey || process.env.__HS_CLI_API_KEY || process.env.HOOKSTREAM_API_KEY || config.api_key || "";
130
+ this.baseUrl = (opts.baseUrl || process.env.__HS_CLI_BASE_URL || process.env.HOOKSTREAM_BASE_URL || config.base_url || DEFAULT_BASE_URL).replace(/\/$/, "");
126
131
  if (!this.apiKey) {
127
132
  throw new Error("Not authenticated. Run `hookstream login` or set HOOKSTREAM_API_KEY.");
128
133
  }
@@ -177,24 +182,110 @@ async function publicRequest(baseUrl, path, opts = {}) {
177
182
  }
178
183
 
179
184
  // src/commands/login.ts
180
- var loginCommand = new Command("login").description("Authenticate with an API key").option("--api-key <key>", "API key (or enter interactively)").option("--base-url <url>", "Override base URL").action(async (opts) => {
185
+ var DEFAULT_BASE_URL2 = "https://hookstream.io";
186
+ function openBrowser(url) {
187
+ const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "${url}"` : `xdg-open "${url}"`;
188
+ exec(cmd, () => {
189
+ });
190
+ }
191
+ async function sleep(ms) {
192
+ return new Promise((r) => setTimeout(r, ms));
193
+ }
194
+ var loginCommand = new Command("login").description("Authenticate with an API key").option("--api-key <key>", "API key (or enter interactively)").option("--base-url <url>", "Override base URL").option("-i, --interactive", "Enter API key manually (skip browser)").action(async (opts) => {
181
195
  let apiKey = opts.apiKey;
182
- if (!apiKey) {
196
+ const baseUrl = (opts.baseUrl || process.env.__HS_CLI_BASE_URL || process.env.HOOKSTREAM_BASE_URL || DEFAULT_BASE_URL2).replace(/\/$/, "");
197
+ if (apiKey) {
198
+ await validateAndSave(apiKey, baseUrl, opts);
199
+ return;
200
+ }
201
+ if (opts.interactive) {
183
202
  const rl = createInterface({ input: stdin, output: stdout });
184
203
  apiKey = await rl.question("Enter your API key: ");
185
204
  rl.close();
205
+ if (!apiKey || !apiKey.trim()) {
206
+ printError("API key cannot be empty.");
207
+ process.exit(1);
208
+ }
209
+ await validateAndSave(apiKey.trim(), baseUrl, opts);
210
+ return;
186
211
  }
187
- if (!apiKey || !apiKey.trim()) {
188
- printError("API key cannot be empty.");
212
+ try {
213
+ console.log();
214
+ console.log(chalk2.bold(" hookstream CLI Login"));
215
+ console.log();
216
+ const res = await fetch(`${baseUrl}/v1/cli/auth`, {
217
+ method: "POST",
218
+ headers: { "Content-Type": "application/json" }
219
+ });
220
+ if (!res.ok) {
221
+ console.log(chalk2.dim(" Browser auth not available, falling back to interactive mode."));
222
+ console.log();
223
+ const rl = createInterface({ input: stdin, output: stdout });
224
+ apiKey = await rl.question(" Enter your API key: ");
225
+ rl.close();
226
+ if (!apiKey?.trim()) {
227
+ printError("API key cannot be empty.");
228
+ process.exit(1);
229
+ }
230
+ await validateAndSave(apiKey.trim(), baseUrl, opts);
231
+ return;
232
+ }
233
+ const session = await res.json();
234
+ console.log(" Opening browser to authenticate...");
235
+ console.log();
236
+ console.log(` ${chalk2.dim("If the browser doesn't open, visit:")}`);
237
+ console.log(` ${chalk2.cyan(session.url)}`);
238
+ console.log();
239
+ console.log(chalk2.dim(" Waiting for authorization..."));
240
+ openBrowser(session.url);
241
+ const deadline = Date.now() + session.expires_in * 1e3;
242
+ let approved = false;
243
+ while (Date.now() < deadline) {
244
+ await sleep(2e3);
245
+ try {
246
+ const pollRes = await fetch(`${baseUrl}/v1/cli/auth/${session.code}`);
247
+ if (!pollRes.ok) {
248
+ break;
249
+ }
250
+ const result = await pollRes.json();
251
+ if (result.status === "approved" && result.api_key) {
252
+ apiKey = result.api_key;
253
+ approved = true;
254
+ break;
255
+ }
256
+ } catch {
257
+ }
258
+ }
259
+ if (!approved || !apiKey) {
260
+ console.log();
261
+ printError("Authorization timed out. Try again or use: hookstream login -i");
262
+ process.exit(1);
263
+ }
264
+ const config = loadConfig();
265
+ config.api_key = apiKey;
266
+ config.base_url = baseUrl;
267
+ saveConfig(config);
268
+ console.log();
269
+ if (isJsonMode()) {
270
+ printJson({ success: true, prefix: apiKey.slice(0, 16) + "..." });
271
+ } else {
272
+ printSuccess(`Logged in as ${apiKey.slice(0, 16)}...`);
273
+ console.log();
274
+ console.log(chalk2.dim(" Config saved to ~/.config/hookstream/config.json"));
275
+ console.log(chalk2.dim(" Run `hookstream whoami` to verify."));
276
+ }
277
+ } catch (err) {
278
+ printError(err instanceof Error ? err.message : String(err));
189
279
  process.exit(1);
190
280
  }
191
- apiKey = apiKey.trim();
281
+ });
282
+ async function validateAndSave(apiKey, baseUrl, opts) {
192
283
  if (!/^hs_(live|test)_[a-f0-9]{64}$/.test(apiKey)) {
193
284
  printError("Invalid API key format. Expected: hs_live_<64 hex> or hs_test_<64 hex>");
194
285
  process.exit(1);
195
286
  }
196
287
  try {
197
- const client = new hookstreamClient({ apiKey, baseUrl: opts.baseUrl });
288
+ const client = new hookstreamClient({ apiKey, baseUrl });
198
289
  await client.get("/sources");
199
290
  } catch (err) {
200
291
  printError(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -211,7 +302,7 @@ var loginCommand = new Command("login").description("Authenticate with an API ke
211
302
  } else {
212
303
  printSuccess(`Logged in as ${apiKey.slice(0, 16)}...`);
213
304
  }
214
- });
305
+ }
215
306
 
216
307
  // src/commands/logout.ts
217
308
  import { Command as Command2 } from "commander";
@@ -840,7 +931,7 @@ var connectionsDeleteCommand = new Command20("delete").description("Delete a con
840
931
  var connectionsCommand = new Command21("connections").description("Manage source-to-destination connections").addCommand(connectionsListCommand).addCommand(connectionsCreateCommand).addCommand(connectionsGetCommand).addCommand(connectionsDeleteCommand);
841
932
 
842
933
  // src/commands/events/index.ts
843
- import { Command as Command25 } from "commander";
934
+ import { Command as Command26 } from "commander";
844
935
 
845
936
  // src/commands/events/list.ts
846
937
  import { Command as Command22 } from "commander";
@@ -893,7 +984,7 @@ var eventsListCommand = new Command22("list").description("List events").option(
893
984
 
894
985
  // src/commands/events/get.ts
895
986
  import { Command as Command23 } from "commander";
896
- import chalk2 from "chalk";
987
+ import chalk3 from "chalk";
897
988
  var eventsGetCommand = new Command23("get").description("Get event details").argument("<id>", "Event ID").action(async (id) => {
898
989
  try {
899
990
  const client = new hookstreamClient();
@@ -918,15 +1009,15 @@ var eventsGetCommand = new Command23("get").description("Get event details").arg
918
1009
  ]);
919
1010
  if (e.headers && Object.keys(e.headers).length > 0) {
920
1011
  console.log();
921
- console.log(chalk2.bold(" Headers"));
1012
+ console.log(chalk3.bold(" Headers"));
922
1013
  for (const [key, val] of Object.entries(e.headers)) {
923
1014
  if (key.startsWith("cf-") || key.startsWith("x-real-") || key === "cdn-loop") continue;
924
- console.log(` ${chalk2.dim(key)}: ${val}`);
1015
+ console.log(` ${chalk3.dim(key)}: ${val}`);
925
1016
  }
926
1017
  }
927
1018
  if (e.payload) {
928
1019
  console.log();
929
- console.log(chalk2.bold(" Payload"));
1020
+ console.log(chalk3.bold(" Payload"));
930
1021
  try {
931
1022
  const parsed = JSON.parse(e.payload);
932
1023
  console.log(JSON.stringify(parsed, null, 2).split("\n").map((l) => ` ${l}`).join("\n"));
@@ -946,7 +1037,7 @@ var eventsGetCommand = new Command23("get").description("Get event details").arg
946
1037
 
947
1038
  // src/commands/events/replay.ts
948
1039
  import { Command as Command24 } from "commander";
949
- import chalk3 from "chalk";
1040
+ import chalk4 from "chalk";
950
1041
  var SKIP_HEADERS = /* @__PURE__ */ new Set([
951
1042
  "cf-connecting-ip",
952
1043
  "cf-ipcountry",
@@ -998,7 +1089,7 @@ var eventsReplayCommand = new Command24("replay").description("Replay an event t
998
1089
  });
999
1090
  return;
1000
1091
  }
1001
- const statusStr = res.ok ? chalk3.green(String(res.status)) : chalk3.red(String(res.status));
1092
+ const statusStr = res.ok ? chalk4.green(String(res.status)) : chalk4.red(String(res.status));
1002
1093
  printSuccess(`Replayed event ${id.slice(0, 12)} to ${opts.to}`);
1003
1094
  console.log();
1004
1095
  printKeyValue([
@@ -1013,15 +1104,32 @@ var eventsReplayCommand = new Command24("replay").description("Replay an event t
1013
1104
  }
1014
1105
  });
1015
1106
 
1107
+ // src/commands/events/retry.ts
1108
+ import { Command as Command25 } from "commander";
1109
+ var eventsRetryCommand = new Command25("retry").description("Retry all deliveries for an event").argument("<id>", "Event ID").action(async (id) => {
1110
+ try {
1111
+ const client = new hookstreamClient();
1112
+ const data = await client.post(`/events/${id}/retry`);
1113
+ if (isJsonMode()) {
1114
+ printJson({ event_id: id, retried: data.retried });
1115
+ return;
1116
+ }
1117
+ printSuccess(`Retried ${data.retried} delivery attempt(s) for event ${id.slice(0, 12)}`);
1118
+ } catch (err) {
1119
+ printError(err instanceof Error ? err.message : String(err));
1120
+ process.exit(1);
1121
+ }
1122
+ });
1123
+
1016
1124
  // src/commands/events/index.ts
1017
- var eventsCommand = new Command25("events").description("Browse and replay events").addCommand(eventsListCommand).addCommand(eventsGetCommand).addCommand(eventsReplayCommand);
1125
+ var eventsCommand = new Command26("events").description("Browse and replay events").addCommand(eventsListCommand).addCommand(eventsGetCommand).addCommand(eventsReplayCommand).addCommand(eventsRetryCommand);
1018
1126
 
1019
1127
  // src/commands/deliveries/index.ts
1020
- import { Command as Command30 } from "commander";
1128
+ import { Command as Command31 } from "commander";
1021
1129
 
1022
1130
  // src/commands/deliveries/list.ts
1023
- import { Command as Command26 } from "commander";
1024
- var deliveriesListCommand = new Command26("list").description("List delivery attempts").option("--event <id>", "Filter by event ID").option("--connection <id>", "Filter by connection ID").option("--destination <id>", "Filter by destination ID").option("--status <status>", "Filter by status (success, failed, timeout, error, dlq)").option("--dlq", "Show only DLQ items").option("--limit <n>", "Max results", "20").action(async (opts) => {
1131
+ import { Command as Command27 } from "commander";
1132
+ var deliveriesListCommand = new Command27("list").description("List delivery attempts").option("--event <id>", "Filter by event ID").option("--connection <id>", "Filter by connection ID").option("--destination <id>", "Filter by destination ID").option("--status <status>", "Filter by status (success, failed, timeout, error, dlq)").option("--dlq", "Show only DLQ items").option("--limit <n>", "Max results", "20").action(async (opts) => {
1025
1133
  try {
1026
1134
  const client = new hookstreamClient();
1027
1135
  const params = new URLSearchParams();
@@ -1073,8 +1181,8 @@ var deliveriesListCommand = new Command26("list").description("List delivery att
1073
1181
  });
1074
1182
 
1075
1183
  // src/commands/deliveries/get.ts
1076
- import { Command as Command27 } from "commander";
1077
- var deliveriesGetCommand = new Command27("get").description("Get delivery attempt details").argument("<id>", "Delivery attempt ID").action(async (id) => {
1184
+ import { Command as Command28 } from "commander";
1185
+ var deliveriesGetCommand = new Command28("get").description("Get delivery attempt details").argument("<id>", "Delivery attempt ID").action(async (id) => {
1078
1186
  try {
1079
1187
  const client = new hookstreamClient();
1080
1188
  const data = await client.get(`/delivery-attempts/${id}`);
@@ -1115,8 +1223,8 @@ var deliveriesGetCommand = new Command27("get").description("Get delivery attemp
1115
1223
  });
1116
1224
 
1117
1225
  // src/commands/deliveries/retry.ts
1118
- import { Command as Command28 } from "commander";
1119
- var deliveriesRetryCommand = new Command28("retry").description("Retry a failed delivery attempt").argument("<id>", "Delivery attempt ID").action(async (id) => {
1226
+ import { Command as Command29 } from "commander";
1227
+ var deliveriesRetryCommand = new Command29("retry").description("Retry a failed delivery attempt").argument("<id>", "Delivery attempt ID").action(async (id) => {
1120
1228
  try {
1121
1229
  const client = new hookstreamClient();
1122
1230
  const data = await client.post(
@@ -1143,8 +1251,8 @@ var deliveriesRetryCommand = new Command28("retry").description("Retry a failed
1143
1251
  });
1144
1252
 
1145
1253
  // src/commands/deliveries/dlq.ts
1146
- import { Command as Command29 } from "commander";
1147
- var deliveriesDlqCommand = new Command29("dlq").description("List dead-letter queue items").option("--destination <id>", "Filter by destination ID").option("--limit <n>", "Max results", "50").action(async (opts) => {
1254
+ import { Command as Command30 } from "commander";
1255
+ var deliveriesDlqCommand = new Command30("dlq").description("List dead-letter queue items").option("--destination <id>", "Filter by destination ID").option("--limit <n>", "Max results", "50").action(async (opts) => {
1148
1256
  try {
1149
1257
  const client = new hookstreamClient();
1150
1258
  const params = new URLSearchParams();
@@ -1195,14 +1303,14 @@ var deliveriesDlqCommand = new Command29("dlq").description("List dead-letter qu
1195
1303
  });
1196
1304
 
1197
1305
  // src/commands/deliveries/index.ts
1198
- var deliveriesCommand = new Command30("deliveries").description("Manage delivery attempts").addCommand(deliveriesListCommand).addCommand(deliveriesGetCommand).addCommand(deliveriesRetryCommand).addCommand(deliveriesDlqCommand);
1306
+ var deliveriesCommand = new Command31("deliveries").description("Manage delivery attempts").addCommand(deliveriesListCommand).addCommand(deliveriesGetCommand).addCommand(deliveriesRetryCommand).addCommand(deliveriesDlqCommand);
1199
1307
 
1200
1308
  // src/commands/metrics/index.ts
1201
- import { Command as Command33 } from "commander";
1309
+ import { Command as Command34 } from "commander";
1202
1310
 
1203
1311
  // src/commands/metrics/overview.ts
1204
- import { Command as Command31 } from "commander";
1205
- var metricsOverviewCommand = new Command31("overview").description("Show metrics overview").action(async () => {
1312
+ import { Command as Command32 } from "commander";
1313
+ var metricsOverviewCommand = new Command32("overview").description("Show metrics overview").action(async () => {
1206
1314
  try {
1207
1315
  const client = new hookstreamClient();
1208
1316
  const data = await client.get("/metrics/overview");
@@ -1228,9 +1336,9 @@ var metricsOverviewCommand = new Command31("overview").description("Show metrics
1228
1336
  });
1229
1337
 
1230
1338
  // src/commands/metrics/volume.ts
1231
- import { Command as Command32 } from "commander";
1232
- import chalk4 from "chalk";
1233
- var metricsVolumeCommand = new Command32("volume").description("Show event volume over time").option("--source <id>", "Filter by source ID").option("--after <iso>", "Start time (ISO 8601)").option("--before <iso>", "End time (ISO 8601)").option("--granularity <period>", "hour or day", "hour").action(async (opts) => {
1339
+ import { Command as Command33 } from "commander";
1340
+ import chalk5 from "chalk";
1341
+ var metricsVolumeCommand = new Command33("volume").description("Show event volume over time").option("--source <id>", "Filter by source ID").option("--after <iso>", "Start time (ISO 8601)").option("--before <iso>", "End time (ISO 8601)").option("--granularity <period>", "hour or day", "hour").action(async (opts) => {
1234
1342
  try {
1235
1343
  const client = new hookstreamClient();
1236
1344
  const params = new URLSearchParams();
@@ -1250,7 +1358,7 @@ var metricsVolumeCommand = new Command32("volume").description("Show event volum
1250
1358
  return;
1251
1359
  }
1252
1360
  if (data.series.length === 0) {
1253
- console.log(chalk4.dim("\n No volume data for this time range.\n"));
1361
+ console.log(chalk5.dim("\n No volume data for this time range.\n"));
1254
1362
  return;
1255
1363
  }
1256
1364
  console.log();
@@ -1285,19 +1393,19 @@ function renderBar(value, max) {
1285
1393
  if (max === 0) return "";
1286
1394
  const width = 15;
1287
1395
  const filled = Math.round(value / max * width);
1288
- return chalk4.cyan("\u2588".repeat(filled)) + chalk4.dim("\u2591".repeat(width - filled));
1396
+ return chalk5.cyan("\u2588".repeat(filled)) + chalk5.dim("\u2591".repeat(width - filled));
1289
1397
  }
1290
1398
 
1291
1399
  // src/commands/metrics/index.ts
1292
- var metricsCommand = new Command33("metrics").description("View metrics and stats").addCommand(metricsOverviewCommand).addCommand(metricsVolumeCommand);
1400
+ var metricsCommand = new Command34("metrics").description("View metrics and stats").addCommand(metricsOverviewCommand).addCommand(metricsVolumeCommand);
1293
1401
 
1294
1402
  // src/commands/test.ts
1295
1403
  import { readFileSync as readFileSync2 } from "fs";
1296
- import { Command as Command34 } from "commander";
1297
- var DEFAULT_BASE_URL2 = "https://hookstream.io";
1298
- var testCommand = new Command34("test").description("Send a test event to a source").argument("<source-id>", "Source ID to send the test event to").option("--payload <json>", "JSON payload string").option("--file <path>", "Read payload from a file").option("--method <method>", "HTTP method", "POST").option("-H, --header <header...>", "Headers in key:value format").option("--base-url <url>", "Override base URL").action(async (sourceId, opts) => {
1404
+ import { Command as Command35 } from "commander";
1405
+ var DEFAULT_BASE_URL3 = "https://hookstream.io";
1406
+ var testCommand = new Command35("test").description("Send a test event to a source").argument("<source-id>", "Source ID to send the test event to").option("--payload <json>", "JSON payload string").option("--file <path>", "Read payload from a file").option("--method <method>", "HTTP method", "POST").option("-H, --header <header...>", "Headers in key:value format").option("--base-url <url>", "Override base URL").action(async (sourceId, opts) => {
1299
1407
  const config = loadConfig();
1300
- const baseUrl = (opts.baseUrl || process.env.HOOKSTREAM_BASE_URL || config.base_url || DEFAULT_BASE_URL2).replace(/\/$/, "");
1408
+ const baseUrl = (opts.baseUrl || process.env.HOOKSTREAM_BASE_URL || config.base_url || DEFAULT_BASE_URL3).replace(/\/$/, "");
1301
1409
  let payload;
1302
1410
  if (opts.file) {
1303
1411
  try {
@@ -1380,9 +1488,9 @@ var testCommand = new Command34("test").description("Send a test event to a sour
1380
1488
  });
1381
1489
 
1382
1490
  // src/commands/listen.ts
1383
- import { Command as Command35 } from "commander";
1384
- import chalk5 from "chalk";
1385
- var DEFAULT_BASE_URL3 = "https://hookstream.io";
1491
+ import { Command as Command36 } from "commander";
1492
+ import chalk6 from "chalk";
1493
+ var DEFAULT_BASE_URL4 = "https://hookstream.io";
1386
1494
  var PROVIDER_SIGNATURES = [
1387
1495
  { header: "stripe-signature", name: "Stripe" },
1388
1496
  { header: "x-shopify-hmac-sha256", name: "Shopify" },
@@ -1410,13 +1518,13 @@ function formatBytes2(bytes) {
1410
1518
  if (bytes < 1024) return `${bytes}B`;
1411
1519
  return `${(bytes / 1024).toFixed(1)}KB`;
1412
1520
  }
1413
- function truncate(str, max) {
1521
+ function truncate2(str, max) {
1414
1522
  if (str.length <= max) return str;
1415
1523
  return str.slice(0, max - 3) + "...";
1416
1524
  }
1417
- var listenCommand = new Command35("listen").description("Create a test URL and stream webhooks in real-time").option("--forward <url>", "Forward received webhooks to this URL").option("--full", "Show full payload (no truncation)").option("--base-url <url>", "Override base URL").action(async (opts) => {
1525
+ var listenCommand = new Command36("listen").description("Create a test URL and stream webhooks in real-time").option("--forward <url>", "Forward received webhooks to this URL").option("--full", "Show full payload (no truncation)").option("--base-url <url>", "Override base URL").action(async (opts) => {
1418
1526
  const config = loadConfig();
1419
- const baseUrl = (opts.baseUrl || process.env.HOOKSTREAM_BASE_URL || config.base_url || DEFAULT_BASE_URL3).replace(/\/$/, "");
1527
+ const baseUrl = (opts.baseUrl || process.env.HOOKSTREAM_BASE_URL || config.base_url || DEFAULT_BASE_URL4).replace(/\/$/, "");
1420
1528
  const jsonMode2 = isJsonMode();
1421
1529
  try {
1422
1530
  let connect2 = function() {
@@ -1453,14 +1561,14 @@ var listenCommand = new Command35("listen").description("Create a test URL and s
1453
1561
  }
1454
1562
  })();
1455
1563
  const provider = detectProviderCli(headers);
1456
- const methodColor = method === "POST" ? chalk5.green : method === "GET" ? chalk5.blue : method === "PUT" ? chalk5.yellow : method === "DELETE" ? chalk5.red : chalk5.white;
1564
+ const methodColor = method === "POST" ? chalk6.green : method === "GET" ? chalk6.blue : method === "PUT" ? chalk6.yellow : method === "DELETE" ? chalk6.red : chalk6.white;
1457
1565
  console.log(
1458
- ` ${chalk5.dim(`[${time}]`)} ${methodColor(method.padEnd(6))} ${chalk5.dim(ct.padEnd(24))} ${size.padEnd(8)} ${provider ? chalk5.magenta(`\u2190 ${provider}`) : ""}`
1566
+ ` ${chalk6.dim(`[${time}]`)} ${methodColor(method.padEnd(6))} ${chalk6.dim(ct.padEnd(24))} ${size.padEnd(8)} ${provider ? chalk6.magenta(`\u2190 ${provider}`) : ""}`
1459
1567
  );
1460
1568
  const payload = detail.payload ?? detail.payload_inline ?? "";
1461
1569
  if (payload) {
1462
- const display = opts.full ? payload : truncate(payload, 120);
1463
- console.log(` ${chalk5.dim(" ")} ${chalk5.dim(display)}`);
1570
+ const display = opts.full ? payload : truncate2(payload, 120);
1571
+ console.log(` ${chalk6.dim(" ")} ${chalk6.dim(display)}`);
1464
1572
  }
1465
1573
  }
1466
1574
  } catch {
@@ -1470,9 +1578,9 @@ var listenCommand = new Command35("listen").description("Create a test URL and s
1470
1578
  if (!jsonMode2) {
1471
1579
  const status = msg.data.status;
1472
1580
  const latency = msg.data.latency_ms;
1473
- const color = status >= 200 && status < 300 ? chalk5.green : chalk5.red;
1581
+ const color = status >= 200 && status < 300 ? chalk6.green : chalk6.red;
1474
1582
  console.log(
1475
- ` ${chalk5.dim(" ")} ${chalk5.dim("\u2192 Forward:")} ${color(String(status))} ${chalk5.dim(`${latency}ms`)}`
1583
+ ` ${chalk6.dim(" ")} ${chalk6.dim("\u2192 Forward:")} ${color(String(status))} ${chalk6.dim(`${latency}ms`)}`
1476
1584
  );
1477
1585
  }
1478
1586
  }
@@ -1493,7 +1601,7 @@ var listenCommand = new Command35("listen").description("Create a test URL and s
1493
1601
  if (!jsonMode2) {
1494
1602
  console.log();
1495
1603
  console.log(
1496
- chalk5.dim(` Session active for 14 days at ${baseUrl.replace(/https:\/\/hookstream\.\w+\.workers\.dev/, "https://hookstream.io")}/test/${session.session_id}`)
1604
+ chalk6.dim(` Session active for 14 days at ${baseUrl.replace(/https:\/\/hookstream\.\w+\.workers\.dev/, "https://hookstream.io")}/test/${session.session_id}`)
1497
1605
  );
1498
1606
  console.log();
1499
1607
  }
@@ -1516,12 +1624,12 @@ var listenCommand = new Command35("listen").description("Create a test URL and s
1516
1624
  const session = await createRes.json();
1517
1625
  if (!jsonMode2) {
1518
1626
  console.log();
1519
- console.log(chalk5.bold.magenta(" hookstream Webhook Listener"));
1627
+ console.log(chalk6.bold.magenta(" hookstream Webhook Listener"));
1520
1628
  console.log();
1521
- console.log(` ${chalk5.dim("URL:")} ${chalk5.cyan(session.url)}`);
1522
- console.log(` ${chalk5.dim("Inspector:")} ${chalk5.cyan(`${baseUrl.replace(/https:\/\/hookstream\.\w+\.workers\.dev/, "https://hookstream.io")}/test/${session.session_id}`)}`);
1523
- console.log(` ${chalk5.dim("Expires:")} ${new Date(session.expires_at).toLocaleDateString()} (14 days)`);
1524
- console.log(` ${chalk5.dim("Limit:")} ${session.request_limit} requests`);
1629
+ console.log(` ${chalk6.dim("URL:")} ${chalk6.cyan(session.url)}`);
1630
+ console.log(` ${chalk6.dim("Inspector:")} ${chalk6.cyan(`${baseUrl.replace(/https:\/\/hookstream\.\w+\.workers\.dev/, "https://hookstream.io")}/test/${session.session_id}`)}`);
1631
+ console.log(` ${chalk6.dim("Expires:")} ${new Date(session.expires_at).toLocaleDateString()} (14 days)`);
1632
+ console.log(` ${chalk6.dim("Limit:")} ${session.request_limit} requests`);
1525
1633
  console.log();
1526
1634
  }
1527
1635
  if (opts.forward) {
@@ -1532,18 +1640,18 @@ var listenCommand = new Command35("listen").description("Create a test URL and s
1532
1640
  body: JSON.stringify({ url: opts.forward })
1533
1641
  });
1534
1642
  if (!jsonMode2) {
1535
- console.log(` ${chalk5.dim("Forward:")} ${chalk5.yellow(opts.forward)}`);
1643
+ console.log(` ${chalk6.dim("Forward:")} ${chalk6.yellow(opts.forward)}`);
1536
1644
  console.log();
1537
1645
  }
1538
1646
  } catch {
1539
1647
  if (!jsonMode2) {
1540
- console.log(chalk5.yellow(" Warning: Could not configure forwarding"));
1648
+ console.log(chalk6.yellow(" Warning: Could not configure forwarding"));
1541
1649
  console.log();
1542
1650
  }
1543
1651
  }
1544
1652
  }
1545
1653
  if (!jsonMode2) {
1546
- console.log(chalk5.dim(" Listening... (Ctrl+C to stop)"));
1654
+ console.log(chalk6.dim(" Listening... (Ctrl+C to stop)"));
1547
1655
  console.log();
1548
1656
  }
1549
1657
  const wsUrl = baseUrl.replace("https://", "wss://").replace("http://", "ws://");
@@ -1560,11 +1668,11 @@ var listenCommand = new Command35("listen").description("Create a test URL and s
1560
1668
  });
1561
1669
 
1562
1670
  // src/commands/topics/index.ts
1563
- import { Command as Command43 } from "commander";
1671
+ import { Command as Command44 } from "commander";
1564
1672
 
1565
1673
  // src/commands/topics/list.ts
1566
- import { Command as Command36 } from "commander";
1567
- var topicsListCommand = new Command36("list").description("List all topics").option("--status <status>", "Filter by status (active, paused)").action(async (opts) => {
1674
+ import { Command as Command37 } from "commander";
1675
+ var topicsListCommand = new Command37("list").description("List all topics").option("--status <status>", "Filter by status (active, paused)").action(async (opts) => {
1568
1676
  try {
1569
1677
  const client = new hookstreamClient();
1570
1678
  const params = new URLSearchParams();
@@ -1600,11 +1708,11 @@ var topicsListCommand = new Command36("list").description("List all topics").opt
1600
1708
  });
1601
1709
 
1602
1710
  // src/commands/topics/create.ts
1603
- import { Command as Command37 } from "commander";
1711
+ import { Command as Command38 } from "commander";
1604
1712
  function slugify3(name) {
1605
1713
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
1606
1714
  }
1607
- var topicsCreateCommand = new Command37("create").description("Create a new topic").argument("<name>", "Topic name").option("--slug <slug>", "URL slug (auto-generated from name if omitted)").option("--description <desc>", "Topic description").action(async (name, opts) => {
1715
+ var topicsCreateCommand = new Command38("create").description("Create a new topic").argument("<name>", "Topic name").option("--slug <slug>", "URL slug (auto-generated from name if omitted)").option("--description <desc>", "Topic description").action(async (name, opts) => {
1608
1716
  try {
1609
1717
  const client = new hookstreamClient();
1610
1718
  const slug = opts.slug || slugify3(name);
@@ -1635,8 +1743,8 @@ var topicsCreateCommand = new Command37("create").description("Create a new topi
1635
1743
  });
1636
1744
 
1637
1745
  // src/commands/topics/get.ts
1638
- import { Command as Command38 } from "commander";
1639
- var topicsGetCommand = new Command38("get").description("Get topic details").argument("<id>", "Topic ID").action(async (id) => {
1746
+ import { Command as Command39 } from "commander";
1747
+ var topicsGetCommand = new Command39("get").description("Get topic details").argument("<id>", "Topic ID").action(async (id) => {
1640
1748
  try {
1641
1749
  const client = new hookstreamClient();
1642
1750
  const data = await client.get(`/topics/${id}`);
@@ -1686,8 +1794,8 @@ var topicsGetCommand = new Command38("get").description("Get topic details").arg
1686
1794
  // src/commands/topics/delete.ts
1687
1795
  import { createInterface as createInterface5 } from "readline/promises";
1688
1796
  import { stdin as stdin5, stdout as stdout5 } from "process";
1689
- import { Command as Command39 } from "commander";
1690
- var topicsDeleteCommand = new Command39("delete").description("Delete a topic").argument("<id>", "Topic ID").option("-f, --force", "Skip confirmation prompt").action(async (id, opts) => {
1797
+ import { Command as Command40 } from "commander";
1798
+ var topicsDeleteCommand = new Command40("delete").description("Delete a topic").argument("<id>", "Topic ID").option("-f, --force", "Skip confirmation prompt").action(async (id, opts) => {
1691
1799
  try {
1692
1800
  if (!opts.force) {
1693
1801
  const rl = createInterface5({ input: stdin5, output: stdout5 });
@@ -1712,8 +1820,8 @@ var topicsDeleteCommand = new Command39("delete").description("Delete a topic").
1712
1820
  });
1713
1821
 
1714
1822
  // src/commands/topics/subscribe.ts
1715
- import { Command as Command40 } from "commander";
1716
- var topicsSubscribeCommand = new Command40("subscribe").description("Subscribe a destination to a topic").argument("<topic-id>", "Topic ID").argument("<destination-id>", "Destination ID").option("--filter <json>", "Filter rules as JSON").option("--transform <expr>", "JSONata transform expression").action(async (topicId, destinationId, opts) => {
1823
+ import { Command as Command41 } from "commander";
1824
+ var topicsSubscribeCommand = new Command41("subscribe").description("Subscribe a destination to a topic").argument("<topic-id>", "Topic ID").argument("<destination-id>", "Destination ID").option("--filter <json>", "Filter rules as JSON").option("--transform <expr>", "JSONata transform expression").action(async (topicId, destinationId, opts) => {
1717
1825
  try {
1718
1826
  const client = new hookstreamClient();
1719
1827
  const body = {
@@ -1751,8 +1859,8 @@ var topicsSubscribeCommand = new Command40("subscribe").description("Subscribe a
1751
1859
  });
1752
1860
 
1753
1861
  // src/commands/topics/unsubscribe.ts
1754
- import { Command as Command41 } from "commander";
1755
- var topicsUnsubscribeCommand = new Command41("unsubscribe").description("Remove a subscription from a topic").argument("<topic-id>", "Topic ID").argument("<subscription-id>", "Subscription ID").action(async (topicId, subscriptionId) => {
1862
+ import { Command as Command42 } from "commander";
1863
+ var topicsUnsubscribeCommand = new Command42("unsubscribe").description("Remove a subscription from a topic").argument("<topic-id>", "Topic ID").argument("<subscription-id>", "Subscription ID").action(async (topicId, subscriptionId) => {
1756
1864
  try {
1757
1865
  const client = new hookstreamClient();
1758
1866
  await client.del(`/topics/${topicId}/subscriptions/${subscriptionId}`);
@@ -1768,8 +1876,8 @@ var topicsUnsubscribeCommand = new Command41("unsubscribe").description("Remove
1768
1876
  });
1769
1877
 
1770
1878
  // src/commands/topics/publish.ts
1771
- import { Command as Command42 } from "commander";
1772
- var topicsPublishCommand = new Command42("publish").description("Publish an event to a topic").argument("<slug>", "Topic slug").requiredOption("--data <json>", "Event payload as JSON").action(async (slug, opts) => {
1879
+ import { Command as Command43 } from "commander";
1880
+ var topicsPublishCommand = new Command43("publish").description("Publish an event to a topic").argument("<slug>", "Topic slug").requiredOption("--data <json>", "Event payload as JSON").action(async (slug, opts) => {
1773
1881
  try {
1774
1882
  const client = new hookstreamClient();
1775
1883
  const payload = JSON.parse(opts.data);
@@ -1792,14 +1900,14 @@ var topicsPublishCommand = new Command42("publish").description("Publish an even
1792
1900
  });
1793
1901
 
1794
1902
  // src/commands/topics/index.ts
1795
- var topicsCommand = new Command43("topics").description("Manage topics and pub/sub").addCommand(topicsListCommand).addCommand(topicsCreateCommand).addCommand(topicsGetCommand).addCommand(topicsDeleteCommand).addCommand(topicsSubscribeCommand).addCommand(topicsUnsubscribeCommand).addCommand(topicsPublishCommand);
1903
+ var topicsCommand = new Command44("topics").description("Manage topics and pub/sub").addCommand(topicsListCommand).addCommand(topicsCreateCommand).addCommand(topicsGetCommand).addCommand(topicsDeleteCommand).addCommand(topicsSubscribeCommand).addCommand(topicsUnsubscribeCommand).addCommand(topicsPublishCommand);
1796
1904
 
1797
1905
  // src/commands/replay/index.ts
1798
- import { Command as Command46 } from "commander";
1906
+ import { Command as Command47 } from "commander";
1799
1907
 
1800
1908
  // src/commands/replay/create.ts
1801
- import { Command as Command44 } from "commander";
1802
- var replayCreateCommand = new Command44("create").description("Create an event replay job").requiredOption("--destination <id>", "Destination ID to replay events to").requiredOption("--from <iso>", "Start time (ISO 8601)").requiredOption("--to <iso>", "End time (ISO 8601)").option("--source <id>", "Filter by source ID").option("--rate-limit <n>", "Max events per second", parseInt).option("--max-events <n>", "Maximum events to replay", parseInt).action(async (opts) => {
1909
+ import { Command as Command45 } from "commander";
1910
+ var replayCreateCommand = new Command45("create").description("Create an event replay job").requiredOption("--destination <id>", "Destination ID to replay events to").requiredOption("--from <iso>", "Start time (ISO 8601)").requiredOption("--to <iso>", "End time (ISO 8601)").option("--source <id>", "Filter by source ID").option("--rate-limit <n>", "Max events per second", parseInt).option("--max-events <n>", "Maximum events to replay", parseInt).action(async (opts) => {
1803
1911
  try {
1804
1912
  const client = new hookstreamClient();
1805
1913
  const body = {
@@ -1836,8 +1944,8 @@ var replayCreateCommand = new Command44("create").description("Create an event r
1836
1944
  });
1837
1945
 
1838
1946
  // src/commands/replay/status.ts
1839
- import { Command as Command45 } from "commander";
1840
- var replayStatusCommand = new Command45("status").description("Get replay job status").argument("<id>", "Replay job ID").action(async (id) => {
1947
+ import { Command as Command46 } from "commander";
1948
+ var replayStatusCommand = new Command46("status").description("Get replay job status").argument("<id>", "Replay job ID").action(async (id) => {
1841
1949
  try {
1842
1950
  const client = new hookstreamClient();
1843
1951
  const data = await client.get(`/replay/${id}`);
@@ -1870,15 +1978,15 @@ var replayStatusCommand = new Command45("status").description("Get replay job st
1870
1978
  });
1871
1979
 
1872
1980
  // src/commands/replay/index.ts
1873
- var replayCommand = new Command46("replay").description("Replay historical events").addCommand(replayCreateCommand).addCommand(replayStatusCommand);
1981
+ var replayCommand = new Command47("replay").description("Replay historical events").addCommand(replayCreateCommand).addCommand(replayStatusCommand);
1874
1982
 
1875
1983
  // src/commands/billing/index.ts
1876
- import { Command as Command49 } from "commander";
1984
+ import { Command as Command50 } from "commander";
1877
1985
 
1878
1986
  // src/commands/billing/usage.ts
1879
- import { Command as Command47 } from "commander";
1880
- import chalk6 from "chalk";
1881
- var billingUsageCommand = new Command47("usage").description("Show current plan usage").action(async () => {
1987
+ import { Command as Command48 } from "commander";
1988
+ import chalk7 from "chalk";
1989
+ var billingUsageCommand = new Command48("usage").description("Show current plan usage").action(async () => {
1882
1990
  try {
1883
1991
  const client = new hookstreamClient();
1884
1992
  const data = await client.get("/billing/usage");
@@ -1888,13 +1996,13 @@ var billingUsageCommand = new Command47("usage").description("Show current plan
1888
1996
  }
1889
1997
  const pct = data.events.percentage;
1890
1998
  const bar = progressBar(pct, 30);
1891
- const pctColor = pct >= 90 ? chalk6.red : pct >= 80 ? chalk6.yellow : chalk6.green;
1999
+ const pctColor = pct >= 90 ? chalk7.red : pct >= 80 ? chalk7.yellow : chalk7.green;
1892
2000
  console.log();
1893
- console.log(chalk6.bold(" Plan:"), chalk6.cyan(data.plan.toUpperCase()));
2001
+ console.log(chalk7.bold(" Plan:"), chalk7.cyan(data.plan.toUpperCase()));
1894
2002
  console.log();
1895
- console.log(chalk6.dim(" Events this month"));
2003
+ console.log(chalk7.dim(" Events this month"));
1896
2004
  console.log(` ${bar} ${pctColor(`${pct}%`)}`);
1897
- console.log(chalk6.dim(` ${data.events.used.toLocaleString()} / ${data.events.limit === Infinity ? "Unlimited" : data.events.limit.toLocaleString()}`));
2005
+ console.log(chalk7.dim(` ${data.events.used.toLocaleString()} / ${data.events.limit === Infinity ? "Unlimited" : data.events.limit.toLocaleString()}`));
1898
2006
  console.log();
1899
2007
  printKeyValue([
1900
2008
  ["Sources", data.sources.limit === Infinity ? `${data.sources.used} (unlimited)` : `${data.sources.used} / ${data.sources.limit}`],
@@ -1910,14 +2018,14 @@ var billingUsageCommand = new Command47("usage").description("Show current plan
1910
2018
  function progressBar(pct, width) {
1911
2019
  const filled = Math.round(Math.min(pct, 100) / 100 * width);
1912
2020
  const empty = width - filled;
1913
- const color = pct >= 90 ? chalk6.red : pct >= 80 ? chalk6.yellow : chalk6.green;
1914
- return color("\u2588".repeat(filled)) + chalk6.dim("\u2591".repeat(empty));
2021
+ const color = pct >= 90 ? chalk7.red : pct >= 80 ? chalk7.yellow : chalk7.green;
2022
+ return color("\u2588".repeat(filled)) + chalk7.dim("\u2591".repeat(empty));
1915
2023
  }
1916
2024
 
1917
2025
  // src/commands/billing/subscription.ts
1918
- import { Command as Command48 } from "commander";
1919
- import chalk7 from "chalk";
1920
- var billingSubscriptionCommand = new Command48("subscription").description("Show current subscription details").action(async () => {
2026
+ import { Command as Command49 } from "commander";
2027
+ import chalk8 from "chalk";
2028
+ var billingSubscriptionCommand = new Command49("subscription").description("Show current subscription details").action(async () => {
1921
2029
  try {
1922
2030
  const client = new hookstreamClient();
1923
2031
  const data = await client.get("/billing/subscription");
@@ -1927,17 +2035,17 @@ var billingSubscriptionCommand = new Command48("subscription").description("Show
1927
2035
  }
1928
2036
  console.log();
1929
2037
  if (!data.subscription) {
1930
- console.log(chalk7.dim(" No active subscription. You are on the Free plan."));
1931
- console.log(chalk7.dim(" Upgrade at https://hookstream.io/settings?tab=billing"));
2038
+ console.log(chalk8.dim(" No active subscription. You are on the Free plan."));
2039
+ console.log(chalk8.dim(" Upgrade at https://hookstream.io/settings?tab=billing"));
1932
2040
  } else {
1933
2041
  const sub = data.subscription;
1934
- const statusColor2 = sub.status === "active" ? chalk7.green : sub.status === "past_due" ? chalk7.yellow : chalk7.red;
2042
+ const statusColor2 = sub.status === "active" ? chalk8.green : sub.status === "past_due" ? chalk8.yellow : chalk8.red;
1935
2043
  printKeyValue([
1936
- ["Plan", chalk7.cyan(sub.plan.toUpperCase())],
2044
+ ["Plan", chalk8.cyan(sub.plan.toUpperCase())],
1937
2045
  ["Status", statusColor2(sub.status)],
1938
2046
  ["Period Start", formatDate(sub.current_period_start)],
1939
2047
  ["Period End", formatDate(sub.current_period_end)],
1940
- ["Cancel at Period End", sub.cancel_at_period_end ? chalk7.yellow("Yes") : "No"],
2048
+ ["Cancel at Period End", sub.cancel_at_period_end ? chalk8.yellow("Yes") : "No"],
1941
2049
  ["Canceled At", sub.canceled_at ? formatDate(sub.canceled_at) : "\u2014"]
1942
2050
  ]);
1943
2051
  }
@@ -1949,15 +2057,750 @@ var billingSubscriptionCommand = new Command48("subscription").description("Show
1949
2057
  });
1950
2058
 
1951
2059
  // src/commands/billing/index.ts
1952
- var billingCommand = new Command49("billing").description("View billing, usage, and subscription info").addCommand(billingUsageCommand).addCommand(billingSubscriptionCommand);
2060
+ var billingCommand = new Command50("billing").description("View billing, usage, and subscription info").addCommand(billingUsageCommand).addCommand(billingSubscriptionCommand);
2061
+
2062
+ // src/commands/issues/index.ts
2063
+ import { Command as Command55 } from "commander";
2064
+
2065
+ // src/commands/issues/list.ts
2066
+ import { Command as Command51 } from "commander";
2067
+ var issuesListCommand = new Command51("list").description("List delivery issues").option("--status <status>", "Filter by status (open, acknowledged, resolved, all)", "open").option("--destination <id>", "Filter by destination ID").option("--limit <n>", "Max results", "50").action(async (opts) => {
2068
+ try {
2069
+ const client = new hookstreamClient();
2070
+ const params = new URLSearchParams();
2071
+ if (opts.status) params.set("status", opts.status);
2072
+ if (opts.destination) params.set("destination_id", opts.destination);
2073
+ if (opts.limit) params.set("limit", opts.limit);
2074
+ const qs = params.toString();
2075
+ const data = await client.get(`/issues${qs ? `?${qs}` : ""}`);
2076
+ if (isJsonMode()) {
2077
+ printJson(data);
2078
+ return;
2079
+ }
2080
+ console.log();
2081
+ printTable(
2082
+ data.issues.map((i) => ({
2083
+ id: i.id.slice(0, 12),
2084
+ status: statusColor(i.status),
2085
+ type: i.issue_type,
2086
+ destination: truncate(i.destination_name || i.destination_id, 18),
2087
+ occurrences: String(i.occurrence_count),
2088
+ last_seen: formatDate(i.last_seen_at)
2089
+ })),
2090
+ [
2091
+ { key: "id", label: "ID", width: 14 },
2092
+ { key: "status", label: "Status", width: 14 },
2093
+ { key: "type", label: "Type", width: 16 },
2094
+ { key: "destination", label: "Destination", width: 20 },
2095
+ { key: "occurrences", label: "Occurrences", width: 13 },
2096
+ { key: "last_seen", label: "Last Seen" }
2097
+ ]
2098
+ );
2099
+ console.log();
2100
+ } catch (err) {
2101
+ printError(err instanceof Error ? err.message : String(err));
2102
+ process.exit(1);
2103
+ }
2104
+ });
2105
+
2106
+ // src/commands/issues/get.ts
2107
+ import { Command as Command52 } from "commander";
2108
+ var issuesGetCommand = new Command52("get").description("Get issue details").argument("<id>", "Issue ID").action(async (id) => {
2109
+ try {
2110
+ const client = new hookstreamClient();
2111
+ const data = await client.get(`/issues/${id}`);
2112
+ const i = data.issue;
2113
+ if (isJsonMode()) {
2114
+ printJson(i);
2115
+ return;
2116
+ }
2117
+ console.log();
2118
+ printKeyValue([
2119
+ ["ID", i.id],
2120
+ ["Status", statusColor(i.status)],
2121
+ ["Type", i.issue_type],
2122
+ ["Destination", i.destination_name || i.destination_id],
2123
+ ["Error Pattern", i.error_pattern || "\u2014"],
2124
+ ["Occurrences", String(i.occurrence_count)],
2125
+ ["First Seen", formatDate(i.first_seen_at)],
2126
+ ["Last Seen", formatDate(i.last_seen_at)],
2127
+ ["Resolved At", formatDate(i.resolved_at)],
2128
+ ["Created", formatDate(i.created_at)],
2129
+ ["Updated", formatDate(i.updated_at)]
2130
+ ]);
2131
+ console.log();
2132
+ } catch (err) {
2133
+ printError(err instanceof Error ? err.message : String(err));
2134
+ process.exit(1);
2135
+ }
2136
+ });
2137
+
2138
+ // src/commands/issues/update.ts
2139
+ import { Command as Command53 } from "commander";
2140
+ var issuesUpdateCommand = new Command53("update").description("Update issue status").argument("<id>", "Issue ID").requiredOption("--status <status>", "New status (open, acknowledged, resolved)").action(async (id, opts) => {
2141
+ try {
2142
+ const validStatuses = ["open", "acknowledged", "resolved"];
2143
+ if (!validStatuses.includes(opts.status)) {
2144
+ printError(`Invalid status. Must be one of: ${validStatuses.join(", ")}`);
2145
+ process.exit(1);
2146
+ }
2147
+ const client = new hookstreamClient();
2148
+ const data = await client.patch(`/issues/${id}`, {
2149
+ status: opts.status
2150
+ });
2151
+ if (isJsonMode()) {
2152
+ printJson(data.issue);
2153
+ return;
2154
+ }
2155
+ printSuccess(`Issue ${id} updated to ${opts.status}.`);
2156
+ } catch (err) {
2157
+ printError(err instanceof Error ? err.message : String(err));
2158
+ process.exit(1);
2159
+ }
2160
+ });
2161
+
2162
+ // src/commands/issues/retry.ts
2163
+ import { Command as Command54 } from "commander";
2164
+ var issuesRetryCommand = new Command54("retry").description("Retry failed deliveries for an issue").argument("<id>", "Issue ID").action(async (id) => {
2165
+ try {
2166
+ const client = new hookstreamClient();
2167
+ const data = await client.post(`/issues/${id}/retry`);
2168
+ if (isJsonMode()) {
2169
+ printJson(data);
2170
+ return;
2171
+ }
2172
+ printSuccess(`Retried ${data.retried}/${data.total} failed deliveries.`);
2173
+ } catch (err) {
2174
+ printError(err instanceof Error ? err.message : String(err));
2175
+ process.exit(1);
2176
+ }
2177
+ });
2178
+
2179
+ // src/commands/issues/index.ts
2180
+ var issuesCommand = new Command55("issues").description("View and manage delivery issues").addCommand(issuesListCommand).addCommand(issuesGetCommand).addCommand(issuesUpdateCommand).addCommand(issuesRetryCommand);
2181
+
2182
+ // src/commands/alert-rules/index.ts
2183
+ import { Command as Command60 } from "commander";
2184
+
2185
+ // src/commands/alert-rules/list.ts
2186
+ import { Command as Command56 } from "commander";
2187
+ var alertsListCommand = new Command56("list").description("List all alert rules").action(async () => {
2188
+ try {
2189
+ const client = new hookstreamClient();
2190
+ const data = await client.get("/alert-rules");
2191
+ if (isJsonMode()) {
2192
+ printJson(data.alert_rules);
2193
+ return;
2194
+ }
2195
+ console.log();
2196
+ printTable(
2197
+ data.alert_rules.map((r) => ({
2198
+ id: r.id.slice(0, 12),
2199
+ name: r.name,
2200
+ type: r.rule_type,
2201
+ status: statusColor(r.enabled ? "active" : "disabled"),
2202
+ cooldown: `${r.cooldown_minutes}m`,
2203
+ last_triggered: formatDate(r.last_triggered_at)
2204
+ })),
2205
+ [
2206
+ { key: "id", label: "ID", width: 14 },
2207
+ { key: "name", label: "Name", width: 22 },
2208
+ { key: "type", label: "Type", width: 16 },
2209
+ { key: "status", label: "Status", width: 10 },
2210
+ { key: "cooldown", label: "Cooldown", width: 10 },
2211
+ { key: "last_triggered", label: "Last Triggered" }
2212
+ ]
2213
+ );
2214
+ console.log();
2215
+ } catch (err) {
2216
+ printError(err instanceof Error ? err.message : String(err));
2217
+ process.exit(1);
2218
+ }
2219
+ });
2220
+
2221
+ // src/commands/alert-rules/create.ts
2222
+ import { Command as Command57 } from "commander";
2223
+ var alertsCreateCommand = new Command57("create").description("Create a new alert rule").argument("<name>", "Alert rule name").requiredOption("--type <type>", "Rule type (failure_rate, latency, dlq, volume_drop, issue_opened)").option("--description <text>", "Rule description").option("--source <id>", "Source ID to scope the alert to").option("--destination <id>", "Destination ID to scope the alert to").option("--channel <id>", "Notification channel ID (repeatable)", (val, acc) => {
2224
+ acc.push(val);
2225
+ return acc;
2226
+ }, []).option("--cooldown <minutes>", "Cooldown between alerts in minutes", "60").option("--config <json>", "Rule config as JSON (e.g. threshold values)").action(async (name, opts) => {
2227
+ try {
2228
+ const validTypes = ["failure_rate", "latency", "dlq", "volume_drop", "issue_opened"];
2229
+ if (!validTypes.includes(opts.type)) {
2230
+ printError(`Invalid type. Must be one of: ${validTypes.join(", ")}`);
2231
+ process.exit(1);
2232
+ }
2233
+ let ruleConfig = {};
2234
+ if (opts.config) {
2235
+ try {
2236
+ ruleConfig = JSON.parse(opts.config);
2237
+ } catch {
2238
+ printError("Invalid JSON for --config");
2239
+ process.exit(1);
2240
+ }
2241
+ }
2242
+ const client = new hookstreamClient();
2243
+ const data = await client.post("/alert-rules", {
2244
+ name,
2245
+ rule_type: opts.type,
2246
+ description: opts.description || void 0,
2247
+ source_id: opts.source || void 0,
2248
+ destination_id: opts.destination || void 0,
2249
+ channel_ids: opts.channel.length > 0 ? opts.channel : void 0,
2250
+ cooldown_minutes: parseInt(opts.cooldown, 10),
2251
+ rule_config: Object.keys(ruleConfig).length > 0 ? ruleConfig : void 0
2252
+ });
2253
+ const r = data.alert_rule;
2254
+ if (isJsonMode()) {
2255
+ printJson(r);
2256
+ return;
2257
+ }
2258
+ printSuccess(`Alert rule created: ${r.name}`);
2259
+ console.log();
2260
+ printKeyValue([
2261
+ ["ID", r.id],
2262
+ ["Name", r.name],
2263
+ ["Type", r.rule_type],
2264
+ ["Cooldown", `${r.cooldown_minutes}m`]
2265
+ ]);
2266
+ console.log();
2267
+ } catch (err) {
2268
+ printError(err instanceof Error ? err.message : String(err));
2269
+ process.exit(1);
2270
+ }
2271
+ });
2272
+
2273
+ // src/commands/alert-rules/get.ts
2274
+ import { Command as Command58 } from "commander";
2275
+ var alertsGetCommand = new Command58("get").description("Get alert rule details").argument("<id>", "Alert rule ID").action(async (id) => {
2276
+ try {
2277
+ const client = new hookstreamClient();
2278
+ const data = await client.get(`/alert-rules/${id}`);
2279
+ const r = data.alert_rule;
2280
+ if (isJsonMode()) {
2281
+ printJson(data);
2282
+ return;
2283
+ }
2284
+ console.log();
2285
+ printKeyValue([
2286
+ ["ID", r.id],
2287
+ ["Name", r.name],
2288
+ ["Description", r.description || "\u2014"],
2289
+ ["Type", r.rule_type],
2290
+ ["Status", statusColor(r.enabled ? "active" : "disabled")],
2291
+ ["Source", r.source_id || "all"],
2292
+ ["Destination", r.destination_id || "all"],
2293
+ ["Channels", r.channel_ids || "none"],
2294
+ ["Cooldown", `${r.cooldown_minutes}m`],
2295
+ ["Config", r.rule_config || "{}"],
2296
+ ["Last Triggered", formatDate(r.last_triggered_at)],
2297
+ ["Created", formatDate(r.created_at)],
2298
+ ["Updated", formatDate(r.updated_at)]
2299
+ ]);
2300
+ if (data.history && data.history.length > 0) {
2301
+ console.log();
2302
+ console.log(` Recent history: ${data.history.length} entries`);
2303
+ }
2304
+ console.log();
2305
+ } catch (err) {
2306
+ printError(err instanceof Error ? err.message : String(err));
2307
+ process.exit(1);
2308
+ }
2309
+ });
2310
+
2311
+ // src/commands/alert-rules/delete.ts
2312
+ import { createInterface as createInterface6 } from "readline/promises";
2313
+ import { stdin as stdin6, stdout as stdout6 } from "process";
2314
+ import { Command as Command59 } from "commander";
2315
+ var alertsDeleteCommand = new Command59("delete").description("Delete an alert rule").argument("<id>", "Alert rule ID").option("-f, --force", "Skip confirmation prompt").action(async (id, opts) => {
2316
+ try {
2317
+ if (!opts.force) {
2318
+ const rl = createInterface6({ input: stdin6, output: stdout6 });
2319
+ const answer = await rl.question(`Delete alert rule ${id}? This cannot be undone. [y/N] `);
2320
+ rl.close();
2321
+ if (answer.toLowerCase() !== "y") {
2322
+ console.log("Cancelled.");
2323
+ return;
2324
+ }
2325
+ }
2326
+ const client = new hookstreamClient();
2327
+ await client.del(`/alert-rules/${id}`);
2328
+ if (isJsonMode()) {
2329
+ printJson({ success: true, id });
2330
+ } else {
2331
+ printSuccess(`Alert rule ${id} deleted.`);
2332
+ }
2333
+ } catch (err) {
2334
+ printError(err instanceof Error ? err.message : String(err));
2335
+ process.exit(1);
2336
+ }
2337
+ });
2338
+
2339
+ // src/commands/alert-rules/index.ts
2340
+ var alertsCommand = new Command60("alerts").description("Manage alert rules").addCommand(alertsListCommand).addCommand(alertsCreateCommand).addCommand(alertsGetCommand).addCommand(alertsDeleteCommand);
2341
+
2342
+ // src/commands/channels/index.ts
2343
+ import { Command as Command66 } from "commander";
2344
+
2345
+ // src/commands/channels/list.ts
2346
+ import { Command as Command61 } from "commander";
2347
+ var channelsListCommand = new Command61("list").description("List all notification channels").action(async () => {
2348
+ try {
2349
+ const client = new hookstreamClient();
2350
+ const data = await client.get("/notification-channels");
2351
+ if (isJsonMode()) {
2352
+ printJson(data.channels);
2353
+ return;
2354
+ }
2355
+ console.log();
2356
+ printTable(
2357
+ data.channels.map((ch) => ({
2358
+ id: ch.id.slice(0, 12),
2359
+ name: ch.name,
2360
+ type: ch.channel_type,
2361
+ status: statusColor(ch.enabled ? "active" : "disabled"),
2362
+ created: formatDate(ch.created_at)
2363
+ })),
2364
+ [
2365
+ { key: "id", label: "ID", width: 14 },
2366
+ { key: "name", label: "Name", width: 22 },
2367
+ { key: "type", label: "Type", width: 10 },
2368
+ { key: "status", label: "Status", width: 10 },
2369
+ { key: "created", label: "Created" }
2370
+ ]
2371
+ );
2372
+ console.log();
2373
+ } catch (err) {
2374
+ printError(err instanceof Error ? err.message : String(err));
2375
+ process.exit(1);
2376
+ }
2377
+ });
2378
+
2379
+ // src/commands/channels/create.ts
2380
+ import { Command as Command62 } from "commander";
2381
+ var channelsCreateCommand = new Command62("create").description("Create a new notification channel").argument("<name>", "Channel name").requiredOption("--type <type>", "Channel type (webhook, slack, discord, email)").requiredOption("--url <url>", "Webhook URL").option("--config <json>", "Extra config as JSON").action(async (name, opts) => {
2382
+ try {
2383
+ const validTypes = ["webhook", "slack", "discord", "email"];
2384
+ if (!validTypes.includes(opts.type)) {
2385
+ printError(`Invalid type. Must be one of: ${validTypes.join(", ")}`);
2386
+ process.exit(1);
2387
+ }
2388
+ let extraConfig = {};
2389
+ if (opts.config) {
2390
+ try {
2391
+ extraConfig = JSON.parse(opts.config);
2392
+ } catch {
2393
+ printError("Invalid JSON for --config");
2394
+ process.exit(1);
2395
+ }
2396
+ }
2397
+ const config = {
2398
+ url: opts.url,
2399
+ ...extraConfig
2400
+ };
2401
+ const client = new hookstreamClient();
2402
+ const data = await client.post("/notification-channels", {
2403
+ name,
2404
+ channel_type: opts.type,
2405
+ config
2406
+ });
2407
+ const ch = data.channel;
2408
+ if (isJsonMode()) {
2409
+ printJson(ch);
2410
+ return;
2411
+ }
2412
+ printSuccess(`Notification channel created: ${ch.name}`);
2413
+ console.log();
2414
+ printKeyValue([
2415
+ ["ID", ch.id],
2416
+ ["Name", ch.name],
2417
+ ["Type", ch.channel_type]
2418
+ ]);
2419
+ console.log();
2420
+ } catch (err) {
2421
+ printError(err instanceof Error ? err.message : String(err));
2422
+ process.exit(1);
2423
+ }
2424
+ });
2425
+
2426
+ // src/commands/channels/get.ts
2427
+ import { Command as Command63 } from "commander";
2428
+ var channelsGetCommand = new Command63("get").description("Get notification channel details").argument("<id>", "Channel ID").action(async (id) => {
2429
+ try {
2430
+ const client = new hookstreamClient();
2431
+ const data = await client.get(`/notification-channels/${id}`);
2432
+ const ch = data.channel;
2433
+ if (isJsonMode()) {
2434
+ printJson(ch);
2435
+ return;
2436
+ }
2437
+ console.log();
2438
+ printKeyValue([
2439
+ ["ID", ch.id],
2440
+ ["Name", ch.name],
2441
+ ["Type", ch.channel_type],
2442
+ ["Status", statusColor(ch.enabled ? "active" : "disabled")],
2443
+ ["Config", ch.config || "{}"],
2444
+ ["Last Sent", formatDate(ch.last_sent_at)],
2445
+ ["Error Count", String(ch.error_count)],
2446
+ ["Last Error", ch.last_error || "\u2014"],
2447
+ ["Created", formatDate(ch.created_at)],
2448
+ ["Updated", formatDate(ch.updated_at)]
2449
+ ]);
2450
+ console.log();
2451
+ } catch (err) {
2452
+ printError(err instanceof Error ? err.message : String(err));
2453
+ process.exit(1);
2454
+ }
2455
+ });
2456
+
2457
+ // src/commands/channels/delete.ts
2458
+ import { createInterface as createInterface7 } from "readline/promises";
2459
+ import { stdin as stdin7, stdout as stdout7 } from "process";
2460
+ import { Command as Command64 } from "commander";
2461
+ var channelsDeleteCommand = new Command64("delete").description("Delete a notification channel").argument("<id>", "Channel ID").option("-f, --force", "Skip confirmation prompt").action(async (id, opts) => {
2462
+ try {
2463
+ if (!opts.force) {
2464
+ const rl = createInterface7({ input: stdin7, output: stdout7 });
2465
+ const answer = await rl.question(`Delete notification channel ${id}? This cannot be undone. [y/N] `);
2466
+ rl.close();
2467
+ if (answer.toLowerCase() !== "y") {
2468
+ console.log("Cancelled.");
2469
+ return;
2470
+ }
2471
+ }
2472
+ const client = new hookstreamClient();
2473
+ await client.del(`/notification-channels/${id}`);
2474
+ if (isJsonMode()) {
2475
+ printJson({ success: true, id });
2476
+ } else {
2477
+ printSuccess(`Notification channel ${id} deleted.`);
2478
+ }
2479
+ } catch (err) {
2480
+ printError(err instanceof Error ? err.message : String(err));
2481
+ process.exit(1);
2482
+ }
2483
+ });
2484
+
2485
+ // src/commands/channels/test.ts
2486
+ import { Command as Command65 } from "commander";
2487
+ var channelsTestCommand = new Command65("test").description("Send a test notification to a channel").argument("<id>", "Channel ID").action(async (id) => {
2488
+ try {
2489
+ const client = new hookstreamClient();
2490
+ const data = await client.post(`/notification-channels/${id}/test`);
2491
+ if (isJsonMode()) {
2492
+ printJson(data);
2493
+ return;
2494
+ }
2495
+ if (data.success) {
2496
+ printSuccess("Test notification sent successfully.");
2497
+ } else {
2498
+ printError(`Test notification failed: ${data.error || "unknown error"}`);
2499
+ process.exit(1);
2500
+ }
2501
+ } catch (err) {
2502
+ printError(err instanceof Error ? err.message : String(err));
2503
+ process.exit(1);
2504
+ }
2505
+ });
2506
+
2507
+ // src/commands/channels/index.ts
2508
+ var channelsCommand = new Command66("channels").description("Manage notification channels").addCommand(channelsListCommand).addCommand(channelsCreateCommand).addCommand(channelsGetCommand).addCommand(channelsDeleteCommand).addCommand(channelsTestCommand);
2509
+
2510
+ // src/commands/collections/index.ts
2511
+ import { Command as Command71 } from "commander";
2512
+
2513
+ // src/commands/collections/list.ts
2514
+ import { Command as Command67 } from "commander";
2515
+ var collectionsListCommand = new Command67("list").description("List all collections").action(async () => {
2516
+ try {
2517
+ const client = new hookstreamClient();
2518
+ const data = await client.get("/collections");
2519
+ if (isJsonMode()) {
2520
+ printJson(data.collections);
2521
+ return;
2522
+ }
2523
+ console.log();
2524
+ printTable(
2525
+ data.collections.map((c) => ({
2526
+ id: c.id.slice(0, 12),
2527
+ name: c.name,
2528
+ slug: c.slug,
2529
+ source: truncate(c.source_name || c.source_id, 16),
2530
+ records: String(c.record_count),
2531
+ created: formatDate(c.created_at)
2532
+ })),
2533
+ [
2534
+ { key: "id", label: "ID", width: 14 },
2535
+ { key: "name", label: "Name", width: 20 },
2536
+ { key: "slug", label: "Slug", width: 16 },
2537
+ { key: "source", label: "Source", width: 18 },
2538
+ { key: "records", label: "Records", width: 10 },
2539
+ { key: "created", label: "Created" }
2540
+ ]
2541
+ );
2542
+ console.log();
2543
+ } catch (err) {
2544
+ printError(err instanceof Error ? err.message : String(err));
2545
+ process.exit(1);
2546
+ }
2547
+ });
2548
+
2549
+ // src/commands/collections/get.ts
2550
+ import { Command as Command68 } from "commander";
2551
+ var collectionsGetCommand = new Command68("get").description("Get collection details").argument("<id>", "Collection ID").action(async (id) => {
2552
+ try {
2553
+ const client = new hookstreamClient();
2554
+ const data = await client.get(`/collections/${id}`);
2555
+ const c = data.collection;
2556
+ if (isJsonMode()) {
2557
+ printJson(c);
2558
+ return;
2559
+ }
2560
+ console.log();
2561
+ printKeyValue([
2562
+ ["ID", c.id],
2563
+ ["Name", c.name],
2564
+ ["Slug", c.slug],
2565
+ ["Status", statusColor(c.status)],
2566
+ ["Source", c.source_name || c.source_id],
2567
+ ["Primary Key", c.primary_key_path],
2568
+ ["Event Type Field", c.event_type_field || "\u2014"],
2569
+ ["Description", c.description || "\u2014"],
2570
+ ["Records", String(c.record_count)],
2571
+ ["Last Sync", formatDate(c.last_sync_at)],
2572
+ ["Created", formatDate(c.created_at)],
2573
+ ["Updated", formatDate(c.updated_at)]
2574
+ ]);
2575
+ console.log();
2576
+ } catch (err) {
2577
+ printError(err instanceof Error ? err.message : String(err));
2578
+ process.exit(1);
2579
+ }
2580
+ });
2581
+
2582
+ // src/commands/collections/stats.ts
2583
+ import { Command as Command69 } from "commander";
2584
+ var collectionsStatsCommand = new Command69("stats").description("Get collection statistics").argument("<id>", "Collection ID").action(async (id) => {
2585
+ try {
2586
+ const client = new hookstreamClient();
2587
+ const data = await client.get(`/collections/${id}/stats`);
2588
+ if (isJsonMode()) {
2589
+ printJson(data);
2590
+ return;
2591
+ }
2592
+ console.log();
2593
+ printKeyValue([
2594
+ ["Record Count", String(data.record_count)],
2595
+ ["Changelog Entries", String(data.changelog_entries)],
2596
+ ["Last Sync", formatDate(data.last_sync_at)]
2597
+ ]);
2598
+ console.log();
2599
+ } catch (err) {
2600
+ printError(err instanceof Error ? err.message : String(err));
2601
+ process.exit(1);
2602
+ }
2603
+ });
2604
+
2605
+ // src/commands/collections/export.ts
2606
+ import { writeFileSync as writeFileSync2 } from "fs";
2607
+ import { Command as Command70 } from "commander";
2608
+ var collectionsExportCommand = new Command70("export").description("Export collection data as NDJSON or CSV").argument("<id>", "Collection ID").option("--format <format>", "Export format (ndjson, csv)", "ndjson").option("--output <path>", "Output file path (prints to stdout if omitted)").action(async (id, opts) => {
2609
+ try {
2610
+ const validFormats = ["ndjson", "csv"];
2611
+ if (!validFormats.includes(opts.format)) {
2612
+ printError(`Invalid format. Must be one of: ${validFormats.join(", ")}`);
2613
+ process.exit(1);
2614
+ }
2615
+ const client = new hookstreamClient();
2616
+ const config = loadConfig();
2617
+ const apiKey = process.env.__HS_CLI_API_KEY || process.env.HOOKSTREAM_API_KEY || config.api_key || "";
2618
+ const apiFormat = opts.format === "ndjson" ? "json" : "csv";
2619
+ const url = `${client.getBaseUrl()}/v1/collections/${id}/export?format=${apiFormat}`;
2620
+ const res = await fetch(url, {
2621
+ headers: {
2622
+ "X-API-Key": apiKey
2623
+ }
2624
+ });
2625
+ if (!res.ok) {
2626
+ const body = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
2627
+ throw new Error(body.error || `HTTP ${res.status}`);
2628
+ }
2629
+ const text = await res.text();
2630
+ if (opts.output) {
2631
+ writeFileSync2(opts.output, text, "utf-8");
2632
+ if (isJsonMode()) {
2633
+ printJson({ success: true, path: opts.output, bytes: text.length });
2634
+ } else {
2635
+ printSuccess(`Exported to ${opts.output} (${text.length} bytes)`);
2636
+ }
2637
+ } else {
2638
+ process.stdout.write(text);
2639
+ }
2640
+ } catch (err) {
2641
+ printError(err instanceof Error ? err.message : String(err));
2642
+ process.exit(1);
2643
+ }
2644
+ });
2645
+
2646
+ // src/commands/collections/index.ts
2647
+ var collectionsCommand = new Command71("collections").description("Manage instant database collections").addCommand(collectionsListCommand).addCommand(collectionsGetCommand).addCommand(collectionsStatsCommand).addCommand(collectionsExportCommand);
2648
+
2649
+ // src/commands/applications/index.ts
2650
+ import { Command as Command76 } from "commander";
2651
+
2652
+ // src/commands/applications/list.ts
2653
+ import { Command as Command72 } from "commander";
2654
+ var applicationsListCommand = new Command72("list").description("List all outbound webhook applications").option("--search <query>", "Search by name or UID").option("--limit <n>", "Max results", "50").action(async (opts) => {
2655
+ try {
2656
+ const client = new hookstreamClient();
2657
+ const params = new URLSearchParams();
2658
+ if (opts.search) params.set("search", opts.search);
2659
+ if (opts.limit) params.set("limit", opts.limit);
2660
+ const qs = params.toString();
2661
+ const data = await client.get(`/applications${qs ? `?${qs}` : ""}`);
2662
+ if (isJsonMode()) {
2663
+ printJson(data.applications);
2664
+ return;
2665
+ }
2666
+ console.log();
2667
+ printTable(
2668
+ data.applications.map((a) => ({
2669
+ id: a.id.slice(0, 12),
2670
+ name: a.name,
2671
+ status: statusColor(a.status),
2672
+ endpoints: String(a.endpoint_count),
2673
+ messages: String(a.message_count),
2674
+ created: formatDate(a.created_at)
2675
+ })),
2676
+ [
2677
+ { key: "id", label: "ID", width: 14 },
2678
+ { key: "name", label: "Name", width: 22 },
2679
+ { key: "status", label: "Status", width: 10 },
2680
+ { key: "endpoints", label: "Endpoints", width: 11 },
2681
+ { key: "messages", label: "Messages", width: 10 },
2682
+ { key: "created", label: "Created" }
2683
+ ]
2684
+ );
2685
+ console.log();
2686
+ } catch (err) {
2687
+ printError(err instanceof Error ? err.message : String(err));
2688
+ process.exit(1);
2689
+ }
2690
+ });
2691
+
2692
+ // src/commands/applications/create.ts
2693
+ import { Command as Command73 } from "commander";
2694
+ var applicationsCreateCommand = new Command73("create").description("Create a new outbound webhook application").argument("<name>", "Application name").option("--uid <uid>", "Unique application identifier").option("--metadata <json>", "Application metadata as JSON").action(async (name, opts) => {
2695
+ try {
2696
+ let metadata;
2697
+ if (opts.metadata) {
2698
+ try {
2699
+ metadata = JSON.parse(opts.metadata);
2700
+ } catch {
2701
+ printError("Invalid JSON for --metadata");
2702
+ process.exit(1);
2703
+ }
2704
+ }
2705
+ const uid = opts.uid || name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
2706
+ const client = new hookstreamClient();
2707
+ const data = await client.post("/applications", {
2708
+ name,
2709
+ uid,
2710
+ metadata
2711
+ });
2712
+ const a = data.application;
2713
+ if (isJsonMode()) {
2714
+ printJson(a);
2715
+ return;
2716
+ }
2717
+ printSuccess(`Application created: ${a.name}`);
2718
+ console.log();
2719
+ printKeyValue([
2720
+ ["ID", a.id],
2721
+ ["Name", a.name],
2722
+ ["UID", a.uid],
2723
+ ["Status", a.status]
2724
+ ]);
2725
+ console.log();
2726
+ } catch (err) {
2727
+ printError(err instanceof Error ? err.message : String(err));
2728
+ process.exit(1);
2729
+ }
2730
+ });
2731
+
2732
+ // src/commands/applications/get.ts
2733
+ import { Command as Command74 } from "commander";
2734
+ var applicationsGetCommand = new Command74("get").description("Get application details").argument("<id>", "Application ID").action(async (id) => {
2735
+ try {
2736
+ const client = new hookstreamClient();
2737
+ const data = await client.get(`/applications/${id}`);
2738
+ const a = data.application;
2739
+ if (isJsonMode()) {
2740
+ printJson(a);
2741
+ return;
2742
+ }
2743
+ console.log();
2744
+ printKeyValue([
2745
+ ["ID", a.id],
2746
+ ["Name", a.name],
2747
+ ["UID", a.uid],
2748
+ ["Status", statusColor(a.status)],
2749
+ ["Description", a.description || "\u2014"],
2750
+ ["Metadata", a.metadata || "\u2014"],
2751
+ ["Rate Limit", a.rate_limit ? String(a.rate_limit) : "\u2014"],
2752
+ ["Endpoints", String(a.endpoint_count)],
2753
+ ["Messages", String(a.message_count)],
2754
+ ["Created", formatDate(a.created_at)],
2755
+ ["Updated", formatDate(a.updated_at)]
2756
+ ]);
2757
+ console.log();
2758
+ } catch (err) {
2759
+ printError(err instanceof Error ? err.message : String(err));
2760
+ process.exit(1);
2761
+ }
2762
+ });
2763
+
2764
+ // src/commands/applications/delete.ts
2765
+ import { createInterface as createInterface8 } from "readline/promises";
2766
+ import { stdin as stdin8, stdout as stdout8 } from "process";
2767
+ import { Command as Command75 } from "commander";
2768
+ var applicationsDeleteCommand = new Command75("delete").description("Delete an application").argument("<id>", "Application ID").option("-f, --force", "Skip confirmation prompt").action(async (id, opts) => {
2769
+ try {
2770
+ if (!opts.force) {
2771
+ const rl = createInterface8({ input: stdin8, output: stdout8 });
2772
+ const answer = await rl.question(`Delete application ${id}? This will remove all endpoints and messages. [y/N] `);
2773
+ rl.close();
2774
+ if (answer.toLowerCase() !== "y") {
2775
+ console.log("Cancelled.");
2776
+ return;
2777
+ }
2778
+ }
2779
+ const client = new hookstreamClient();
2780
+ await client.del(`/applications/${id}`);
2781
+ if (isJsonMode()) {
2782
+ printJson({ success: true, id });
2783
+ } else {
2784
+ printSuccess(`Application ${id} deleted.`);
2785
+ }
2786
+ } catch (err) {
2787
+ printError(err instanceof Error ? err.message : String(err));
2788
+ process.exit(1);
2789
+ }
2790
+ });
2791
+
2792
+ // src/commands/applications/index.ts
2793
+ var applicationsCommand = new Command76("applications").description("Manage outbound webhook applications").addCommand(applicationsListCommand).addCommand(applicationsCreateCommand).addCommand(applicationsGetCommand).addCommand(applicationsDeleteCommand);
1953
2794
 
1954
2795
  // src/index.ts
1955
- var program = new Command50();
1956
- program.name("hookstream").description("hookstream CLI \u2014 manage webhooks from the terminal").version("0.1.0").option("--json", "Output as JSON").hook("preAction", (thisCommand) => {
2796
+ var program = new Command77();
2797
+ program.name("hookstream").description("hookstream CLI \u2014 manage webhooks from the terminal").version("0.2.1").option("--json", "Output as JSON").option("--api-key <key>", "API key (overrides config + env)").option("--base-url <url>", "Base URL (overrides config + env)").hook("preAction", (thisCommand) => {
1957
2798
  const opts = thisCommand.optsWithGlobals();
1958
2799
  if (opts.json) {
1959
2800
  setJsonMode(true);
1960
2801
  }
2802
+ if (opts.apiKey) process.env.__HS_CLI_API_KEY = opts.apiKey;
2803
+ if (opts.baseUrl) process.env.__HS_CLI_BASE_URL = opts.baseUrl;
1961
2804
  });
1962
2805
  program.addCommand(loginCommand);
1963
2806
  program.addCommand(logoutCommand);
@@ -1973,4 +2816,9 @@ program.addCommand(listenCommand);
1973
2816
  program.addCommand(topicsCommand);
1974
2817
  program.addCommand(replayCommand);
1975
2818
  program.addCommand(billingCommand);
2819
+ program.addCommand(issuesCommand);
2820
+ program.addCommand(alertsCommand);
2821
+ program.addCommand(channelsCommand);
2822
+ program.addCommand(collectionsCommand);
2823
+ program.addCommand(applicationsCommand);
1976
2824
  program.parse();