@clawcard/cli 1.1.5 → 2.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/README.md +38 -40
- package/package.json +1 -1
- package/src/agent-api.js +115 -0
- package/src/api.js +14 -0
- package/src/commands/agent.js +238 -0
- package/src/commands/billing.js +2 -1
- package/src/commands/help.js +24 -7
- package/src/commands/keys.js +133 -24
- package/src/index.js +172 -20
- package/src/splash.js +61 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
On-demand credit cards, email, and phone for your AI agents.
|
|
4
4
|
|
|
5
|
-
ClawCard gives your agent a full identity — dedicated email inbox, SMS-enabled US phone number, virtual Mastercards with spend limits, and an encrypted credential vault.
|
|
5
|
+
ClawCard gives your agent a full identity — dedicated email inbox, SMS-enabled US phone number, virtual Mastercards with spend limits, and an encrypted credential vault. Your agent interacts with ClawCard through CLI commands — the API key never enters the agent's context.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -16,10 +16,10 @@ npm install -g @clawcard/cli
|
|
|
16
16
|
# Sign up (requires invite code)
|
|
17
17
|
clawcard signup
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
clawcard
|
|
19
|
+
# Top up your balance (minimum $5)
|
|
20
|
+
clawcard billing topup
|
|
21
21
|
|
|
22
|
-
# Create
|
|
22
|
+
# Create your agent key
|
|
23
23
|
clawcard keys create
|
|
24
24
|
|
|
25
25
|
# Set up your agent (installs skill + configures API key)
|
|
@@ -28,54 +28,52 @@ clawcard setup
|
|
|
28
28
|
|
|
29
29
|
`clawcard setup` uses [skills.sh](https://skills.sh) to install the ClawCard skill for your agent. Works with Claude Code, Cursor, Codex, Cline, OpenClaw, and 40+ other agents.
|
|
30
30
|
|
|
31
|
-
## Commands
|
|
31
|
+
## Agent Commands
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
| Command | Description |
|
|
35
|
-
|---|---|
|
|
36
|
-
| `clawcard login` | Log in via browser |
|
|
37
|
-
| `clawcard signup` | Sign up with invite code |
|
|
38
|
-
| `clawcard logout` | Clear session |
|
|
39
|
-
| `clawcard whoami` | Show current account |
|
|
33
|
+
Your agent runs these commands directly. All support `--json` for machine-parseable output.
|
|
40
34
|
|
|
41
|
-
### Keys
|
|
42
35
|
| Command | Description |
|
|
43
36
|
|---|---|
|
|
44
|
-
| `clawcard
|
|
45
|
-
| `clawcard
|
|
46
|
-
| `clawcard
|
|
47
|
-
| `clawcard
|
|
48
|
-
| `clawcard
|
|
37
|
+
| `clawcard agent info --json` | Agent identity (email, phone, budget) |
|
|
38
|
+
| `clawcard agent emails --json` | List inbox |
|
|
39
|
+
| `clawcard agent emails send --to --subject --body --json` | Send email |
|
|
40
|
+
| `clawcard agent sms --json` | List SMS messages |
|
|
41
|
+
| `clawcard agent sms send --to --body --json` | Send SMS |
|
|
42
|
+
| `clawcard agent cards --json` | List virtual cards |
|
|
43
|
+
| `clawcard agent cards create --amount --type --memo --json` | Create card |
|
|
44
|
+
| `clawcard agent cards details <id> --json` | Get PAN, CVV, expiry |
|
|
45
|
+
| `clawcard agent cards close <id> --json` | Close card |
|
|
46
|
+
| `clawcard agent creds --json` | List stored credentials |
|
|
47
|
+
| `clawcard agent creds set --service --key --value --json` | Store credential |
|
|
48
|
+
| `clawcard agent creds get --service --key --json` | Retrieve credential |
|
|
49
|
+
| `clawcard agent budget --json` | Check remaining budget |
|
|
50
|
+
| `clawcard agent activity --json` | View activity log |
|
|
51
|
+
|
|
52
|
+
## User Commands
|
|
49
53
|
|
|
50
|
-
### Setup
|
|
51
54
|
| Command | Description |
|
|
52
55
|
|---|---|
|
|
53
|
-
| `clawcard
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
58
|
-
| `clawcard
|
|
59
|
-
| `clawcard
|
|
56
|
+
| `clawcard login` | Log in via browser |
|
|
57
|
+
| `clawcard signup` | Sign up with invite code |
|
|
58
|
+
| `clawcard logout` | Clear session |
|
|
59
|
+
| `clawcard whoami` | Show current account |
|
|
60
|
+
| `clawcard agent` | Show agent identity |
|
|
61
|
+
| `clawcard agent fund` | Add budget to your agent |
|
|
62
|
+
| `clawcard keys create` | Create agent key |
|
|
63
|
+
| `clawcard keys revoke` | Revoke key (exports credentials first) |
|
|
64
|
+
| `clawcard setup` | Install ClawCard skill |
|
|
65
|
+
| `clawcard billing` | Billing dashboard |
|
|
60
66
|
| `clawcard billing topup` | Top up balance |
|
|
61
|
-
| `clawcard billing
|
|
62
|
-
| `clawcard billing transactions` | View transaction history |
|
|
63
|
-
|
|
64
|
-
### Other
|
|
65
|
-
| Command | Description |
|
|
66
|
-
|---|---|
|
|
67
|
+
| `clawcard billing balance` | Quick balance check |
|
|
67
68
|
| `clawcard referral` | Show referral code |
|
|
68
69
|
| `clawcard help` | Show all commands |
|
|
69
70
|
|
|
70
|
-
##
|
|
71
|
-
|
|
72
|
-
Each API key comes with:
|
|
71
|
+
## How It Works
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
- **Budget Controls** — Per-key spending limits with full audit trail
|
|
73
|
+
1. You create an agent key via the CLI — it gets an email, phone number, and budget
|
|
74
|
+
2. `clawcard setup` installs a skill file that teaches your agent the available commands
|
|
75
|
+
3. Your agent runs `clawcard agent` commands to send emails, create cards, store credentials, etc.
|
|
76
|
+
4. The API key stays in `~/.clawcard/.env` — the agent never sees it
|
|
79
77
|
|
|
80
78
|
## Pricing
|
|
81
79
|
|
package/package.json
CHANGED
package/src/agent-api.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { BASE_URL } from "./config.js";
|
|
5
|
+
|
|
6
|
+
const ENV_PATH = join(homedir(), ".clawcard", ".env");
|
|
7
|
+
|
|
8
|
+
function getApiKey() {
|
|
9
|
+
try {
|
|
10
|
+
const content = readFileSync(ENV_PATH, "utf-8");
|
|
11
|
+
const match = content.match(/^CLAWCARD_API_KEY=(.+)$/m);
|
|
12
|
+
return match?.[1]?.trim() ?? null;
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function agentRequest(path, options = {}) {
|
|
19
|
+
const apiKey = getApiKey();
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
throw new Error("No API key found. Run: clawcard setup");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const url = `${BASE_URL}${path}`;
|
|
25
|
+
const headers = {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
Authorization: `Bearer ${apiKey}`,
|
|
28
|
+
...options.headers,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const res = await fetch(url, { ...options, headers });
|
|
32
|
+
const body = await res.json().catch(() => null);
|
|
33
|
+
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const msg = body?.error || body?.message || `API error ${res.status}`;
|
|
36
|
+
throw new Error(msg);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return body;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Identity
|
|
43
|
+
export const getIdentity = () => agentRequest("/api/me");
|
|
44
|
+
|
|
45
|
+
// Email
|
|
46
|
+
export const listEmails = (agentId, params = {}) => {
|
|
47
|
+
const qs = new URLSearchParams();
|
|
48
|
+
if (params.limit) qs.set("limit", params.limit);
|
|
49
|
+
if (params.unread) qs.set("unread", "true");
|
|
50
|
+
if (params.from) qs.set("from", params.from);
|
|
51
|
+
if (params.subject_contains) qs.set("subject_contains", params.subject_contains);
|
|
52
|
+
const query = qs.toString();
|
|
53
|
+
return agentRequest(`/api/agents/${agentId}/emails${query ? "?" + query : ""}`);
|
|
54
|
+
};
|
|
55
|
+
export const sendEmail = (agentId, data) =>
|
|
56
|
+
agentRequest(`/api/agents/${agentId}/emails/send`, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
body: JSON.stringify(data),
|
|
59
|
+
});
|
|
60
|
+
export const markEmailRead = (agentId, emailId) =>
|
|
61
|
+
agentRequest(`/api/agents/${agentId}/emails/${emailId}/read`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// SMS
|
|
66
|
+
export const listSms = (agentId, params = {}) => {
|
|
67
|
+
const qs = new URLSearchParams();
|
|
68
|
+
if (params.limit) qs.set("limit", params.limit);
|
|
69
|
+
const query = qs.toString();
|
|
70
|
+
return agentRequest(`/api/agents/${agentId}/sms${query ? "?" + query : ""}`);
|
|
71
|
+
};
|
|
72
|
+
export const sendSms = (agentId, data) =>
|
|
73
|
+
agentRequest(`/api/agents/${agentId}/sms/send`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
body: JSON.stringify(data),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Cards
|
|
79
|
+
export const listCards = (agentId) =>
|
|
80
|
+
agentRequest(`/api/agents/${agentId}/cards`);
|
|
81
|
+
export const createCard = (agentId, data) =>
|
|
82
|
+
agentRequest(`/api/agents/${agentId}/cards`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
body: JSON.stringify(data),
|
|
85
|
+
});
|
|
86
|
+
export const getCardDetails = (agentId, cardId) =>
|
|
87
|
+
agentRequest(`/api/agents/${agentId}/cards/${cardId}`);
|
|
88
|
+
export const updateCard = (agentId, cardId, action) =>
|
|
89
|
+
agentRequest(`/api/agents/${agentId}/cards/${cardId}`, {
|
|
90
|
+
method: "PATCH",
|
|
91
|
+
body: JSON.stringify({ action }),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Credentials
|
|
95
|
+
export const listCredentials = (agentId) =>
|
|
96
|
+
agentRequest(`/api/agents/${agentId}/credentials`);
|
|
97
|
+
export const setCredential = (agentId, data) =>
|
|
98
|
+
agentRequest(`/api/agents/${agentId}/credentials`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
body: JSON.stringify(data),
|
|
101
|
+
});
|
|
102
|
+
export const getCredential = (agentId, service, key) =>
|
|
103
|
+
agentRequest(`/api/agents/${agentId}/credentials/${service}/${key}`);
|
|
104
|
+
|
|
105
|
+
// Budget
|
|
106
|
+
export const getBudget = (agentId) =>
|
|
107
|
+
agentRequest(`/api/agents/${agentId}/budget`);
|
|
108
|
+
|
|
109
|
+
// Activity
|
|
110
|
+
export const listActivity = (agentId, params = {}) => {
|
|
111
|
+
const qs = new URLSearchParams();
|
|
112
|
+
if (params.limit) qs.set("limit", params.limit);
|
|
113
|
+
const query = qs.toString();
|
|
114
|
+
return agentRequest(`/api/agents/${agentId}/activity${query ? "?" + query : ""}`);
|
|
115
|
+
};
|
package/src/api.js
CHANGED
|
@@ -51,5 +51,19 @@ export const createCheckout = (amountCents) =>
|
|
|
51
51
|
export const createPortal = () =>
|
|
52
52
|
request("/api/billing/portal", { method: "POST" });
|
|
53
53
|
|
|
54
|
+
// Budget
|
|
55
|
+
export const getAgentBudget = (id) => request(`/api/agents/${id}/budget`);
|
|
56
|
+
export const allocateBudget = (id, amountCents) =>
|
|
57
|
+
request(`/api/agents/${id}/budget`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
body: JSON.stringify({ amountCents }),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Credentials (session auth)
|
|
63
|
+
export const listAgentCredentials = (agentId) =>
|
|
64
|
+
request(`/api/agents/${agentId}/credentials`);
|
|
65
|
+
export const getAgentCredential = (agentId, service, key) =>
|
|
66
|
+
request(`/api/agents/${agentId}/credentials/${service}/${key}`);
|
|
67
|
+
|
|
54
68
|
// User
|
|
55
69
|
export const getMe = () => request("/api/me");
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import {
|
|
3
|
+
getIdentity,
|
|
4
|
+
listEmails,
|
|
5
|
+
sendEmail,
|
|
6
|
+
markEmailRead,
|
|
7
|
+
listSms,
|
|
8
|
+
sendSms,
|
|
9
|
+
listCards,
|
|
10
|
+
createCard,
|
|
11
|
+
getCardDetails,
|
|
12
|
+
updateCard,
|
|
13
|
+
listCredentials,
|
|
14
|
+
setCredential,
|
|
15
|
+
getCredential,
|
|
16
|
+
getBudget,
|
|
17
|
+
listActivity,
|
|
18
|
+
} from "../agent-api.js";
|
|
19
|
+
|
|
20
|
+
const orange = chalk.hex("#FF6B35");
|
|
21
|
+
|
|
22
|
+
async function getAgentId() {
|
|
23
|
+
const me = await getIdentity();
|
|
24
|
+
if (!me.keyId) throw new Error("Could not determine agent ID");
|
|
25
|
+
return me.keyId;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function output(data, json) {
|
|
29
|
+
if (json) {
|
|
30
|
+
console.log(JSON.stringify(data, null, 2));
|
|
31
|
+
}
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ── Info ──
|
|
36
|
+
export async function agentInfoCmd(options) {
|
|
37
|
+
const me = await getIdentity();
|
|
38
|
+
if (options.json) return output(me, true);
|
|
39
|
+
|
|
40
|
+
const budget = await getBudget(me.keyId);
|
|
41
|
+
console.log();
|
|
42
|
+
console.log(` Name: ${orange.bold(me.name || "unnamed")}`);
|
|
43
|
+
console.log(` Email: ${me.email || "-"}`);
|
|
44
|
+
console.log(` Phone: ${me.phone || "-"}`);
|
|
45
|
+
console.log(` Key ID: ${chalk.dim(me.keyId)}`);
|
|
46
|
+
console.log(` Budget: ${chalk.green("$" + ((budget.budgetCents || 0) / 100).toFixed(2))}`);
|
|
47
|
+
console.log();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Emails ──
|
|
51
|
+
export async function agentEmailsCmd(options) {
|
|
52
|
+
const agentId = await getAgentId();
|
|
53
|
+
const result = await listEmails(agentId, {
|
|
54
|
+
limit: options.limit,
|
|
55
|
+
unread: options.unread,
|
|
56
|
+
});
|
|
57
|
+
if (options.json) return output(result, true);
|
|
58
|
+
|
|
59
|
+
const emails = result.emails || result;
|
|
60
|
+
if (!emails.length) {
|
|
61
|
+
console.log(" No emails");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.log();
|
|
65
|
+
for (const e of emails) {
|
|
66
|
+
const read = e.isRead ? chalk.dim("read") : orange("NEW");
|
|
67
|
+
console.log(` ${read} ${chalk.dim(e.sender)} ${e.subject}`);
|
|
68
|
+
}
|
|
69
|
+
console.log();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function agentEmailsSendCmd(options) {
|
|
73
|
+
const agentId = await getAgentId();
|
|
74
|
+
const result = await sendEmail(agentId, {
|
|
75
|
+
to: options.to,
|
|
76
|
+
subject: options.subject,
|
|
77
|
+
body: options.body,
|
|
78
|
+
});
|
|
79
|
+
if (options.json) return output(result, true);
|
|
80
|
+
console.log(` Email sent to ${options.to}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function agentEmailsReadCmd(emailId, options) {
|
|
84
|
+
const agentId = await getAgentId();
|
|
85
|
+
const result = await markEmailRead(agentId, emailId);
|
|
86
|
+
if (options.json) return output(result, true);
|
|
87
|
+
console.log(` Marked as read`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── SMS ──
|
|
91
|
+
export async function agentSmsCmd(options) {
|
|
92
|
+
const agentId = await getAgentId();
|
|
93
|
+
const result = await listSms(agentId, { limit: options.limit });
|
|
94
|
+
if (options.json) return output(result, true);
|
|
95
|
+
|
|
96
|
+
const messages = result.messages || result;
|
|
97
|
+
if (!messages.length) {
|
|
98
|
+
console.log(" No messages");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
console.log();
|
|
102
|
+
for (const m of messages) {
|
|
103
|
+
const dir = m.direction === "inbound" ? chalk.green("←") : chalk.blue("→");
|
|
104
|
+
console.log(` ${dir} ${chalk.dim(m.direction === "inbound" ? m.fromNumber : m.toNumber)} ${m.body}`);
|
|
105
|
+
}
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function agentSmsSendCmd(options) {
|
|
110
|
+
const agentId = await getAgentId();
|
|
111
|
+
const result = await sendSms(agentId, {
|
|
112
|
+
to: options.to,
|
|
113
|
+
body: options.body,
|
|
114
|
+
});
|
|
115
|
+
if (options.json) return output(result, true);
|
|
116
|
+
console.log(` SMS sent to ${options.to}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Cards ──
|
|
120
|
+
export async function agentCardsCmd(options) {
|
|
121
|
+
const agentId = await getAgentId();
|
|
122
|
+
const result = await listCards(agentId);
|
|
123
|
+
if (options.json) return output(result, true);
|
|
124
|
+
|
|
125
|
+
const cards = result.cards || result;
|
|
126
|
+
if (!cards.length) {
|
|
127
|
+
console.log(" No cards");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
console.log();
|
|
131
|
+
for (const c of cards) {
|
|
132
|
+
const status = c.status === "open" ? chalk.green(c.status) : chalk.red(c.status);
|
|
133
|
+
console.log(` ${c.id} ****${c.lastFour} ${c.type} $${(c.spendLimitCents / 100).toFixed(2)} ${status} ${chalk.dim(c.memo)}`);
|
|
134
|
+
}
|
|
135
|
+
console.log();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function agentCardsCreateCmd(options) {
|
|
139
|
+
const agentId = await getAgentId();
|
|
140
|
+
const result = await createCard(agentId, {
|
|
141
|
+
amountCents: parseInt(options.amount),
|
|
142
|
+
type: options.type,
|
|
143
|
+
memo: options.memo || "Agent card",
|
|
144
|
+
});
|
|
145
|
+
if (options.json) return output(result, true);
|
|
146
|
+
|
|
147
|
+
console.log();
|
|
148
|
+
console.log(` Card created: ${result.id}`);
|
|
149
|
+
console.log(` PAN: ${orange(result.pan)}`);
|
|
150
|
+
console.log(` CVV: ${result.cvv}`);
|
|
151
|
+
console.log(` Expiry: ${result.exp_month}/${result.exp_year}`);
|
|
152
|
+
console.log(` Limit: $${(result.spendLimitCents / 100).toFixed(2)}`);
|
|
153
|
+
console.log();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function agentCardsDetailsCmd(cardId, options) {
|
|
157
|
+
const agentId = await getAgentId();
|
|
158
|
+
const result = await getCardDetails(agentId, cardId);
|
|
159
|
+
if (options.json) return output(result, true);
|
|
160
|
+
|
|
161
|
+
console.log();
|
|
162
|
+
console.log(` PAN: ${orange(result.pan)}`);
|
|
163
|
+
console.log(` CVV: ${result.cvv}`);
|
|
164
|
+
console.log(` Expiry: ${result.exp_month}/${result.exp_year}`);
|
|
165
|
+
console.log(` Status: ${result.status}`);
|
|
166
|
+
console.log(` Limit: $${(result.spendLimitCents / 100).toFixed(2)}`);
|
|
167
|
+
console.log();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function agentCardsActionCmd(cardId, action, options) {
|
|
171
|
+
const agentId = await getAgentId();
|
|
172
|
+
const result = await updateCard(agentId, cardId, action);
|
|
173
|
+
if (options.json) return output(result, true);
|
|
174
|
+
console.log(` Card ${cardId}: ${action}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ── Credentials ──
|
|
178
|
+
export async function agentCredsCmd(options) {
|
|
179
|
+
const agentId = await getAgentId();
|
|
180
|
+
const result = await listCredentials(agentId);
|
|
181
|
+
if (options.json) return output(result, true);
|
|
182
|
+
|
|
183
|
+
const creds = Array.isArray(result) ? result : result.credentials || [];
|
|
184
|
+
if (!creds.length) {
|
|
185
|
+
console.log(" No stored credentials");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
console.log();
|
|
189
|
+
for (const c of creds) {
|
|
190
|
+
console.log(` ${orange(c.service)} / ${c.key}`);
|
|
191
|
+
}
|
|
192
|
+
console.log();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export async function agentCredsSetCmd(options) {
|
|
196
|
+
const agentId = await getAgentId();
|
|
197
|
+
const result = await setCredential(agentId, {
|
|
198
|
+
service: options.service,
|
|
199
|
+
key: options.key,
|
|
200
|
+
value: options.value,
|
|
201
|
+
});
|
|
202
|
+
if (options.json) return output(result, true);
|
|
203
|
+
console.log(` Stored: ${options.service}/${options.key}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function agentCredsGetCmd(options) {
|
|
207
|
+
const agentId = await getAgentId();
|
|
208
|
+
const result = await getCredential(agentId, options.service, options.key);
|
|
209
|
+
if (options.json) return output(result, true);
|
|
210
|
+
console.log(` ${options.service}/${options.key}: ${result.value}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ── Budget ──
|
|
214
|
+
export async function agentBudgetCmd(options) {
|
|
215
|
+
const agentId = await getAgentId();
|
|
216
|
+
const result = await getBudget(agentId);
|
|
217
|
+
if (options.json) return output(result, true);
|
|
218
|
+
console.log(` Budget: ${chalk.green("$" + ((result.budgetCents || 0) / 100).toFixed(2))}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── Activity ──
|
|
222
|
+
export async function agentActivityCmd(options) {
|
|
223
|
+
const agentId = await getAgentId();
|
|
224
|
+
const result = await listActivity(agentId, { limit: options.limit });
|
|
225
|
+
if (options.json) return output(result, true);
|
|
226
|
+
|
|
227
|
+
const activity = result.activity || result;
|
|
228
|
+
if (!activity.length) {
|
|
229
|
+
console.log(" No activity");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
console.log();
|
|
233
|
+
for (const a of activity) {
|
|
234
|
+
const date = new Date(a.createdAt).toLocaleString();
|
|
235
|
+
console.log(` ${chalk.dim(date)} ${orange(a.action)} ${chalk.dim(a.details)}`);
|
|
236
|
+
}
|
|
237
|
+
console.log();
|
|
238
|
+
}
|
package/src/commands/billing.js
CHANGED
|
@@ -104,7 +104,8 @@ export async function billingTransactionsCommand() {
|
|
|
104
104
|
s.start("Fetching transactions...");
|
|
105
105
|
|
|
106
106
|
try {
|
|
107
|
-
const
|
|
107
|
+
const res = await getTransactions();
|
|
108
|
+
const txns = res.transactions || res;
|
|
108
109
|
s.stop("");
|
|
109
110
|
|
|
110
111
|
if (!txns.length) {
|
package/src/commands/help.js
CHANGED
|
@@ -11,15 +11,32 @@ const COMMANDS = [
|
|
|
11
11
|
[" logout", "Clear session"],
|
|
12
12
|
[" whoami", "Show current account"],
|
|
13
13
|
["", ""],
|
|
14
|
-
[orange.bold("Agent"), ""],
|
|
14
|
+
[orange.bold("Agent (user)"), ""],
|
|
15
15
|
[" agent", "Show your agent's identity"],
|
|
16
|
+
[" agent fund", "Add budget to your agent"],
|
|
17
|
+
["", ""],
|
|
18
|
+
[orange.bold("Agent (machine — pass --json)"), ""],
|
|
19
|
+
[" agent info", "Agent identity"],
|
|
20
|
+
[" agent emails", "List inbox"],
|
|
21
|
+
[" agent emails send", "Send email"],
|
|
22
|
+
[" agent emails read", "Mark email as read"],
|
|
23
|
+
[" agent sms", "List SMS messages"],
|
|
24
|
+
[" agent sms send", "Send SMS"],
|
|
25
|
+
[" agent cards", "List cards"],
|
|
26
|
+
[" agent cards create", "Create card"],
|
|
27
|
+
[" agent cards details", "Card PAN/CVV/expiry"],
|
|
28
|
+
[" agent cards close", "Close card"],
|
|
29
|
+
[" agent cards pause", "Pause card"],
|
|
30
|
+
[" agent cards resume", "Resume card"],
|
|
31
|
+
[" agent creds", "List credentials"],
|
|
32
|
+
[" agent creds set", "Store credential"],
|
|
33
|
+
[" agent creds get", "Retrieve credential"],
|
|
34
|
+
[" agent budget", "Check budget"],
|
|
35
|
+
[" agent activity", "Activity log"],
|
|
16
36
|
["", ""],
|
|
17
37
|
[orange.bold("Keys"), ""],
|
|
18
|
-
[" keys", "
|
|
19
|
-
[" keys
|
|
20
|
-
[" keys list", "List all keys"],
|
|
21
|
-
[" keys info", "Show key details"],
|
|
22
|
-
[" keys revoke", "Revoke a key"],
|
|
38
|
+
[" keys create", "Create a new agent key"],
|
|
39
|
+
[" keys revoke", "Revoke agent key"],
|
|
23
40
|
["", ""],
|
|
24
41
|
[orange.bold("Setup"), ""],
|
|
25
42
|
[" setup", "Install ClawCard skill for your agent"],
|
|
@@ -45,7 +62,7 @@ export function helpCommand() {
|
|
|
45
62
|
} else if (!desc) {
|
|
46
63
|
console.log(` ${cmd}`);
|
|
47
64
|
} else {
|
|
48
|
-
console.log(` ${cmd.padEnd(
|
|
65
|
+
console.log(` ${cmd.padEnd(26)}${dim(desc)}`);
|
|
49
66
|
}
|
|
50
67
|
}
|
|
51
68
|
console.log();
|
package/src/commands/keys.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { requireAuth } from "../auth-guard.js";
|
|
4
|
-
import { listAgents, createAgent, getAgent, deleteAgent } from "../api.js";
|
|
4
|
+
import { listAgents, createAgent, getAgent, deleteAgent, getBalance, getAgentBudget, allocateBudget, listAgentCredentials, getAgentCredential } from "../api.js";
|
|
5
5
|
import { saveKey } from "../config.js";
|
|
6
6
|
|
|
7
7
|
const orange = chalk.hex("#FF6B35");
|
|
@@ -63,26 +63,43 @@ export async function keysCreateCommand() {
|
|
|
63
63
|
});
|
|
64
64
|
if (p.isCancel(name)) return;
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
// Check account balance to show how much they can allocate
|
|
67
|
+
let accountBalance = 0;
|
|
68
|
+
try {
|
|
69
|
+
const bal = await getBalance();
|
|
70
|
+
accountBalance = bal.amountCents || 0;
|
|
71
|
+
} catch {}
|
|
72
|
+
|
|
73
|
+
const budget = await p.text({
|
|
74
|
+
message: `How much budget for this agent? (account balance: $${(accountBalance / 100).toFixed(2)})`,
|
|
75
|
+
placeholder: String(Math.min(accountBalance / 100, 20)),
|
|
69
76
|
validate: (v) => {
|
|
70
77
|
const n = parseFloat(v);
|
|
71
78
|
if (isNaN(n) || n <= 0) return "Enter a valid dollar amount";
|
|
79
|
+
if (Math.round(n * 100) > accountBalance) return `Exceeds your account balance of $${(accountBalance / 100).toFixed(2)}`;
|
|
72
80
|
},
|
|
73
81
|
});
|
|
74
|
-
if (p.isCancel(
|
|
82
|
+
if (p.isCancel(budget)) return;
|
|
83
|
+
|
|
84
|
+
const budgetCents = Math.round(parseFloat(budget) * 100);
|
|
75
85
|
|
|
76
86
|
const s = p.spinner();
|
|
77
|
-
s.start("Creating
|
|
87
|
+
s.start("Creating agent...");
|
|
78
88
|
|
|
79
89
|
try {
|
|
80
90
|
const result = await createAgent({
|
|
81
91
|
name,
|
|
82
|
-
spendLimitCents:
|
|
92
|
+
spendLimitCents: budgetCents,
|
|
83
93
|
});
|
|
84
94
|
|
|
85
|
-
|
|
95
|
+
// Allocate budget from account balance to agent
|
|
96
|
+
try {
|
|
97
|
+
await allocateBudget(result.id, budgetCents);
|
|
98
|
+
} catch {
|
|
99
|
+
// non-critical — agent created but budget not allocated
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
s.stop("Agent created!");
|
|
86
103
|
|
|
87
104
|
// Save raw key locally so `clawcard setup` can use it later
|
|
88
105
|
saveKey(result.id, result.apiKey, {
|
|
@@ -90,27 +107,32 @@ export async function keysCreateCommand() {
|
|
|
90
107
|
keyPrefix: result.keyPrefix,
|
|
91
108
|
});
|
|
92
109
|
|
|
93
|
-
p.note(
|
|
94
|
-
`${orange.bold(result.apiKey)}\n\n${chalk.dim("This key will only be shown once. Copy it now.")}`,
|
|
95
|
-
"Your API Key"
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const copy = await p.confirm({ message: "Copy to clipboard?" });
|
|
99
|
-
if (copy && !p.isCancel(copy)) {
|
|
100
|
-
const { default: clipboardy } = await import("clipboardy");
|
|
101
|
-
await clipboardy.write(result.apiKey);
|
|
102
|
-
p.log.success("Copied to clipboard");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
110
|
// Show provisioned resources
|
|
106
111
|
const details = [
|
|
107
|
-
`Name:
|
|
108
|
-
result.email && `Email:
|
|
109
|
-
result.phone && `Phone:
|
|
110
|
-
`
|
|
112
|
+
`Name: ${orange.bold(result.name || "unnamed")}`,
|
|
113
|
+
result.email && `Email: ${result.email}`,
|
|
114
|
+
result.phone && `Phone: ${result.phone}`,
|
|
115
|
+
`Budget: ${chalk.green("$" + (budgetCents / 100).toFixed(2))}`,
|
|
111
116
|
].filter(Boolean).join("\n");
|
|
112
117
|
|
|
113
118
|
p.note(details, "Your Agent");
|
|
119
|
+
|
|
120
|
+
p.log.info(
|
|
121
|
+
chalk.dim("Next step: install the ClawCard skill so your agent knows how to use its email, phone, and cards.")
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const runSetup = await p.select({
|
|
125
|
+
message: "Set up your agent now?",
|
|
126
|
+
options: [
|
|
127
|
+
{ value: "yes", label: "Yes, set up now" },
|
|
128
|
+
{ value: "skip", label: "Skip for now", hint: "run clawcard setup later" },
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (!p.isCancel(runSetup) && runSetup === "yes") {
|
|
133
|
+
const { setupCommand } = await import("./setup.js");
|
|
134
|
+
await setupCommand();
|
|
135
|
+
}
|
|
114
136
|
} catch (err) {
|
|
115
137
|
s.stop("Failed to create key");
|
|
116
138
|
p.log.error(err.message);
|
|
@@ -208,6 +230,60 @@ export async function keysInfoCommand(keyIdOrPrefix) {
|
|
|
208
230
|
}
|
|
209
231
|
}
|
|
210
232
|
|
|
233
|
+
export async function agentFundCommand() {
|
|
234
|
+
requireAuth();
|
|
235
|
+
|
|
236
|
+
const s = p.spinner();
|
|
237
|
+
s.start("Fetching agent...");
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const [agents, balance] = await Promise.all([
|
|
241
|
+
listAgents(),
|
|
242
|
+
getBalance(),
|
|
243
|
+
]);
|
|
244
|
+
const active = agents.filter((a) => a.status === "active");
|
|
245
|
+
|
|
246
|
+
if (!active.length) {
|
|
247
|
+
s.stop("");
|
|
248
|
+
p.log.info("No active agent. Create one with: clawcard keys create");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const agent = active[0];
|
|
253
|
+
const currentBudget = await getAgentBudget(agent.id);
|
|
254
|
+
s.stop("");
|
|
255
|
+
|
|
256
|
+
p.log.info(
|
|
257
|
+
`Account balance: ${chalk.green("$" + (balance.amountCents / 100).toFixed(2))} | Agent budget: ${chalk.green("$" + ((currentBudget.budgetCents || 0) / 100).toFixed(2))}`
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const amount = await p.text({
|
|
261
|
+
message: "How much to add to agent budget?",
|
|
262
|
+
placeholder: "10",
|
|
263
|
+
validate: (v) => {
|
|
264
|
+
const n = parseFloat(v);
|
|
265
|
+
if (isNaN(n) || n <= 0) return "Enter a valid dollar amount";
|
|
266
|
+
if (Math.round(n * 100) > balance.amountCents)
|
|
267
|
+
return `Exceeds account balance of $${(balance.amountCents / 100).toFixed(2)}`;
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
if (p.isCancel(amount)) return;
|
|
271
|
+
|
|
272
|
+
const s2 = p.spinner();
|
|
273
|
+
s2.start("Allocating budget...");
|
|
274
|
+
|
|
275
|
+
const result = await allocateBudget(agent.id, Math.round(parseFloat(amount) * 100));
|
|
276
|
+
s2.stop("Budget allocated!");
|
|
277
|
+
|
|
278
|
+
p.log.success(
|
|
279
|
+
`Agent budget: ${chalk.green("$" + ((result.agentBudgetCents || 0) / 100).toFixed(2))} | Account remaining: ${chalk.green("$" + ((result.balanceRemainingCents || 0) / 100).toFixed(2))}`
|
|
280
|
+
);
|
|
281
|
+
} catch (err) {
|
|
282
|
+
s.stop("Failed");
|
|
283
|
+
p.log.error(err.message);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
211
287
|
export async function agentStatusCommand() {
|
|
212
288
|
requireAuth();
|
|
213
289
|
|
|
@@ -267,6 +343,39 @@ export async function keysRevokeCommand() {
|
|
|
267
343
|
});
|
|
268
344
|
if (p.isCancel(selected)) return;
|
|
269
345
|
|
|
346
|
+
// Check for stored credentials and offer export
|
|
347
|
+
try {
|
|
348
|
+
const creds = await listAgentCredentials(selected);
|
|
349
|
+
const credList = Array.isArray(creds) ? creds : creds.credentials || [];
|
|
350
|
+
if (credList.length > 0) {
|
|
351
|
+
p.log.warn(`This agent has ${credList.length} stored credential(s):`);
|
|
352
|
+
for (const c of credList) {
|
|
353
|
+
p.log.info(` ${orange(c.service)} / ${c.key}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const exportCreds = await p.confirm({
|
|
357
|
+
message: "Export credentials before revoking?",
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
if (!p.isCancel(exportCreds) && exportCreds) {
|
|
361
|
+
console.log();
|
|
362
|
+
for (const c of credList) {
|
|
363
|
+
try {
|
|
364
|
+
const full = await getAgentCredential(selected, c.service, c.key);
|
|
365
|
+
console.log(` ${orange(c.service)}/${c.key}: ${full.value}`);
|
|
366
|
+
} catch {
|
|
367
|
+
console.log(` ${orange(c.service)}/${c.key}: ${chalk.red("(could not retrieve)")}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
console.log();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} catch {
|
|
374
|
+
// couldn't fetch creds — continue
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
p.log.warn("This will close all cards, delete credentials, and return allocated budget to your account balance.");
|
|
378
|
+
|
|
270
379
|
const confirm = await p.confirm({
|
|
271
380
|
message: "Are you sure? This cannot be undone.",
|
|
272
381
|
});
|
package/src/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import * as p from "@clack/prompts";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import { showSplash,
|
|
4
|
+
import { showSplash, performAutoUpdate, VERSION } from "./splash.js";
|
|
5
5
|
import { isLoggedIn } from "./config.js";
|
|
6
6
|
|
|
7
7
|
const orange = chalk.hex("#FF6B35");
|
|
8
|
+
const isJsonMode = process.argv.includes("--json");
|
|
8
9
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
// Suppress splash and auto-update for --json (agent) mode
|
|
11
|
+
if (!isJsonMode) {
|
|
12
|
+
showSplash();
|
|
13
|
+
await performAutoUpdate();
|
|
14
|
+
}
|
|
14
15
|
|
|
15
16
|
const program = new Command();
|
|
16
17
|
program.name("clawcard").description("The ClawCard CLI").version(VERSION);
|
|
@@ -88,13 +89,172 @@ keys
|
|
|
88
89
|
await keysRevokeCommand();
|
|
89
90
|
});
|
|
90
91
|
|
|
91
|
-
// Agent
|
|
92
|
-
program
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
// Agent
|
|
93
|
+
const agent = program.command("agent").description("Agent commands");
|
|
94
|
+
|
|
95
|
+
agent.action(async () => {
|
|
96
|
+
const { agentStatusCommand } = await import("./commands/keys.js");
|
|
97
|
+
await agentStatusCommand();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
agent
|
|
101
|
+
.command("fund")
|
|
102
|
+
.description("Add budget to your agent")
|
|
95
103
|
.action(async () => {
|
|
96
|
-
const {
|
|
97
|
-
await
|
|
104
|
+
const { agentFundCommand } = await import("./commands/keys.js");
|
|
105
|
+
await agentFundCommand();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
agent
|
|
109
|
+
.command("info")
|
|
110
|
+
.description("Show agent identity")
|
|
111
|
+
.option("--json", "Output as JSON")
|
|
112
|
+
.action(async (options) => {
|
|
113
|
+
const { agentInfoCmd } = await import("./commands/agent.js");
|
|
114
|
+
await agentInfoCmd(options);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Agent emails
|
|
118
|
+
const agentEmails = agent.command("emails").description("Agent email inbox");
|
|
119
|
+
agentEmails
|
|
120
|
+
.option("--limit <n>", "Max results", "20")
|
|
121
|
+
.option("--unread", "Unread only")
|
|
122
|
+
.option("--json", "Output as JSON")
|
|
123
|
+
.action(async (options) => {
|
|
124
|
+
const { agentEmailsCmd } = await import("./commands/agent.js");
|
|
125
|
+
await agentEmailsCmd(options);
|
|
126
|
+
});
|
|
127
|
+
agentEmails
|
|
128
|
+
.command("send")
|
|
129
|
+
.requiredOption("--to <email>", "Recipient")
|
|
130
|
+
.requiredOption("--subject <subject>", "Subject line")
|
|
131
|
+
.requiredOption("--body <body>", "Email body")
|
|
132
|
+
.option("--json", "Output as JSON")
|
|
133
|
+
.action(async (options) => {
|
|
134
|
+
const { agentEmailsSendCmd } = await import("./commands/agent.js");
|
|
135
|
+
await agentEmailsSendCmd(options);
|
|
136
|
+
});
|
|
137
|
+
agentEmails
|
|
138
|
+
.command("read <emailId>")
|
|
139
|
+
.option("--json", "Output as JSON")
|
|
140
|
+
.action(async (emailId, options) => {
|
|
141
|
+
const { agentEmailsReadCmd } = await import("./commands/agent.js");
|
|
142
|
+
await agentEmailsReadCmd(emailId, options);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Agent SMS
|
|
146
|
+
const agentSms = agent.command("sms").description("Agent SMS messages");
|
|
147
|
+
agentSms
|
|
148
|
+
.option("--limit <n>", "Max results", "50")
|
|
149
|
+
.option("--json", "Output as JSON")
|
|
150
|
+
.action(async (options) => {
|
|
151
|
+
const { agentSmsCmd } = await import("./commands/agent.js");
|
|
152
|
+
await agentSmsCmd(options);
|
|
153
|
+
});
|
|
154
|
+
agentSms
|
|
155
|
+
.command("send")
|
|
156
|
+
.requiredOption("--to <phone>", "Recipient phone (E.164)")
|
|
157
|
+
.requiredOption("--body <body>", "Message text")
|
|
158
|
+
.option("--json", "Output as JSON")
|
|
159
|
+
.action(async (options) => {
|
|
160
|
+
const { agentSmsSendCmd } = await import("./commands/agent.js");
|
|
161
|
+
await agentSmsSendCmd(options);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Agent cards
|
|
165
|
+
const agentCards = agent.command("cards").description("Virtual cards");
|
|
166
|
+
agentCards
|
|
167
|
+
.option("--json", "Output as JSON")
|
|
168
|
+
.action(async (options) => {
|
|
169
|
+
const { agentCardsCmd } = await import("./commands/agent.js");
|
|
170
|
+
await agentCardsCmd(options);
|
|
171
|
+
});
|
|
172
|
+
agentCards
|
|
173
|
+
.command("create")
|
|
174
|
+
.requiredOption("--amount <cents>", "Spend limit in cents")
|
|
175
|
+
.requiredOption("--type <type>", "single_use or merchant_locked")
|
|
176
|
+
.option("--memo <memo>", "Card description", "Agent card")
|
|
177
|
+
.option("--json", "Output as JSON")
|
|
178
|
+
.action(async (options) => {
|
|
179
|
+
const { agentCardsCreateCmd } = await import("./commands/agent.js");
|
|
180
|
+
await agentCardsCreateCmd(options);
|
|
181
|
+
});
|
|
182
|
+
agentCards
|
|
183
|
+
.command("details <cardId>")
|
|
184
|
+
.option("--json", "Output as JSON")
|
|
185
|
+
.action(async (cardId, options) => {
|
|
186
|
+
const { agentCardsDetailsCmd } = await import("./commands/agent.js");
|
|
187
|
+
await agentCardsDetailsCmd(cardId, options);
|
|
188
|
+
});
|
|
189
|
+
agentCards
|
|
190
|
+
.command("close <cardId>")
|
|
191
|
+
.option("--json", "Output as JSON")
|
|
192
|
+
.action(async (cardId, options) => {
|
|
193
|
+
const { agentCardsActionCmd } = await import("./commands/agent.js");
|
|
194
|
+
await agentCardsActionCmd(cardId, "close", options);
|
|
195
|
+
});
|
|
196
|
+
agentCards
|
|
197
|
+
.command("pause <cardId>")
|
|
198
|
+
.option("--json", "Output as JSON")
|
|
199
|
+
.action(async (cardId, options) => {
|
|
200
|
+
const { agentCardsActionCmd } = await import("./commands/agent.js");
|
|
201
|
+
await agentCardsActionCmd(cardId, "pause", options);
|
|
202
|
+
});
|
|
203
|
+
agentCards
|
|
204
|
+
.command("resume <cardId>")
|
|
205
|
+
.option("--json", "Output as JSON")
|
|
206
|
+
.action(async (cardId, options) => {
|
|
207
|
+
const { agentCardsActionCmd } = await import("./commands/agent.js");
|
|
208
|
+
await agentCardsActionCmd(cardId, "resume", options);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Agent credentials
|
|
212
|
+
const agentCreds = agent.command("creds").description("Credential vault");
|
|
213
|
+
agentCreds
|
|
214
|
+
.option("--json", "Output as JSON")
|
|
215
|
+
.action(async (options) => {
|
|
216
|
+
const { agentCredsCmd } = await import("./commands/agent.js");
|
|
217
|
+
await agentCredsCmd(options);
|
|
218
|
+
});
|
|
219
|
+
agentCreds
|
|
220
|
+
.command("set")
|
|
221
|
+
.requiredOption("--service <name>", "Service name")
|
|
222
|
+
.requiredOption("--key <key>", "Credential key")
|
|
223
|
+
.requiredOption("--value <value>", "Secret value")
|
|
224
|
+
.option("--json", "Output as JSON")
|
|
225
|
+
.action(async (options) => {
|
|
226
|
+
const { agentCredsSetCmd } = await import("./commands/agent.js");
|
|
227
|
+
await agentCredsSetCmd(options);
|
|
228
|
+
});
|
|
229
|
+
agentCreds
|
|
230
|
+
.command("get")
|
|
231
|
+
.requiredOption("--service <name>", "Service name")
|
|
232
|
+
.requiredOption("--key <key>", "Credential key")
|
|
233
|
+
.option("--json", "Output as JSON")
|
|
234
|
+
.action(async (options) => {
|
|
235
|
+
const { agentCredsGetCmd } = await import("./commands/agent.js");
|
|
236
|
+
await agentCredsGetCmd(options);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Agent budget
|
|
240
|
+
agent
|
|
241
|
+
.command("budget")
|
|
242
|
+
.description("Check agent budget")
|
|
243
|
+
.option("--json", "Output as JSON")
|
|
244
|
+
.action(async (options) => {
|
|
245
|
+
const { agentBudgetCmd } = await import("./commands/agent.js");
|
|
246
|
+
await agentBudgetCmd(options);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Agent activity
|
|
250
|
+
agent
|
|
251
|
+
.command("activity")
|
|
252
|
+
.description("View activity log")
|
|
253
|
+
.option("--limit <n>", "Max results", "50")
|
|
254
|
+
.option("--json", "Output as JSON")
|
|
255
|
+
.action(async (options) => {
|
|
256
|
+
const { agentActivityCmd } = await import("./commands/agent.js");
|
|
257
|
+
await agentActivityCmd(options);
|
|
98
258
|
});
|
|
99
259
|
|
|
100
260
|
// Setup
|
|
@@ -178,14 +338,6 @@ program
|
|
|
178
338
|
if (process.argv.length <= 2) {
|
|
179
339
|
const loggedIn = isLoggedIn();
|
|
180
340
|
|
|
181
|
-
// Show update notice if available (non-blocking, already fetched)
|
|
182
|
-
const latest = await updatePromise;
|
|
183
|
-
if (latest) {
|
|
184
|
-
p.log.warn(
|
|
185
|
-
`Update available: ${chalk.dim(VERSION)} → ${orange.bold(latest)} Run ${orange("npm i -g @clawcard/cli@latest")}`
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
341
|
let options;
|
|
190
342
|
|
|
191
343
|
if (!loggedIn) {
|
package/src/splash.js
CHANGED
|
@@ -1,33 +1,87 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { readFileSync } from "fs";
|
|
2
|
+
import { readFileSync, existsSync, copyFileSync } from "fs";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { getConfig, saveConfig } from "./config.js";
|
|
5
8
|
|
|
6
9
|
const o = chalk.hex("#FF6B35");
|
|
7
10
|
const d = chalk.dim;
|
|
8
11
|
|
|
9
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const pkg = JSON.parse(
|
|
13
|
+
const pkg = JSON.parse(
|
|
14
|
+
readFileSync(join(__dirname, "..", "package.json"), "utf-8")
|
|
15
|
+
);
|
|
11
16
|
export const VERSION = pkg.version;
|
|
12
17
|
|
|
18
|
+
const SKILL_SOURCE = join(__dirname, "..", "skill", "SKILL.md");
|
|
19
|
+
const home = homedir();
|
|
20
|
+
|
|
21
|
+
const SKILL_TARGETS = [
|
|
22
|
+
join(home, ".claude", "skills", "clawcard", "SKILL.md"),
|
|
23
|
+
join(home, ".openclaw", "skills", "clawcard", "SKILL.md"),
|
|
24
|
+
join(home, ".cursor", "skills", "clawcard", "SKILL.md"),
|
|
25
|
+
join(home, ".agents", "skills", "clawcard", "SKILL.md"),
|
|
26
|
+
];
|
|
27
|
+
|
|
13
28
|
export async function checkForUpdate() {
|
|
14
29
|
try {
|
|
15
|
-
const res = await fetch(
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
const res = await fetch(
|
|
31
|
+
"https://registry.npmjs.org/@clawcard/cli/latest",
|
|
32
|
+
{ signal: AbortSignal.timeout(3000) }
|
|
33
|
+
);
|
|
18
34
|
const data = await res.json();
|
|
19
35
|
if (data.version && data.version !== VERSION) {
|
|
20
36
|
return data.version;
|
|
21
37
|
}
|
|
22
38
|
} catch {
|
|
23
|
-
// offline or timeout
|
|
39
|
+
// offline or timeout
|
|
24
40
|
}
|
|
25
41
|
return null;
|
|
26
42
|
}
|
|
27
43
|
|
|
44
|
+
export async function performAutoUpdate() {
|
|
45
|
+
const config = getConfig() || {};
|
|
46
|
+
const lastCheck = config.lastUpdateCheck || 0;
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
|
|
49
|
+
// Skip if checked within last hour
|
|
50
|
+
if (now - lastCheck < 3600000) return;
|
|
51
|
+
|
|
52
|
+
// Save check time
|
|
53
|
+
saveConfig({ ...config, lastUpdateCheck: now });
|
|
54
|
+
|
|
55
|
+
const latest = await checkForUpdate();
|
|
56
|
+
if (!latest) return;
|
|
57
|
+
|
|
58
|
+
console.log(` Updating ClawCard CLI to v${latest}...`);
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
execSync("npm i -g @clawcard/cli@latest", {
|
|
62
|
+
stdio: "pipe",
|
|
63
|
+
timeout: 60000,
|
|
64
|
+
});
|
|
65
|
+
console.log(` Updated to v${latest}`);
|
|
66
|
+
|
|
67
|
+
// Refresh installed SKILL.md files
|
|
68
|
+
if (existsSync(SKILL_SOURCE)) {
|
|
69
|
+
for (const target of SKILL_TARGETS) {
|
|
70
|
+
if (existsSync(target)) {
|
|
71
|
+
copyFileSync(SKILL_SOURCE, target);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// update failed — continue normally
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
28
80
|
export function showSplash() {
|
|
29
81
|
console.log();
|
|
30
82
|
console.log(` 🦞 ${o.bold("clawcard.sh")} ${d("v" + VERSION)}`);
|
|
31
|
-
console.log(
|
|
83
|
+
console.log(
|
|
84
|
+
` ${o("on-demand credit cards, email and phone for your agents")}`
|
|
85
|
+
);
|
|
32
86
|
console.log();
|
|
33
87
|
}
|