@koda-sl/baker-cli 0.432.0 → 0.433.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1121,6 +1121,7 @@ baker ads linkedin top-companies --campaign-id 1234 --last-days 30
1121
1121
  # most engaged first ({org, name, impressions, clicks, engagements, costLocal}).
1122
1122
  # Defaults: last 12 calendar months, the only selected ad account, top 100 (--full for all).
1123
1123
  baker ads linkedin companies --account-id 1234
1124
+ baker ads linkedin companies --page 2 # companies 101-200, most engaged first
1124
1125
  baker ads linkedin companies --by month --min-impressions 50 # months[] per company
1125
1126
  baker ads linkedin companies --campaign-id 1,2 --by campaign # campaigns[] per company
1126
1127
  baker ads linkedin companies --with-domains --stats --output csv # + domain/domainSource; read-quality stats
@@ -4018,13 +4019,14 @@ No write surface against HubSpot itself — a form's fields, redirect, or consen
4018
4019
 
4019
4020
  ### `baker accounts influenced | engagement | warm | company | campaigns | crm`
4020
4021
 
4021
- Account insights: which companies the LinkedIn ads are warming, and which CRM deals they influenced. Reads Baker's nightly copy of LinkedIn and the CRM, so it is fast and covers up to 24 months; nothing is changed anywhere. Only for companies with Account insights switched on — otherwise every command exits 1 with `ACCOUNTS_NOT_ENABLED` and an `error.fix` pointing at the live read (`baker ads linkedin companies`, plus `baker hubspot companies search`). `--start`/`--end` must be real calendar days within 24 calendar months; a bad flag or an unsupported `--output` exits 1 with `INVALID_ARGS`.
4022
+ Account insights: which companies the LinkedIn ads are warming, and which CRM deals they influenced. Reads Baker's nightly copy of LinkedIn and the CRM, so it is fast and covers up to 24 months; nothing is changed anywhere. Every company has it once a LinkedIn ad account is chosen; before the first nightly read the reports are empty — read live with `baker ads linkedin companies`. `--start`/`--end` must be real calendar days within 24 calendar months; a bad flag or an unsupported `--output` exits 1 with `INVALID_ARGS`.
4022
4023
 
