@clawcard/cli 0.1.3 → 1.0.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/bin/clawcard.mjs +2 -0
- package/package.json +13 -5
- package/src/api.js +55 -0
- package/src/auth-guard.js +9 -0
- package/src/auth-server.js +63 -0
- package/src/commands/billing.js +169 -0
- package/src/commands/help.js +49 -0
- package/src/commands/keys.js +230 -0
- package/src/commands/login.js +39 -0
- package/src/commands/logout.js +12 -0
- package/src/commands/referral.js +37 -0
- package/src/commands/setup.js +291 -0
- package/src/commands/signup.js +47 -0
- package/src/commands/whoami.js +31 -0
- package/src/config.js +68 -0
- package/src/index.js +209 -0
- package/src/splash.js +11 -0
- package/bin/setup.mjs +0 -207
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import {
|
|
4
|
+
readFileSync,
|
|
5
|
+
writeFileSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
existsSync,
|
|
8
|
+
} from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { requireAuth } from "../auth-guard.js";
|
|
12
|
+
import { listAgents } from "../api.js";
|
|
13
|
+
import { getSavedKey } from "../config.js";
|
|
14
|
+
import chalk from "chalk";
|
|
15
|
+
|
|
16
|
+
const orange = chalk.hex("#FF6B35");
|
|
17
|
+
const home = homedir();
|
|
18
|
+
|
|
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) {
|
|
112
|
+
const envPath = join(home, ".clawcard", ".env");
|
|
113
|
+
mkdirSync(join(home, ".clawcard"), { recursive: true });
|
|
114
|
+
let content = "";
|
|
115
|
+
try {
|
|
116
|
+
content = readFileSync(envPath, "utf-8");
|
|
117
|
+
} catch {
|
|
118
|
+
// doesn't exist
|
|
119
|
+
}
|
|
120
|
+
const regex = /^CLAWCARD_API_KEY=.*$/m;
|
|
121
|
+
if (regex.test(content)) {
|
|
122
|
+
content = content.replace(regex, `CLAWCARD_API_KEY=${apiKey}`);
|
|
123
|
+
} else {
|
|
124
|
+
content = content.trimEnd() + `\nCLAWCARD_API_KEY=${apiKey}\n`;
|
|
125
|
+
}
|
|
126
|
+
writeFileSync(envPath, content);
|
|
127
|
+
return envPath;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function setupCommand() {
|
|
131
|
+
requireAuth();
|
|
132
|
+
|
|
133
|
+
p.intro(orange.bold("Agent Setup"));
|
|
134
|
+
|
|
135
|
+
// Step 1: Pick or create a key
|
|
136
|
+
const s = p.spinner();
|
|
137
|
+
s.start("Fetching your keys...");
|
|
138
|
+
|
|
139
|
+
let agents;
|
|
140
|
+
try {
|
|
141
|
+
agents = await listAgents();
|
|
142
|
+
s.stop("");
|
|
143
|
+
} catch (err) {
|
|
144
|
+
s.stop("Failed to fetch keys");
|
|
145
|
+
p.log.error(err.message);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
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") {
|
|
171
|
+
const { keysCreateCommand } = await import("./keys.js");
|
|
172
|
+
await keysCreateCommand();
|
|
173
|
+
return setupCommand();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const selectedKey = agents.find((a) => a.id === keyChoice);
|
|
177
|
+
|
|
178
|
+
// Step 2: Get the raw API key
|
|
179
|
+
let apiKey = getSavedKey(keyChoice);
|
|
180
|
+
|
|
181
|
+
if (!apiKey) {
|
|
182
|
+
p.log.warn("Raw API key not found locally (it's only shown at creation).");
|
|
183
|
+
const pastedKey = await p.text({
|
|
184
|
+
message: "Paste the API key for this agent",
|
|
185
|
+
placeholder: "ak_live_...",
|
|
186
|
+
validate: (v) => {
|
|
187
|
+
if (!v?.startsWith("ak_live_")) return "Must start with ak_live_";
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
if (p.isCancel(pastedKey)) return;
|
|
191
|
+
apiKey = pastedKey;
|
|
192
|
+
}
|
|
193
|
+
|
|
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?
|
|
207
|
+
const scope = await p.select({
|
|
208
|
+
message: "Install skill globally or for this project?",
|
|
209
|
+
options: [
|
|
210
|
+
{
|
|
211
|
+
value: "global",
|
|
212
|
+
label: "Global",
|
|
213
|
+
hint: "available to all your agents everywhere",
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
value: "project",
|
|
217
|
+
label: "This project",
|
|
218
|
+
hint: "scoped to current directory",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (p.isCancel(scope)) return;
|
|
224
|
+
|
|
225
|
+
// Step 5: Install skill via skills.sh
|
|
226
|
+
const s2 = p.spinner();
|
|
227
|
+
s2.start("Installing ClawCard skill...");
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const globalFlag = scope === "global" ? " -g" : "";
|
|
231
|
+
execSync(`npx -y skills add clawcard/skill${globalFlag} -y`, {
|
|
232
|
+
stdio: "pipe",
|
|
233
|
+
timeout: 60_000,
|
|
234
|
+
});
|
|
235
|
+
s2.stop("Skill installed!");
|
|
236
|
+
} catch {
|
|
237
|
+
s2.stop("Skill installation failed");
|
|
238
|
+
p.log.warn(
|
|
239
|
+
"Failed to install skill via skills.sh. You can install it manually:"
|
|
240
|
+
);
|
|
241
|
+
p.log.info(
|
|
242
|
+
`Run: npx skills add clawcard/skill${scope === "global" ? " -g" : ""}`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
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
|
+
}
|
|
275
|
+
|
|
276
|
+
// Step 8: Summary
|
|
277
|
+
p.log.success("Your agent is ready to use ClawCard!");
|
|
278
|
+
p.log.info(
|
|
279
|
+
[
|
|
280
|
+
`Key: ${orange(selectedKey.name || "unnamed")} (${selectedKey.keyPrefix}...)`,
|
|
281
|
+
selectedKey.email && `Email: ${selectedKey.email}`,
|
|
282
|
+
selectedKey.phone && `Phone: ${selectedKey.phone}`,
|
|
283
|
+
]
|
|
284
|
+
.filter(Boolean)
|
|
285
|
+
.join("\n")
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
p.outro(
|
|
289
|
+
chalk.dim('Tell your agent to "use ClawCard to check your identity"')
|
|
290
|
+
);
|
|
291
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import open from "open";
|
|
3
|
+
import { startCallbackServer } from "../auth-server.js";
|
|
4
|
+
import { saveConfig, BASE_URL } from "../config.js";
|
|
5
|
+
|
|
6
|
+
export async function signupCommand() {
|
|
7
|
+
const code = await p.text({
|
|
8
|
+
message: "Enter your invite code",
|
|
9
|
+
placeholder: "CLAW-XXXX",
|
|
10
|
+
validate: (val) => {
|
|
11
|
+
if (!/^CLAW-[A-Z0-9]{4}$/i.test(val)) {
|
|
12
|
+
return "Invite code must be in CLAW-XXXX format";
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (p.isCancel(code)) {
|
|
18
|
+
p.cancel("Signup cancelled");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const s = p.spinner();
|
|
23
|
+
s.start("Opening browser...");
|
|
24
|
+
|
|
25
|
+
const { promise, port } = startCallbackServer();
|
|
26
|
+
const actualPort = await port;
|
|
27
|
+
const signupUrl = `${BASE_URL}/login?cli=true&port=${actualPort}&signup=true&code=${code}`;
|
|
28
|
+
await open(signupUrl);
|
|
29
|
+
|
|
30
|
+
s.message("Waiting for signup in browser...");
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const { token, email } = await promise;
|
|
34
|
+
s.stop("Account created!");
|
|
35
|
+
|
|
36
|
+
saveConfig({
|
|
37
|
+
token,
|
|
38
|
+
email,
|
|
39
|
+
loggedInAt: new Date().toISOString(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
p.log.success(`Welcome to ClawCard, ${email}!`);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
s.stop("Signup failed");
|
|
45
|
+
p.log.error(err.message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { requireAuth } from "../auth-guard.js";
|
|
4
|
+
import { getConfig } from "../config.js";
|
|
5
|
+
import { getSubscription } from "../api.js";
|
|
6
|
+
|
|
7
|
+
const orange = chalk.hex("#FF6B35");
|
|
8
|
+
|
|
9
|
+
export async function whoamiCommand() {
|
|
10
|
+
requireAuth();
|
|
11
|
+
|
|
12
|
+
const config = getConfig();
|
|
13
|
+
const s = p.spinner();
|
|
14
|
+
s.start("Fetching account...");
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const sub = await getSubscription();
|
|
18
|
+
s.stop("");
|
|
19
|
+
|
|
20
|
+
p.note(
|
|
21
|
+
[
|
|
22
|
+
`Email: ${orange(config.email)}`,
|
|
23
|
+
`Plan: ${sub.subscription?.planName || "Free"}`,
|
|
24
|
+
].join("\n"),
|
|
25
|
+
"Account"
|
|
26
|
+
);
|
|
27
|
+
} catch {
|
|
28
|
+
s.stop("");
|
|
29
|
+
p.note(`Email: ${orange(config.email)}`, "Account");
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
|
|
5
|
+
const CONFIG_DIR = join(homedir(), ".clawcard");
|
|
6
|
+
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
7
|
+
|
|
8
|
+
export const BASE_URL = "https://www.clawcard.sh";
|
|
9
|
+
|
|
10
|
+
export function getConfig() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = readFileSync(CONFIG_PATH, "utf-8");
|
|
13
|
+
return JSON.parse(raw);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function saveConfig(config) {
|
|
20
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
21
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function clearConfig() {
|
|
25
|
+
try {
|
|
26
|
+
unlinkSync(CONFIG_PATH);
|
|
27
|
+
} catch {
|
|
28
|
+
// already gone
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isLoggedIn() {
|
|
33
|
+
const config = getConfig();
|
|
34
|
+
if (!config?.token) return false;
|
|
35
|
+
if (config.expiresAt && new Date(config.expiresAt) < new Date()) return false;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getToken() {
|
|
40
|
+
const config = getConfig();
|
|
41
|
+
return config?.token ?? null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Local key storage ──
|
|
45
|
+
// Saves raw API keys locally so `clawcard setup` can reference them later.
|
|
46
|
+
// Keys are stored in ~/.clawcard/keys.json keyed by agent ID.
|
|
47
|
+
|
|
48
|
+
const KEYS_PATH = join(CONFIG_DIR, "keys.json");
|
|
49
|
+
|
|
50
|
+
export function saveKey(agentId, apiKey, meta = {}) {
|
|
51
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
52
|
+
const keys = getSavedKeys();
|
|
53
|
+
keys[agentId] = { apiKey, ...meta, savedAt: new Date().toISOString() };
|
|
54
|
+
writeFileSync(KEYS_PATH, JSON.stringify(keys, null, 2));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getSavedKeys() {
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(readFileSync(KEYS_PATH, "utf-8"));
|
|
60
|
+
} catch {
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getSavedKey(agentId) {
|
|
66
|
+
const keys = getSavedKeys();
|
|
67
|
+
return keys[agentId]?.apiKey ?? null;
|
|
68
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
|
+
import { showSplash } from "./splash.js";
|
|
4
|
+
import { isLoggedIn } from "./config.js";
|
|
5
|
+
|
|
6
|
+
// Show splash on every invocation
|
|
7
|
+
showSplash();
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program.name("clawcard").description("The ClawCard CLI").version("1.0.0");
|
|
11
|
+
|
|
12
|
+
// Auth commands
|
|
13
|
+
program
|
|
14
|
+
.command("login")
|
|
15
|
+
.description("Log in via browser")
|
|
16
|
+
.action(async () => {
|
|
17
|
+
const { loginCommand } = await import("./commands/login.js");
|
|
18
|
+
await loginCommand();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.command("signup")
|
|
23
|
+
.description("Sign up with invite code")
|
|
24
|
+
.action(async () => {
|
|
25
|
+
const { signupCommand } = await import("./commands/signup.js");
|
|
26
|
+
await signupCommand();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
program
|
|
30
|
+
.command("logout")
|
|
31
|
+
.description("Clear session")
|
|
32
|
+
.action(async () => {
|
|
33
|
+
const { logoutCommand } = await import("./commands/logout.js");
|
|
34
|
+
await logoutCommand();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
program
|
|
38
|
+
.command("whoami")
|
|
39
|
+
.description("Show current account")
|
|
40
|
+
.action(async () => {
|
|
41
|
+
const { whoamiCommand } = await import("./commands/whoami.js");
|
|
42
|
+
await whoamiCommand();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Keys
|
|
46
|
+
const keys = program.command("keys").description("Manage API keys");
|
|
47
|
+
|
|
48
|
+
keys.action(async () => {
|
|
49
|
+
const { keysCommand } = await import("./commands/keys.js");
|
|
50
|
+
await keysCommand();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
keys
|
|
54
|
+
.command("create")
|
|
55
|
+
.description("Create a new API key")
|
|
56
|
+
.action(async () => {
|
|
57
|
+
const { keysCreateCommand } = await import("./commands/keys.js");
|
|
58
|
+
await keysCreateCommand();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
keys
|
|
62
|
+
.command("list")
|
|
63
|
+
.description("List all keys")
|
|
64
|
+
.action(async () => {
|
|
65
|
+
const { keysListCommand } = await import("./commands/keys.js");
|
|
66
|
+
await keysListCommand();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
keys
|
|
70
|
+
.command("info [keyId]")
|
|
71
|
+
.description("Show key details")
|
|
72
|
+
.action(async (keyId) => {
|
|
73
|
+
const { keysInfoCommand } = await import("./commands/keys.js");
|
|
74
|
+
await keysInfoCommand(keyId);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
keys
|
|
78
|
+
.command("revoke")
|
|
79
|
+
.description("Revoke a key")
|
|
80
|
+
.action(async () => {
|
|
81
|
+
const { keysRevokeCommand } = await import("./commands/keys.js");
|
|
82
|
+
await keysRevokeCommand();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Setup
|
|
86
|
+
program
|
|
87
|
+
.command("setup")
|
|
88
|
+
.description("Install ClawCard skill for your agent")
|
|
89
|
+
.action(async () => {
|
|
90
|
+
const { setupCommand } = await import("./commands/setup.js");
|
|
91
|
+
await setupCommand();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Billing
|
|
95
|
+
const billing = program.command("billing").description("Billing dashboard");
|
|
96
|
+
|
|
97
|
+
billing.action(async () => {
|
|
98
|
+
const { billingCommand } = await import("./commands/billing.js");
|
|
99
|
+
await billingCommand();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
billing
|
|
103
|
+
.command("balance")
|
|
104
|
+
.description("Quick balance check")
|
|
105
|
+
.action(async () => {
|
|
106
|
+
const { billingBalanceCommand } = await import("./commands/billing.js");
|
|
107
|
+
await billingBalanceCommand();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
billing
|
|
111
|
+
.command("topup")
|
|
112
|
+
.description("Top up balance")
|
|
113
|
+
.action(async () => {
|
|
114
|
+
const { billingTopupCommand } = await import("./commands/billing.js");
|
|
115
|
+
await billingTopupCommand();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
billing
|
|
119
|
+
.command("upgrade")
|
|
120
|
+
.description("Upgrade subscription")
|
|
121
|
+
.action(async () => {
|
|
122
|
+
const { billingUpgradeCommand } = await import("./commands/billing.js");
|
|
123
|
+
await billingUpgradeCommand();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
billing
|
|
127
|
+
.command("transactions")
|
|
128
|
+
.description("View transaction history")
|
|
129
|
+
.action(async () => {
|
|
130
|
+
const { billingTransactionsCommand } = await import(
|
|
131
|
+
"./commands/billing.js"
|
|
132
|
+
);
|
|
133
|
+
await billingTransactionsCommand();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Referral
|
|
137
|
+
program
|
|
138
|
+
.command("referral")
|
|
139
|
+
.description("Show referral code")
|
|
140
|
+
.action(async () => {
|
|
141
|
+
const { referralCommand } = await import("./commands/referral.js");
|
|
142
|
+
await referralCommand();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Help
|
|
146
|
+
program
|
|
147
|
+
.command("help")
|
|
148
|
+
.description("Show all commands")
|
|
149
|
+
.action(async () => {
|
|
150
|
+
const { helpCommand } = await import("./commands/help.js");
|
|
151
|
+
helpCommand();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// If no subcommand, show interactive menu
|
|
155
|
+
if (process.argv.length <= 2) {
|
|
156
|
+
const loggedIn = isLoggedIn();
|
|
157
|
+
|
|
158
|
+
const options = loggedIn
|
|
159
|
+
? [
|
|
160
|
+
{
|
|
161
|
+
value: "setup",
|
|
162
|
+
label: "Setup",
|
|
163
|
+
hint: "connect ClawCard to your agent",
|
|
164
|
+
},
|
|
165
|
+
{ value: "keys", label: "Keys", hint: "create & manage API keys" },
|
|
166
|
+
{ value: "billing", label: "Billing", hint: "check balance & top up" },
|
|
167
|
+
{ value: "referral", label: "Referral", hint: "share & earn $10" },
|
|
168
|
+
{ value: "whoami", label: "Who am I?", hint: "show current account" },
|
|
169
|
+
{ value: "logout", label: "Logout", hint: "clear session" },
|
|
170
|
+
{ value: "help", label: "Help", hint: "show all commands" },
|
|
171
|
+
]
|
|
172
|
+
: [
|
|
173
|
+
{ value: "login", label: "Log in", hint: "authenticate via browser" },
|
|
174
|
+
{
|
|
175
|
+
value: "signup",
|
|
176
|
+
label: "Sign up",
|
|
177
|
+
hint: "create account with invite code",
|
|
178
|
+
},
|
|
179
|
+
{ value: "help", label: "Help", hint: "show all commands" },
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const action = await p.select({
|
|
183
|
+
message: "What would you like to do?",
|
|
184
|
+
options,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (p.isCancel(action)) {
|
|
188
|
+
p.cancel("Goodbye!");
|
|
189
|
+
process.exit(0);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const commands = {
|
|
193
|
+
login: () => import("./commands/login.js").then((m) => m.loginCommand()),
|
|
194
|
+
signup: () => import("./commands/signup.js").then((m) => m.signupCommand()),
|
|
195
|
+
logout: () => import("./commands/logout.js").then((m) => m.logoutCommand()),
|
|
196
|
+
whoami: () => import("./commands/whoami.js").then((m) => m.whoamiCommand()),
|
|
197
|
+
keys: () => import("./commands/keys.js").then((m) => m.keysCommand()),
|
|
198
|
+
setup: () => import("./commands/setup.js").then((m) => m.setupCommand()),
|
|
199
|
+
billing: () =>
|
|
200
|
+
import("./commands/billing.js").then((m) => m.billingCommand()),
|
|
201
|
+
referral: () =>
|
|
202
|
+
import("./commands/referral.js").then((m) => m.referralCommand()),
|
|
203
|
+
help: () => import("./commands/help.js").then((m) => m.helpCommand()),
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
await commands[action]?.();
|
|
207
|
+
} else {
|
|
208
|
+
program.parse();
|
|
209
|
+
}
|
package/src/splash.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
const o = chalk.hex("#FF6B35");
|
|
4
|
+
const d = chalk.dim;
|
|
5
|
+
|
|
6
|
+
export function showSplash() {
|
|
7
|
+
console.log();
|
|
8
|
+
console.log(` 🦞 ${o.bold("clawcard.sh")}`);
|
|
9
|
+
console.log(` ${d("on-demand credit cards, email and phone for your agents")}`);
|
|
10
|
+
console.log();
|
|
11
|
+
}
|