@clawcard/cli 1.0.5 → 1.0.9

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
@@ -79,11 +79,13 @@ Each API key comes with:
79
79
 
80
80
  ## Pricing
81
81
 
82
- - **Free** 1 key, 1 card/month
83
- - **Starter** ($7/mo) — 2 keys, 5 cards/month
84
- - **Pro** ($19/mo) — 5 keys, 25 cards/month
82
+ Pay as you go. No subscriptions, no monthly fees.
85
83
 
86
- Card spend is separate — top up your balance anytime.
84
+ - **Minimum top-up** $5
85
+ - **Processing fee** — 10%
86
+ - **Cards** — Unlimited
87
+
88
+ Top up your balance and your agent spends it on virtual cards.
87
89
 
88
90
  ## Links
89
91
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawcard/cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.9",
4
4
  "description": "The ClawCard CLI — manage your agent keys, billing, and setup from the terminal",
5
5
  "bin": {
6
6
  "clawcard": "./bin/clawcard.mjs"
@@ -2,13 +2,7 @@ import * as p from "@clack/prompts";
2
2
  import chalk from "chalk";
3
3
  import open from "open";
4
4
  import { requireAuth } from "../auth-guard.js";
5
- import {
6
- getBalance,
7
- getSubscription,
8
- getTransactions,
9
- createCheckout,
10
- createPortal,
11
- } from "../api.js";
5
+ import { getBalance, getTransactions, createCheckout } from "../api.js";
12
6
 
13
7
  const orange = chalk.hex("#FF6B35");
14
8
 
@@ -19,26 +13,13 @@ export async function billingCommand() {
19
13
  s.start("Loading billing info...");
20
14
 
21
15
  try {
22
- const [balance, sub] = await Promise.all([
23
- getBalance(),
24
- getSubscription(),
25
- ]);
16
+ const balance = await getBalance();
26
17
  s.stop("Billing loaded");
27
18
 
28
- const planColors = {
29
- free: chalk.gray,
30
- starter: chalk.blue,
31
- pro: orange,
32
- };
33
- const colorPlan = (planColors[sub.subscription?.plan] || chalk.white)(
34
- sub.subscription?.planName || sub.subscription?.plan || "Free"
35
- );
36
-
37
19
  const info = [
38
- `Plan: ${colorPlan}`,
39
20
  `Balance: ${chalk.green("$" + (balance.amountCents / 100).toFixed(2))}`,
40
- `Keys: ${sub.subscription?.currentAgentKeys || 0}/${sub.subscription?.maxAgentKeys || 1}`,
41
- `Cards: ${sub.subscription?.currentCards || 0}/${sub.subscription?.maxCardsPerMonth || 1} this period`,
21
+ "",
22
+ chalk.dim("Pay as you go 10% processing fee on top-ups"),
42
23
  ].join("\n");
43
24
 
44
25
  p.note(info, "Billing");
@@ -47,7 +28,6 @@ export async function billingCommand() {
47
28
  message: "What would you like to do?",
48
29
  options: [
49
30
  { value: "topup", label: "Top up balance" },
50
- { value: "upgrade", label: "Upgrade plan" },
51
31
  { value: "transactions", label: "View transactions" },
52
32
  { value: "back", label: "Back" },
53
33
  ],
@@ -58,8 +38,6 @@ export async function billingCommand() {
58
38
  switch (action) {
59
39
  case "topup":
60
40
  return billingTopupCommand();
61
- case "upgrade":
62
- return billingUpgradeCommand();
63
41
  case "transactions":
64
42
  return billingTransactionsCommand();
65
43
  }
@@ -91,13 +69,13 @@ export async function billingTopupCommand() {
91
69
  requireAuth();
92
70
 
93
71
  const amount = await p.select({
94
- message: "How much would you like to add?",
72
+ message: "How much would you like to add? (10% processing fee)",
95
73
  options: [
96
- { value: 500, label: "$5.00" },
97
- { value: 1000, label: "$10.00" },
98
- { value: 2500, label: "$25.00" },
99
- { value: 5000, label: "$50.00" },
100
- { value: 10000, label: "$100.00" },
74
+ { value: 500, label: "$5.00", hint: "you pay $5.50" },
75
+ { value: 1000, label: "$10.00", hint: "you pay $11.00" },
76
+ { value: 2500, label: "$25.00", hint: "you pay $27.50" },
77
+ { value: 5000, label: "$50.00", hint: "you pay $55.00" },
78
+ { value: 10000, label: "$100.00", hint: "you pay $110.00" },
101
79
  ],
102
80
  });
103
81
 
@@ -119,24 +97,6 @@ export async function billingTopupCommand() {
119
97
  }
120
98
  }
121
99
 
122
- export async function billingUpgradeCommand() {
123
- requireAuth();
124
-
125
- const s = p.spinner();
126
- s.start("Opening billing portal...");
127
-
128
- try {
129
- const result = await createPortal();
130
- s.stop("");
131
-
132
- await open(result.url);
133
- p.log.success("Manage your subscription in the browser");
134
- } catch (err) {
135
- s.stop("Failed");
136
- p.log.error(err.message);
137
- }
138
- }
139
-
140
100
  export async function billingTransactionsCommand() {
141
101
  requireAuth();
142
102
 
@@ -25,7 +25,6 @@ const COMMANDS = [
25
25
  [" billing", "Interactive billing dashboard"],
26
26
  [" billing balance", "Quick balance check"],
27
27
  [" billing topup", "Top up balance"],
28
- [" billing upgrade", "Upgrade subscription"],
29
28
  [" billing transactions", "View transaction history"],
30
29
  ["", ""],
31
30
  [orange.bold("Other"), ""],
@@ -49,19 +49,7 @@ async function pollForToken(code, timeout = 120_000) {
49
49
  const data = await res.json();
50
50
 
51
51
  if (data.status === "complete" && data.token) {
52
- // Fetch email using the token
53
- let email = "";
54
- try {
55
- const meRes = await fetch(`${BASE_URL}/api/me`, {
56
- headers: { Authorization: `Bearer ${data.token}` },
57
- });
58
- const me = await meRes.json();
59
- email = me.email || "";
60
- } catch {
61
- // non-critical
62
- }
63
-
64
- return { token: data.token, email };
52
+ return { token: data.token, email: data.email || "" };
65
53
  }
66
54
  } catch {
67
55
  // network error, keep polling
@@ -1,11 +1,6 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import { execSync } from "child_process";
3
- import {
4
- readFileSync,
5
- writeFileSync,
6
- mkdirSync,
7
- existsSync,
8
- } from "fs";
3
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
9
4
  import { join } from "path";
10
5
  import { homedir } from "os";
11
6
  import { requireAuth } from "../auth-guard.js";
@@ -16,107 +11,13 @@ import chalk from "chalk";
16
11
  const orange = chalk.hex("#FF6B35");
17
12
  const home = homedir();
18
13
 
19
- // ── Per-harness env var writers ──
20
-
21
- const HARNESSES = [
22
- {
23
- value: "claude-code",
24
- label: "Claude Code",
25
- hint: "~/.claude/settings.json",
26
- write(apiKey) {
27
- const settingsPath = join(home, ".claude", "settings.json");
28
- let settings = {};
29
- try {
30
- settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
31
- } catch {
32
- // doesn't exist yet
33
- }
34
- if (!settings.env) settings.env = {};
35
- settings.env.CLAWCARD_API_KEY = apiKey;
36
- mkdirSync(join(home, ".claude"), { recursive: true });
37
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
38
- return settingsPath;
39
- },
40
- },
41
- {
42
- value: "cursor",
43
- label: "Cursor",
44
- hint: ".cursor/environment.json (project)",
45
- write(apiKey) {
46
- const envPath = join(process.cwd(), ".cursor", "environment.json");
47
- let config = {};
48
- try {
49
- config = JSON.parse(readFileSync(envPath, "utf-8"));
50
- } catch {
51
- // doesn't exist yet
52
- }
53
- if (!config.env) config.env = {};
54
- config.env.CLAWCARD_API_KEY = apiKey;
55
- mkdirSync(join(process.cwd(), ".cursor"), { recursive: true });
56
- writeFileSync(envPath, JSON.stringify(config, null, 2));
57
- return envPath;
58
- },
59
- },
60
- {
61
- value: "openclaw",
62
- label: "OpenClaw",
63
- hint: "~/.openclaw/.env",
64
- write(apiKey) {
65
- const envPath = join(home, ".openclaw", ".env");
66
- let content = "";
67
- try {
68
- content = readFileSync(envPath, "utf-8");
69
- } catch {
70
- // doesn't exist
71
- }
72
- const regex = /^CLAWCARD_API_KEY=.*$/m;
73
- if (regex.test(content)) {
74
- content = content.replace(regex, `CLAWCARD_API_KEY=${apiKey}`);
75
- } else {
76
- content = content.trimEnd() + `\nCLAWCARD_API_KEY=${apiKey}\n`;
77
- }
78
- mkdirSync(join(home, ".openclaw"), { recursive: true });
79
- writeFileSync(envPath, content);
80
- return envPath;
81
- },
82
- },
83
- {
84
- value: "codex",
85
- label: "Codex (OpenAI)",
86
- hint: "shell env",
87
- write(apiKey) {
88
- // Codex reads from shell env; write to ~/.clawcard/.env and instruct user
89
- return writeToClawcardEnv(apiKey);
90
- },
91
- },
92
- {
93
- value: "cline",
94
- label: "Cline",
95
- hint: "shell env",
96
- write(apiKey) {
97
- // Cline reads from shell env; write to ~/.clawcard/.env and instruct user
98
- return writeToClawcardEnv(apiKey);
99
- },
100
- },
101
- {
102
- value: "other",
103
- label: "Other / manual",
104
- hint: "~/.clawcard/.env",
105
- write(apiKey) {
106
- return writeToClawcardEnv(apiKey);
107
- },
108
- },
109
- ];
110
-
111
- function writeToClawcardEnv(apiKey) {
14
+ function writeApiKey(apiKey) {
112
15
  const envPath = join(home, ".clawcard", ".env");
113
16
  mkdirSync(join(home, ".clawcard"), { recursive: true });
114
17
  let content = "";
115
18
  try {
116
19
  content = readFileSync(envPath, "utf-8");
117
- } catch {
118
- // doesn't exist
119
- }
20
+ } catch {}
120
21
  const regex = /^CLAWCARD_API_KEY=.*$/m;
121
22
  if (regex.test(content)) {
122
23
  content = content.replace(regex, `CLAWCARD_API_KEY=${apiKey}`);
@@ -147,36 +48,17 @@ export async function setupCommand() {
147
48
  }
148
49
 
149
50
  if (agents.length === 0) {
150
- p.log.info("You don't have any keys yet. Let's create one first.");
151
- const { keysCreateCommand } = await import("./keys.js");
152
- await keysCreateCommand();
153
- return setupCommand();
154
- }
155
-
156
- const keyChoice = await p.select({
157
- message: "Which API key should this agent use?",
158
- options: [
159
- ...agents.map((a) => ({
160
- value: a.id,
161
- label: `${a.name || "unnamed"} (${a.keyPrefix}...)`,
162
- hint: a.email,
163
- })),
164
- { value: "create", label: "Create a new key" },
165
- ],
166
- });
167
-
168
- if (p.isCancel(keyChoice)) return;
169
-
170
- if (keyChoice === "create") {
51
+ p.log.info("You don't have an agent key yet. Let's create one.");
171
52
  const { keysCreateCommand } = await import("./keys.js");
172
53
  await keysCreateCommand();
173
54
  return setupCommand();
174
55
  }
175
56
 
176
- const selectedKey = agents.find((a) => a.id === keyChoice);
57
+ // Auto-select if there's only one key
58
+ const selectedKey = agents[0];
177
59
 
178
60
  // Step 2: Get the raw API key
179
- let apiKey = getSavedKey(keyChoice);
61
+ let apiKey = getSavedKey(selectedKey.id);
180
62
 
181
63
  if (!apiKey) {
182
64
  p.log.warn("Raw API key not found locally (it's only shown at creation).");
@@ -191,19 +73,7 @@ export async function setupCommand() {
191
73
  apiKey = pastedKey;
192
74
  }
193
75
 
194
- // Step 3: Which harness?
195
- const harness = await p.select({
196
- message: "Which agent harness are you using?",
197
- options: HARNESSES.map((h) => ({
198
- value: h.value,
199
- label: h.label,
200
- hint: h.hint,
201
- })),
202
- });
203
-
204
- if (p.isCancel(harness)) return;
205
-
206
- // Step 4: Global or project?
76
+ // Step 3: Global or project?
207
77
  const scope = await p.select({
208
78
  message: "Install skill globally or for this project?",
209
79
  options: [
@@ -222,7 +92,7 @@ export async function setupCommand() {
222
92
 
223
93
  if (p.isCancel(scope)) return;
224
94
 
225
- // Step 5: Install skill via skills.sh
95
+ // Step 4: Install skill via skills.sh (auto-detects all agents)
226
96
  const s2 = p.spinner();
227
97
  s2.start("Installing ClawCard skill...");
228
98
 
@@ -243,37 +113,11 @@ export async function setupCommand() {
243
113
  );
244
114
  }
245
115
 
246
- // Step 6: Write API key to harness-specific location
247
- const s3 = p.spinner();
248
- s3.start("Configuring API key...");
249
-
250
- const harnessConfig = HARNESSES.find((h) => h.value === harness);
251
- const writtenPath = harnessConfig.write(apiKey);
252
-
253
- s3.stop("API key configured!");
254
-
255
- // Step 7: Harness-specific follow-up instructions
256
- const needsShellExport = ["codex", "cline", "other"].includes(harness);
257
-
258
- if (needsShellExport) {
259
- p.note(
260
- [
261
- `Key saved to: ${chalk.dim(writtenPath)}`,
262
- "",
263
- `Add this to your shell profile (.zshrc, .bashrc):`,
264
- "",
265
- orange(` export CLAWCARD_API_KEY="${apiKey}"`),
266
- "",
267
- chalk.dim("Or source the env file:"),
268
- orange(` source ${writtenPath}`),
269
- ].join("\n"),
270
- "Manual step required"
271
- );
272
- } else {
273
- p.log.info(`Key written to ${chalk.dim(writtenPath)}`);
274
- }
116
+ // Step 5: Write API key to ~/.clawcard/.env
117
+ const envPath = writeApiKey(apiKey);
118
+ p.log.info(`API key saved to ${chalk.dim(envPath)}`);
275
119
 
276
- // Step 8: Summary
120
+ // Step 6: Summary
277
121
  p.log.success("Your agent is ready to use ClawCard!");
278
122
  p.log.info(
279
123
  [
@@ -1,8 +1,11 @@
1
1
  import * as p from "@clack/prompts";
2
+ import chalk from "chalk";
2
3
  import open from "open";
3
4
  import crypto from "crypto";
4
5
  import { saveConfig, BASE_URL } from "../config.js";
5
6
 
7
+ const orange = chalk.hex("#FF6B35");
8
+
6
9
  export async function signupCommand() {
7
10
  const inviteCode = await p.text({
8
11
  message: "Enter your invite code",
@@ -39,6 +42,38 @@ export async function signupCommand() {
39
42
  });
40
43
 
41
44
  p.log.success(`Welcome to ClawCard, ${result.email}!`);
45
+
46
+ p.note(
47
+ [
48
+ `${orange.bold("Step 1:")} Create an API key`,
49
+ ` ${chalk.dim("$ clawcard keys create")}`,
50
+ "",
51
+ `${orange.bold("Step 2:")} Set up your agent`,
52
+ ` ${chalk.dim("$ clawcard setup")}`,
53
+ "",
54
+ `${orange.bold("Step 3:")} Top up your balance`,
55
+ ` ${chalk.dim("$ clawcard billing topup")}`,
56
+ ].join("\n"),
57
+ "Getting Started"
58
+ );
59
+
60
+ const next = await p.confirm({
61
+ message: "Create your first API key now?",
62
+ });
63
+
64
+ if (!p.isCancel(next) && next) {
65
+ const { keysCreateCommand } = await import("./keys.js");
66
+ await keysCreateCommand();
67
+
68
+ const runSetup = await p.confirm({
69
+ message: "Set up your agent now?",
70
+ });
71
+
72
+ if (!p.isCancel(runSetup) && runSetup) {
73
+ const { setupCommand } = await import("./setup.js");
74
+ await setupCommand();
75
+ }
76
+ }
42
77
  } catch (err) {
43
78
  s.stop("Signup failed");
44
79
  p.log.error(err.message);
@@ -57,18 +92,7 @@ async function pollForToken(code, timeout = 120_000) {
57
92
  const data = await res.json();
58
93
 
59
94
  if (data.status === "complete" && data.token) {
60
- let email = "";
61
- try {
62
- const meRes = await fetch(`${BASE_URL}/api/me`, {
63
- headers: { Authorization: `Bearer ${data.token}` },
64
- });
65
- const me = await meRes.json();
66
- email = me.email || "";
67
- } catch {
68
- // non-critical
69
- }
70
-
71
- return { token: data.token, email };
95
+ return { token: data.token, email: data.email || "" };
72
96
  }
73
97
  } catch {
74
98
  // network error, keep polling
package/src/index.js CHANGED
@@ -121,14 +121,6 @@ billing
121
121
  await billingTopupCommand();
122
122
  });
123
123
 
124
- billing
125
- .command("upgrade")
126
- .description("Upgrade subscription")
127
- .action(async () => {
128
- const { billingUpgradeCommand } = await import("./commands/billing.js");
129
- await billingUpgradeCommand();
130
- });
131
-
132
124
  billing
133
125
  .command("transactions")
134
126
  .description("View transaction history")
@@ -190,9 +182,8 @@ if (process.argv.length <= 2) {
190
182
  {
191
183
  value: "setup",
192
184
  label: "Setup",
193
- hint: "connect ClawCard to your agent",
185
+ hint: "set up your agent",
194
186
  },
195
- { value: "keys", label: "Keys", hint: "create & manage API keys" },
196
187
  { value: "billing", label: "Billing", hint: "check balance & top up" },
197
188
  { value: "referral", label: "Referral", hint: "share & earn $10" },
198
189
  { value: "whoami", label: "Who am I?", hint: "show current account" },