@hyperline/cli 0.1.0-build.1.9301c8c → 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.
@@ -71,6 +71,87 @@ async function clearCredentials() {
71
71
  } catch {
72
72
  }
73
73
  }
74
+ async function resolveCompanyId({ flagCompanyId }) {
75
+ if (flagCompanyId)
76
+ return flagCompanyId;
77
+ const envCompanyId = process.env.HYPERLINE_COMPANY_ID;
78
+ if (envCompanyId)
79
+ return envCompanyId;
80
+ const configFile = await readJsonFile(CONFIG_FILE);
81
+ return configFile?.companyId;
82
+ }
83
+ async function saveCompanyId({ companyId }) {
84
+ const existing = await readJsonFile(CONFIG_FILE) ?? {};
85
+ existing.companyId = companyId;
86
+ await writeJsonFile(CONFIG_FILE, existing);
87
+ }
88
+
89
+ // build/prompt.js
90
+ import * as readline from "node:readline";
91
+ function selectPrompt({ message, items }) {
92
+ if (items.length === 0)
93
+ return Promise.resolve(void 0);
94
+ if (items.length === 1)
95
+ return Promise.resolve(items[0].value);
96
+ return new Promise((resolve) => {
97
+ const input = process.stdin;
98
+ const output = process.stderr;
99
+ let selectedIndex = 0;
100
+ const wasRaw = input.isRaw;
101
+ if (!input.isTTY) {
102
+ resolve(items[0].value);
103
+ return;
104
+ }
105
+ input.setRawMode(true);
106
+ readline.emitKeypressEvents(input);
107
+ function render() {
108
+ output.write(`\x1B[?25l`);
109
+ output.write(`\x1B[${items.length}A`);
110
+ for (const [index, item] of items.entries()) {
111
+ const isSelected = index === selectedIndex;
112
+ const pointer = isSelected ? "\x1B[36m>\x1B[0m" : " ";
113
+ const label = isSelected ? `\x1B[36m${item.label}\x1B[0m` : item.label;
114
+ const hint = item.hint ? `\x1B[2m ${item.hint}\x1B[0m` : "";
115
+ output.write(`\x1B[2K${pointer} ${label}${hint}
116
+ `);
117
+ }
118
+ }
119
+ output.write(`${message}
120
+ `);
121
+ for (const [index, item] of items.entries()) {
122
+ const isSelected = index === selectedIndex;
123
+ const pointer = isSelected ? "\x1B[36m>\x1B[0m" : " ";
124
+ const label = isSelected ? `\x1B[36m${item.label}\x1B[0m` : item.label;
125
+ const hint = item.hint ? `\x1B[2m ${item.hint}\x1B[0m` : "";
126
+ output.write(`${pointer} ${label}${hint}
127
+ `);
128
+ }
129
+ function cleanup() {
130
+ input.setRawMode(wasRaw ?? false);
131
+ input.removeListener("keypress", onKeypress);
132
+ input.pause();
133
+ output.write("\x1B[?25h");
134
+ }
135
+ function onKeypress(_str, key) {
136
+ if (!key)
137
+ return;
138
+ if (key.name === "up" || key.name === "k" && !key.ctrl) {
139
+ selectedIndex = selectedIndex <= 0 ? items.length - 1 : selectedIndex - 1;
140
+ render();
141
+ } else if (key.name === "down" || key.name === "j" && !key.ctrl) {
142
+ selectedIndex = selectedIndex >= items.length - 1 ? 0 : selectedIndex + 1;
143
+ render();
144
+ } else if (key.name === "return") {
145
+ cleanup();
146
+ resolve(items[selectedIndex]?.value);
147
+ } else if (key.name === "escape" || key.name === "c" && key.ctrl) {
148
+ cleanup();
149
+ resolve(void 0);
150
+ }
151
+ }
152
+ input.on("keypress", onKeypress);
153
+ });
154
+ }
74
155
 
