@getbags/cash 0.3.1 → 0.4.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 +10 -0
- package/dist/api.d.ts +7 -1
- package/dist/api.js +13 -2
- package/dist/index.js +7 -4
- package/dist/mcp-server.d.ts +3 -1
- package/dist/mcp-server.js +23 -2
- package/dist/parse.d.ts +3 -0
- package/dist/parse.js +33 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,3 +117,13 @@ Public `search` and `discover` work without credentials. Paid calls still requir
|
|
|
117
117
|
`bags result <id> --field response.items --offset 0 --limit 20` and the `wallet_result` MCP tool read retained output without another provider call or payment. Results are private to the wallet and expire after 24 hours. Authentication fields are redacted; provider asset URLs may expire earlier. The fetch API and MCP accept `result_mode: "summary"`.
|
|
118
118
|
|
|
119
119
|
MPP supports bounded one-shot `tempo:charge` with explicit chain/currency checks. Hosted session opening is disabled until durable close can be confirmed. `bags sessions list` shows existing session reservations. Hosted `sessions close` currently reports unsupported cleanup and keeps the hold; it does not report the channel closed. Existing channels and reservations remain owned when cleanup fails.
|
|
120
|
+
|
|
121
|
+
### Task fit, accepted jobs and asset copies (0.4.0)
|
|
122
|
+
|
|
123
|
+
`bags search "email from LinkedIn" --requirements '{"inputs":{"depth":"full"},"outputs":["email"],"unit":"contact"}' --format json` returns advertised capability fit, health and conservative cost comparisons. Unknown controls, contracts, units or funding remain unknown. Prices are indicative: check the exact method/body before paying.
|
|
124
|
+
|
|
125
|
+
`bags result <id> --refresh` explicitly makes one unpaid GET to an advertised async status URL. It never repeats the initial request, pays a status challenge or forwards your wallet credentials. Providers without a safe polling contract remain explicit; terminal costs are separately labelled when provider-reported.
|
|
126
|
+
|
|
127
|
+
`bags result <id> --retain-asset '<exact-advertised-url>'` explicitly requests a private copy. At most two assets per result, 2 MiB each; copies expire with the retained result. The response includes measured size/SHA256 and an authenticated download URL. Use this option separately from field/page/refresh flags. Plain `bags result <id>` remains a stored read.
|
|
128
|
+
|
|
129
|
+
Registration returns `reusable_skill.markdown`, a small origin/task pointer that refreshes discovery and contracts on each use. It includes no credential or spending authority.
|
package/dist/api.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ export type DiscoverEndpoint = {
|
|
|
28
28
|
source?: 'bags' | 'bazaar' | 'registered';
|
|
29
29
|
input_schema?: unknown;
|
|
30
30
|
output_schema?: unknown;
|
|
31
|
+
pricing?: {
|
|
32
|
+
kind?: string;
|
|
33
|
+
indicative?: boolean;
|
|
34
|
+
};
|
|
31
35
|
score?: number;
|
|
32
36
|
signals?: {
|
|
33
37
|
text_match: number;
|
|
@@ -123,7 +127,7 @@ export declare function redeem(opts: {
|
|
|
123
127
|
}): Promise<RedeemResponse>;
|
|
124
128
|
export declare function getBalance(token: string): Promise<BalanceResponse>;
|
|
125
129
|
export declare function discover(token: string, query?: string): Promise<DiscoverResponse>;
|
|
126
|
-
export declare function search(token: string, query: string): Promise<DiscoverResponse>;
|
|
130
|
+
export declare function search(token: string, query: string, requirements?: Record<string, unknown>): Promise<DiscoverResponse>;
|
|
127
131
|
export declare function checkEndpoint(token: string, input: {
|
|
128
132
|
url: string;
|
|
129
133
|
method?: string;
|
|
@@ -188,4 +192,6 @@ export declare function getResult(token: string, input: {
|
|
|
188
192
|
field?: string;
|
|
189
193
|
offset?: number;
|
|
190
194
|
limit?: number;
|
|
195
|
+
refresh?: boolean;
|
|
196
|
+
retain_asset_url?: string;
|
|
191
197
|
}): Promise<Record<string, unknown>>;
|
package/dist/api.js
CHANGED
|
@@ -98,8 +98,8 @@ export async function discover(token, query) {
|
|
|
98
98
|
query: data.query,
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
|
-
export async function search(token, query) {
|
|
102
|
-
const path = `/api/wallet/search?q=${encodeURIComponent(query)}`;
|
|
101
|
+
export async function search(token, query, requirements) {
|
|
102
|
+
const path = `/api/wallet/search?q=${encodeURIComponent(query)}${requirements ? '&requirements=' + encodeURIComponent(JSON.stringify(requirements)) : ''}`;
|
|
103
103
|
const body = await apiRequest(path, { token });
|
|
104
104
|
const data = unwrapPayload(body);
|
|
105
105
|
return {
|
|
@@ -245,7 +245,18 @@ export async function registerOrigin(token, origin, opts = {}) {
|
|
|
245
245
|
}
|
|
246
246
|
/** Read an owned result; never starts another provider call or payment. */
|
|
247
247
|
export async function getResult(token, input) {
|
|
248
|
+
if (input.retain_asset_url) {
|
|
249
|
+
if (input.refresh || input.field || input.offset !== undefined || input.limit !== undefined)
|
|
250
|
+
throw new Error('Asset retention cannot be combined with read/refresh options');
|
|
251
|
+
return unwrapPayload(await apiRequest('/api/wallet/results', {
|
|
252
|
+
method: 'POST',
|
|
253
|
+
token,
|
|
254
|
+
body: { id: input.id, retain_asset_url: input.retain_asset_url },
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
248
257
|
const query = new URLSearchParams({ id: input.id });
|
|
258
|
+
if (input.refresh)
|
|
259
|
+
query.set('refresh', 'true');
|
|
249
260
|
if (input.field)
|
|
250
261
|
query.set('field', input.field);
|
|
251
262
|
if (input.offset !== undefined)
|
package/dist/index.js
CHANGED
|
@@ -22,9 +22,12 @@ function printDiscoverTable(endpoints) {
|
|
|
22
22
|
const rows = endpoints.map((ep) => ({
|
|
23
23
|
url: ep.url,
|
|
24
24
|
method: ep.method,
|
|
25
|
-
price:
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
price: ep.pricing?.kind === 'unknown' ||
|
|
26
|
+
(ep.source === 'registered' && Number(ep.price_usd) === 0 && ep.pricing?.kind !== 'free')
|
|
27
|
+
? 'unknown (check)'
|
|
28
|
+
: typeof ep.price_usd === 'number'
|
|
29
|
+
? `$${ep.price_usd.toFixed(6).replace(/(\.\d{2,}?)0+$/, '$1')}`
|
|
30
|
+
: String(ep.price_usd),
|
|
28
31
|
merchant: ep.merchant_name ?? '',
|
|
29
32
|
source: ep.source ?? 'bags',
|
|
30
33
|
}));
|
|
@@ -90,7 +93,7 @@ async function main() {
|
|
|
90
93
|
return;
|
|
91
94
|
}
|
|
92
95
|
case 'search': {
|
|
93
|
-
const result = await search(loadCredentials()?.token ?? '', parsed.query);
|
|
96
|
+
const result = await search(loadCredentials()?.token ?? '', parsed.query, parsed.requirements);
|
|
94
97
|
if (parsed.format === 'json') {
|
|
95
98
|
printJson(result);
|
|
96
99
|
}
|
package/dist/mcp-server.d.ts
CHANGED
|
@@ -7,10 +7,12 @@ export type WalletMcpApi = {
|
|
|
7
7
|
field?: string;
|
|
8
8
|
offset?: number;
|
|
9
9
|
limit?: number;
|
|
10
|
+
refresh?: boolean;
|
|
11
|
+
retain_asset_url?: string;
|
|
10
12
|
}) => Promise<Record<string, unknown>>;
|
|
11
13
|
getBalance: (token: string) => Promise<BalanceResponse>;
|
|
12
14
|
discover: (token: string, query?: string) => Promise<DiscoverResponse>;
|
|
13
|
-
search: (token: string, query: string) => Promise<DiscoverResponse>;
|
|
15
|
+
search: (token: string, query: string, requirements?: Record<string, unknown>) => Promise<DiscoverResponse>;
|
|
14
16
|
checkEndpoint: (token: string, input: {
|
|
15
17
|
url: string;
|
|
16
18
|
method?: string;
|
package/dist/mcp-server.js
CHANGED
|
@@ -117,9 +117,20 @@ export function createWalletMcpServer(deps = {}) {
|
|
|
117
117
|
description: 'Natural-language search for x402 endpoints. BAGS catalog first, best-effort ecosystem (Bazaar) second. Returns price, description, schemas when available.',
|
|
118
118
|
inputSchema: {
|
|
119
119
|
query: z.string().min(1).describe('Natural-language or keyword query'),
|
|
120
|
+
requirements: z
|
|
121
|
+
.object({
|
|
122
|
+
inputs: z
|
|
123
|
+
.record(z.string().min(1).max(100), z.union([z.string().max(256), z.number(), z.boolean(), z.null()]))
|
|
124
|
+
.refine((v) => Object.keys(v).length <= 32, 'At most 32 input controls')
|
|
125
|
+
.optional(),
|
|
126
|
+
outputs: z.array(z.string().min(1).max(100)).max(16).optional(),
|
|
127
|
+
unit: z.string().min(1).max(50).optional(),
|
|
128
|
+
})
|
|
129
|
+
.strict()
|
|
130
|
+
.optional(),
|
|
120
131
|
},
|
|
121
132
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
122
|
-
}, async (args) => textContent(await api.search(resolveToken(deps) ?? '', String(args.query ?? '').trim())));
|
|
133
|
+
}, async (args) => textContent(await api.search(resolveToken(deps) ?? '', String(args.query ?? '').trim(), args.requirements)));
|
|
123
134
|
server.registerTool('wallet_check_endpoint', {
|
|
124
135
|
title: 'Check endpoint quote',
|
|
125
136
|
description: 'Probe an x402 URL for schema + exact price quote without paying (AgentCash check parity). Use before wallet_fetch.',
|
|
@@ -139,11 +150,21 @@ export function createWalletMcpServer(deps = {}) {
|
|
|
139
150
|
description: 'Retrieve full output, fields or array pages from a previous wallet_fetch result for 24 hours. Does not execute or charge again. Provider asset URLs may expire earlier.',
|
|
140
151
|
inputSchema: {
|
|
141
152
|
id: z.string().uuid(),
|
|
153
|
+
refresh: z
|
|
154
|
+
.boolean()
|
|
155
|
+
.optional()
|
|
156
|
+
.describe('Make one unpaid GET to the advertised async status URL; never authorizes payment'),
|
|
157
|
+
retain_asset_url: z
|
|
158
|
+
.string()
|
|
159
|
+
.url()
|
|
160
|
+
.max(8192)
|
|
161
|
+
.optional()
|
|
162
|
+
.describe('Explicitly copy an advertised asset privately, at most two 2 MiB assets for the result lifetime'),
|
|
142
163
|
field: z.string().max(200).optional(),
|
|
143
164
|
offset: z.number().int().nonnegative().optional(),
|
|
144
165
|
limit: z.number().int().min(1).max(100).optional(),
|
|
145
166
|
},
|
|
146
|
-
annotations: { readOnlyHint:
|
|
167
|
+
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
147
168
|
}, async (args) => withToken(async (token) => textContent(await api.getResult(token, args))));
|
|
148
169
|
server.registerTool('wallet_fetch', {
|
|
149
170
|
title: 'Wallet fetch (pay + proxy)',
|
package/dist/parse.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export type ParsedArgs = {
|
|
|
17
17
|
field?: string;
|
|
18
18
|
offset?: number;
|
|
19
19
|
limit?: number;
|
|
20
|
+
refresh?: boolean;
|
|
21
|
+
retain_asset_url?: string;
|
|
20
22
|
} | {
|
|
21
23
|
command: 'discover';
|
|
22
24
|
format: 'json' | 'pretty';
|
|
@@ -78,6 +80,7 @@ export type ParsedArgs = {
|
|
|
78
80
|
} | {
|
|
79
81
|
command: 'search';
|
|
80
82
|
query: string;
|
|
83
|
+
requirements?: Record<string, unknown>;
|
|
81
84
|
format: 'json' | 'pretty';
|
|
82
85
|
} | {
|
|
83
86
|
command: 'register';
|
package/dist/parse.js
CHANGED
|
@@ -7,7 +7,7 @@ const VALID_CLIENTS = ['cursor', 'claude', 'codex'];
|
|
|
7
7
|
const COMMAND_USAGE = {
|
|
8
8
|
redeem: 'usage: bags redeem <code> | bags redeem --test',
|
|
9
9
|
balance: 'usage: bags balance',
|
|
10
|
-
result: 'usage: bags result <id> [--field path] [--offset N] [--limit N]',
|
|
10
|
+
result: 'usage: bags result <id> [--field path] [--offset N] [--limit N] [--refresh] [--retain-asset URL]',
|
|
11
11
|
discover: 'usage: bags discover [query] [--format json|pretty]',
|
|
12
12
|
search: 'usage: bags search <query> [--format json|pretty]',
|
|
13
13
|
check: 'usage: bags check <url> [flags]',
|
|
@@ -449,9 +449,18 @@ export function parseArgs(argv) {
|
|
|
449
449
|
id,
|
|
450
450
|
};
|
|
451
451
|
for (let i = 1; i < rest.length; i += 2) {
|
|
452
|
+
if (rest[i] === '--refresh') {
|
|
453
|
+
out.refresh = true;
|
|
454
|
+
i--;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
452
457
|
const flag = rest[i], value = rest[i + 1];
|
|
453
|
-
if (!value || !['--field', '--offset', '--limit'].includes(flag))
|
|
458
|
+
if (!value || !['--field', '--offset', '--limit', '--retain-asset'].includes(flag))
|
|
454
459
|
return { command: 'error', message: COMMAND_USAGE.result };
|
|
460
|
+
if (flag === '--retain-asset') {
|
|
461
|
+
out.retain_asset_url = value;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
455
464
|
if (flag === '--field')
|
|
456
465
|
out.field = value;
|
|
457
466
|
else {
|
|
@@ -466,6 +475,9 @@ export function parseArgs(argv) {
|
|
|
466
475
|
out.limit = n;
|
|
467
476
|
}
|
|
468
477
|
}
|
|
478
|
+
if (out.retain_asset_url &&
|
|
479
|
+
(out.refresh || out.field || out.offset !== undefined || out.limit !== undefined))
|
|
480
|
+
return { command: 'error', message: '--retain-asset cannot be combined with read/refresh flags' };
|
|
469
481
|
return out;
|
|
470
482
|
}
|
|
471
483
|
case 'transactions': {
|
|
@@ -647,7 +659,24 @@ export function parseArgs(argv) {
|
|
|
647
659
|
if ('error' in parsed)
|
|
648
660
|
return { command: 'error', message: parsed.error };
|
|
649
661
|
const queryWords = [];
|
|
650
|
-
|
|
662
|
+
let requirements;
|
|
663
|
+
for (let i = 0; i < parsed.rest.length; i++) {
|
|
664
|
+
const arg = parsed.rest[i];
|
|
665
|
+
if (arg === '--requirements') {
|
|
666
|
+
const value = parsed.rest[++i];
|
|
667
|
+
try {
|
|
668
|
+
if (!value || value.length > 4096)
|
|
669
|
+
throw new Error();
|
|
670
|
+
const obj = JSON.parse(value);
|
|
671
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj))
|
|
672
|
+
throw new Error();
|
|
673
|
+
requirements = obj;
|
|
674
|
+
}
|
|
675
|
+
catch {
|
|
676
|
+
return { command: 'error', message: '--requirements must be a bounded JSON object' };
|
|
677
|
+
}
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
651
680
|
if (arg.startsWith('-'))
|
|
652
681
|
return { command: 'error', message: `unknown flag: ${arg}` };
|
|
653
682
|
queryWords.push(arg);
|
|
@@ -655,7 +684,7 @@ export function parseArgs(argv) {
|
|
|
655
684
|
const query = queryWords.join(' ').trim();
|
|
656
685
|
if (!query)
|
|
657
686
|
return { command: 'error', message: 'usage: bags search <query>' };
|
|
658
|
-
return { command: 'search', query, format: parsed.format };
|
|
687
|
+
return { command: 'search', query, format: parsed.format, ...(requirements ? { requirements } : {}) };
|
|
659
688
|
}
|
|
660
689
|
case 'register': {
|
|
661
690
|
const origin = rest[0];
|