agentspend 0.1.6 → 0.1.8
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/dist/commands/card.js +81 -5
- package/package.json +1 -1
- package/src/commands/card.ts +106 -5
package/dist/commands/card.js
CHANGED
|
@@ -27,7 +27,19 @@ async function readSetupId() {
|
|
|
27
27
|
catch {
|
|
28
28
|
// file doesn't exist or is invalid
|
|
29
29
|
}
|
|
30
|
-
throw new Error("No setup_id found. Run 'agentspend card
|
|
30
|
+
throw new Error("No setup_id found. Run 'agentspend card configure' first.");
|
|
31
|
+
}
|
|
32
|
+
async function readCardFile() {
|
|
33
|
+
try {
|
|
34
|
+
const data = JSON.parse(await (0, promises_1.readFile)(CARD_FILE, "utf-8"));
|
|
35
|
+
if (typeof data.card_id === "string" && typeof data.card_secret === "string") {
|
|
36
|
+
return { card_id: data.card_id, card_secret: data.card_secret };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// file doesn't exist or is invalid
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
31
43
|
}
|
|
32
44
|
async function createCard() {
|
|
33
45
|
const response = await fetch(`${API_BASE}/v1/card/create`, {
|
|
@@ -41,6 +53,18 @@ async function createCard() {
|
|
|
41
53
|
}
|
|
42
54
|
return (await response.json());
|
|
43
55
|
}
|
|
56
|
+
async function requestConfigure(cardId, cardSecret) {
|
|
57
|
+
const response = await fetch(`${API_BASE}/v1/card/configure`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "content-type": "application/json" },
|
|
60
|
+
body: JSON.stringify({ card_id: cardId, card_secret: cardSecret })
|
|
61
|
+
});
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const body = await response.text();
|
|
64
|
+
throw new Error(`Failed to open configure page (${response.status}): ${body}`);
|
|
65
|
+
}
|
|
66
|
+
return (await response.json());
|
|
67
|
+
}
|
|
44
68
|
async function getSetupStatus(setupId) {
|
|
45
69
|
const response = await fetch(`${API_BASE}/v1/card/setup/${encodeURIComponent(setupId)}`);
|
|
46
70
|
if (!response.ok) {
|
|
@@ -49,18 +73,61 @@ async function getSetupStatus(setupId) {
|
|
|
49
73
|
}
|
|
50
74
|
return (await response.json());
|
|
51
75
|
}
|
|
76
|
+
async function getCardStatus(cardId, cardSecret) {
|
|
77
|
+
const response = await fetch(`${API_BASE}/v1/card/${encodeURIComponent(cardId)}/status`, {
|
|
78
|
+
headers: { "x-card-secret": cardSecret }
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
const body = await response.text();
|
|
82
|
+
throw new Error(`Failed to get card status (${response.status}): ${body}`);
|
|
83
|
+
}
|
|
84
|
+
return (await response.json());
|
|
85
|
+
}
|
|
52
86
|
function sleep(ms) {
|
|
53
87
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
54
88
|
}
|
|
89
|
+
function formatCents(cents) {
|
|
90
|
+
return `$${(cents / 100).toFixed(2)}`;
|
|
91
|
+
}
|
|
55
92
|
function registerCardCommands(program) {
|
|
56
93
|
const card = program
|
|
57
94
|
.command("card")
|
|
58
95
|
.description("Manage AgentSpend cards");
|
|
59
96
|
card
|
|
60
97
|
.command("status")
|
|
61
|
-
.description("
|
|
98
|
+
.description("Show card dashboard: weekly budget, services, and recent charges")
|
|
62
99
|
.action(async () => {
|
|
63
100
|
try {
|
|
101
|
+
// If card.json exists, show full dashboard
|
|
102
|
+
const cardData = await readCardFile();
|
|
103
|
+
if (cardData) {
|
|
104
|
+
const status = await getCardStatus(cardData.card_id, cardData.card_secret);
|
|
105
|
+
console.log(`Card ID: ${status.card_id}`);
|
|
106
|
+
console.log(`Weekly budget: ${formatCents(status.weekly_spent_cents)} / ${formatCents(status.weekly_limit_cents)} used this week`);
|
|
107
|
+
console.log(`Remaining: ${formatCents(status.weekly_remaining_cents)}`);
|
|
108
|
+
console.log();
|
|
109
|
+
if (status.services.length > 0) {
|
|
110
|
+
console.log("Authorized services:");
|
|
111
|
+
for (const svc of status.services) {
|
|
112
|
+
console.log(` - ${svc.name} (${svc.status}, since ${new Date(svc.created_at).toLocaleDateString()})`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.log("Authorized services: No services authorized yet");
|
|
117
|
+
}
|
|
118
|
+
console.log();
|
|
119
|
+
if (status.recent_charges.length > 0) {
|
|
120
|
+
console.log("Recent charges:");
|
|
121
|
+
for (const ch of status.recent_charges) {
|
|
122
|
+
console.log(` - ${ch.service_name}: ${formatCents(ch.amount_cents)} on ${new Date(ch.created_at).toLocaleDateString()}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
console.log("Recent charges: No charges yet");
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Fall back to checking pending setup status
|
|
64
131
|
const setupId = await readSetupId();
|
|
65
132
|
const status = await getSetupStatus(setupId);
|
|
66
133
|
console.log(`Setup ID: ${status.setup_id}`);
|
|
@@ -76,13 +143,22 @@ function registerCardCommands(program) {
|
|
|
76
143
|
}
|
|
77
144
|
});
|
|
78
145
|
card
|
|
79
|
-
.command("
|
|
80
|
-
.description("Set up
|
|
146
|
+
.command("configure")
|
|
147
|
+
.description("Set up or reconfigure your card — opens the configuration page in your browser")
|
|
81
148
|
.action(async () => {
|
|
82
149
|
try {
|
|
150
|
+
// Check if card.json exists — reconfigure flow
|
|
151
|
+
const cardData = await readCardFile();
|
|
152
|
+
if (cardData) {
|
|
153
|
+
const result = await requestConfigure(cardData.card_id, cardData.card_secret);
|
|
154
|
+
console.log("Opened configuration page in browser.");
|
|
155
|
+
openUrl(result.configure_url);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// First-time setup flow
|
|
83
159
|
const result = await createCard();
|
|
84
160
|
console.log(`Setup ID: ${result.setup_id}`);
|
|
85
|
-
console.log(`Opening
|
|
161
|
+
console.log(`Opening configuration page in browser...`);
|
|
86
162
|
openUrl(result.setup_url);
|
|
87
163
|
await ensureConfigDir();
|
|
88
164
|
await (0, promises_1.writeFile)(SETUP_FILE, JSON.stringify({ setup_id: result.setup_id }, null, 2));
|
package/package.json
CHANGED
package/src/commands/card.ts
CHANGED
|
@@ -21,6 +21,20 @@ interface CardSetupStatusResponse {
|
|
|
21
21
|
card_secret?: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
interface CardConfigureResponse {
|
|
25
|
+
setup_id: string;
|
|
26
|
+
configure_url: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface CardStatusResponse {
|
|
30
|
+
card_id: string;
|
|
31
|
+
weekly_limit_cents: number;
|
|
32
|
+
weekly_spent_cents: number;
|
|
33
|
+
weekly_remaining_cents: number;
|
|
34
|
+
services: { name: string; status: string; created_at: string }[];
|
|
35
|
+
recent_charges: { service_name: string; amount_cents: number; created_at: string }[];
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
const CONFIG_DIR = join(homedir(), ".agentspend");
|
|
25
39
|
const SETUP_FILE = join(CONFIG_DIR, "setup.json");
|
|
26
40
|
const CARD_FILE = join(CONFIG_DIR, "card.json");
|
|
@@ -44,7 +58,19 @@ async function readSetupId(): Promise<string> {
|
|
|
44
58
|
} catch {
|
|
45
59
|
// file doesn't exist or is invalid
|
|
46
60
|
}
|
|
47
|
-
throw new Error("No setup_id found. Run 'agentspend card
|
|
61
|
+
throw new Error("No setup_id found. Run 'agentspend card configure' first.");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function readCardFile(): Promise<{ card_id: string; card_secret: string } | null> {
|
|
65
|
+
try {
|
|
66
|
+
const data = JSON.parse(await readFile(CARD_FILE, "utf-8"));
|
|
67
|
+
if (typeof data.card_id === "string" && typeof data.card_secret === "string") {
|
|
68
|
+
return { card_id: data.card_id, card_secret: data.card_secret };
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// file doesn't exist or is invalid
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
48
74
|
}
|
|
49
75
|
|
|
50
76
|
async function createCard(): Promise<CardCreateResponse> {
|
|
@@ -62,6 +88,21 @@ async function createCard(): Promise<CardCreateResponse> {
|
|
|
62
88
|
return (await response.json()) as CardCreateResponse;
|
|
63
89
|
}
|
|
64
90
|
|
|
91
|
+
async function requestConfigure(cardId: string, cardSecret: string): Promise<CardConfigureResponse> {
|
|
92
|
+
const response = await fetch(`${API_BASE}/v1/card/configure`, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: { "content-type": "application/json" },
|
|
95
|
+
body: JSON.stringify({ card_id: cardId, card_secret: cardSecret })
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
const body = await response.text();
|
|
100
|
+
throw new Error(`Failed to open configure page (${response.status}): ${body}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (await response.json()) as CardConfigureResponse;
|
|
104
|
+
}
|
|
105
|
+
|
|
65
106
|
async function getSetupStatus(setupId: string): Promise<CardSetupStatusResponse> {
|
|
66
107
|
const response = await fetch(`${API_BASE}/v1/card/setup/${encodeURIComponent(setupId)}`);
|
|
67
108
|
|
|
@@ -73,10 +114,27 @@ async function getSetupStatus(setupId: string): Promise<CardSetupStatusResponse>
|
|
|
73
114
|
return (await response.json()) as CardSetupStatusResponse;
|
|
74
115
|
}
|
|
75
116
|
|
|
117
|
+
async function getCardStatus(cardId: string, cardSecret: string): Promise<CardStatusResponse> {
|
|
118
|
+
const response = await fetch(`${API_BASE}/v1/card/${encodeURIComponent(cardId)}/status`, {
|
|
119
|
+
headers: { "x-card-secret": cardSecret }
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
const body = await response.text();
|
|
124
|
+
throw new Error(`Failed to get card status (${response.status}): ${body}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (await response.json()) as CardStatusResponse;
|
|
128
|
+
}
|
|
129
|
+
|
|
76
130
|
function sleep(ms: number): Promise<void> {
|
|
77
131
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
78
132
|
}
|
|
79
133
|
|
|
134
|
+
function formatCents(cents: number): string {
|
|
135
|
+
return `$${(cents / 100).toFixed(2)}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
80
138
|
export function registerCardCommands(program: Command): void {
|
|
81
139
|
const card = program
|
|
82
140
|
.command("card")
|
|
@@ -84,9 +142,42 @@ export function registerCardCommands(program: Command): void {
|
|
|
84
142
|
|
|
85
143
|
card
|
|
86
144
|
.command("status")
|
|
87
|
-
.description("
|
|
145
|
+
.description("Show card dashboard: weekly budget, services, and recent charges")
|
|
88
146
|
.action(async () => {
|
|
89
147
|
try {
|
|
148
|
+
// If card.json exists, show full dashboard
|
|
149
|
+
const cardData = await readCardFile();
|
|
150
|
+
if (cardData) {
|
|
151
|
+
const status = await getCardStatus(cardData.card_id, cardData.card_secret);
|
|
152
|
+
|
|
153
|
+
console.log(`Card ID: ${status.card_id}`);
|
|
154
|
+
console.log(`Weekly budget: ${formatCents(status.weekly_spent_cents)} / ${formatCents(status.weekly_limit_cents)} used this week`);
|
|
155
|
+
console.log(`Remaining: ${formatCents(status.weekly_remaining_cents)}`);
|
|
156
|
+
|
|
157
|
+
console.log();
|
|
158
|
+
if (status.services.length > 0) {
|
|
159
|
+
console.log("Authorized services:");
|
|
160
|
+
for (const svc of status.services) {
|
|
161
|
+
console.log(` - ${svc.name} (${svc.status}, since ${new Date(svc.created_at).toLocaleDateString()})`);
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
console.log("Authorized services: No services authorized yet");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log();
|
|
168
|
+
if (status.recent_charges.length > 0) {
|
|
169
|
+
console.log("Recent charges:");
|
|
170
|
+
for (const ch of status.recent_charges) {
|
|
171
|
+
console.log(` - ${ch.service_name}: ${formatCents(ch.amount_cents)} on ${new Date(ch.created_at).toLocaleDateString()}`);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
console.log("Recent charges: No charges yet");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Fall back to checking pending setup status
|
|
90
181
|
const setupId = await readSetupId();
|
|
91
182
|
const status = await getSetupStatus(setupId);
|
|
92
183
|
console.log(`Setup ID: ${status.setup_id}`);
|
|
@@ -102,13 +193,23 @@ export function registerCardCommands(program: Command): void {
|
|
|
102
193
|
});
|
|
103
194
|
|
|
104
195
|
card
|
|
105
|
-
.command("
|
|
106
|
-
.description("Set up
|
|
196
|
+
.command("configure")
|
|
197
|
+
.description("Set up or reconfigure your card — opens the configuration page in your browser")
|
|
107
198
|
.action(async () => {
|
|
108
199
|
try {
|
|
200
|
+
// Check if card.json exists — reconfigure flow
|
|
201
|
+
const cardData = await readCardFile();
|
|
202
|
+
if (cardData) {
|
|
203
|
+
const result = await requestConfigure(cardData.card_id, cardData.card_secret);
|
|
204
|
+
console.log("Opened configuration page in browser.");
|
|
205
|
+
openUrl(result.configure_url);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// First-time setup flow
|
|
109
210
|
const result = await createCard();
|
|
110
211
|
console.log(`Setup ID: ${result.setup_id}`);
|
|
111
|
-
console.log(`Opening
|
|
212
|
+
console.log(`Opening configuration page in browser...`);
|
|
112
213
|
openUrl(result.setup_url);
|
|
113
214
|
|
|
114
215
|
await ensureConfigDir();
|