@hyperline/cli 0.1.0-build.1.bf6bdf2 → 0.1.0-build.1.deb4fbd

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.
@@ -379,6 +379,287 @@ async function promptCompanySelection({ baseUrl, accessToken }) {
379
379
  `);
380
380
  }
381
381
 
382
+ // build/commands/confirm.js
383
+ import * as readline2 from "node:readline";
384
+ function confirmPrompt(message) {
385
+ const rl = readline2.createInterface({
386
+ input: process.stdin,
387
+ output: process.stderr
388
+ });
389
+ return new Promise((resolve) => {
390
+ rl.question(message, (answer) => {
391
+ rl.close();
392
+ resolve(answer.toLowerCase() === "y");
393
+ });
394
+ });
395
+ }
396
+
397
+ // build/commands/events.js
398
+ function resolveIngestBaseUrl(apiBaseUrl) {
399
+ return apiBaseUrl.replace("api.", "ingest.");
400
+ }
401
+ function registerEventsCommands(parent) {
402
+ const resource = parent.command("events").description("Manage billable events");
403
+ resource.command("list").description(`List billable events filtered by event type with optional filters for customer_id, record_id, and timestamp range. Paginated with take/skip.`).requiredOption("--event-type <value>", `Event type to filter by`).option("--take <number>", `take`).option("--skip <number>", `skip`).option("--customer-id <value>", `Filter by customer ID`).option("--record-id-in <value>", `Filter by record ID (comma-separated for multiple)`).option("--timestamp-gte <value>", `Filter events with timestamp >= this ISO8601 date`).option("--timestamp-lte <value>", `Filter events with timestamp <= this ISO8601 date`).option("--order <value>", `Sort order for timestamp`).addHelpText("after", `
404
+ Examples:
405
+ hyperline events list --event-type api_call
406
+ hyperline events list --event-type api_call --customer-id cus_xyz789 --take 10`).action(async (opts) => {
407
+ const ctx = resource.parent?.opts()._ctx;
408
+ if (!ctx) {
409
+ process.stderr.write("Error: Not authenticated\n");
410
+ process.exit(1);
411
+ }
412
+ const args = {};
413
+ if (opts.eventType !== void 0)
414
+ args.event_type = opts.eventType;
415
+ if (opts.customerId !== void 0)
416
+ args.customer_id = opts.customerId;
417
+ if (opts.recordIdIn !== void 0)
418
+ args.record_id__in = opts.recordIdIn;
419
+ if (opts.timestampGte !== void 0)
420
+ args.timestamp_gte = opts.timestampGte;
421
+ if (opts.timestampLte !== void 0)
422
+ args.timestamp_lte = opts.timestampLte;
423
+ if (opts.order !== void 0)
424
+ args.order = opts.order;
425
+ if (opts.take !== void 0)
426
+ args.take = Number(opts.take);
427
+ if (opts.skip !== void 0)
428
+ args.skip = Number(opts.skip);
429
+ await ctx.execute({
430
+ method: "GET",
431
+ path: "/v1/events",
432
+ args,
433
+ queryParamKeys: [
434
+ "event_type",
435
+ "customer_id",
436
+ "record_id__in",
437
+ "timestamp_gte",
438
+ "timestamp_lte",
439
+ "order",
440
+ "take",
441
+ "skip"
442
+ ],
443
+ baseUrlOverride: resolveIngestBaseUrl(ctx.baseUrl)
444
+ });
445
+ });
446
+ resource.command("send").description(`Send a single billable event.`).requiredOption("--customer-id <value>", `Customer ID or external ID`).requiredOption("--event-type <value>", `Event type`).requiredOption("--timestamp <value>", `ISO8601 timestamp`).requiredOption("--record <value>", `Event record as JSON (must include "id" key)`).option("--with-price-calculation", `Whether to calculate prices for the event`).addHelpText("after", `
447
+ Examples:
448
+ hyperline events send --customer-id cus_xyz --event-type api_call --timestamp 2024-01-15T10:00:00Z --record '{"id":"evt_1","duration":32}'
449
+ hyperline events send --customer-id cus_xyz --event-type api_call --timestamp 2024-01-15T10:00:00Z --record '{"id":"evt_1"}' --with-price-calculation`).action(async (opts) => {
450
+ const ctx = resource.parent?.opts()._ctx;
451
+ if (!ctx) {
452
+ process.stderr.write("Error: Not authenticated\n");
453
+ process.exit(1);
454
+ }
455
+ let record;
456
+ try {
457
+ record = JSON.parse(opts.record);
458
+ } catch {
459
+ process.stderr.write(`Error: --record must be valid JSON (e.g. '{"id":"evt_1"}')
460
+ `);
461
+ process.exit(1);
462
+ }
463
+ const body = {
464
+ customer_id: opts.customerId,
465
+ event_type: opts.eventType,
466
+ timestamp: opts.timestamp,
467
+ record
468
+ };
469
+ const args = {};
470
+ if (opts.withPriceCalculation !== void 0)
471
+ args.with_price_calculation = true;
472
+ await ctx.execute({
473
+ method: "POST",
474
+ path: "/v1/events",
475
+ args,
476
+ queryParamKeys: ["with_price_calculation"],
477
+ body,
478
+ baseUrlOverride: resolveIngestBaseUrl(ctx.baseUrl)
479
+ });
480
+ });
481
+ resource.command("send-batch").description(`Send multiple billable events in batch (max 5000 per request). All events must have the same event_type.`).requiredOption("--events <value>", `JSON array of events, each with customer_id, event_type, timestamp, record`).addHelpText("after", `
482
+ Examples:
483
+ hyperline events send-batch --events '[{"customer_id":"cus_xyz","event_type":"api_call","timestamp":"2024-01-15T10:00:00Z","record":{"id":"evt_1"}},{"customer_id":"cus_xyz","event_type":"api_call","timestamp":"2024-01-15T10:01:00Z","record":{"id":"evt_2"}}]'`).action(async (opts) => {
484
+ const ctx = resource.parent?.opts()._ctx;
485
+ if (!ctx) {
486
+ process.stderr.write("Error: Not authenticated\n");
487
+ process.exit(1);
488
+ }
489
+ let events;
490
+ try {
491
+ events = JSON.parse(opts.events);
492
+ } catch {
493
+ process.stderr.write("Error: --events must be valid JSON array\n");
494
+ process.exit(1);
495
+ }
496
+ if (!Array.isArray(events)) {
497
+ process.stderr.write("Error: --events must be a JSON array\n");
498
+ process.exit(1);
499
+ }
500
+ await ctx.execute({
501
+ method: "POST",
502
+ path: "/v1/events/batch",
503
+ args: {},
504
+ body: events,
505
+ baseUrlOverride: resolveIngestBaseUrl(ctx.baseUrl)
506
+ });
507
+ });
508
+ resource.command("delete").description(`Delete billable events by record IDs, customer ID, or all events.`).option("--matching <value>", `Matching strategy: record_ids, customer_id, or all`).option("--record-ids <value>", `Comma-separated list of record IDs (when matching=record_ids)`).option("--customer-id <value>", `Customer ID (when matching=customer_id)`).addHelpText("after", `
509
+ Examples:
510
+ hyperline events delete --matching record_ids --record-ids evt_1,evt_2
511
+ hyperline events delete --matching customer_id --customer-id cus_xyz
512
+ hyperline events delete --matching all`).action(async (opts) => {
513
+ const ctx = resource.parent?.opts()._ctx;
514
+ if (!ctx) {
515
+ process.stderr.write("Error: Not authenticated\n");
516
+ process.exit(1);
517
+ }
518
+ const matching = opts.matching;
519
+ if (!matching || !["record_ids", "customer_id", "all"].includes(matching)) {
520
+ process.stderr.write("Error: --matching must be one of: record_ids, customer_id, all\n");
521
+ process.exit(1);
522
+ }
523
+ let body;
524
+ if (matching === "record_ids") {
525
+ if (!opts.recordIds) {
526
+ process.stderr.write("Error: --record-ids is required when matching=record_ids\n");
527
+ process.exit(1);
528
+ }
529
+ body = {
530
+ matching: "record_ids",
531
+ record_ids: opts.recordIds.split(",")
532
+ };
533
+ } else if (matching === "customer_id") {
534
+ if (!opts.customerId) {
535
+ process.stderr.write("Error: --customer-id is required when matching=customer_id\n");
536
+ process.exit(1);
537
+ }
538
+ body = { matching: "customer_id", customer_id: opts.customerId };
539
+ } else {
540
+ const confirmed = await confirmPrompt("Are you sure you want to delete ALL events? (y/N) ");
541
+ if (!confirmed) {
542
+ process.stdout.write("Aborted.\n");
543
+ return;
544
+ }
545
+ body = { matching: "all" };
546
+ }
547
+ await ctx.execute({
548
+ method: "DELETE",
549
+ path: "/v1/events",
550
+ args: {},
551
+ body,
552
+ baseUrlOverride: resolveIngestBaseUrl(ctx.baseUrl)
553
+ });
554
+ });
555
+ resource.command("calculate-prices").description(`Ingest a billable event and calculate its prices. After first ingestion, returns cached prices.`).requiredOption("--customer-id <value>", `Customer ID or external ID`).requiredOption("--event-type <value>", `Event type`).requiredOption("--timestamp <value>", `ISO8601 timestamp`).requiredOption("--record <value>", `Event record as JSON (must include "id" key)`).option("--product-id <value>", `Hyperline product ID`).option("--subscription-id <value>", `Hyperline subscription ID`).option("--include-tax", `Include amounts with tax`).addHelpText("after", `
556
+ Examples:
557
+ hyperline events calculate-prices --customer-id cus_xyz --event-type api_call --timestamp 2024-01-15T10:00:00Z --record '{"id":"evt_1"}'
558
+ hyperline events calculate-prices --customer-id cus_xyz --event-type api_call --timestamp 2024-01-15T10:00:00Z --record '{"id":"evt_1"}' --product-id itm_abc --include-tax`).action(async (opts) => {
559
+ const ctx = resource.parent?.opts()._ctx;
560
+ if (!ctx) {
561
+ process.stderr.write("Error: Not authenticated\n");
562
+ process.exit(1);
563
+ }
564
+ let record;
565
+ try {
566
+ record = JSON.parse(opts.record);
567
+ } catch {
568
+ process.stderr.write(`Error: --record must be valid JSON (e.g. '{"id":"evt_1"}')
569
+ `);
570
+ process.exit(1);
571
+ }
572
+ const body = {
573
+ customer_id: opts.customerId,
574
+ event_type: opts.eventType,
575
+ timestamp: opts.timestamp,
576
+ record
577
+ };
578
+ const args = {};
579
+ if (opts.productId !== void 0)
580
+ args.product_id = opts.productId;
581
+ if (opts.subscriptionId !== void 0)
582
+ args.subscription_id = opts.subscriptionId;
583
+ if (opts.includeTax !== void 0)
584
+ args.include_tax = true;
585
+ await ctx.execute({
586
+ method: "POST",
587
+ path: "/v1/events/prices",
588
+ args,
589
+ queryParamKeys: ["product_id", "subscription_id", "include_tax"],
590
+ body,
591
+ baseUrlOverride: resolveIngestBaseUrl(ctx.baseUrl)
592
+ });
593
+ });
594
+ resource.command("simulate-prices").description(`Simulate prices for a billable event without ingesting it.`).requiredOption("--event-type <value>", `Event type`).requiredOption("--record <value>", `Event record as JSON (must include "id" key)`).option("--customer-id <value>", `Customer ID or external ID`).option("--timestamp <value>", `ISO8601 timestamp`).option("--product-id <value>", `Hyperline product ID`).option("--subscription-id <value>", `Hyperline subscription ID`).option("--include-tax", `Include amounts with tax`).addHelpText("after", `
595
+ Examples:
596
+ hyperline events simulate-prices --event-type api_call --record '{"id":"evt_1","duration":32}'
597
+ hyperline events simulate-prices --event-type api_call --record '{"id":"evt_1"}' --product-id itm_abc --include-tax`).action(async (opts) => {
598
+ const ctx = resource.parent?.opts()._ctx;
599
+ if (!ctx) {
600
+ process.stderr.write("Error: Not authenticated\n");
601
+ process.exit(1);
602
+ }
603
+ let record;
604
+ try {
605
+ record = JSON.parse(opts.record);
606
+ } catch {
607
+ process.stderr.write(`Error: --record must be valid JSON (e.g. '{"id":"evt_1"}')
608
+ `);
609
+ process.exit(1);
610
+ }
611
+ const body = {
612
+ event_type: opts.eventType,
613
+ record
614
+ };
615
+ if (opts.customerId !== void 0)
616
+ body.customer_id = opts.customerId;
617
+ if (opts.timestamp !== void 0)
618
+ body.timestamp = opts.timestamp;
619
+ const args = {};
620
+ if (opts.productId !== void 0)
621
+ args.product_id = opts.productId;
622
+ if (opts.subscriptionId !== void 0)
623
+ args.subscription_id = opts.subscriptionId;
624
+ if (opts.includeTax !== void 0)
625
+ args.include_tax = true;
626
+ await ctx.execute({
627
+ method: "POST",
628
+ path: "/v1/events/simulate-prices",
629
+ args,
630
+ queryParamKeys: ["product_id", "subscription_id", "include_tax"],
631
+ body,
632
+ baseUrlOverride: resolveIngestBaseUrl(ctx.baseUrl)
633
+ });
634
+ });
635
+ resource.command("get-prices").description(`Get previous price calculation results by calculation ID or record ID.`).option("--record-id <value>", `Record ID to look up`).option("--calculation-id <value>", `Calculation ID to look up`).addHelpText("after", `
636
+ Examples:
637
+ hyperline events get-prices --record-id D32NAA8
638
+ hyperline events get-prices --calculation-id cal_1234567890`).action(async (opts) => {
639
+ const ctx = resource.parent?.opts()._ctx;
640
+ if (!ctx) {
641
+ process.stderr.write("Error: Not authenticated\n");
642
+ process.exit(1);
643
+ }
644
+ if (!opts.recordId && !opts.calculationId) {
645
+ process.stderr.write("Error: at least one of --record-id or --calculation-id is required\n");
646
+ process.exit(1);
647
+ }
648
+ const args = {};
649
+ if (opts.recordId !== void 0)
650
+ args.record_id = opts.recordId;
651
+ if (opts.calculationId !== void 0)
652
+ args.calculation_id = opts.calculationId;
653
+ await ctx.execute({
654
+ method: "GET",
655
+ path: "/v1/events/prices",
656
+ args,
657
+ queryParamKeys: ["record_id", "calculation_id"],
658
+ baseUrlOverride: resolveIngestBaseUrl(ctx.baseUrl)
659
+ });
660
+ });
661
+ }
662
+
382
663
  // build/commands/generated/analytics.js
383
664
  function registerAnalyticsCommands(parent) {
384
665
  const resource = parent.command("analytics").description("Manage analytics");
@@ -489,21 +770,6 @@ Examples:
489
770
  });
490
771
  }
491
772
 
492
- // build/commands/confirm.js
493
- import * as readline2 from "node:readline";
494
- function confirmPrompt(message) {
495
- const rl = readline2.createInterface({
496
- input: process.stdin,
497
- output: process.stderr
498
- });
499
- return new Promise((resolve) => {
500
- rl.question(message, (answer) => {
501
- rl.close();
502
- resolve(answer.toLowerCase() === "y");
503
- });
504
- });
505
- }
506
-
507
773
  // build/commands/generated/coupons.js
508
774
  function registerCouponsCommands(parent) {
509
775
  const resource = parent.command("coupons").description("Manage coupons");
@@ -8954,8 +9220,9 @@ function createCLIContext({ apiKey, baseUrl, outputFormat, source, companyId })
8954
9220
  }
8955
9221
  });
8956
9222
  return {
9223
+ baseUrl,
8957
9224
  outputFormat,
8958
- async execute({ method, path: path2, args, queryParamKeys = [], body }) {
9225
+ async execute({ method, path: path2, args, queryParamKeys = [], body, baseUrlOverride }) {
8959
9226
  const { url, params, data } = buildRequestConfig(path2, args, method, queryParamKeys);
8960
9227
  const requestData = body ?? data;
8961
9228
  const hasData = Array.isArray(requestData) ? requestData.length > 0 : requestData != null && Object.keys(requestData).length > 0;
@@ -8965,6 +9232,7 @@ function createCLIContext({ apiKey, baseUrl, outputFormat, source, companyId })
8965
9232
  url,
8966
9233
  params,
8967
9234
  data: hasData ? requestData : void 0,
9235
+ ...baseUrlOverride ? { baseURL: baseUrlOverride } : {},
8968
9236
  headers: {
8969
9237
  "Hyperline-Source": source,
8970
9238
  ...companyId ? { "Hyperline-CompanyId": companyId } : {}
@@ -9077,6 +9345,7 @@ companyCommand.command("current").description("Show the currently active company
9077
9345
  }
9078
9346
  });
9079
9347
  registerAllCommands(program);
9348
+ registerEventsCommands(program);
9080
9349
  var selfAuthCommands = /* @__PURE__ */ new Set(["login", "logout", "company"]);
9081
9350
  program.hook("preAction", async (thisCommand, actionCommand) => {
9082
9351
  const commandName = actionCommand.name();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperline/cli",
3
- "version": "0.1.0-build.1.bf6bdf2",
3
+ "version": "0.1.0-build.1.deb4fbd",
4
4
  "description": "Agent-first CLI for Hyperline API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",