@fluxa-pay/planner 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/planner.mjs +39 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluxa-pay/planner",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "FluxA Planner CLI — recommend the right models/APIs/skills for a task, and run paid calls via x402.",
5
5
  "type": "module",
6
6
  "bin": {
package/planner.mjs CHANGED
@@ -301,6 +301,36 @@ async function rawCall(url, method, data, extraHeaders = {}, auth = false) {
301
301
  return { status: res.status, text };
302
302
  }
303
303
 
304
+ // The proxy's API/MCP lanes bill the caller identified by the X-Agent-ID header
305
+ // and reject every non-discovery request without it. Default to the agent_id
306
+ // (UUID) from `fluxa-wallet status`; configs with require_agent_auth need a JWT
307
+ // instead, so agentCall() retries with a freshly minted wallet JWT on the
308
+ // "valid JWT" 401. Both values are cached for the process.
309
+ let _agentId = null;
310
+ async function agentId() {
311
+ if (_agentId) return _agentId;
312
+ const s = unwrap(await walletJson(["status"]).catch(() => ({})));
313
+ const id = s.agent_id || s.agentId || s.id;
314
+ if (!id) die("could not read agent_id from `fluxa-wallet status` — register once with `fluxa-wallet init`.");
315
+ return (_agentId = id);
316
+ }
317
+ let _walletJwt = null;
318
+ async function walletJwt() {
319
+ if (_walletJwt) return _walletJwt;
320
+ const j = unwrap(await walletJson(["refreshJWT"]).catch(() => ({})));
321
+ const jwt = j.jwt || j.token;
322
+ if (!jwt) die("could not mint a wallet JWT (`fluxa-wallet refreshJWT`).");
323
+ return (_walletJwt = jwt);
324
+ }
325
+ // rawCall + X-Agent-ID, with the UUID→JWT fallback for require_agent_auth configs.
326
+ async function agentCall(url, method, data, extra = {}) {
327
+ const r = await rawCall(url, method, data, { ...extra, "X-Agent-ID": await agentId() });
328
+ if (r.status === 401 && /valid JWT/i.test(r.text)) {
329
+ return rawCall(url, method, data, { ...extra, "X-Agent-ID": await walletJwt() });
330
+ }
331
+ return r;
332
+ }
333
+
304
334
  async function ensureMandate(currency, budget, seconds, url) {
305
335
  const cfg = loadConfig();
306
336
  // only reuse a signed mandate of the SAME currency with budget left
@@ -339,9 +369,12 @@ function descriptorEndpoints(text) {
339
369
  }
340
370
 
341
371
  async function cmdCall(url, opts = {}) {
342
- if (!url || !/^https?:\/\//.test(url)) {
343
- return die("usage: planner call <url> [--endpoint <path>] [--data '<json>'] [--pay credits|usdc] [--method GET|POST] [--budget <units>] [--raw]");
372
+ if (!url) {
373
+ return die("usage: planner call <url|slug> [--endpoint <path>] [--data '<json>'] [--pay credits|usdc] [--method GET|POST] [--budget <units>] [--raw]");
344
374
  }
375
+ // accept a bare AgentMarket slug (e.g. `web-search-api`) → the proxy API base.
376
+ // full http(s) URLs are used as-is.
377
+ if (!/^https?:\/\//.test(url)) url = `${PROXY}/api/${encodeURIComponent(url)}`;
345
378
  const pay = (opts.pay || loadConfig().pay || "credits").toLowerCase();
346
379
  const currency = pay === "usdc" ? "USDC" : "FLUXA_MONETIZE_CREDITS";
347
380
  const budget = opts.budget ? Number(opts.budget) : (pay === "usdc" ? 5000000 : 500); // ~$5 default
@@ -357,7 +390,7 @@ async function cmdCall(url, opts = {}) {
357
390
  // real paid sub-endpoints. Resolve it (unless --raw) so the agent doesn't have
358
391
  // to read the catalog itself. We never spend without a supplied body.
359
392
  if (!opts.raw) {
360
- const probe = await rawCall(url, "GET");
393
+ const probe = await agentCall(url, "GET");
361
394
  const desc = probe.status === 200 ? descriptorEndpoints(probe.text) : null;
362
395
  if (desc) {
363
396
  const eps = desc.endpoints.filter((e) => e.enabled !== false);
@@ -388,7 +421,7 @@ async function cmdCall(url, opts = {}) {
388
421
  }
389
422
 
390
423
  // call the resolved endpoint — pay if it answers 402
391
- let r = await rawCall(targetUrl, method, data);
424
+ let r = await agentCall(targetUrl, method, data);
392
425
  if (r.status !== 402) {
393
426
  if (r.status >= 400) die(`${r.status} — ${r.text.slice(0, 300)}`);
394
427
  return console.log(r.text); // raw body, no post-processing
@@ -402,8 +435,8 @@ async function cmdCall(url, opts = {}) {
402
435
  const xPayment = paid.xPaymentB64 || paid.xPayment;
403
436
  if (!xPayment) die("x402-v3 did not return a payment token (xPaymentB64).");
404
437
 
405
- // 3. retry with the payment token
406
- r = await rawCall(url, method, opts.data, { "X-Payment": xPayment });
438
+ // 3. retry with the payment token (against the resolved endpoint, with X-Agent-ID)
439
+ r = await agentCall(targetUrl, method, opts.data, { "X-Payment": xPayment });
407
440
  if (r.status >= 400) die(`paid retry failed: ${r.status} — ${r.text.slice(0, 300)}`);
408
441
  console.log(r.text); // raw body, no post-processing
409
442
  }