@koda-sl/baker-cli 0.438.0 → 0.439.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 +2 -1
- package/dist/cli.js +72 -2
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4077,11 +4077,12 @@ baker accounts campaigns --last-months 6 --output md # per campaign: reached,
|
|
|
4077
4077
|
baker accounts influenced --model awareness --threshold 10 --output csv # another rule, this call only
|
|
4078
4078
|
baker accounts warm --min 0 # every New/Rising company, below the threshold too
|
|
4079
4079
|
baker accounts crm status # deals from HubSpot or sent by the client; what arrived, linked Google Sheets
|
|
4080
|
+
baker accounts crm sheet # create and link the deals sheet in the client's Google Drive
|
|
4080
4081
|
baker accounts crm check --file deals.csv # judge a deals or company CSV offline, nothing sent
|
|
4081
4082
|
baker accounts crm contract # the send-your-CRM contract, as Markdown for a developer
|
|
4082
4083
|
```
|
|
4083
4084
|
|
|
4084
|
-
A deal is **influenced** when its company reached the company's influence rule — by default 3+ LinkedIn ad engagements (Awareness: 7+ impressions; Intent: 3+ clicks) — in the 12 months up to and including the month the deal was created (`--basis pipeline`, deals not won) or closed won (`--basis revenue`). It is a labelled rule, not credit: campaign rows overlap, and money is reported per currency, never summed across currencies. Deals whose company match is only a guess come back under `lowConfidence` and stay out of `summary` until the match is confirmed in Measure → Accounts. Without the deals set up there — HubSpot's pipelines and won/lost stages, or Another CRM — `influenced` and `campaigns` exit 1 with `ACCOUNTS_NOT_SET_UP`. A CRM Baker cannot read sends its companies and deals to `POST /v1/accounts/crm` (a `crm_ingest` key, `X-Baker-Mode: test` to rehearse, up to 500 records per request, records naming a person refused), is uploaded as a CSV in Measure → Accounts, or lives in a Google Sheet linked there that Baker reads every night (a row deleted from the sheet leaves Baker). A file is a deals export or a company list — a company needs only its website or LinkedIn page; `crm check` runs the same rules offline and `crm contract` prints the whole contract. The CLI never sends deals or links a sheet. `--model engagement|awareness|intent` and `--threshold N` on `influenced`, `campaigns` and `warm` read with another rule for that call only; the saved setting is unchanged. A **warm** account is New or Rising, reached the company's influence threshold this period on the rule's metric (`--min N` overrides, `--min 0` drops it), and has no open deal, no won deal and no deal lost in the last 6 months; `engagement` and `warm` rows carry `dealStatus` (`won` > `open` > `lost` > `none`), `engagement --output csv` adds `lastMonth` and `previousImpressions/Clicks/Engagements`, and `campaigns` / `company` carry each campaign's `campaignName` (null when LinkedIn could not name it). `company` returns `months` (the company's totals), `byCampaign` (the same numbers per campaign — overlapping, never add them) and its `deals` with `touchValue` and `influenced`. `warm --output csv` is the file `baker ads linkedin audiences create --type company-list --list-file` takes.
|
|
4085
|
+
A deal is **influenced** when its company reached the company's influence rule — by default 3+ LinkedIn ad engagements (Awareness: 7+ impressions; Intent: 3+ clicks) — in the 12 months up to and including the month the deal was created (`--basis pipeline`, deals not won) or closed won (`--basis revenue`). It is a labelled rule, not credit: campaign rows overlap, and money is reported per currency, never summed across currencies. Deals whose company match is only a guess come back under `lowConfidence` and stay out of `summary` until the match is confirmed in Measure → Accounts. Without the deals set up there — HubSpot's pipelines and won/lost stages, or Another CRM — `influenced` and `campaigns` exit 1 with `ACCOUNTS_NOT_SET_UP`. A CRM Baker cannot read sends its companies and deals to `POST /v1/accounts/crm` (a `crm_ingest` key, `X-Baker-Mode: test` to rehearse, up to 500 records per request, records naming a person refused), is uploaded as a CSV in Measure → Accounts, or lives in a Google Sheet linked there that Baker reads every night (a row deleted from the sheet leaves Baker). A file is a deals export or a company list — a company needs only its website or LinkedIn page; `crm check` runs the same rules offline and `crm contract` prints the whole contract. The CLI never sends deals or links a sheet of the client's; `crm sheet` creates the empty deals sheet in their Google Drive and links it, applied at once (asking again returns the same sheet). `--model engagement|awareness|intent` and `--threshold N` on `influenced`, `campaigns` and `warm` read with another rule for that call only; the saved setting is unchanged. A **warm** account is New or Rising, reached the company's influence threshold this period on the rule's metric (`--min N` overrides, `--min 0` drops it), and has no open deal, no won deal and no deal lost in the last 6 months; `engagement` and `warm` rows carry `dealStatus` (`won` > `open` > `lost` > `none`), `engagement --output csv` adds `lastMonth` and `previousImpressions/Clicks/Engagements`, and `campaigns` / `company` carry each campaign's `campaignName` (null when LinkedIn could not name it). `company` returns `months` (the company's totals), `byCampaign` (the same numbers per campaign — overlapping, never add them) and its `deals` with `touchValue` and `influenced`. `warm --output csv` is the file `baker ads linkedin audiences create --type company-list --list-file` takes.
|
|
4085
4086
|
|
|
4086
4087
|
---
|
|
4087
4088
|
|
package/dist/cli.js
CHANGED
|
@@ -22438,6 +22438,20 @@ var accountsCrmStatusResponseSchema = z2.object({
|
|
|
22438
22438
|
contents: crmContentsSchema.nullable()
|
|
22439
22439
|
})
|
|
22440
22440
|
});
|
|
22441
|
+
var accountsCrmSheetRequestSchema = z2.object({
|
|
22442
|
+
/** The Google Drive connection to make it with, when the company has more than one. */
|
|
22443
|
+
account: z2.string().optional()
|
|
22444
|
+
});
|
|
22445
|
+
var accountsCrmSheetResponseSchema = z2.object({
|
|
22446
|
+
ok: z2.literal(true),
|
|
22447
|
+
data: z2.object({
|
|
22448
|
+
/** False when the company already had the sheet: it is handed back, never made twice. */
|
|
22449
|
+
created: z2.boolean(),
|
|
22450
|
+
spreadsheetName: z2.string(),
|
|
22451
|
+
url: z2.string(),
|
|
22452
|
+
tabs: z2.array(z2.string())
|
|
22453
|
+
})
|
|
22454
|
+
});
|
|
22441
22455
|
var CRM_PUSH_EXAMPLE = {
|
|
22442
22456
|
companies: [{ id: "acct-001", name: "Acme Inc", domain: "acme.com", linkedinPage: "acme-inc" }],
|
|
22443
22457
|
deals: [
|
|
@@ -33213,12 +33227,68 @@ var contractCommand = defineCommand({
|
|
|
33213
33227
|
process.stdout.write(crmPushBrief(crmPushUrl(env.BAKER_PUBLIC_API_URL, env.BAKER_API_URL)));
|
|
33214
33228
|
}
|
|
33215
33229
|
});
|
|
33230
|
+
var SHEET_ARGS = {
|
|
33231
|
+
account: {
|
|
33232
|
+
type: "string",
|
|
33233
|
+
description: "The Google Drive connection to create it with, when the company has more than one",
|
|
33234
|
+
required: false
|
|
33235
|
+
}
|
|
33236
|
+
};
|
|
33237
|
+
registerSchema({
|
|
33238
|
+
command: "accounts.crm.sheet",
|
|
33239
|
+
description: `Create the deals sheet LinkedIn's Account insights reads, for a company running LinkedIn campaigns whose CRM Baker cannot read (anything but HubSpot). Makes "Deals and companies for Baker" in the client's Google Drive \u2014 a Deals tab and a Companies tab headed by the columns Baker reads \u2014 and links both, so Baker reads it every night; chooses Another CRM first when nobody has said where the deals are. Applies at once (it is a real file) and goes on the chat's change list. Empty: nothing counts until the client fills it or pastes their CRM export into it. Asking again returns the sheet already linked. Needs a Google Drive connection and a chat owned by an admin of the company; refused when HubSpot is where the deals come from. Returns {created, spreadsheetName, url, tabs}.`,
|
|
33240
|
+
args: SHEET_ARGS
|
|
33241
|
+
});
|
|
33242
|
+
function sheetFailureFix(code, message) {
|
|
33243
|
+
if (code === "NOT_FOUND" && message.startsWith("No Google Drive account is connected")) {
|
|
33244
|
+
return {
|
|
33245
|
+
action: "request_connection",
|
|
33246
|
+
explanation: 'Google Drive is not connected, and the sheet is made in it. Don\'t send the user to Settings or end the turn \u2014 call the `request_connection` tool with { platform: "google-drive", reason: "so I can create the sheet of your deals that LinkedIn Account insights reads" }; once they connect, run `baker accounts crm sheet` again. If they decline or the call fails, carry on with the rest of the work and leave a Task saying the deals sheet is still to create.'
|
|
33247
|
+
};
|
|
33248
|
+
}
|
|
33249
|
+
if (message.startsWith("Only an admin")) {
|
|
33250
|
+
return {
|
|
33251
|
+
action: "continue_without",
|
|
33252
|
+
explanation: "Carry on with the rest of the work and leave a Task for an admin: create the deals sheet for LinkedIn Account insights."
|
|
33253
|
+
};
|
|
33254
|
+
}
|
|
33255
|
+
return null;
|
|
33256
|
+
}
|
|
33257
|
+
var sheetCommand = defineCommand({
|
|
33258
|
+
meta: {
|
|
33259
|
+
name: "sheet",
|
|
33260
|
+
description: "Start here when the client runs LinkedIn ads and their deals are not in HubSpot: create and link the Google Sheet of deals Account insights reads. Example: baker accounts crm sheet"
|
|
33261
|
+
},
|
|
33262
|
+
args: SHEET_ARGS,
|
|
33263
|
+
run: async ({ args: args8 }) => {
|
|
33264
|
+
try {
|
|
33265
|
+
const account = typeof args8.account === "string" && args8.account.trim() !== "" ? args8.account.trim() : void 0;
|
|
33266
|
+
const response = await apiPost("/api/accounts/crm/sheet", account ? { account } : {});
|
|
33267
|
+
writeJson({
|
|
33268
|
+
...response,
|
|
33269
|
+
hints: [
|
|
33270
|
+
`Give the user the link (${response.data.url}). The client fills the Deals tab \u2014 or pastes their CRM's deals export into it \u2014 and keeps it up to date; Baker reads it every night, and "Read now" in Measure \u2192 Sources \u2192 Deals reads it at once. A website on each deal is enough to match it to the companies the LinkedIn ads reach.`,
|
|
33271
|
+
"Check what arrived later with `baker accounts crm status`."
|
|
33272
|
+
]
|
|
33273
|
+
});
|
|
33274
|
+
} catch (err) {
|
|
33275
|
+
if (err instanceof ApiError) {
|
|
33276
|
+
const fix = sheetFailureFix(err.code, err.message);
|
|
33277
|
+
if (fix) {
|
|
33278
|
+
writeJson({ ok: false, error: { code: err.code, message: err.message, fix } });
|
|
33279
|
+
process.exit(1);
|
|
33280
|
+
}
|
|
33281
|
+
}
|
|
33282
|
+
failApi(err);
|
|
33283
|
+
}
|
|
33284
|
+
}
|
|
33285
|
+
});
|
|
33216
33286
|
var crmCommand = defineCommand({
|
|
33217
33287
|
meta: {
|
|
33218
33288
|
name: "crm",
|
|
33219
|
-
description: "A CRM Baker cannot read (Salesforce, Pipedrive, a Google Sheet, their own): where the deals and companies stand, check a file before it is sent, and the contract a developer sends to. Nothing here sends
|
|
33289
|
+
description: "A CRM Baker cannot read (Salesforce, Pipedrive, a Google Sheet, their own): where the deals and companies stand, create the deals sheet Baker reads, check a file before it is sent, and the contract a developer sends to. Nothing here sends a deal."
|
|
33220
33290
|
},
|
|
33221
|
-
subCommands: { status: statusCommand, check: checkCommand, contract: contractCommand }
|
|
33291
|
+
subCommands: { status: statusCommand, sheet: sheetCommand, check: checkCommand, contract: contractCommand }
|
|
33222
33292
|
});
|
|
33223
33293
|
|
|
33224
33294
|
// src/commands/accounts/report.ts
|