75
156
  // build/auth.js
76
157
  var CLI_CLIENT_NAME = "Hyperline CLI";
@@ -88,7 +169,10 @@ function generateState() {
88
169
  async function registerDynamicClient({ baseUrl, redirectUri }) {
89
170
  const response = await fetch(`${baseUrl}/oauth/register`, {
90
171
  method: "POST",
91
- headers: { "Content-Type": "application/json" },
172
+ headers: {
173
+ "Content-Type": "application/json",
174
+ "Hyperline-Source": "cli"
175
+ },
92
176
  body: JSON.stringify({
93
177
  client_name: CLI_CLIENT_NAME,
94
178
  redirect_uris: [redirectUri],
@@ -106,7 +190,10 @@ async function registerDynamicClient({ baseUrl, redirectUri }) {
106
190
  async function exchangeCodeForTokens({ baseUrl, code, clientId, codeVerifier, redirectUri }) {
107
191
  const response = await fetch(`${baseUrl}/oauth/tokens`, {
108
192
  method: "POST",
109
- headers: { "Content-Type": "application/json" },
193
+ headers: {
194
+ "Content-Type": "application/json",
195
+ "Hyperline-Source": "cli"
196
+ },
110
197
  body: JSON.stringify({
111
198
  grant_type: "authorization_code",
112
199
  code,
@@ -238,11 +325,340 @@ Please open this URL in your browser:
238
325
  expiresIn: tokens.expires_in
239
326
  });
240
327
  process.stdout.write("Login successful!\n");
328
+ await promptCompanySelection({
329
+ baseUrl,
330
+ accessToken: tokens.access_token
331
+ });
241
332
  } catch (error) {
242
333
  server.close();
243
334
  throw error;
244
335
  }
245
336
  }
337
+ async function fetchCompanies({ baseUrl, accessToken }) {
338
+ const response = await fetch(`${baseUrl}/v1/companies`, {
339
+ headers: {
340
+ Authorization: `Bearer ${accessToken}`,
341
+ "Hyperline-Source": "cli"
342
+ }
343
+ });
344
+ if (!response.ok) {
345
+ throw new Error(`Failed to fetch companies: ${response.status}`);
346
+ }
347
+ const body = await response.json();
348
+ return body.data;
349
+ }
350
+ async function promptCompanySelection({ baseUrl, accessToken }) {
351
+ process.stdout.write("\nFetching your companies...\n");
352
+ const companies = await fetchCompanies({ baseUrl, accessToken });
353
+ if (companies.length === 0) {
354
+ process.stdout.write("No companies found for this account.\n");
355
+ return;
356
+ }
357
+ if (companies.length === 1) {
358
+ const onlyCompany = companies[0];
359
+ await saveCompanyId({ companyId: onlyCompany.id });
360
+ process.stdout.write(`Company set to ${onlyCompany.name}
361
+ `);
362
+ return;
363
+ }
364
+ const selectedCompanyId = await selectPrompt({
365
+ message: "Select a company:",
366
+ items: companies.map((company) => ({
367
+ label: company.name,
368
+ value: company.id,
369
+ hint: company.id
370
+ }))
371
+ });
372
+ if (!selectedCompanyId) {
373
+ process.stdout.write("No company selected. You can set one later with: hyperline company select\n");
374
+ return;
375
+ }
376
+ await saveCompanyId({ companyId: selectedCompanyId });
377
+ const selectedCompany = companies.find((company) => company.id === selectedCompanyId);
378
+ process.stdout.write(`Company set to ${selectedCompany?.name ?? selectedCompanyId}
379
+ `);
380
+ }
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
+ }
246
662
 
247
663
  // build/commands/generated/analytics.js
248
664
  function registerAnalyticsCommands(parent) {
@@ -354,21 +770,6 @@ Examples:
354
770
  });
355
771
  }
356
772
 
357
- // build/commands/confirm.js
358
- import * as readline from "node:readline";
359
- function confirmPrompt(message) {
360
- const rl = readline.createInterface({
361
- input: process.stdin,
362
- output: process.stderr
363
- });
364
- return new Promise((resolve) => {
365
- rl.question(message, (answer) => {
366
- rl.close();
367
- resolve(answer.toLowerCase() === "y");
368
- });
369
- });
370
- }
371
-
372
773
  // build/commands/generated/coupons.js
373
774
  function registerCouponsCommands(parent) {
374
775
  const resource = parent.command("coupons").description("Manage coupons");
@@ -8805,7 +9206,7 @@ ${formatText(value, indent + 2)}`;
8805
9206
  }
8806
9207
 
8807
9208
  // build/http.js
8808
- function createCLIContext({ apiKey, baseUrl, outputFormat, source }) {
9209
+ function createCLIContext({ apiKey, baseUrl, outputFormat, source, companyId }) {
8809
9210
  const noop = () => {
8810
9211
  };
8811
9212
  const httpClient = buildHttpClient({
@@ -8819,8 +9220,9 @@ function createCLIContext({ apiKey, baseUrl, outputFormat, source }) {
8819
9220
  }
8820
9221
  });
8821
9222
  return {
9223
+ baseUrl,
8822
9224
  outputFormat,
8823
- async execute({ method, path: path2, args, queryParamKeys = [], body }) {
9225
+ async execute({ method, path: path2, args, queryParamKeys = [], body, baseUrlOverride }) {
8824
9226
  const { url, params, data } = buildRequestConfig(path2, args, method, queryParamKeys);
8825
9227
  const requestData = body ?? data;
8826
9228
  const hasData = Array.isArray(requestData) ? requestData.length > 0 : requestData != null && Object.keys(requestData).length > 0;
@@ -8830,8 +9232,10 @@ function createCLIContext({ apiKey, baseUrl, outputFormat, source }) {
8830
9232
  url,
8831
9233
  params,
8832
9234
  data: hasData ? requestData : void 0,
9235
+ ...baseUrlOverride ? { baseURL: baseUrlOverride } : {},
8833
9236
  headers: {
8834
- "Hyperline-Source": source
9237
+ "Hyperline-Source": source,
9238
+ ...companyId ? { "Hyperline-CompanyId": companyId } : {}
8835
9239
  }
8836
9240
  });
8837
9241
  process.stdout.write(formatOutput({ data: response.data, format: outputFormat }));
@@ -8877,7 +9281,7 @@ function isAxiosError2(error) {
8877
9281
 
8878
9282
  // build/bin/hyperline.js
8879
9283
  var program = new Command();
8880
- program.name("hyperline").description("Agent-first CLI for the Hyperline API").version("0.1.0").option("--api-key <key>", "Hyperline API key (overrides HYPERLINE_API_KEY)").option("--base-url <url>", "API base URL (overrides HYPERLINE_API_URL)").option("--output <format>", "Output format: json or text", "text");
9284
+ program.name("hyperline").description("Agent-first CLI for the Hyperline API").version("0.1.0").option("--api-key <key>", "Hyperline API key (overrides HYPERLINE_API_KEY)").option("--base-url <url>", "API base URL (overrides HYPERLINE_API_URL)").option("--output <format>", "Output format: json or text", "text").option("--company-id <id>", "Company ID (overrides HYPERLINE_COMPANY_ID and stored selection)");
8881
9285
  program.command("login").description("Authenticate with Hyperline via browser").action(async () => {
8882
9286
  const options = program.opts();
8883
9287
  const baseUrl = await resolveBaseUrl({
@@ -8889,10 +9293,64 @@ program.command("logout").description("Clear stored credentials").action(async (
8889
9293
  await clearCredentials();
8890
9294
  process.stdout.write("Logged out.\n");
8891
9295
  });
9296
+ var companyCommand = program.command("company").description("Manage active company");
9297
+ companyCommand.command("select").description("Interactively select the active company").action(async () => {
9298
+ const options = program.opts();
9299
+ const baseUrl = await resolveBaseUrl({
9300
+ flagBaseUrl: options.baseUrl
9301
+ });
9302
+ const apiKey = resolveApiKey({
9303
+ flagApiKey: options.apiKey
9304
+ });
9305
+ const accessToken = apiKey ?? await resolveAccessToken();
9306
+ if (!accessToken) {
9307
+ process.stderr.write("Error: No authentication found. Run: hyperline login\n");
9308
+ process.exit(1);
9309
+ }
9310
+ await promptCompanySelection({ baseUrl, accessToken });
9311
+ });
9312
+ companyCommand.command("set <companyId>").description("Set the active company by ID (for scripts and agents)").action(async (companyId) => {
9313
+ await saveCompanyId({ companyId });
9314
+ process.stdout.write(`Company set to ${companyId}
9315
+ `);
9316
+ });
9317
+ companyCommand.command("list").description("List all accessible companies").action(async () => {
9318
+ const options = program.opts();
9319
+ const baseUrl = await resolveBaseUrl({
9320
+ flagBaseUrl: options.baseUrl
9321
+ });
9322
+ const apiKey = resolveApiKey({
9323
+ flagApiKey: options.apiKey
9324
+ });
9325
+ const accessToken = apiKey ?? await resolveAccessToken();
9326
+ if (!accessToken) {
9327
+ process.stderr.write("Error: No authentication found. Run: hyperline login\n");
9328
+ process.exit(1);
9329
+ }
9330
+ const companies = await fetchCompanies({ baseUrl, accessToken });
9331
+ const currentCompanyId = await resolveCompanyId({});
9332
+ for (const company of companies) {
9333
+ const marker = company.id === currentCompanyId ? " (active)" : "";
9334
+ process.stdout.write(`${company.name} ${company.id}${marker}
9335
+ `);
9336
+ }
9337
+ });
9338
+ companyCommand.command("current").description("Show the currently active company").action(async () => {
9339
+ const companyId = await resolveCompanyId({});
9340
+ if (companyId) {
9341
+ process.stdout.write(`${companyId}
9342
+ `);
9343
+ } else {
9344
+ process.stdout.write("No company selected. Run: hyperline company select\n");
9345
+ }
9346
+ });
8892
9347
  registerAllCommands(program);
9348
+ registerEventsCommands(program);
9349
+ var selfAuthCommands = /* @__PURE__ */ new Set(["login", "logout", "company"]);
8893
9350
  program.hook("preAction", async (thisCommand, actionCommand) => {
8894
9351
  const commandName = actionCommand.name();
8895
- if (commandName === "login" || commandName === "logout")
9352
+ const parentName = actionCommand.parent?.name();
9353
+ if (selfAuthCommands.has(commandName) || selfAuthCommands.has(parentName ?? ""))
8896
9354
  return;
8897
9355
  const options = program.opts();
8898
9356
  const outputFormat = options.output ?? "text";
@@ -8907,11 +9365,15 @@ program.hook("preAction", async (thisCommand, actionCommand) => {
8907
9365
  const baseUrl = await resolveBaseUrl({
8908
9366
  flagBaseUrl: options.baseUrl
8909
9367
  });
9368
+ const companyId = await resolveCompanyId({
9369
+ flagCompanyId: options.companyId
9370
+ });
8910
9371
  const ctx = createCLIContext({
8911
9372
  apiKey: token,
8912
9373
  baseUrl,
8913
9374
  outputFormat,
8914
- source: apiKey ? "cli" : "cli-oauth"
9375
+ source: apiKey ? "cli" : "cli-oauth",
9376
+ companyId
8915
9377
  });
8916
9378
  thisCommand.setOptionValue("_ctx", ctx);
8917
9379
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperline/cli",
3
- "version": "0.1.0-build.1.9301c8c",
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",