@cargo-ai/cli 1.0.49 → 1.0.51

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
@@ -21,6 +21,8 @@ Or run once with `npx` without installing:
21
21
  npx @cargo-ai/cli orchestration workflow list
22
22
  ```
23
23
 
24
+ Using a coding agent? Skip the manual setup — see [Agent Skills](#agent-skills) below to have Claude Code, Codex, or Cursor drive this CLI for you.
25
+
24
26
  ## Authentication
25
27
 
26
28
  A single `cargo-ai login` command supports three channels — pass exactly one of `--email`, `--oauth` or `--token`:
@@ -188,12 +190,35 @@ Other release verbs (`list`, `get <uuid>`, `get-deployed`, `get-draft`, `update-
188
190
 
189
191
  ## Agent Skills
190
192
 
191
- [Cargo Skills](https://github.com/getcargohq/cargo-skills) teaches AI coding agents (Claude Code, Cursor, Windsurf, GitHub Copilot, etc.) how to use the Cargo CLI. Install the skill to let your agent build, run, and manage revenue automation workflows programmatically:
193
+ [Cargo Skills](https://github.com/getcargohq/cargo-skills) teaches AI coding agents (Claude Code, Codex, Cursor, Windsurf, GitHub Copilot, Hermes Agent, etc.) how to use this CLI seventeen skills covering lead sourcing, enrichment, email verification, lead scoring, CRM sync, buying-signal monitoring, orchestration, storage, and workspace-as-code.
194
+
195
+ **Let your agent install everything.** Paste this into Claude Code, Codex, or Cursor and it handles the CLI, sign-in, and skills for its own platform:
196
+
197
+ ```
198
+ Install Cargo by following every step in https://api.getcargo.io/agent-install.txt
199
+ ```
200
+
201
+ **Or install manually.** As a plugin (Claude Code, Codex, Cursor — bundles the skills plus approval and session hooks):
202
+
203
+ ```bash
204
+ /plugin marketplace add getcargohq/cargo-skills # Claude Code
205
+ codex plugin marketplace add getcargohq/cargo-skills
206
+ ```
207
+
208
+ As skills only (any [skills.sh](https://skills.sh)-compatible agent):
192
209
 
193
210
  ```bash
194
211
  npx skills add getcargohq/cargo-skills
195
212
  ```
196
213
 
214
+ Pick one channel — installing both duplicates every skill.
215
+
216
+ On [Hermes Agent](https://github.com/NousResearch/hermes-agent), install from its own hub instead:
217
+
218
+ ```bash
219
+ hermes skills install skills-sh/getcargohq/cargo-skills/cargo-gtm
220
+ ```
221
+
197
222
  ## Development
198
223
 
199
224
  ```bash
@@ -2,7 +2,7 @@ import { type Billing } from "@cargo-ai/api";
2
2
  import type { Api } from "../../api.js";
3
3
  export declare const DEFAULT_TIMEOUT_SECONDS = 300;
4
4
  export declare const DEFAULT_POLL_INTERVAL_SECONDS = 3;
5
- export type AddPaymentMethodOptions = {
5
+ export type HostedCardFormOptions = {
6
6
  timeout: number;
7
7
  pollInterval: number;
8
8
  open: boolean;
@@ -14,13 +14,17 @@ export type AddPaymentMethodOptions = {
14
14
  * The card never touches this process: the CLI only ever sees the last four
15
15
  * digits that the API already exposes. Polling is what makes the handoff usable
16
16
  * from a script — the caller gets a definite answer instead of having to guess
17
- * whether the human finished.
17
+ * whether the operator finished.
18
+ *
19
+ * The URL lands directly on the card form, so an agent driving a browser can
20
+ * complete this unattended. An agent holding the card details themselves should
21
+ * pass them to `update-payment-method` instead and skip the browser entirely.
18
22
  */
19
- export declare function runAddPaymentMethod(api: Api, opts: AddPaymentMethodOptions): Promise<void>;
23
+ export declare function runHostedCardForm(api: Api, opts: HostedCardFormOptions): Promise<void>;
20
24
  /**
21
25
  * Stripe never exposes a raw card number here, so a change is detected from the
22
26
  * fields the API does return. Replacing a card with the very same one is
23
27
  * therefore indistinguishable from doing nothing, and the command times out.
24
28
  */
25
29
  export declare function fingerprintCard(card: Billing.CreditCard | undefined): string;
26
- //# sourceMappingURL=addPaymentMethod.d.ts.map
30
+ //# sourceMappingURL=hostedCardForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostedCardForm.d.ts","sourceRoot":"","sources":["../../../src/commands/billing/hostedCardForm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAWxC,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAE/C,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAgDf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,GAAG,SAAS,GAAG,MAAM,CAW5E"}
@@ -9,11 +9,15 @@ export const DEFAULT_POLL_INTERVAL_SECONDS = 3;
9
9
  * The card never touches this process: the CLI only ever sees the last four
10
10
  * digits that the API already exposes. Polling is what makes the handoff usable
11
11
  * from a script — the caller gets a definite answer instead of having to guess
12
- * whether the human finished.
12
+ * whether the operator finished.
13
+ *
14
+ * The URL lands directly on the card form, so an agent driving a browser can
15
+ * complete this unattended. An agent holding the card details themselves should
16
+ * pass them to `update-payment-method` instead and skip the browser entirely.
13
17
  */
14
- export async function runAddPaymentMethod(api, opts) {
18
+ export async function runHostedCardForm(api, opts) {
15
19
  const before = await handleApiCall(() => api.billing.subscription.getCreditCard());
16
- const { portalSession } = await handleApiCall(() => api.billing.subscription.createPortalSession());
20
+ const { portalSession } = await handleApiCall(() => api.billing.subscription.createPaymentMethodPortalSession());
17
21
  info("");
18
22
  info("To add or replace your card, open this URL in a browser:");
19
23
  info(` ${portalSession.url}`);
@@ -1 +1 @@
1
- {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../../src/commands/billing/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAQxC,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAqSN"}
1
+ {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../../src/commands/billing/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAuFxC,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAkSN"}
@@ -1,5 +1,45 @@
1
- import { handleApiCall, outputJson, parsePositiveInt } from "../runHandler.js";
2
- import { DEFAULT_POLL_INTERVAL_SECONDS, DEFAULT_TIMEOUT_SECONDS, runAddPaymentMethod, } from "./addPaymentMethod.js";
1
+ import { ExitCodes, failWith, handleApiCall, outputJson, parsePositiveInt, success, } from "../runHandler.js";
2
+ import { DEFAULT_POLL_INTERVAL_SECONDS, DEFAULT_TIMEOUT_SECONDS, runHostedCardForm, } from "./hostedCardForm.js";
3
+ import { parseCardExpiry, readCardFromStdin, tokenizeCard, } from "./tokenizeCard.js";
4
+ /**
5
+ * Resolves whichever way the caller supplied a card into a payment method ID,
6
+ * or `undefined` when they supplied nothing and want the hosted form.
7
+ */
8
+ async function resolvePaymentMethodId(api, opts) {
9
+ const hasCardFlags = opts.cardNumber !== undefined ||
10
+ opts.cardExp !== undefined ||
11
+ opts.cardCvc !== undefined;
12
+ if (opts.cardStdin === true && hasCardFlags === true) {
13
+ failWith("Pass either --card-stdin or the --card-* flags, not both.", {
14
+ code: ExitCodes.InvalidUsage,
15
+ });
16
+ }
17
+ let card;
18
+ if (opts.cardStdin === true) {
19
+ card = await readCardFromStdin();
20
+ }
21
+ else if (hasCardFlags === true) {
22
+ if (opts.cardNumber === undefined ||
23
+ opts.cardExp === undefined ||
24
+ opts.cardCvc === undefined) {
25
+ failWith("Card details need --card-number, --card-exp and --card-cvc together.", { code: ExitCodes.InvalidUsage });
26
+ }
27
+ const { expMonth, expYear } = parseCardExpiry(opts.cardExp);
28
+ card = {
29
+ number: opts.cardNumber,
30
+ expMonth,
31
+ expYear,
32
+ cvc: opts.cardCvc,
33
+ };
34
+ }
35
+ if (card === undefined) {
36
+ return undefined;
37
+ }
38
+ // Fetched rather than baked in so the CLI follows whichever Stripe
39
+ // environment the API it is pointed at belongs to.
40
+ const { publishableKey } = await handleApiCall(() => api.billing.subscription.getStripePublishableKey(), { spinner: "Preparing..." });
41
+ return tokenizeCard(publishableKey, card);
42
+ }
3
43
  export function registerSubscriptionCommands(parent, getApi) {
4
44
  const subscription = parent
5
45
  .command("subscription")
@@ -40,55 +80,69 @@ Example:
40
80
  outputJson(result);
41
81
  });
42
82
  subscription
43
- .command("create-setup-intent")
44
- .description("Create a Stripe setup intent for adding/updating a payment method")
45
- .addHelpText("after", `
46
- Example:
47
- $ cargo-ai billing subscription create-setup-intent | jq -r .clientSecret`)
48
- .action(async () => {
49
- const api = getApi();
50
- const result = await handleApiCall(() => api.billing.subscription.createSetupIntent());
51
- outputJson(result);
52
- });
53
- subscription
54
- .command("add-payment-method")
55
- .description("Add or replace the card on file via Stripe's hosted portal, then wait for it to land")
56
- .option("--timeout <seconds>", `How long to wait for the card to change (default: ${String(DEFAULT_TIMEOUT_SECONDS)})`)
57
- .option("--poll-interval <seconds>", `How often to check for the new card (default: ${String(DEFAULT_POLL_INTERVAL_SECONDS)})`)
58
- .option("--no-open", "Print the URL without launching a browser")
83
+ .command("update-payment-method")
84
+ .description("Add or replace the card on file")
85
+ .option("--card-number <number>", "Card number")
86
+ .option("--card-exp <MM/YYYY>", "Card expiry, e.g. 12/2030")
87
+ .option("--card-cvc <cvc>", "Card security code")
88
+ .option("--card-stdin", 'Read {"number","expMonth","expYear","cvc"} as JSON from stdin')
89
+ .option("--timeout <seconds>", `Hosted-form mode: how long to wait for the card to change (default: ${String(DEFAULT_TIMEOUT_SECONDS)})`)
90
+ .option("--poll-interval <seconds>", `Hosted-form mode: how often to check for the new card (default: ${String(DEFAULT_POLL_INTERVAL_SECONDS)})`)
91
+ .option("--no-open", "Hosted-form mode: print the URL without launching a browser")
59
92
  .addHelpText("after", `
60
93
  Examples:
61
- $ cargo-ai billing subscription add-payment-method
62
- $ cargo-ai billing subscription add-payment-method --no-open --timeout 600
94
+ # Card details, no browser
95
+ $ cargo-ai billing subscription update-payment-method \\
96
+ --card-number 4242424242424242 --card-exp 12/2030 --card-cvc 123
63
97
 
64
- Card details are entered on Stripe's hosted portal and never pass through this
65
- CLI. The URL is printed as well as opened, so this works over SSH and in
66
- sandboxes relay the link and the command will detect the new card itself.`)
67
- .action(async (opts) => {
68
- const api = getApi();
69
- await runAddPaymentMethod(api, {
70
- timeout: opts.timeout !== undefined
71
- ? parsePositiveInt(opts.timeout, "--timeout")
72
- : DEFAULT_TIMEOUT_SECONDS,
73
- pollInterval: opts.pollInterval !== undefined
74
- ? parsePositiveInt(opts.pollInterval, "--poll-interval")
75
- : DEFAULT_POLL_INTERVAL_SECONDS,
76
- open: opts.open,
77
- });
78
- });
79
- subscription
80
- .command("update-payment-method")
81
- .description("Replace the workspace's payment method")
82
- .requiredOption("--payment-method-id <id>", "Stripe payment method ID (e.g. pm_1OabcXYZ...)")
83
- .addHelpText("after", `
84
- Example:
85
- $ cargo-ai billing subscription update-payment-method --payment-method-id pm_1OabcXYZ`)
98
+ # Same, keeping the number out of shell history and the process list
99
+ $ echo '{"number":"4242424242424242","expMonth":12,"expYear":2030,"cvc":"123"}' \\
100
+ | cargo-ai billing subscription update-payment-method --card-stdin
101
+
102
+ # No card details: hand a human Stripe's hosted form and wait for it
103
+ $ cargo-ai billing subscription update-payment-method
104
+
105
+ With card details, they go from this machine straight to Stripe in exchange for
106
+ a token; they never pass through the Cargo API, and nothing prints them.
107
+
108
+ With no card details, this falls back to Stripe's hosted form. The URL opens
109
+ directly on the card form and is printed as well as opened, so it works over
110
+ SSH and in sandboxes — relay the link, or drive it with a browser, and the
111
+ command detects the new card itself.
112
+
113
+ Either way the card is verified with a zero-amount authorization before it
114
+ becomes the default, so a declined card — or one whose spend limit or merchant
115
+ restrictions rule us out — fails here rather than at the next renewal. Declines
116
+ report a 'declineCode' you can act on.`)
86
117
  .action(async (opts) => {
87
118
  const api = getApi();
88
- const result = await handleApiCall(() => api.billing.subscription.updatePaymentMethod({
89
- paymentMethodId: opts.paymentMethodId,
90
- }));
91
- outputJson(result);
119
+ const paymentMethodId = await resolvePaymentMethodId(api, opts);
120
+ // Nothing to tokenize and nothing handed to us: fall back to letting a
121
+ // person (or a browser-driving agent) fill in Stripe's hosted form.
122
+ if (paymentMethodId === undefined) {
123
+ await runHostedCardForm(api, {
124
+ timeout: opts.timeout !== undefined
125
+ ? parsePositiveInt(opts.timeout, "--timeout")
126
+ : DEFAULT_TIMEOUT_SECONDS,
127
+ pollInterval: opts.pollInterval !== undefined
128
+ ? parsePositiveInt(opts.pollInterval, "--poll-interval")
129
+ : DEFAULT_POLL_INTERVAL_SECONDS,
130
+ open: opts.open,
131
+ });
132
+ return;
133
+ }
134
+ await handleApiCall(() => api.billing.subscription.updatePaymentMethod({ paymentMethodId }));
135
+ // The card is already set at this point, so a failure to read it back
136
+ // is cosmetic: reporting it as an error would tell the caller the
137
+ // update failed when it did not.
138
+ const creditCard = await api.billing.subscription
139
+ .getCreditCard()
140
+ .then((result) => result.creditCard)
141
+ .catch(() => undefined);
142
+ if (creditCard !== undefined) {
143
+ success(`Payment method updated: ${creditCard.brand} ending ${creditCard.last4}.`);
144
+ }
145
+ outputJson({ ok: true, status: "updated", creditCard });
92
146
  });
93
147
  subscription
94
148
  .command("create-portal-session")
@@ -101,17 +155,6 @@ Example:
101
155
  const result = await handleApiCall(() => api.billing.subscription.createPortalSession());
102
156
  outputJson(result);
103
157
  });
104
- subscription
105
- .command("create-stripe-credits-checkout-session")
106
- .description("Create a Stripe checkout session for purchasing credits")
107
- .addHelpText("after", `
108
- Example:
109
- $ cargo-ai billing subscription create-stripe-credits-checkout-session | jq -r .url`)
110
- .action(async () => {
111
- const api = getApi();
112
- const result = await handleApiCall(() => api.billing.subscription.createStripeCreditsCheckoutSession());
113
- outputJson(result);
114
- });
115
158
  subscription
116
159
  .command("update-plan")
117
160
  .description("Change the credit allotment and billing cadence")
@@ -0,0 +1,31 @@
1
+ export type CardDetails = {
2
+ number: string;
3
+ expMonth: number;
4
+ expYear: number;
5
+ cvc: string;
6
+ };
7
+ /**
8
+ * Exchanges raw card details for a Stripe payment method ID, talking to Stripe
9
+ * directly with the publishable key.
10
+ *
11
+ * The card deliberately does not travel through the Cargo API: this request
12
+ * goes from the caller's machine straight to Stripe, and only the resulting
13
+ * `pm_...` handle is ever sent to us. That is the same path Stripe.js takes in
14
+ * a browser, and it is what keeps card data out of our servers and logs.
15
+ */
16
+ export declare function tokenizeCard(publishableKey: string, card: CardDetails): Promise<string>;
17
+ /**
18
+ * Parses `MM/YYYY` (also accepting `MM/YY` and `M/YYYY`), the form printed on
19
+ * the card itself.
20
+ */
21
+ export declare function parseCardExpiry(value: string): {
22
+ expMonth: number;
23
+ expYear: number;
24
+ };
25
+ /**
26
+ * Reads `{ number, expMonth, expYear, cvc }` from stdin. Preferred over flags
27
+ * for the card number, which would otherwise sit in shell history and in
28
+ * `ps` output for every other process on the machine.
29
+ */
30
+ export declare function readCardFromStdin(): Promise<CardDetails>;
31
+ //# sourceMappingURL=tokenizeCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizeCard.d.ts","sourceRoot":"","sources":["../../../src/commands/billing/tokenizeCard.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAWF;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,MAAM,CAAC,CAiEjB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CA+BA;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,WAAW,CAAC,CAmD9D"}
@@ -0,0 +1,137 @@
1
+ import { ExitCodes, failWith } from "../runHandler.js";
2
+ const STRIPE_PAYMENT_METHODS_URL = "https://api.stripe.com/v1/payment_methods";
3
+ /**
4
+ * Exchanges raw card details for a Stripe payment method ID, talking to Stripe
5
+ * directly with the publishable key.
6
+ *
7
+ * The card deliberately does not travel through the Cargo API: this request
8
+ * goes from the caller's machine straight to Stripe, and only the resulting
9
+ * `pm_...` handle is ever sent to us. That is the same path Stripe.js takes in
10
+ * a browser, and it is what keeps card data out of our servers and logs.
11
+ */
12
+ export async function tokenizeCard(publishableKey, card) {
13
+ const body = new URLSearchParams({
14
+ type: "card",
15
+ "card[number]": card.number,
16
+ "card[exp_month]": String(card.expMonth),
17
+ "card[exp_year]": String(card.expYear),
18
+ "card[cvc]": card.cvc,
19
+ });
20
+ let response;
21
+ try {
22
+ response = await fetch(STRIPE_PAYMENT_METHODS_URL, {
23
+ method: "POST",
24
+ headers: {
25
+ // Stripe takes the key as the basic-auth username with an empty
26
+ // password, which is what `curl -u pk_live_xxx:` sends.
27
+ Authorization: `Basic ${Buffer.from(`${publishableKey}:`).toString("base64")}`,
28
+ "Content-Type": "application/x-www-form-urlencoded",
29
+ },
30
+ body,
31
+ });
32
+ }
33
+ catch (error) {
34
+ failWith(`Could not reach Stripe to tokenize the card: ${error instanceof Error ? error.message : String(error)}`);
35
+ }
36
+ const payload = (await response.json().catch(() => ({})));
37
+ if (response.ok === false) {
38
+ const stripeError = payload.error;
39
+ const message = stripeError !== undefined && stripeError.message !== undefined
40
+ ? stripeError.message
41
+ : `Stripe rejected the card (HTTP ${String(response.status)}).`;
42
+ const extra = {};
43
+ if (stripeError !== undefined) {
44
+ if (stripeError.code !== undefined) {
45
+ extra["code"] = stripeError.code;
46
+ }
47
+ if (stripeError.decline_code !== undefined) {
48
+ extra["declineCode"] = stripeError.decline_code;
49
+ }
50
+ if (stripeError.param !== undefined) {
51
+ extra["param"] = stripeError.param;
52
+ }
53
+ }
54
+ failWith(message, {
55
+ code: ExitCodes.InvalidUsage,
56
+ extra: Object.keys(extra).length > 0 ? extra : undefined,
57
+ });
58
+ }
59
+ const paymentMethodId = payload.id;
60
+ if (typeof paymentMethodId !== "string") {
61
+ failWith("Stripe accepted the card but returned no payment method ID.");
62
+ }
63
+ return paymentMethodId;
64
+ }
65
+ /**
66
+ * Parses `MM/YYYY` (also accepting `MM/YY` and `M/YYYY`), the form printed on
67
+ * the card itself.
68
+ */
69
+ export function parseCardExpiry(value) {
70
+ const match = /^(\d{1,2})\s*\/\s*(\d{2}|\d{4})$/.exec(value.trim());
71
+ if (match === null) {
72
+ failWith(`--card-exp must look like MM/YYYY (got "${value}").`, {
73
+ code: ExitCodes.InvalidUsage,
74
+ });
75
+ }
76
+ const monthPart = match[1];
77
+ const yearPart = match[2];
78
+ if (monthPart === undefined || yearPart === undefined) {
79
+ failWith(`--card-exp must look like MM/YYYY (got "${value}").`, {
80
+ code: ExitCodes.InvalidUsage,
81
+ });
82
+ }
83
+ const expMonth = parseInt(monthPart, 10);
84
+ const expYear = yearPart.length === 2
85
+ ? 2000 + parseInt(yearPart, 10)
86
+ : parseInt(yearPart, 10);
87
+ if (expMonth < 1 || expMonth > 12) {
88
+ failWith(`--card-exp month must be between 1 and 12 (got "${value}").`, {
89
+ code: ExitCodes.InvalidUsage,
90
+ });
91
+ }
92
+ return { expMonth, expYear };
93
+ }
94
+ /**
95
+ * Reads `{ number, expMonth, expYear, cvc }` from stdin. Preferred over flags
96
+ * for the card number, which would otherwise sit in shell history and in
97
+ * `ps` output for every other process on the machine.
98
+ */
99
+ export async function readCardFromStdin() {
100
+ const chunks = [];
101
+ for await (const chunk of process.stdin) {
102
+ chunks.push(Buffer.from(chunk));
103
+ }
104
+ const raw = Buffer.concat(chunks).toString("utf8").trim();
105
+ if (raw.length === 0) {
106
+ failWith("No card JSON received on stdin.", {
107
+ code: ExitCodes.InvalidUsage,
108
+ });
109
+ }
110
+ let parsed;
111
+ try {
112
+ parsed = JSON.parse(raw);
113
+ }
114
+ catch {
115
+ // Deliberately does not echo the input: it contains a card number.
116
+ failWith("Card JSON on stdin is not valid JSON.", {
117
+ code: ExitCodes.InvalidUsage,
118
+ });
119
+ }
120
+ if (parsed === null || typeof parsed !== "object") {
121
+ failWith("Card JSON on stdin must be an object.", {
122
+ code: ExitCodes.InvalidUsage,
123
+ });
124
+ }
125
+ const record = parsed;
126
+ const number = record["number"];
127
+ const expMonth = record["expMonth"];
128
+ const expYear = record["expYear"];
129
+ const cvc = record["cvc"];
130
+ if (typeof number !== "string" ||
131
+ typeof expMonth !== "number" ||
132
+ typeof expYear !== "number" ||
133
+ typeof cvc !== "string") {
134
+ failWith('Card JSON must be {"number": string, "expMonth": number, "expYear": number, "cvc": string}.', { code: ExitCodes.InvalidUsage });
135
+ }
136
+ return { number, expMonth, expYear, cvc };
137
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"runHandler.d.ts","sourceRoot":"","sources":["../../src/commands/runHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runHandler.d.ts","sourceRoot":"","sources":["../../src/commands/runHandler.ts"],"names":[],"mappings":"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"}
@@ -208,7 +208,14 @@ export async function handleApiCall(fn, opts) {
208
208
  if (determineIfIsFetcherError(error)) {
209
209
  const parsedError = error;
210
210
  const status = parsedError.status;
211
- const body = parsedError.body !== undefined ? parsedError.body : String(error);
211
+ // The fetcher's error classes carry the parsed response as `data`;
212
+ // without it the body degrades to the error string and structured detail
213
+ // (a decline code, say) never reaches the caller.
214
+ const body = parsedError.data !== undefined
215
+ ? parsedError.data
216
+ : parsedError.body !== undefined
217
+ ? parsedError.body
218
+ : String(error);
212
219
  const { message, code } = describeApiError(status, body, opts !== undefined ? opts.notAuthenticatedHint : undefined);
213
220
  failWith(message, {
214
221
  code,
@@ -265,6 +272,9 @@ function extractApiErrorDetail(body) {
265
272
  }
266
273
  if (body !== null && typeof body === "object") {
267
274
  const record = body;
275
+ if (typeof record["errorMessage"] === "string") {
276
+ return record["errorMessage"];
277
+ }
268
278
  if (typeof record["message"] === "string") {
269
279
  return record["message"];
270
280
  }
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@cargo-ai/cli",
3
- "version": "1.0.49",
3
+ "version": "1.0.51",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
+ "homepage": "https://getcargo.ai",
7
+ "bugs": {
8
+ "url": "https://github.com/getcargohq/cargo-skills/issues"
9
+ },
6
10
  "description": "GTM automation from your terminal or coding agent — build lead lists, enrich and verify contacts, run plays and workflows, query workspace data with SQL, and deploy a whole Cargo workspace as code.",
7
11
  "keywords": [
8
12
  "cargo",
@@ -55,7 +59,7 @@
55
59
  "format:check": "prettier --check ."
56
60
  },
57
61
  "dependencies": {
58
- "@cargo-ai/api": "^1.0.58",
62
+ "@cargo-ai/api": "^1.0.59",
59
63
  "@cargo-ai/app-sdk": "^1.0.6",
60
64
  "@cargo-ai/cdk": "^1.0.43",
61
65
  "@cargo-ai/types": "^1.0.56",
@@ -1 +0,0 @@
1
- {"version":3,"file":"addPaymentMethod.d.ts","sourceRoot":"","sources":["../../../src/commands/billing/addPaymentMethod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAWxC,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAE/C,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAgDf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,GAAG,SAAS,GAAG,MAAM,CAW5E"}