@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 +26 -1
- package/build/commands/billing/{addPaymentMethod.d.ts → hostedCardForm.d.ts} +8 -4
- package/build/commands/billing/hostedCardForm.d.ts.map +1 -0
- package/build/commands/billing/{addPaymentMethod.js → hostedCardForm.js} +7 -3
- package/build/commands/billing/subscription.d.ts.map +1 -1
- package/build/commands/billing/subscription.js +100 -57
- package/build/commands/billing/tokenizeCard.d.ts +31 -0
- package/build/commands/billing/tokenizeCard.d.ts.map +1 -0
- package/build/commands/billing/tokenizeCard.js +137 -0
- package/build/commands/runHandler.d.ts.map +1 -1
- package/build/commands/runHandler.js +11 -1
- package/package.json +6 -2
- package/build/commands/billing/addPaymentMethod.d.ts.map +0 -1
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
|
|
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
|
|
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
|
|
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
|
|
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=
|
|
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
|
|
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
|
|
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.
|
|
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;
|
|
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,
|
|
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("
|
|
44
|
-
.description("
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
62
|
-
$ cargo-ai billing subscription
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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":"AAEA,eAAO,MAAM,SAAS;;;;;;;CAOZ,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAclE,eAAO,MAAM,MAAM;aACR,MAAM,KAAG,MAAM;eACb,MAAM,KAAG,MAAM;gBACd,MAAM,KAAG,MAAM;cACjB,MAAM,KAAG,MAAM;aAChB,MAAM,KAAG,MAAM;cACd,MAAM,KAAG,MAAM;CAC1B,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAM/C;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAID,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUhE;AAED,KAAK,YAAY,GAAG;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,KAAK,CAgCpE;AAKD,MAAM,MAAM,OAAO,GAAG;IAEpB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAGlC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAIF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAuBrD;AAkDD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,GAAG,CAQhE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ1E;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,OAAO,EAAE,CAAC,EAAE,EACZ,KAAK,EAAE,MAAM,GACZ;IAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CASxD;AAKD,KAAK,iBAAiB,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACzB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,wBAAsB,aAAa,CAAC,CAAC,EACnC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,CAAC,EAAE,iBAAiB,GACvB,OAAO,CAAC,CAAC,CAAC,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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"}
|