4023
4024
  ```bash
4024
4025
  baker accounts influenced # influenced pipeline, last 12 calendar months
4025
4026
  baker accounts influenced --basis revenue --start 2026-01-01 --end 2026-06-30
4026
4027
  baker accounts influenced --full # every deal in the period, influenced or not
4027
4028
  baker accounts engagement --last-months 3 --limit 50 # companies reached, New/Rising, CRM match and deals
4029
+ baker accounts engagement --last-months 3 --limit 50 --page 2 # the next 50 (pageInfo.total counts them all)
4028
4030
  baker accounts engagement --campaign-id 123456 --output csv
4029
4031
  baker accounts warm --output csv > warm.csv # companyName,domain,organizationUrn
4030
4032
  baker accounts company acme.com # or a LinkedIn company id / urn:li:organization:N
package/dist/cli.js CHANGED
@@ -22414,7 +22414,12 @@ var crmSheetLinkSchema = z2.object({
22414
22414
  var accountsCrmStatusResponseSchema = z2.object({
22415
22415
  ok: z2.literal(true),
22416
22416
  data: z2.object({
22417
- enabled: z2.boolean(),
22417
+ /**
22418
+ * Always true, for CLIs from when Account insights was a per-company
22419
+ * switch: they read its absence as "switched off". Drop it once no
22420
+ * Runtime runs one of them.
22421
+ */
22422
+ enabled: z2.literal(true).optional(),
22418
22423
  /** `hubspot` (Baker reads it), `api` (the client sends it), or null before anyone chose. */
22419
22424
  source: z2.enum(["hubspot", "api"]).nullable(),
22420
22425
  /** Where a client's system sends. */
@@ -22702,7 +22707,10 @@ var accountsEngagementRequestSchema = z3.object({
22702
22707
  ...windowFields,
22703
22708
  /** One campaign's companies; every campaign together when absent. */
22704
22709
  campaignId: z3.string().regex(/^\d+$/, "use the numeric campaign id").optional(),
22705
- limit: z3.number().int().min(1).max(ACCOUNTS_ENGAGEMENT_MAX_LIMIT).default(ACCOUNTS_ENGAGEMENT_DEFAULT_LIMIT)
22710
+ /** Companies per page. */
22711
+ limit: z3.number().int().min(1).max(ACCOUNTS_ENGAGEMENT_MAX_LIMIT).default(ACCOUNTS_ENGAGEMENT_DEFAULT_LIMIT),
22712
+ /** Which page, 1-based, most engaged first. */
22713
+ page: z3.number().int().min(1).default(1)
22706
22714
  }).superRefine(windowRules);
22707
22715
  var accountsEngagementResponseSchema = z3.object({
22708
22716
  ok: z3.literal(true),
@@ -22710,8 +22718,10 @@ var accountsEngagementResponseSchema = z3.object({
22710
22718
  window: accountsWindowSchema,
22711
22719
  previousWindow: accountsWindowSchema,
22712
22720
  companies: z3.array(accountEngagementRowSchema),
22713
- /** True when more companies were reached than `limit`. */
22721
+ /** True when more companies were reached than this page and the ones before it hold. */
22714
22722
  truncated: z3.boolean(),
22723
+ /** Which page this is, and how many companies there are in all. Absent from older servers. */
22724
+ pageInfo: z3.object({ page: z3.number(), pageSize: z3.number(), total: z3.number(), hasMore: z3.boolean() }).optional(),
22715
22725
  /** The company's influence rule (the default one before setup): the minimum `baker accounts warm` applies. */
22716
22726
  rule: influenceParamsSchema.optional()
22717
22727
  })
@@ -22860,7 +22870,6 @@ var accountsCampaignsResponseSchema = z3.object({
22860
22870
  campaigns: z3.array(accountCampaignRowSchema)
22861
22871
  })
22862
22872
  });
22863
- var ACCOUNTS_NOT_ENABLED_MESSAGE = "Account insights are not switched on for this company yet. The Baker team turns them on on request.";
22864
22873
  var ACCOUNTS_NO_SETTINGS_MESSAGE = "Account insights are not set up yet: in Measure \u2192 Accounts, say where your deals are (HubSpot or another CRM) and what won and lost mean.";
22865
22874
 
22866
22875
  // src/commands/accounts/index.ts
@@ -23718,6 +23727,8 @@ var linkedinCompaniesRequestSchema = z6.object({
23718
23727
  withDomains: z6.boolean().default(false),
23719
23728
  minImpressions: z6.number().int().min(0).default(0),
23720
23729
  full: z6.boolean().default(false),
23730
+ /** Which page of 100 companies, most engaged first; ignored with `full`. */
23731
+ page: z6.number().int().min(1).default(1),
23721
23732
  stats: z6.boolean().default(false),
23722
23733
  skipCache: z6.boolean().optional()
23723
23734
  }).superRefine((req, ctx) => {
@@ -23772,8 +23783,10 @@ var linkedinCompaniesStatsSchema = z6.object({
23772
23783
  var linkedinCompaniesWarningSchema = z6.object({ code: z6.string(), message: z6.string() });
23773
23784
  var linkedinCompaniesResponseSchema = z6.object({
23774
23785
  rows: z6.array(linkedinCompanyRowSchema),
23775
- /** Companies above minImpressions before the compact cap. */
23786
+ /** Companies above minImpressions, every page together. */
23776
23787
  totalCompanies: z6.number(),
23788
+ /** Which page of 100 `rows` is (1 with `full`). Absent from older servers. */
23789
+ page: z6.number().optional(),
23777
23790
  stats: linkedinCompaniesStatsSchema.optional(),
23778
23791
  warnings: z6.array(linkedinCompaniesWarningSchema),
23779
23792
  query: z6.object({
@@ -32953,7 +32966,6 @@ function refusedHints(lastLive) {
32953
32966
  ];
32954
32967
  }
32955
32968
  function statusHints(data) {
32956
- if (!data.enabled) return ["Account insights is not switched on for this company; the Baker team switches it on."];
32957
32969
  if (data.source === null) {
32958
32970
  return [
32959
32971
  "Nobody has said where the deals are yet. The user chooses in Measure \u2192 Accounts: HubSpot, or Another CRM (they send the deals)."
@@ -33039,15 +33051,6 @@ var crmCommand = defineCommand({
33039
33051
 
33040
33052
  // src/commands/accounts/report.ts
33041
33053
  function accountsRefusal(code, message) {
33042
- if (code === "CONFLICT" && message.includes(ACCOUNTS_NOT_ENABLED_MESSAGE)) {
33043
- return {
33044
- code: "ACCOUNTS_NOT_ENABLED",
33045
- fix: {
33046
- action: "continue_without",
33047
- explanation: "Account insights is not switched on for this company, so none of `baker accounts` can answer \u2014 do not retry it. Read the same thing live instead: `baker ads linkedin companies --with-domains` lists the companies the LinkedIn ads reached (--by month shows who is warming up); if HubSpot is connected, `baker hubspot companies search --domains <domain,\u2026> --with-deals` shows which are already customers or open deals. Say the numbers come from that live read, not from Account insights, and finish the rest of the task."
33048
- }
33049
- };
33050
- }
33051
33054
  if (code === "CONFLICT" && message.includes(ACCOUNTS_NO_SETTINGS_MESSAGE)) {
33052
33055
  return {
33053
33056
  code: "ACCOUNTS_NOT_SET_UP",
@@ -33111,6 +33114,14 @@ function engagementHints(data, request) {
33111
33114
  `No company the LinkedIn ads reached shows up for ${data.window.start} to ${data.window.end}. The nightly sync may not have run since the campaigns started delivering, or LinkedIn left the companies out (fewer than 3 events in a month). Try a longer period (--last-months 12), or read the ad account live with \`baker ads linkedin companies\`.`
