@web42/w42 0.1.20 → 0.1.22
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 +167 -67
- package/dist/commands/auth.js +2 -1
- package/dist/commands/cart.d.ts +2 -0
- package/dist/commands/cart.js +219 -0
- package/dist/commands/intent.d.ts +2 -0
- package/dist/commands/intent.js +105 -0
- package/dist/commands/register.js +13 -20
- package/dist/commands/search.js +4 -2
- package/dist/commands/send.js +38 -12
- package/dist/commands/serve.js +1 -0
- package/dist/index.js +4 -2
- package/dist/utils/api.d.ts +7 -0
- package/dist/utils/api.js +24 -2
- package/dist/utils/config.d.ts +12 -0
- package/dist/utils/config.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/commands/pay.d.ts +0 -2
- package/dist/commands/pay.js +0 -260
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { apiGet, apiPost, hintForError } from "../utils/api.js";
|
|
5
|
+
import { requireAuth, setConfigValue } from "../utils/config.js";
|
|
6
|
+
export const intentCommand = new Command("intent").description("Manage payment intents");
|
|
7
|
+
intentCommand
|
|
8
|
+
.command("get")
|
|
9
|
+
.description("Fetch an intent by nick")
|
|
10
|
+
.argument("<nick>", "Intent nick")
|
|
11
|
+
.action(async (nick) => {
|
|
12
|
+
requireAuth();
|
|
13
|
+
const spinner = ora(`Fetching intent ${nick}...`).start();
|
|
14
|
+
try {
|
|
15
|
+
const res = await apiGet(`/api/pay/intent/${encodeURIComponent(nick)}`);
|
|
16
|
+
spinner.stop();
|
|
17
|
+
if (res.status === "active") {
|
|
18
|
+
setConfigValue(`intents.${nick}`, JSON.stringify(res));
|
|
19
|
+
}
|
|
20
|
+
console.log(JSON.stringify(res, null, 2));
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
spinner.fail("Failed to fetch intent");
|
|
24
|
+
console.error(chalk.red(String(err)));
|
|
25
|
+
console.error(chalk.dim(hintForError(err)));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
intentCommand
|
|
30
|
+
.command("list")
|
|
31
|
+
.description("List all your intents")
|
|
32
|
+
.action(async () => {
|
|
33
|
+
requireAuth();
|
|
34
|
+
const spinner = ora("Fetching intents...").start();
|
|
35
|
+
try {
|
|
36
|
+
const intents = await apiGet("/api/pay/intent");
|
|
37
|
+
spinner.stop();
|
|
38
|
+
for (const intent of intents) {
|
|
39
|
+
if (intent.status === "active" && typeof intent.nick === "string") {
|
|
40
|
+
setConfigValue(`intents.${intent.nick}`, JSON.stringify(intent));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
console.log(JSON.stringify(intents, null, 2));
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
spinner.fail("Failed to list intents");
|
|
47
|
+
console.error(chalk.red(String(err)));
|
|
48
|
+
console.error(chalk.dim(hintForError(err)));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
intentCommand
|
|
53
|
+
.command("revoke")
|
|
54
|
+
.description("Revoke an active intent")
|
|
55
|
+
.argument("<nick>", "Intent nick")
|
|
56
|
+
.action(async (nick) => {
|
|
57
|
+
requireAuth();
|
|
58
|
+
const spinner = ora(`Revoking intent ${nick}...`).start();
|
|
59
|
+
try {
|
|
60
|
+
const res = await apiPost(`/api/pay/intent/${encodeURIComponent(nick)}/revoke`, {});
|
|
61
|
+
spinner.stop();
|
|
62
|
+
setConfigValue(`intents.${nick}`, "");
|
|
63
|
+
console.log(JSON.stringify(res, null, 2));
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
spinner.fail("Failed to revoke intent");
|
|
67
|
+
console.error(chalk.red(String(err)));
|
|
68
|
+
console.error(chalk.dim(hintForError(err)));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
intentCommand
|
|
73
|
+
.command("propose")
|
|
74
|
+
.description("Generate an intent creation URL for the user to authorize in the browser")
|
|
75
|
+
.requiredOption("--nick <nick>", "Short identifier for the intent")
|
|
76
|
+
.requiredOption("--agents <slugs>", "Comma-separated merchant agent slugs (e.g. w42/starbucks)")
|
|
77
|
+
.requiredOption("--max-amount <dollars>", "Max amount per transaction in dollars")
|
|
78
|
+
.requiredOption("--prompt-playback <text>", "Human-readable description of the intent")
|
|
79
|
+
.option("--currency <code>", "Currency code", "USD")
|
|
80
|
+
.option("--recurring <type>", "Recurring type: once, daily, weekly, monthly", "once")
|
|
81
|
+
.option("--budget <dollars>", "Lifetime budget in dollars")
|
|
82
|
+
.option("--expires <date>", "Expiry date (ISO 8601)")
|
|
83
|
+
.action(async (opts) => {
|
|
84
|
+
const cfg = requireAuth();
|
|
85
|
+
const username = cfg.username;
|
|
86
|
+
if (!username) {
|
|
87
|
+
console.error(chalk.red("No username found. Please log in again."));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const baseUrl = cfg.apiUrl ?? "https://web42-network.vercel.app";
|
|
91
|
+
const params = new URLSearchParams({
|
|
92
|
+
nick: opts.nick,
|
|
93
|
+
agents: opts.agents,
|
|
94
|
+
max_amount: opts.maxAmount,
|
|
95
|
+
currency: opts.currency,
|
|
96
|
+
recurring: opts.recurring,
|
|
97
|
+
prompt_playback: opts.promptPlayback,
|
|
98
|
+
});
|
|
99
|
+
if (opts.budget)
|
|
100
|
+
params.set("budget", opts.budget);
|
|
101
|
+
if (opts.expires)
|
|
102
|
+
params.set("expires_at", opts.expires);
|
|
103
|
+
const url = `${baseUrl}/@${username}/intents/create?${params.toString()}`;
|
|
104
|
+
console.log(JSON.stringify({ url }, null, 2));
|
|
105
|
+
});
|
|
@@ -2,8 +2,8 @@ import chalk from "chalk";
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import inquirer from "inquirer";
|
|
4
4
|
import ora from "ora";
|
|
5
|
-
import { apiGet, apiPost } from "../utils/api.js";
|
|
6
|
-
import { getConfig
|
|
5
|
+
import { apiGet, apiPost, hintForError } from "../utils/api.js";
|
|
6
|
+
import { getConfig } from "../utils/config.js";
|
|
7
7
|
function toSlugPart(name) {
|
|
8
8
|
return name
|
|
9
9
|
.toLowerCase()
|
|
@@ -35,6 +35,7 @@ export const registerCommand = new Command("register")
|
|
|
35
35
|
catch (err) {
|
|
36
36
|
cardSpinner.fail("Could not fetch agent card");
|
|
37
37
|
console.error(chalk.red(` ${cardUrl}: ${String(err)}`));
|
|
38
|
+
console.error(chalk.dim(" Make sure the agent server is running and serving /.well-known/agent-card.json"));
|
|
38
39
|
process.exit(1);
|
|
39
40
|
}
|
|
40
41
|
cardSpinner.stop();
|
|
@@ -45,7 +46,7 @@ export const registerCommand = new Command("register")
|
|
|
45
46
|
const check = await apiGet(`/api/agents/check?url=${encodeURIComponent(url)}`);
|
|
46
47
|
if (check.registered) {
|
|
47
48
|
alreadyRegistered = true;
|
|
48
|
-
const displaySlug = check.slug
|
|
49
|
+
const displaySlug = check.slug;
|
|
49
50
|
checkSpinner.warn(`Already registered as ${chalk.cyan(displaySlug)}`);
|
|
50
51
|
}
|
|
51
52
|
else {
|
|
@@ -67,16 +68,8 @@ export const registerCommand = new Command("register")
|
|
|
67
68
|
if (!proceed)
|
|
68
69
|
process.exit(0);
|
|
69
70
|
}
|
|
70
|
-
// ── Step 3:
|
|
71
|
-
if (!isAuthenticated()) {
|
|
72
|
-
console.log();
|
|
73
|
-
console.log(chalk.yellow("You must be logged in to register an agent."));
|
|
74
|
-
console.log(chalk.dim("Run: ") + chalk.cyan("w42 auth login"));
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
71
|
+
// ── Step 3: Slug prompt + availability check ───────────────────────────
|
|
77
72
|
const cfg = getConfig();
|
|
78
|
-
const username = cfg.username;
|
|
79
|
-
// ── Step 4: Slug prompt + availability check ───────────────────────────
|
|
80
73
|
const defaultSlugPart = toSlugPart(agentCard.name ?? "my-agent") || "my-agent";
|
|
81
74
|
console.log();
|
|
82
75
|
if (agentCard.name)
|
|
@@ -90,7 +83,7 @@ export const registerCommand = new Command("register")
|
|
|
90
83
|
{
|
|
91
84
|
type: "input",
|
|
92
85
|
name: "slugPart",
|
|
93
|
-
message: `Agent slug ${chalk.dim(
|
|
86
|
+
message: `Agent slug ${chalk.dim("(w42/")}`,
|
|
94
87
|
default: defaultSlugPart,
|
|
95
88
|
validate: (input) => {
|
|
96
89
|
if (!input)
|
|
@@ -102,12 +95,12 @@ export const registerCommand = new Command("register")
|
|
|
102
95
|
},
|
|
103
96
|
]);
|
|
104
97
|
slugPart = answer.slugPart;
|
|
105
|
-
const fullSlug =
|
|
98
|
+
const fullSlug = "w42/" + slugPart;
|
|
106
99
|
const slugSpinner = ora("Checking slug availability...").start();
|
|
107
100
|
try {
|
|
108
101
|
const check = await apiGet(`/api/agents/check?slug=${encodeURIComponent(fullSlug)}`);
|
|
109
102
|
if (check.taken) {
|
|
110
|
-
slugSpinner.fail(`${chalk.cyan(
|
|
103
|
+
slugSpinner.fail(`${chalk.cyan(`w42/${slugPart}`)} is already taken — choose another`);
|
|
111
104
|
continue;
|
|
112
105
|
}
|
|
113
106
|
slugSpinner.stop();
|
|
@@ -115,7 +108,7 @@ export const registerCommand = new Command("register")
|
|
|
115
108
|
catch {
|
|
116
109
|
slugSpinner.warn("Could not verify slug — proceeding anyway");
|
|
117
110
|
}
|
|
118
|
-
console.log(chalk.dim(` Full slug:
|
|
111
|
+
console.log(chalk.dim(` Full slug: w42/${slugPart}`));
|
|
119
112
|
break;
|
|
120
113
|
}
|
|
121
114
|
// ── Step 5: POST ───────────────────────────────────────────────────────
|
|
@@ -129,19 +122,19 @@ export const registerCommand = new Command("register")
|
|
|
129
122
|
const data = await apiPost("/api/agents", body);
|
|
130
123
|
const slug = data.agent?.slug ?? "unknown";
|
|
131
124
|
const name = data.agent?.agent_card?.name ?? agentCard.name ?? slug;
|
|
132
|
-
const displaySlug = slug.replace("~", "/");
|
|
133
125
|
if (data.created) {
|
|
134
|
-
registerSpinner.succeed(`Registered "${name}" (${
|
|
126
|
+
registerSpinner.succeed(`Registered "${name}" (${slug})`);
|
|
135
127
|
}
|
|
136
128
|
else {
|
|
137
|
-
registerSpinner.succeed(`Updated "${name}" (${
|
|
129
|
+
registerSpinner.succeed(`Updated "${name}" (${slug})`);
|
|
138
130
|
}
|
|
139
131
|
console.log(chalk.dim(` Send: w42 send ${slug} "hello"`));
|
|
140
|
-
console.log(chalk.dim(` View: ${cfg.apiUrl}/${
|
|
132
|
+
console.log(chalk.dim(` View: ${cfg.apiUrl}/${slug}`));
|
|
141
133
|
}
|
|
142
134
|
catch (err) {
|
|
143
135
|
registerSpinner.fail("Registration failed");
|
|
144
136
|
console.error(chalk.red(String(err)));
|
|
137
|
+
console.error(chalk.dim(hintForError(err)));
|
|
145
138
|
process.exit(1);
|
|
146
139
|
}
|
|
147
140
|
});
|
package/dist/commands/search.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import ora from "ora";
|
|
4
|
-
import { apiGet } from "../utils/api.js";
|
|
4
|
+
import { apiGet, hintForError } from "../utils/api.js";
|
|
5
5
|
import { printBanner } from "../utils/banner.js";
|
|
6
6
|
import { getConfig } from "../utils/config.js";
|
|
7
7
|
function getCardName(card) {
|
|
@@ -71,6 +71,7 @@ export const searchCommand = new Command("search")
|
|
|
71
71
|
spinner.stop();
|
|
72
72
|
if (agents.length === 0) {
|
|
73
73
|
console.log(chalk.yellow(`No agents found for "${query}".`));
|
|
74
|
+
console.log(chalk.dim("Try different keywords or a broader search term."));
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
77
|
const limit = parseInt(opts.limit, 10) || 10;
|
|
@@ -83,7 +84,7 @@ export const searchCommand = new Command("search")
|
|
|
83
84
|
const stars = agent.stars_count > 0 ? `★ ${agent.stars_count}` : "";
|
|
84
85
|
const skills = formatSkills(agent.agent_card.skills);
|
|
85
86
|
// Header: - <name (link)> | <slug> | <stars>
|
|
86
|
-
const nameLink = chalk.bold.cyan(terminalLink(name, `${config.apiUrl}/${agent.slug
|
|
87
|
+
const nameLink = chalk.bold.cyan(terminalLink(name, `${config.apiUrl}/${agent.slug}`));
|
|
87
88
|
const slugLabel = chalk.dim(agent.slug);
|
|
88
89
|
const separator = chalk.dim(" | ");
|
|
89
90
|
const headerParts = [nameLink, slugLabel];
|
|
@@ -112,6 +113,7 @@ export const searchCommand = new Command("search")
|
|
|
112
113
|
catch (error) {
|
|
113
114
|
spinner.fail("Search failed");
|
|
114
115
|
console.error(chalk.red(String(error)));
|
|
116
|
+
console.error(chalk.dim(hintForError(error)));
|
|
115
117
|
process.exit(1);
|
|
116
118
|
}
|
|
117
119
|
});
|
package/dist/commands/send.js
CHANGED
|
@@ -4,8 +4,8 @@ import { Command } from "commander";
|
|
|
4
4
|
import ora from "ora";
|
|
5
5
|
import { v4 as uuidv4 } from "uuid";
|
|
6
6
|
import { isCartMandatePart, parseCartMandate } from "@web42/auth";
|
|
7
|
-
import { apiPost } from "../utils/api.js";
|
|
8
|
-
import { getConfig, getConfigValue, isTelemetryEnabled, requireAuth, setConfigValue } from "../utils/config.js";
|
|
7
|
+
import { apiPost, hintForError } from "../utils/api.js";
|
|
8
|
+
import { getCachedIntents, getConfig, getConfigValue, isTelemetryEnabled, requireAuth, setConfigValue } from "../utils/config.js";
|
|
9
9
|
import { getTx, saveTx, updateTx } from "../utils/tx-store.js";
|
|
10
10
|
function isUrl(s) {
|
|
11
11
|
return s.startsWith("http://") || s.startsWith("https://");
|
|
@@ -32,16 +32,42 @@ function printPart(part, agentSlug) {
|
|
|
32
32
|
const cart = parseCartMandate(part);
|
|
33
33
|
if (cart) {
|
|
34
34
|
const total = cart.contents.payment_request.details.total;
|
|
35
|
+
const slug = agentSlug ?? "unknown";
|
|
35
36
|
const txId = saveTx({
|
|
36
37
|
cartMandate: cart,
|
|
37
|
-
agentSlug:
|
|
38
|
+
agentSlug: slug,
|
|
38
39
|
});
|
|
39
40
|
console.log(chalk.cyan(`\n[CartMandate] ${txId}`));
|
|
40
41
|
for (const item of cart.contents.payment_request.details.displayItems) {
|
|
41
42
|
console.log(` ${item.label}: ${item.amount.currency} ${item.amount.value.toFixed(2)}`);
|
|
42
43
|
}
|
|
43
44
|
console.log(chalk.bold(` Total: ${total.amount.currency} ${total.amount.value.toFixed(2)}`));
|
|
44
|
-
|
|
45
|
+
// Check locally-cached intents for auto-checkout hints
|
|
46
|
+
const totalCents = Math.round(total.amount.value * 100);
|
|
47
|
+
const cartCurrency = total.amount.currency.toLowerCase();
|
|
48
|
+
const now = new Date();
|
|
49
|
+
const matchingIntents = getCachedIntents().filter((intent) => {
|
|
50
|
+
if (intent.status !== "active")
|
|
51
|
+
return false;
|
|
52
|
+
if (intent.expires_at && new Date(intent.expires_at) <= now)
|
|
53
|
+
return false;
|
|
54
|
+
if (!intent.agent_slugs.includes(slug))
|
|
55
|
+
return false;
|
|
56
|
+
if (totalCents > intent.max_amount_cents)
|
|
57
|
+
return false;
|
|
58
|
+
if (intent.currency.toLowerCase() !== cartCurrency)
|
|
59
|
+
return false;
|
|
60
|
+
return true;
|
|
61
|
+
});
|
|
62
|
+
if (matchingIntents.length > 0) {
|
|
63
|
+
console.log(chalk.green(`\n Auto-checkout available:`));
|
|
64
|
+
for (const intent of matchingIntents) {
|
|
65
|
+
console.log(chalk.green(` w42 cart checkout ${txId} --intent ${intent.nick}`));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(chalk.dim(`\nTo pay: w42 cart sign ${txId}`));
|
|
70
|
+
}
|
|
45
71
|
return;
|
|
46
72
|
}
|
|
47
73
|
}
|
|
@@ -103,12 +129,10 @@ export const sendCommand = new Command("send")
|
|
|
103
129
|
.option("--new", "Start a new conversation (clears saved context)")
|
|
104
130
|
.option("--context <id>", "Use a specific context ID")
|
|
105
131
|
.option("--task-id <id>", "Reply to a specific task (e.g. one in input-required state)")
|
|
106
|
-
.option("--pay <tx_id>", "Attach PaymentMandate from a transaction (use tx ID from w42
|
|
132
|
+
.option("--pay <tx_id>", "Attach PaymentMandate from a transaction (use tx ID from w42 cart list)")
|
|
107
133
|
.action(async (rawAgent, userMessage, opts) => {
|
|
108
|
-
//
|
|
109
|
-
const agent = rawAgent
|
|
110
|
-
? rawAgent.replace("/", "~")
|
|
111
|
-
: rawAgent;
|
|
134
|
+
// Agent slug can be provided directly (no conversion needed)
|
|
135
|
+
const agent = rawAgent;
|
|
112
136
|
const config = requireAuth();
|
|
113
137
|
let agentUrl;
|
|
114
138
|
let bearerToken;
|
|
@@ -135,6 +159,7 @@ export const sendCommand = new Command("send")
|
|
|
135
159
|
catch (err) {
|
|
136
160
|
spinner.fail("Failed to get auth token");
|
|
137
161
|
console.error(chalk.red(String(err)));
|
|
162
|
+
console.error(chalk.dim(hintForError(err)));
|
|
138
163
|
process.exit(1);
|
|
139
164
|
}
|
|
140
165
|
}
|
|
@@ -165,6 +190,7 @@ export const sendCommand = new Command("send")
|
|
|
165
190
|
catch (err) {
|
|
166
191
|
spinner.fail(`Failed to authenticate with ${agent}`);
|
|
167
192
|
console.error(chalk.red(String(err)));
|
|
193
|
+
console.error(chalk.dim(hintForError(err)));
|
|
168
194
|
process.exit(1);
|
|
169
195
|
}
|
|
170
196
|
}
|
|
@@ -218,15 +244,15 @@ export const sendCommand = new Command("send")
|
|
|
218
244
|
if (opts.pay) {
|
|
219
245
|
const tx = getTx(opts.pay);
|
|
220
246
|
if (!tx) {
|
|
221
|
-
console.error(chalk.red(`Transaction ${opts.pay} not found. Run: w42
|
|
247
|
+
console.error(chalk.red(`Transaction ${opts.pay} not found. Run: w42 cart list`));
|
|
222
248
|
process.exit(1);
|
|
223
249
|
}
|
|
224
250
|
if (tx.status === "cart_received") {
|
|
225
|
-
console.error(chalk.red(`Session not created yet. Run: w42
|
|
251
|
+
console.error(chalk.red(`Session not created yet. Run: w42 cart sign ${opts.pay}`));
|
|
226
252
|
process.exit(1);
|
|
227
253
|
}
|
|
228
254
|
if (tx.status === "session_created") {
|
|
229
|
-
console.error(chalk.red(`Payment not yet approved. Run: w42
|
|
255
|
+
console.error(chalk.red(`Payment not yet approved. Run: w42 cart poll ${opts.pay}`));
|
|
230
256
|
process.exit(1);
|
|
231
257
|
}
|
|
232
258
|
if (!tx.paymentMandate) {
|
package/dist/commands/serve.js
CHANGED
|
@@ -135,6 +135,7 @@ export const serveCommand = new Command("serve")
|
|
|
135
135
|
}
|
|
136
136
|
catch {
|
|
137
137
|
console.error(chalk.red("Failed to parse agent-card.json."));
|
|
138
|
+
console.error(chalk.dim("Validate the file with a JSON linter — it may have a syntax error."));
|
|
138
139
|
process.exit(1);
|
|
139
140
|
}
|
|
140
141
|
const agentName = cardData.name ?? "Untitled Agent";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { authCommand } from "./commands/auth.js";
|
|
4
|
-
import {
|
|
4
|
+
import { cartCommand } from "./commands/cart.js";
|
|
5
|
+
import { intentCommand } from "./commands/intent.js";
|
|
5
6
|
import { registerCommand } from "./commands/register.js";
|
|
6
7
|
import { searchCommand } from "./commands/search.js";
|
|
7
8
|
import { sendCommand } from "./commands/send.js";
|
|
@@ -22,7 +23,8 @@ program
|
|
|
22
23
|
}
|
|
23
24
|
});
|
|
24
25
|
program.addCommand(authCommand);
|
|
25
|
-
program.addCommand(
|
|
26
|
+
program.addCommand(cartCommand);
|
|
27
|
+
program.addCommand(intentCommand);
|
|
26
28
|
program.addCommand(registerCommand);
|
|
27
29
|
program.addCommand(searchCommand);
|
|
28
30
|
program.addCommand(sendCommand);
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export declare function apiRequest(path: string, options?: RequestInit): Promise<Response>;
|
|
2
|
+
export declare class ApiError extends Error {
|
|
3
|
+
readonly code: string | undefined;
|
|
4
|
+
readonly status: number;
|
|
5
|
+
constructor(message: string, code: string | undefined, status: number);
|
|
6
|
+
}
|
|
2
7
|
export declare function apiGet<T>(path: string): Promise<T>;
|
|
3
8
|
export declare function apiPost<T>(path: string, data: unknown): Promise<T>;
|
|
9
|
+
/** Returns a short contextual hint based on the error type. */
|
|
10
|
+
export declare function hintForError(err: unknown): string;
|
|
4
11
|
export declare function apiDelete<T>(path: string): Promise<T>;
|
|
5
12
|
export declare function apiFormData<T>(path: string, formData: FormData): Promise<T>;
|
package/dist/utils/api.js
CHANGED
|
@@ -14,11 +14,21 @@ export async function apiRequest(path, options = {}) {
|
|
|
14
14
|
headers,
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
+
export class ApiError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
status;
|
|
20
|
+
constructor(message, code, status) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.status = status;
|
|
24
|
+
this.name = "ApiError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
17
27
|
export async function apiGet(path) {
|
|
18
28
|
const res = await apiRequest(path);
|
|
19
29
|
if (!res.ok) {
|
|
20
30
|
const body = await res.json().catch(() => ({}));
|
|
21
|
-
throw new
|
|
31
|
+
throw new ApiError(body.error || `API error: ${res.status}`, body.code, res.status);
|
|
22
32
|
}
|
|
23
33
|
return res.json();
|
|
24
34
|
}
|
|
@@ -29,10 +39,22 @@ export async function apiPost(path, data) {
|
|
|
29
39
|
});
|
|
30
40
|
if (!res.ok) {
|
|
31
41
|
const body = await res.json().catch(() => ({}));
|
|
32
|
-
throw new
|
|
42
|
+
throw new ApiError(body.error || `API error: ${res.status}`, body.code, res.status);
|
|
33
43
|
}
|
|
34
44
|
return res.json();
|
|
35
45
|
}
|
|
46
|
+
/** Returns a short contextual hint based on the error type. */
|
|
47
|
+
export function hintForError(err) {
|
|
48
|
+
if (err instanceof ApiError) {
|
|
49
|
+
if (err.status === 401)
|
|
50
|
+
return "Run `w42 auth login` to reauthenticate.";
|
|
51
|
+
if (err.status === 403)
|
|
52
|
+
return "You don't have permission to do this.";
|
|
53
|
+
if (err.status >= 500)
|
|
54
|
+
return "Server error — try again later.";
|
|
55
|
+
}
|
|
56
|
+
return "Check your network connection and try again.";
|
|
57
|
+
}
|
|
36
58
|
export async function apiDelete(path) {
|
|
37
59
|
const res = await apiRequest(path, { method: "DELETE" });
|
|
38
60
|
if (!res.ok) {
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -22,6 +22,18 @@ export declare function isAuthenticated(): boolean;
|
|
|
22
22
|
export declare function requireAuth(): W42Config;
|
|
23
23
|
export declare function setConfigValue(key: string, value: string): void;
|
|
24
24
|
export declare function getConfigValue(key: string): string | undefined;
|
|
25
|
+
export interface CachedIntent {
|
|
26
|
+
nick: string;
|
|
27
|
+
agent_slugs: string[];
|
|
28
|
+
max_amount_cents: number;
|
|
29
|
+
currency: string;
|
|
30
|
+
status: string;
|
|
31
|
+
expires_at?: string | null;
|
|
32
|
+
spent_cents: number;
|
|
33
|
+
budget_cents?: number | null;
|
|
34
|
+
}
|
|
35
|
+
/** Return all locally-cached intents that are still plausibly active. */
|
|
36
|
+
export declare function getCachedIntents(): CachedIntent[];
|
|
25
37
|
export declare function isTelemetryEnabled(): boolean;
|
|
26
38
|
export declare function setTelemetry(enabled: boolean): void;
|
|
27
39
|
export {};
|
package/dist/utils/config.js
CHANGED
|
@@ -59,6 +59,27 @@ export function getConfigValue(key) {
|
|
|
59
59
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
60
|
return config.get(key);
|
|
61
61
|
}
|
|
62
|
+
/** Return all locally-cached intents that are still plausibly active. */
|
|
63
|
+
export function getCachedIntents() {
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
|
+
const intentsMap = config.get("intents");
|
|
66
|
+
if (!intentsMap)
|
|
67
|
+
return [];
|
|
68
|
+
const results = [];
|
|
69
|
+
for (const raw of Object.values(intentsMap)) {
|
|
70
|
+
if (!raw)
|
|
71
|
+
continue;
|
|
72
|
+
try {
|
|
73
|
+
const intent = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
74
|
+
if (intent && typeof intent === "object")
|
|
75
|
+
results.push(intent);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// skip corrupted entry
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
62
83
|
export function isTelemetryEnabled() {
|
|
63
84
|
return config.get("telemetry") !== false;
|
|
64
85
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.22";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.1.
|
|
1
|
+
export const CLI_VERSION = "0.1.22";
|
package/package.json
CHANGED
package/dist/commands/pay.d.ts
DELETED