@agentgrant.cash/cli 1.4.6 → 1.5.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/.env.example +1 -1
- package/dist/cli/commands/agent.js +49 -0
- package/dist/lib/agent-client.d.ts +55 -0
- package/dist/lib/agent-client.js +35 -0
- package/dist/lib/config.js +1 -1
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# ── Money side (Perfolio backend — gold, portfolio, cash) ── [develop defaults]
|
|
15
15
|
GRANTCASH_API_URL=https://api-dev.perfolio.ai/api
|
|
16
16
|
GRANTCASH_FIAT_URL=https://api-dev-fiat.perfolio.ai/api
|
|
17
|
-
GRANTCASH_APP_URL=https://
|
|
17
|
+
GRANTCASH_APP_URL=https://agentgrant.cash
|
|
18
18
|
|
|
19
19
|
# ── Agent side (Agent-mode backend — pay-per-use services, x402) ── [develop]
|
|
20
20
|
GRANTCASH_AGENT_URL=https://agent-mode-backend-develop.up.railway.app
|
|
@@ -72,6 +72,55 @@ export function registerAgent(program) {
|
|
|
72
72
|
});
|
|
73
73
|
emit(ctx, { result: data }, () => ui.green("✓ Done."));
|
|
74
74
|
});
|
|
75
|
+
program
|
|
76
|
+
.command("do-task <query>")
|
|
77
|
+
.description("Run any pay-per-use task from a plain-language request (the backend picks + pays the vendor)")
|
|
78
|
+
.option("-i, --input <json>", "structured input for the task, as JSON")
|
|
79
|
+
.option("--confirm", "confirm a spend the backend flagged as needing approval")
|
|
80
|
+
.action(async (query, opts, cmd) => {
|
|
81
|
+
const ctx = buildContext(cmd);
|
|
82
|
+
if (!ctx.agent.connected)
|
|
83
|
+
throw new NotConnectedError("agent");
|
|
84
|
+
let input;
|
|
85
|
+
if (opts.input) {
|
|
86
|
+
try {
|
|
87
|
+
input = JSON.parse(String(opts.input));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new Error("--input must be valid JSON.");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const data = await ctx.agent.doTask({
|
|
94
|
+
query,
|
|
95
|
+
...(input !== undefined ? { input } : {}),
|
|
96
|
+
...(opts.confirm ? { confirm: true } : {}),
|
|
97
|
+
});
|
|
98
|
+
emit(ctx, { result: data }, () => `${ui.title("Task")} ${ui.dim("(use --json for the full result)")}`);
|
|
99
|
+
});
|
|
100
|
+
program
|
|
101
|
+
.command("best-vendor <capability>")
|
|
102
|
+
.description("See the ranked vendors for a capability, top pick first (no charge)")
|
|
103
|
+
.option("--include-untested", "also surface unverified vendors, flagged untested")
|
|
104
|
+
.action(async (capability, opts, cmd) => {
|
|
105
|
+
const ctx = buildContext(cmd);
|
|
106
|
+
if (!ctx.agent.connected)
|
|
107
|
+
throw new NotConnectedError("agent");
|
|
108
|
+
const data = await ctx.agent.bestVendor({
|
|
109
|
+
capability,
|
|
110
|
+
...(opts.includeUntested ? { includeUntested: true } : {}),
|
|
111
|
+
});
|
|
112
|
+
emit(ctx, { result: data }, () => `${ui.title("Vendors")} ${ui.dim("(use --json for the ranking)")}`);
|
|
113
|
+
});
|
|
114
|
+
program
|
|
115
|
+
.command("find-capability <query>")
|
|
116
|
+
.description("Match a plain-language request to the capabilities the agent can run (no charge)")
|
|
117
|
+
.action(async (query, _opts, cmd) => {
|
|
118
|
+
const ctx = buildContext(cmd);
|
|
119
|
+
if (!ctx.agent.connected)
|
|
120
|
+
throw new NotConnectedError("agent");
|
|
121
|
+
const data = await ctx.agent.findCapability(query);
|
|
122
|
+
emit(ctx, { result: data }, () => `${ui.title("Capabilities")} ${ui.dim("(use --json for the matches)")}`);
|
|
123
|
+
});
|
|
75
124
|
program
|
|
76
125
|
.command("discover <origin>")
|
|
77
126
|
.alias("add")
|
|
@@ -5,6 +5,35 @@ export interface AgentClientOpts {
|
|
|
5
5
|
auth?: TokenHolder;
|
|
6
6
|
fetchImpl?: typeof fetch;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* The backend's `NamedFunctionResult` (POST /do-task), returned VERBATIM. The
|
|
10
|
+
* client is a thin caller — it never re-implements selection/failover/refund;
|
|
11
|
+
* the money-safe engine owns those and encodes its outcome in `status`. A driving
|
|
12
|
+
* agent keys off `status` (`settlement_pending` = still confirming, don't re-run;
|
|
13
|
+
* `blocked` + `code` = terminal, stop). `data` shape varies by provider.
|
|
14
|
+
*/
|
|
15
|
+
export interface NamedFunctionResult {
|
|
16
|
+
status: "ok" | "needs_input" | "no_vendor" | "all_failed" | "settlement_pending" | "unknown_function" | "blocked";
|
|
17
|
+
[k: string]: unknown;
|
|
18
|
+
}
|
|
19
|
+
/** One ranked vendor for a capability (GET /agent/best-vendor). */
|
|
20
|
+
export interface VendorPick {
|
|
21
|
+
slug: string;
|
|
22
|
+
name: string;
|
|
23
|
+
priceMinor: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
method?: string;
|
|
26
|
+
asset?: string;
|
|
27
|
+
untested?: boolean;
|
|
28
|
+
[k: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
/** Ranked vendors + top pick for a capability (GET /agent/best-vendor). */
|
|
31
|
+
export interface VendorSelectionResult {
|
|
32
|
+
capability: string;
|
|
33
|
+
vendors: VendorPick[];
|
|
34
|
+
best: VendorPick | null;
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
8
37
|
export declare class AgentClient {
|
|
9
38
|
private base;
|
|
10
39
|
private auth?;
|
|
@@ -40,6 +69,19 @@ export declare class AgentClient {
|
|
|
40
69
|
tx(id: string): Promise<Record<string, unknown>>;
|
|
41
70
|
/** Exact POST /x402/pay contract for a catalog entry by slug (requestSchema + price). No spend. */
|
|
42
71
|
schema(slug: string): Promise<Record<string, unknown>>;
|
|
72
|
+
/**
|
|
73
|
+
* Ranked vendors for a capability (`GET /agent/best-vendor`). Reads only — no
|
|
74
|
+
* spend. `includeUntested` opts in 'probed' vendors ranked below every verified
|
|
75
|
+
* one and flagged `untested`; omit it to route only execution-verified vendors.
|
|
76
|
+
*/
|
|
77
|
+
bestVendor(b: {
|
|
78
|
+
capability: string;
|
|
79
|
+
includeUntested?: boolean;
|
|
80
|
+
}): Promise<VendorSelectionResult>;
|
|
81
|
+
/** Free-text capability match (`GET /agent/find-capability`). Reads only — no spend. */
|
|
82
|
+
findCapability(query: string): Promise<{
|
|
83
|
+
matches: unknown[];
|
|
84
|
+
}>;
|
|
43
85
|
/** Ingest any x402 origin's endpoints into the catalog so they become searchable/payable. */
|
|
44
86
|
discover(origin: string): Promise<Record<string, unknown>>;
|
|
45
87
|
/** Claim an invite / bonus credit by code. Mints or tops up the session's spendable funds. */
|
|
@@ -50,6 +92,19 @@ export declare class AgentClient {
|
|
|
50
92
|
code: string;
|
|
51
93
|
expiresAt: number;
|
|
52
94
|
}>;
|
|
95
|
+
/**
|
|
96
|
+
* One-shot natural-language task (`POST /do-task`): the backend resolves the
|
|
97
|
+
* query to the best vendor, validates input, pays under the session guardrails,
|
|
98
|
+
* and fails over — all server-side. Returns the `NamedFunctionResult` VERBATIM
|
|
99
|
+
* (including demote/refund/stop outcomes); the client never re-implements the
|
|
100
|
+
* engine. An `Idempotency-Key` guards the write path against a double-charge on
|
|
101
|
+
* retry; a transport error is tagged `recoverable` exactly like {@link fetchPay}.
|
|
102
|
+
*/
|
|
103
|
+
doTask(b: {
|
|
104
|
+
query: string;
|
|
105
|
+
input?: Record<string, unknown>;
|
|
106
|
+
confirm?: boolean;
|
|
107
|
+
}): Promise<NamedFunctionResult>;
|
|
53
108
|
/**
|
|
54
109
|
* Pay + run an endpoint via the session. Body matches `POST /x402/pay`'s
|
|
55
110
|
* schema: `method` is required (uppercased), `expectedPriceMinor` (the spend
|
package/dist/lib/agent-client.js
CHANGED
|
@@ -142,6 +142,22 @@ export class AgentClient {
|
|
|
142
142
|
schema(slug) {
|
|
143
143
|
return this.request("GET", `/marketplace/x402/${encodeURIComponent(slug)}`, { auth: true });
|
|
144
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Ranked vendors for a capability (`GET /agent/best-vendor`). Reads only — no
|
|
147
|
+
* spend. `includeUntested` opts in 'probed' vendors ranked below every verified
|
|
148
|
+
* one and flagged `untested`; omit it to route only execution-verified vendors.
|
|
149
|
+
*/
|
|
150
|
+
bestVendor(b) {
|
|
151
|
+
const p = new URLSearchParams({ capability: b.capability });
|
|
152
|
+
if (b.includeUntested)
|
|
153
|
+
p.set("includeUntested", "true");
|
|
154
|
+
return this.request("GET", `/agent/best-vendor?${p}`, { auth: true });
|
|
155
|
+
}
|
|
156
|
+
/** Free-text capability match (`GET /agent/find-capability`). Reads only — no spend. */
|
|
157
|
+
findCapability(query) {
|
|
158
|
+
const p = new URLSearchParams({ query });
|
|
159
|
+
return this.request("GET", `/agent/find-capability?${p}`, { auth: true });
|
|
160
|
+
}
|
|
145
161
|
// ── catalog + account management ──
|
|
146
162
|
/** Ingest any x402 origin's endpoints into the catalog so they become searchable/payable. */
|
|
147
163
|
discover(origin) {
|
|
@@ -162,6 +178,25 @@ export class AgentClient {
|
|
|
162
178
|
return this.request("POST", "/account/link-code", { body: {}, auth: true });
|
|
163
179
|
}
|
|
164
180
|
// ── spends (session-owned, guardrailed) ──
|
|
181
|
+
/**
|
|
182
|
+
* One-shot natural-language task (`POST /do-task`): the backend resolves the
|
|
183
|
+
* query to the best vendor, validates input, pays under the session guardrails,
|
|
184
|
+
* and fails over — all server-side. Returns the `NamedFunctionResult` VERBATIM
|
|
185
|
+
* (including demote/refund/stop outcomes); the client never re-implements the
|
|
186
|
+
* engine. An `Idempotency-Key` guards the write path against a double-charge on
|
|
187
|
+
* retry; a transport error is tagged `recoverable` exactly like {@link fetchPay}.
|
|
188
|
+
*/
|
|
189
|
+
doTask(b) {
|
|
190
|
+
return this.request("POST", "/do-task", {
|
|
191
|
+
body: {
|
|
192
|
+
query: b.query,
|
|
193
|
+
...(b.input !== undefined ? { input: b.input } : {}),
|
|
194
|
+
...(b.confirm !== undefined ? { confirm: b.confirm } : {}),
|
|
195
|
+
},
|
|
196
|
+
auth: true,
|
|
197
|
+
idempotent: true,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
165
200
|
/**
|
|
166
201
|
* Pay + run an endpoint via the session. Body matches `POST /x402/pay`'s
|
|
167
202
|
* schema: `method` is required (uppercased), `expectedPriceMinor` (the spend
|
package/dist/lib/config.js
CHANGED
|
@@ -12,7 +12,7 @@ export function baseUrls(env = process.env) {
|
|
|
12
12
|
return {
|
|
13
13
|
api: env.GRANTCASH_API_URL || "https://api.perfolio.ai/api",
|
|
14
14
|
fiat: env.GRANTCASH_FIAT_URL || "https://api-fiat.perfolio.ai/api",
|
|
15
|
-
app: env.GRANTCASH_APP_URL || "https://
|
|
15
|
+
app: env.GRANTCASH_APP_URL || "https://agentgrant.cash",
|
|
16
16
|
agent: env.GRANTCASH_AGENT_URL || "https://agent-mode-backend-prod-merge.up.railway.app",
|
|
17
17
|
};
|
|
18
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentgrant.cash/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Grant Cash — one CLI for your money (gold) and your agent (pay-per-use services). Routes to the Perfolio backend and the Agent-mode backend behind a single, plain-language surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/lib/index.js",
|