agentbundle-cli 0.2.2 → 0.2.4
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 +5 -1
- package/bin/agent-bundle.js +16 -2
- package/lib/agentcard-api.js +1 -1
- package/lib/steps/agentcard.js +1 -1
- package/lib/steps/agentmail.js +1 -1
- package/lib/steps/agentphone.js +1 -1
- package/lib/steps/bundle.js +114 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ npx agentbundle-cli
|
|
|
11
11
|
## What it does
|
|
12
12
|
|
|
13
13
|
It asks for **one email up front** and reuses it for all three sign-ups (each product emails a
|
|
14
|
-
verification code), then runs a guided,
|
|
14
|
+
verification code), then runs a guided, four-step onboarding:
|
|
15
15
|
|
|
16
16
|
1. **Agentcard** — signs you up (you type in the emailed verification code), registers the
|
|
17
17
|
hosted HTTP MCP server (OAuth sign-in on first tool use), and offers to switch to
|
|
@@ -21,6 +21,10 @@ verification code), then runs a guided, three-step onboarding:
|
|
|
21
21
|
(`mcp.agentmail.to`). Sign-ups are tagged with the `agentbundle` referrer.
|
|
22
22
|
3. **AgentPhone** — fully automated: requests a verification code by email, you type it in, it
|
|
23
23
|
provisions a real US number, and registers the stdio MCP server (`npx -y agentphone-mcp`). No browser.
|
|
24
|
+
4. **Agent Bundle subscription** — checks whether your email already has the $20/mo bundle and,
|
|
25
|
+
if not, prints a Stripe checkout link for you to open. Nothing is charged by the CLI, and
|
|
26
|
+
nothing is charged until you confirm on that page. Until you do, the three products bill
|
|
27
|
+
separately. Skipped with `--only` and in `--sandbox`/`--dry-run`.
|
|
24
28
|
|
|
25
29
|
All MCP servers are registered at **user scope** (available in every project), via `claude mcp add`.
|
|
26
30
|
|
package/bin/agent-bundle.js
CHANGED
|
@@ -8,6 +8,7 @@ const ac = require('../lib/agentcard-api');
|
|
|
8
8
|
const agentcard = require('../lib/steps/agentcard');
|
|
9
9
|
const agentmail = require('../lib/steps/agentmail');
|
|
10
10
|
const agentphone = require('../lib/steps/agentphone');
|
|
11
|
+
const bundle = require('../lib/steps/bundle');
|
|
11
12
|
|
|
12
13
|
const MODULES = {
|
|
13
14
|
agentcard,
|
|
@@ -86,7 +87,7 @@ async function main() {
|
|
|
86
87
|
if (ctx.sandbox) ui.warn('SANDBOX — stub signups, throwaway config, no real accounts or charges.');
|
|
87
88
|
|
|
88
89
|
// Preflight
|
|
89
|
-
ui.heading(0,
|
|
90
|
+
ui.heading(0, 4, 'Preflight');
|
|
90
91
|
ui.info('Node ' + process.version);
|
|
91
92
|
ui.info('MCP config target: ' + ui.c.bold(mcp.describeBackend()));
|
|
92
93
|
ui.dim('Each product loads as MCP tools after you restart your agent session at the end.');
|
|
@@ -138,6 +139,18 @@ async function main() {
|
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
// The bundle subscription itself. Without this the three products are just three separate
|
|
143
|
+
// sign-ups billed separately — the customer finishes the flow believing they're on the
|
|
144
|
+
// bundle. Skipped for --only runs (one product isn't a bundle) and never fatal.
|
|
145
|
+
if (!ctx.only) {
|
|
146
|
+
try {
|
|
147
|
+
results.push(await bundle.step(ctx));
|
|
148
|
+
} catch (e) {
|
|
149
|
+
ui.fail(`bundle crashed: ${e.message}`);
|
|
150
|
+
results.push({ product: 'bundle', icon: 'fail', note: e.message });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
141
154
|
// Summary
|
|
142
155
|
ui.blank();
|
|
143
156
|
ui.info(ui.c.bold('── Summary ──────────────────────────────────────────'));
|
|
@@ -146,7 +159,8 @@ async function main() {
|
|
|
146
159
|
}
|
|
147
160
|
ui.blank();
|
|
148
161
|
|
|
149
|
-
|
|
162
|
+
// Only the three products register MCP servers — a bundle 'ok' must not imply a restart is due.
|
|
163
|
+
const anyConfigured = results.some((r) => r.icon === 'ok' && r.product !== 'bundle');
|
|
150
164
|
if (anyConfigured && !ctx.dryRun) {
|
|
151
165
|
ui.info(ui.c.bold('Next:'));
|
|
152
166
|
ui.step('Restart your agent session so the new MCP tools load.');
|
package/lib/agentcard-api.js
CHANGED
|
@@ -92,7 +92,7 @@ async function startSignup(email) {
|
|
|
92
92
|
return { mode: 'workos' };
|
|
93
93
|
}
|
|
94
94
|
if (r.data._emailError) {
|
|
95
|
-
throw new Error('Email delivery failed on our side. Try again in a minute
|
|
95
|
+
throw new Error('Email delivery failed on our side. Try again in a minute. If it keeps happening, email support@agentcard.com.');
|
|
96
96
|
}
|
|
97
97
|
return { mode: 'legacy', sessionId: r.data.sessionId };
|
|
98
98
|
}
|
package/lib/steps/agentcard.js
CHANGED
|
@@ -95,7 +95,7 @@ async function completeSignup(ctx) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async function step(ctx) {
|
|
98
|
-
ui.heading(1,
|
|
98
|
+
ui.heading(1, 4, 'Agentcard — a virtual card for your agent');
|
|
99
99
|
ui.dim('Prepaid virtual cards with per-task budgets, controlled through MCP tools.');
|
|
100
100
|
|
|
101
101
|
if (mcp.exists(MCP_NAME)) {
|
package/lib/steps/agentmail.js
CHANGED
|
@@ -63,7 +63,7 @@ function register(key, ctx, extraNote) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
async function step(ctx) {
|
|
66
|
-
ui.heading(2,
|
|
66
|
+
ui.heading(2, 4, 'AgentMail — an email inbox for your agent');
|
|
67
67
|
ui.dim('Real inboxes the agent owns — for sign-ups, verification emails, and outreach.');
|
|
68
68
|
|
|
69
69
|
if (mcp.exists(MCP_NAME)) {
|
package/lib/steps/agentphone.js
CHANGED
|
@@ -47,7 +47,7 @@ function register(key, ctx, extraNote) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
async function step(ctx) {
|
|
50
|
-
ui.heading(3,
|
|
50
|
+
ui.heading(3, 4, 'AgentPhone — a real phone number for your agent');
|
|
51
51
|
ui.dim('A live US number for SMS + voice. This step is fully automated — no browser needed.');
|
|
52
52
|
|
|
53
53
|
if (mcp.exists(MCP_NAME)) {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// [4/4] Agent Bundle subscription — the one thing that makes the three products a *bundle*.
|
|
3
|
+
//
|
|
4
|
+
// The three product steps each sign the customer up separately; none of them starts the
|
|
5
|
+
// $20/mo bundle subscription. That subscription lives on the bundle site (Stripe, keyed by
|
|
6
|
+
// email) — `apps/agent-bundle/src/app/api/checkout/route.ts` is written for this CLI to call
|
|
7
|
+
// ("the `agent-bundle` CLI calls this after installing the three MCP servers") but nothing
|
|
8
|
+
// ever did, so every run finished with the customer unsubscribed and no way to tell.
|
|
9
|
+
//
|
|
10
|
+
// We never charge here. We check status, and when there's no subscription we hand back the
|
|
11
|
+
// Stripe hosted checkout URL — the customer enters a card there or ignores it. A missing or
|
|
12
|
+
// unreachable bundle site is a warning, never a failure: the three products are already set up.
|
|
13
|
+
|
|
14
|
+
const ui = require('../ui');
|
|
15
|
+
const ac = require('../agentcard-api');
|
|
16
|
+
|
|
17
|
+
const SITE_URL = (process.env.AGENT_BUNDLE_SITE_URL || 'https://theagentbundle.com').replace(/\/+$/, '');
|
|
18
|
+
const PRICE = '$20/mo';
|
|
19
|
+
|
|
20
|
+
async function getJson(url, init) {
|
|
21
|
+
const ctrl = new AbortController();
|
|
22
|
+
const t = setTimeout(() => ctrl.abort(), 20000);
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(url, { ...init, signal: ctrl.signal });
|
|
25
|
+
let body = null;
|
|
26
|
+
try {
|
|
27
|
+
body = await res.json();
|
|
28
|
+
} catch {
|
|
29
|
+
/* non-JSON body — treated as an empty response below */
|
|
30
|
+
}
|
|
31
|
+
return { status: res.status, body: body || {} };
|
|
32
|
+
} finally {
|
|
33
|
+
clearTimeout(t);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// True when this run should not touch the bundle site at all.
|
|
38
|
+
function needsSetup(ctx) {
|
|
39
|
+
return !ctx.dryRun && !ctx.sandbox;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function step(ctx) {
|
|
43
|
+
const product = 'bundle';
|
|
44
|
+
ui.heading(4, 4, `Agent Bundle subscription (${PRICE})`);
|
|
45
|
+
|
|
46
|
+
if (ctx.dryRun) {
|
|
47
|
+
ui.step(`would GET ${SITE_URL}/api/status?email=… and, if not subscribed, POST /api/checkout for a ${PRICE} Stripe link`);
|
|
48
|
+
return { product, icon: 'ok', note: 'dry run — no subscription started' };
|
|
49
|
+
}
|
|
50
|
+
if (ctx.sandbox) {
|
|
51
|
+
ui.dim('sandbox — skipping the subscription (no money, no Stripe).');
|
|
52
|
+
return { product, icon: 'ok', note: 'skipped in sandbox' };
|
|
53
|
+
}
|
|
54
|
+
// On a re-run where all three products are already configured the CLI never asks for an email,
|
|
55
|
+
// so fall back to the one the Agentcard sign-up stored. Without this, exactly the customer who
|
|
56
|
+
// most needs the subscription check — the one who already finished the flow — never gets it.
|
|
57
|
+
const email = ctx.email || (ac.readConfig().email || '').trim();
|
|
58
|
+
if (!email) {
|
|
59
|
+
ui.warn('No email set, so I can\'t look up or start the bundle subscription.');
|
|
60
|
+
ui.dim(`Re-run with --email you@example.com, or subscribe at ${SITE_URL}.`);
|
|
61
|
+
return { product, icon: 'warn', note: 'no email — subscribe at ' + SITE_URL };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let status;
|
|
65
|
+
try {
|
|
66
|
+
status = await getJson(`${SITE_URL}/api/status?email=${encodeURIComponent(email)}`);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
ui.warn(`Couldn't reach the bundle site (${e.message}).`);
|
|
69
|
+
ui.dim(`Your three products are set up. Subscribe when you can at ${SITE_URL}.`);
|
|
70
|
+
return { product, icon: 'warn', note: 'site unreachable — subscribe at ' + SITE_URL };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (status.status !== 200 || status.body.configured !== true) {
|
|
74
|
+
ui.warn('The Agent Bundle subscription isn\'t open yet.');
|
|
75
|
+
ui.dim('Your three products work regardless — each is billed on its own for now.');
|
|
76
|
+
return { product, icon: 'warn', note: 'bundle not available yet' };
|
|
77
|
+
}
|
|
78
|
+
if (status.body.active === true) {
|
|
79
|
+
ui.ok(`Bundle already active for ${ui.c.bold(email)} — nothing to pay.`);
|
|
80
|
+
return { product, icon: 'ok', note: 'already subscribed' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let checkout;
|
|
84
|
+
try {
|
|
85
|
+
checkout = await getJson(`${SITE_URL}/api/checkout`, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify({ email }),
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {
|
|
91
|
+
ui.warn(`Couldn't start the subscription (${e.message}).`);
|
|
92
|
+
ui.dim(`Subscribe at ${SITE_URL} whenever you're ready.`);
|
|
93
|
+
return { product, icon: 'warn', note: 'checkout failed — subscribe at ' + SITE_URL };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 409 = this email already has an active bundle; the status call raced us. Not an error.
|
|
97
|
+
if (checkout.status === 409) {
|
|
98
|
+
ui.ok(`Bundle already active for ${ui.c.bold(email)} — nothing to pay.`);
|
|
99
|
+
return { product, icon: 'ok', note: 'already subscribed' };
|
|
100
|
+
}
|
|
101
|
+
if (checkout.status !== 200 || !checkout.body.checkoutUrl) {
|
|
102
|
+
ui.warn(`Couldn't start the subscription${checkout.body.error ? ': ' + checkout.body.error : '.'}`);
|
|
103
|
+
ui.dim(`Subscribe at ${SITE_URL} whenever you're ready.`);
|
|
104
|
+
return { product, icon: 'warn', note: 'checkout failed — subscribe at ' + SITE_URL };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
ui.blank();
|
|
108
|
+
ui.info(`Last step — open this to start the ${ui.c.bold(PRICE)} bundle and enter a card:`);
|
|
109
|
+
ui.info(' ' + ui.c.cyan(checkout.body.checkoutUrl));
|
|
110
|
+
ui.dim('Nothing is charged until you confirm on that page. Until then the three products bill separately.');
|
|
111
|
+
return { product, icon: 'warn', note: `open the checkout link to start the ${PRICE} bundle` };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = { step, needsSetup, SITE_URL, PRICE };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentbundle-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "One command to give your AI agent a card, an inbox, and a phone — installs Agentcard, AgentMail, and AgentPhone via a guided onboarding flow.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agentbundle-cli": "bin/agent-bundle.js",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=18"
|
|
12
12
|
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node --test test/*.test.js"
|
|
15
|
+
},
|
|
13
16
|
"files": [
|
|
14
17
|
"bin",
|
|
15
18
|
"lib",
|