33112
33115
  ];
33113
33116
  }
33117
+ const page = data.pageInfo;
33118
+ if (page?.hasMore) {
33119
+ const first = (page.page - 1) * page.pageSize + 1;
33120
+ const last = first + data.companies.length - 1;
33121
+ return [
33122
+ `Companies ${first}-${last} of ${page.total.toLocaleString("en")} shown. Read the next ones with --page ${page.page + 1} --limit ${page.pageSize}.`
33123
+ ];
33124
+ }
33114
33125
  return data.truncated ? [`More companies were reached than --limit ${request.limit}; raise it to see the tail.`] : [];
33115
33126
  }
33116
33127
  var ENGAGEMENT_TABLE_FIELDS = [
@@ -33392,21 +33403,27 @@ var ENGAGEMENT_ARGS = {
33392
33403
  "campaign-id": { type: "string", description: "Only this campaign's companies (numeric id)", required: false },
33393
33404
  limit: {
33394
33405
  type: "string",
33395
- description: `Most companies, 1-${ACCOUNTS_ENGAGEMENT_MAX_LIMIT} (default 500)`,
33406
+ description: `Companies per page, 1-${ACCOUNTS_ENGAGEMENT_MAX_LIMIT} (default 500)`,
33407
+ required: false
33408
+ },
33409
+ page: {
33410
+ type: "string",
33411
+ description: "Which page to read, 1-based, most engaged first (default 1). pageInfo.hasMore says another page follows; pageInfo.total counts every company reached",
33396
33412
  required: false
33397
33413
  },
33398
33414
  output: OUTPUT_ARG
33399
33415
  };
33400
33416
  registerSchema({
33401
33417
  command: "accounts.engagement",
33402
- description: "Every company the LinkedIn ads reached, most engaged first, with the period before for comparison, New/Rising flags, and where it stands in the CRM (matched company; dealStatus won > open > lost > none, lost meaning every deal it had was lost; lastLostAt). Default the last 3 calendar months. From the nightly sync, so it is fast and covers up to 24 months; for a live read of one ad account use `baker ads linkedin companies`.",
33418
+ description: "Every company the LinkedIn ads reached, most engaged first, with the period before for comparison, New/Rising flags, and where it stands in the CRM (matched company; dealStatus won > open > lost > none, lost meaning every deal it had was lost; lastLostAt). Default the last 3 calendar months. From the nightly sync, so it is fast and covers up to 24 months; for a live read of one ad account use `baker ads linkedin companies`. Paged: --limit companies per page, --page N; pageInfo {page, pageSize, total, hasMore}.",
33403
33419
  args: ENGAGEMENT_ARGS
33404
33420
  });
33405
33421
  function engagementInput(args8, limit) {
33406
33422
  return {
33407
33423
  ...windowInput(args8),
33408
33424
  campaignId: optionalString(args8["campaign-id"])?.replace(/^urn:li:sponsoredCampaign:/, ""),
33409
- limit: limit ?? optionalNumber(args8.limit)
33425
+ limit: limit ?? optionalNumber(args8.limit),
33426
+ page: limit === void 0 ? optionalNumber(args8.page) : void 0
33410
33427
  };
33411
33428
  }
33412
33429
  var engagementCommand = defineCommand2({
@@ -33430,7 +33447,7 @@ var engagementCommand = defineCommand2({
33430
33447
  writeJson({
33431
33448
  ...response,
33432
33449
  data: { ...response.data, companies },
33433
- meta: { count: companies.length },
33450
+ meta: { count: companies.length, ...response.data.pageInfo ? { total: response.data.pageInfo.total } : {} },
33434
33451
  ...hints2.length > 0 ? { hints: hints2 } : {}
33435
33452
  });
33436
33453
  } catch (err) {
@@ -51098,7 +51115,7 @@ registerSchema({
51098
51115
  });
51099
51116
  registerSchema({
51100
51117
  command: "ads.linkedin.companies",
51101
- description: "Which companies the LinkedIn ads reached \u2014 one row per company over the whole period ({org, name, impressions, clicks, engagements, costLocal}), most engaged first. Start here for warm accounts / ABM reporting. Defaults: last 12 calendar months, the only selected ad account, top 100 (--full for all). --by month|campaign adds a months[] or campaigns[] breakdown; --with-domains adds domain + domainSource for CRM matching; --stats adds row counts and cost/reach/domain coverage (no names). LinkedIn drops companies under 3 events a month.",
51118
+ description: "Which companies the LinkedIn ads reached \u2014 one row per company over the whole period ({org, name, impressions, clicks, engagements, costLocal}), most engaged first. Start here for warm accounts / ABM reporting. Defaults: last 12 calendar months, the only selected ad account, 100 companies a page (--page N for the next, --full for all; totalCompanies counts them all). --by month|campaign adds a months[] or campaigns[] breakdown; --with-domains adds domain + domainSource for CRM matching; --stats adds row counts and cost/reach/domain coverage (no names). LinkedIn drops companies under 3 events a month.",
51102
51119
  args: {
51103
51120
  "account-id": {
51104
51121
  type: "string",
@@ -51117,6 +51134,11 @@ registerSchema({
51117
51134
  "with-domains": { type: "boolean", description: "Add website domain + domainSource per company", required: false },
51118
51135
  "min-impressions": { type: "string", description: "Leave out companies under N impressions", required: false },
51119
51136
  full: { type: "boolean", description: "Every company, plus videoViews", required: false },
51137
+ page: {
51138
+ type: "string",
51139
+ description: "Which page of 100 companies, most engaged first (default 1)",
51140
+ required: false
51141
+ },
51120
51142
  stats: { type: "boolean", description: "Add read-quality stats (counts and rates only)", required: false },
51121
51143
  "no-cache": NO_CACHE_ARG,
51122
51144
  output: { type: "string", description: "json|csv|jsonl|md", required: false, default: "json" }
@@ -53618,6 +53640,7 @@ Examples:
53618
53640
 
53619
53641
  // src/commands/ads/linkedin/companies.ts
53620
53642
  import { defineCommand as defineCommand68 } from "citty";
53643
+ var COMPANIES_PAGE_SIZE = 100;
53621
53644
  var COMPANIES_TIMEOUT_MS = 18e4;
53622
53645
  var CSV_FIELDS = [
53623
53646
  "org",
@@ -53651,8 +53674,12 @@ function companiesHints(data, request) {
53651
53674
  );
53652
53675
  }
53653
53676
  if (data.totalCompanies > data.rows.length) {
53677
+ const page = data.page ?? 1;
53678
+ const first = (page - 1) * COMPANIES_PAGE_SIZE + 1;
53679
+ const last = first + data.rows.length - 1;
53680
+ const next = last < data.totalCompanies ? ` \u2014 --page ${page + 1} for the next ${COMPANIES_PAGE_SIZE}, ` : " \u2014 ";
53654
53681
  hints2.push(
53655
- `Showing the ${data.rows.length} most engaged of ${data.totalCompanies} companies \u2014 pass --full for all, or --min-impressions to trim the tail.`
53682
+ `Showing companies ${first}-${last} of ${data.totalCompanies.toLocaleString("en")}, most engaged first${next}--full for all, or --min-impressions to trim the tail.`
53656
53683
  );
53657
53684
  }
53658
53685
  if (data.query.by === "none" && data.rows.length > 0) {
@@ -53672,7 +53699,8 @@ var FLAG_OF_FIELD2 = {
53672
53699
  start: "--start",
53673
53700
  end: "--end",
53674
53701
  by: "--by",
53675
- minImpressions: "--min-impressions"
53702
+ minImpressions: "--min-impressions",
53703
+ page: "--page"
53676
53704
  };
53677
53705
  function flagOf(field) {
53678
53706
  return FLAG_OF_FIELD2[field.split(".")[0] ?? ""] ?? (field || "args");
@@ -53690,10 +53718,11 @@ var companiesCommand = defineCommand68({
53690
53718
 
53691
53719
  Start here for "which companies are our ads warming", ABM reporting, or building a warm-account list.
53692
53720
  Rows: {org, name, impressions, clicks, engagements, costLocal}. Defaults: the last 12 calendar
53693
- months, the only selected ad account, top 100 companies (--full for all).
53721
+ months, the only selected ad account, 100 companies a page (--page 2 for the next, --full for all).
53694
53722
 
53695
53723
  Examples:
53696
53724
  baker ads linkedin companies # last 12 months, whole account
53725
+ baker ads linkedin companies --page 2 # the next 100 companies
53697
53726
  baker ads linkedin companies --by month --min-impressions 50 # when each company warmed up
53698
53727
  baker ads linkedin companies --campaign-id 333728193,810055513 --by campaign
53699
53728
  baker ads linkedin companies --with-domains --output csv # + website domain, for CRM matching
@@ -53714,7 +53743,11 @@ so small companies are undercounted. engagements = LinkedIn's totalEngagements.`
53714
53743
  by: { type: "string", description: "none | month | campaign \u2014 break each company down (default none)" },
53715
53744
  "with-domains": { type: "boolean", description: "Add each company's website domain and domainSource" },
53716
53745
  "min-impressions": { type: "string", description: "Leave out companies under N impressions over the period" },
53717
- full: { type: "boolean", description: "Every company (not just the top 100), plus videoViews" },
53746
+ full: { type: "boolean", description: "Every company (not just a page of 100), plus videoViews" },
53747
+ page: {
53748
+ type: "string",
53749
+ description: "Which page of 100 companies, most engaged first (default 1); totalCompanies counts them all"
53750
+ },
53718
53751
  stats: { type: "boolean", description: "Add read-quality stats: row counts, cost/reach/domain coverage" },
53719
53752
  "no-cache": NO_CACHE_ARG,
53720
53753
  output: { type: "string", description: "json|csv|jsonl|md", default: "json" }
@@ -53730,6 +53763,7 @@ so small companies are undercounted. engagements = LinkedIn's totalEngagements.`
53730
53763
  withDomains: Boolean(args8["with-domains"]),
53731
53764
  minImpressions: optionalNumber2(args8["min-impressions"]),
53732
53765
  full: Boolean(args8.full),
53766
+ page: optionalNumber2(args8.page),
53733
53767
  stats: Boolean(args8.stats),
53734
53768
  skipCache: Boolean(args8["no-cache"])
53735
53769
  };