autonomo-sdk 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.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Autonomo
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Autonomo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,106 +1,142 @@
1
- # Autonomo SDK for TypeScript
2
-
3
- One call for AI agents. Say what you need, and the SDK:
4
-
5
- 1. **Finds the right API** on [Autonomo](https://autonomo.fun), from your words and the kind of input you give
6
- (a token or wallet address, a transaction hash, a link).
7
- 2. **Pays for it** with x402 on Robinhood Chain, in USDG or $AUTONOMO, whichever the wallet can afford.
8
- 3. **Pays privately through [Gloam](https://gloam.trade)** when you ask, so the payment doesn't link to your wallet.
9
- 4. **Borrows from a [Borro](https://borro.markets) credit line** when the wallet is short of USDG.
10
- 5. **Keeps to your caps**: a most-per-call and a daily budget, checked before anything is signed.
11
- 6. **Returns the answer and a receipt**: which API, how it was paid, the amount and the transaction link.
12
-
13
- ```bash
14
- npm install autonomo-sdk
15
- ```
16
-
17
- Full examples for each way to pay: [autonomo.fun/docs/sdk](https://autonomo.fun/docs/sdk).
18
-
19
- ```ts
20
- import { Autonomo } from "autonomo-sdk";
21
-
22
- const autonomo = new Autonomo({ privateKey: process.env.AUTONOMO_WALLET_KEY });
23
-
24
- const { data, receipt } = await autonomo.call("is this token safe", {
25
- input: "0x86b57bcb9d249cf096cf8e21426c9dc9cafdec49",
26
- });
27
- console.log(data);
28
- console.log(receipt.route, receipt.amount, receipt.token, receipt.explorer);
29
- ```
30
-
31
- Use a **new wallet just for the agent**, holding only what it may spend. A little USDG covers many calls (most APIs
32
- cost 0.01 USDG). Paying in $AUTONOMO needs a little ETH once, for a Permit2 approval. Node 20 or later.
33
-
34
- ## Finding APIs without paying
35
-
36
- ```ts
37
- const autonomo = new Autonomo(); // no key needed to look
38
- await autonomo.find("wallet holdings"); // best matches first
39
- await autonomo.quote("explain this transaction", { input: "0x…" }); // the API, the URL and every price
40
- ```
41
-
42
- To skip the search, name the API: `autonomo.call("", { slug: "token-risk", path: "/risk/0x…" })`.
43
-
44
- ## Limits
45
-
46
- ```ts
47
- new Autonomo({
48
- privateKey,
49
- payWith: "auto", // or "usdg" / "autonomo"
50
- limits: {
51
- perCall: { usdg: "0.05", autonomo: "5000" }, // the defaults
52
- daily: { usdg: "1", autonomo: "10000" }, // per UTC day; Gloam and Borro count as USDG
53
- },
54
- });
55
- ```
56
-
57
- A call over a limit is refused before anything is signed, with the reason. Spending is kept in
58
- `~/.autonomo/spend.json` (shared with the Python SDK); pass `spendStore: memoryStore()` or your own store to change
59
- that.
60
-
61
- ## Private payments with Gloam
62
-
63
- `private: true` pays from a private Gloam balance through Gloam's own MCP server, `@gloamtrade/mcp`, running on your
64
- machine with its key and limits in `~/.gloam/agent.env`. The SDK calls its `gloam_fetch_paid` tool; Gloam cashes out
65
- exactly the price through its relay and calls the API with it. The SDK holds no Gloam keys, and no wallet key is needed.
66
- See the [SDK guide](https://autonomo.fun/docs/sdk) for the setup and a full example.
67
-
68
- ```ts
69
- const autonomo = new Autonomo({
70
- gloam: {
71
- fetchPaid: async (args) => {
72
- const out = await gloam.callTool({ name: "gloam_fetch_paid", arguments: args }); // an MCP client for @gloamtrade/mcp
73
- return JSON.parse((out.content as Array<{ text: string }>)[0].text);
74
- },
75
- },
76
- });
77
- await autonomo.call("newest launches", { private: true });
78
- ```
79
-
80
- A private call only ever pays through Gloam. If the API doesn't offer it, or Gloam isn't set up, it's refused, never
81
- sent as a public payment. The price includes Gloam's 0.5% cash-out fee.
82
-
83
- ## Borrowing with Borro
84
-
85
- ```ts
86
- const autonomo = new Autonomo({ privateKey, borro: { account: "0xYourCreditLine" } });
87
- ```
88
-
89
- When the wallet can't afford a call, the SDK asks Borro's MCP server to prepare a borrow of just what it's short,
90
- sends it from the wallet (this needs a little ETH for gas), waits for the USDG and pays. The line needs x402 bills
91
- turned on; see [Pay with a Borro credit line](https://autonomo.fun/docs/borro). Borro's own daily limit still applies.
92
-
93
- ## The receipt
94
-
95
- | Field | |
96
- | --- | --- |
97
- | `api` | The API's slug and name |
98
- | `route` | `usdg`, `autonomo`, `gloam` or `borro` |
99
- | `amount`, `token` | What was paid |
100
- | `payer`, `payTo` | The wallet (or "private (Gloam)") and the API's payment address |
101
- | `tx`, `explorer` | The payment transaction and its Blockscout link |
102
- | `borrowTxs`, `approveTx` | Borro borrows and the one-time Permit2 approval, when there were any |
103
- | `spentToday` | Today's total against the daily budget |
104
-
105
- Errors are `AutonomoError` with a `code` such as `cannot_pay`, `over_limit`, `no_private_option` or `payment_refused`.
106
- If an API fails after a payment is signed, the payment doesn't settle, so you aren't charged.
1
+ # Autonomo SDK for TypeScript
2
+
3
+ One call for AI agents. Say what you need, and the SDK:
4
+
5
+ 1. **Finds the right API** on [Autonomo](https://autonomo.fun), from your words and the kind of input you give
6
+ (a token or wallet address, a transaction hash, a link).
7
+ 2. **Pays for it** with x402 on Robinhood Chain, in USDG or $AUTONOMO, whichever the wallet can afford.
8
+ 3. **Pays privately through [Gloam](https://gloam.trade)** when you ask, so the payment doesn't link to your wallet.
9
+ 4. **Borrows from a [Borro](https://borro.markets) credit line** when the wallet is short of USDG.
10
+ 5. **Keeps to your caps**: a most-per-call and a daily budget, checked before anything is signed.
11
+ 6. **Returns the answer and a receipt**: which API, how it was paid, the amount and the transaction link.
12
+
13
+ ```bash
14
+ npm install autonomo-sdk
15
+ ```
16
+
17
+ Full examples for each way to pay: [autonomo.fun/docs/sdk](https://autonomo.fun/docs/sdk).
18
+
19
+ ```ts
20
+ import { Autonomo } from "autonomo-sdk";
21
+
22
+ const autonomo = new Autonomo({ privateKey: process.env.AUTONOMO_WALLET_KEY });
23
+
24
+ const { data, receipt } = await autonomo.call("is this token safe", {
25
+ input: "0x86b57bcb9d249cf096cf8e21426c9dc9cafdec49",
26
+ });
27
+ console.log(data);
28
+ console.log(receipt.route, receipt.amount, receipt.token, receipt.explorer);
29
+ ```
30
+
31
+ Use a **new wallet just for the agent**, holding only what it may spend. A little USDG covers many calls (most APIs
32
+ cost 0.01 USDG). Paying in $AUTONOMO needs a little ETH once, for a Permit2 approval. Node 20 or later.
33
+
34
+ ## Finding APIs without paying
35
+
36
+ ```ts
37
+ const autonomo = new Autonomo(); // no key needed to look
38
+ await autonomo.find("wallet holdings"); // best matches first
39
+ await autonomo.quote("explain this transaction", { input: "0x…" }); // the API, the URL and every price
40
+ ```
41
+
42
+ To skip the search, name the API: `autonomo.call("", { slug: "token-risk", path: "/risk/0x…" })`.
43
+
44
+ ## From the terminal
45
+
46
+ The package also installs a command, for people and for agents that run shell commands. `find`, `quote`, `spend` and
47
+ `--dry-run` need no wallet:
48
+
49
+ ```bash
50
+ npx autonomo-sdk find "wallet holdings"
51
+ npx autonomo-sdk risk 0x86b57bcb9d249cf096cf8e21426c9dc9cafdec49 --dry-run # the price, without paying
52
+ ```
53
+
54
+ To pay, put the agent wallet's key in `AUTONOMO_WALLET_KEY`. It's never taken as a flag, so it stays out of your
55
+ shell history.
56
+
57
+ ```bash
58
+ npx autonomo-sdk risk 0x86b57bcb9d249cf096cf8e21426c9dc9cafdec49
59
+ npx autonomo-sdk call "explain this transaction" 0x… --max 0.02 --daily 0.5 --json
60
+ ```
61
+
62
+ | Command | |
63
+ | --- | --- |
64
+ | `find [words] [input]` | List APIs, best matches first |
65
+ | `quote <words> [input]` | The API a call would use, and its prices |
66
+ | `call <words> [input]` | Find the right API, pay for one call, print the answer |
67
+ | `price [token]`, `launches [n]`, `risk <token>`, `tx <hash>`, `portfolio <wallet>`, `read <link>`, `offers <words>` | Shortcuts for the official APIs |
68
+ | `spend` | Today's spending against your limits |
69
+
70
+ `--max` and `--daily` set the USDG limits (`--max-autonomo` and `--daily-autonomo` for $AUTONOMO), `--pay-with`
71
+ picks the token and `--borro <credit line>` borrows when the wallet is short. At a terminal it shows the price and
72
+ asks before paying; `--yes` skips the question, and scripts are never asked. The answer goes to stdout and the
73
+ receipt to stderr, so piping into `jq` works, and `--json` prints `{ data, receipt }` as one object. Exit codes: 0
74
+ done, 1 failed, 2 bad usage, 3 over a limit, 4 can't pay, 5 payment refused, 6 no such API.
75
+
76
+ After `npm install -g autonomo-sdk` the command is `autonomo`. (`npx autonomo` is someone else's package.) Private
77
+ Gloam payments aren't in the command yet; use the SDK for those.
78
+
79
+ ## Limits
80
+
81
+ ```ts
82
+ new Autonomo({
83
+ privateKey,
84
+ payWith: "auto", // or "usdg" / "autonomo"
85
+ limits: {
86
+ perCall: { usdg: "0.05", autonomo: "5000" }, // the defaults
87
+ daily: { usdg: "1", autonomo: "10000" }, // per UTC day; Gloam and Borro count as USDG
88
+ },
89
+ });
90
+ ```
91
+
92
+ A call over a limit is refused before anything is signed, with the reason. Spending is kept in
93
+ `~/.autonomo/spend.json` (shared with the Python SDK); pass `spendStore: memoryStore()` or your own store to change
94
+ that.
95
+
96
+ ## Private payments with Gloam
97
+
98
+ `private: true` pays from a private Gloam balance through Gloam's own MCP server, `@gloamtrade/mcp`, running on your
99
+ machine with its key and limits in `~/.gloam/agent.env`. The SDK calls its `gloam_fetch_paid` tool; Gloam cashes out
100
+ exactly the price through its relay and calls the API with it. The SDK holds no Gloam keys, and no wallet key is needed.
101
+ See the [SDK guide](https://autonomo.fun/docs/sdk) for the setup and a full example.
102
+
103
+ ```ts
104
+ const autonomo = new Autonomo({
105
+ gloam: {
106
+ fetchPaid: async (args) => {
107
+ // gloam: an MCP client for @gloamtrade/mcp. A private payment can take a few minutes, so wait up to 5.
108
+ const out = await gloam.callTool({ name: "gloam_fetch_paid", arguments: args }, undefined, { timeout: 300_000 });
109
+ return JSON.parse((out.content as Array<{ text: string }>)[0].text);
110
+ },
111
+ },
112
+ });
113
+ await autonomo.call("newest launches", { private: true });
114
+ ```
115
+
116
+ A private call only ever pays through Gloam. If the API doesn't offer it, or Gloam isn't set up, it's refused, never
117
+ sent as a public payment. The price includes Gloam's cash-out fee (about 0.0105 USDG for a 0.01 call).
118
+
119
+ ## Borrowing with Borro
120
+
121
+ ```ts
122
+ const autonomo = new Autonomo({ privateKey, borro: { account: "0xYourCreditLine" } });
123
+ ```
124
+
125
+ When the wallet can't afford a call, the SDK asks Borro's MCP server to prepare a borrow of just what it's short,
126
+ sends it from the wallet (this needs a little ETH for gas), waits for the USDG and pays. The line needs x402 bills
127
+ turned on; see [Pay with a Borro credit line](https://autonomo.fun/docs/borro). Borro's own daily limit still applies.
128
+
129
+ ## The receipt
130
+
131
+ | Field | |
132
+ | --- | --- |
133
+ | `api` | The API's slug and name |
134
+ | `route` | `usdg`, `autonomo`, `gloam` or `borro` |
135
+ | `amount`, `token` | What was paid |
136
+ | `payer`, `payTo` | The wallet (or "private (Gloam)") and the API's payment address |
137
+ | `tx`, `explorer` | The payment transaction and its Blockscout link |
138
+ | `borrowTxs`, `approveTx` | Borro borrows and the one-time Permit2 approval, when there were any |
139
+ | `spentToday` | Today's total against the daily budget |
140
+
141
+ Errors are `AutonomoError` with a `code` such as `cannot_pay`, `over_limit`, `no_private_option` or `payment_refused`.
142
+ If an API fails after a payment is signed, the payment doesn't settle, so you aren't charged.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { type SpendStore } from "./index.js";
3
+ /** Everything the CLI touches outside itself, so tests can swap it. */
4
+ export type Io = {
5
+ env: Record<string, string | undefined>;
6
+ out: (text: string) => void;
7
+ err: (text: string) => void;
8
+ /** True when a person is at the terminal, so a paid call asks first. */
9
+ interactive: boolean;
10
+ confirm: (question: string) => Promise<boolean>;
11
+ fetch?: typeof fetch;
12
+ spendStore?: SpendStore;
13
+ };
14
+ export declare const EXIT: {
15
+ readonly ok: 0;
16
+ readonly failed: 1;
17
+ readonly usage: 2;
18
+ readonly overLimit: 3;
19
+ readonly cannotPay: 4;
20
+ readonly refused: 5;
21
+ readonly notFound: 6;
22
+ };
23
+ /** Runs one command and returns its exit code. `argv` is what follows the program name. */
24
+ export declare function run(argv: string[], io: Io): Promise<number>;
package/dist/cli.js ADDED
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+ // Autonomo CLI: the SDK from a terminal, for people and for agents that run shell commands.
3
+ //
4
+ // npx autonomo-sdk find "wallet holdings" look, free
5
+ // npx autonomo-sdk risk 0x… --dry-run the price, without paying
6
+ // AUTONOMO_WALLET_KEY=0x… npx autonomo-sdk risk 0x… pay and print the answer
7
+ //
8
+ // The answer goes to stdout and everything else (the receipt, questions, errors) to stderr, so `autonomo … | jq`
9
+ // works. The wallet key is only ever read from AUTONOMO_WALLET_KEY, never from a flag, so it stays out of shell
10
+ // history. The SDK's per-call cap and daily budget apply to every paid call.
11
+ import { realpathSync } from "node:fs";
12
+ import { createInterface } from "node:readline/promises";
13
+ import { fileURLToPath } from "node:url";
14
+ import { parseArgs } from "node:util";
15
+ import { formatUnits } from "viem";
16
+ import { Autonomo, AutonomoError, DEFAULT_LIMITS, TOKENS, VERSION, fileStore } from "./index.js";
17
+ export const EXIT = { ok: 0, failed: 1, usage: 2, overLimit: 3, cannotPay: 4, refused: 5, notFound: 6 };
18
+ const HELP = `Autonomo CLI ${VERSION}: find and pay for APIs on autonomo.fun
19
+
20
+ Usage
21
+ autonomo find [words] [input] List APIs, best matches first. Free.
22
+ autonomo quote <words> [input] The API a call would use and its prices. Free.
23
+ autonomo call <words> [input] Find the right API, pay for one call, print the answer.
24
+ autonomo spend Today's spending against your limits.
25
+
26
+ Shortcuts for the official APIs
27
+ autonomo price [token address] $AUTONOMO's price, or any launchpad token's
28
+ autonomo launches [how many] Newest token launches
29
+ autonomo risk <token address> Rug-risk check
30
+ autonomo tx <transaction hash> A transaction in plain words
31
+ autonomo portfolio <wallet> What a wallet holds
32
+ autonomo read <https link> A web page's text
33
+ autonomo offers <what you need> Matching offers on Ostrapad
34
+
35
+ Options
36
+ --max <usdg> Most to pay for one call (default ${DEFAULT_LIMITS.perCall.usdg})
37
+ --daily <usdg> Most to spend per UTC day (default ${DEFAULT_LIMITS.daily.usdg})
38
+ --max-autonomo <amount> The same two limits when paying in $AUTONOMO
39
+ --daily-autonomo <amount> (defaults ${DEFAULT_LIMITS.perCall.autonomo} and ${DEFAULT_LIMITS.daily.autonomo})
40
+ --pay-with <token> usdg, autonomo or auto (default: USDG, then $AUTONOMO)
41
+ --borro <credit line> Borrow from this Borro line when the wallet is short of USDG
42
+ --slug <api> --path <p> With call or quote: use this API and path, skipping the search
43
+ --dry-run Show the price instead of paying
44
+ --yes Don't ask before paying
45
+ --json One JSON object on stdout: { data, receipt }
46
+ --help, --version
47
+
48
+ Wallet
49
+ Set AUTONOMO_WALLET_KEY to the private key of a wallet made just for the agent, holding only what it may spend.
50
+ find, quote, spend and --dry-run need no wallet.
51
+
52
+ Exit codes
53
+ 0 done 1 failed 2 bad usage 3 over a limit 4 can't pay 5 payment refused 6 no such API`;
54
+ const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
55
+ const TX_HASH = /^0x[0-9a-fA-F]{64}$/;
56
+ const AMOUNT = /^\d+(\.\d+)?$/;
57
+ class UsageError extends Error {
58
+ }
59
+ const usage = (message) => {
60
+ throw new UsageError(message);
61
+ };
62
+ /** Each shortcut names its API and builds the path, so nothing is searched. */
63
+ const SHORTCUTS = {
64
+ price: ([token]) => {
65
+ if (token === undefined)
66
+ return { slug: "token-price", path: "/price/autonomo" };
67
+ if (!ADDRESS.test(token))
68
+ usage("price takes a token contract address (0x and 40 hex characters), or nothing for $AUTONOMO.");
69
+ return { slug: "token-lookup", path: `/price/${token}` };
70
+ },
71
+ launches: ([count = "10"]) => {
72
+ if (!/^\d{1,3}$/.test(count) || Number(count) < 1)
73
+ usage("launches takes how many to show, e.g. autonomo launches 5.");
74
+ return { slug: "new-launches", path: `/launches?limit=${Number(count)}` };
75
+ },
76
+ risk: ([token]) => {
77
+ if (!token || !ADDRESS.test(token))
78
+ usage("risk takes a token contract address: autonomo risk 0x…");
79
+ return { slug: "token-risk", path: `/risk/${token}` };
80
+ },
81
+ tx: ([hash]) => {
82
+ if (!hash || !TX_HASH.test(hash))
83
+ usage("tx takes a transaction hash (0x and 64 hex characters).");
84
+ return { slug: "tx-explainer", path: `/tx/${hash}` };
85
+ },
86
+ portfolio: ([wallet]) => {
87
+ if (!wallet || !ADDRESS.test(wallet))
88
+ usage("portfolio takes a wallet address: autonomo portfolio 0x…");
89
+ return { slug: "wallet-portfolio", path: `/portfolio/${wallet}` };
90
+ },
91
+ read: ([link]) => {
92
+ if (!link || !/^https:\/\/\S+$/i.test(link))
93
+ usage("read takes an https:// link.");
94
+ return { slug: "page-reader", path: `/page?url=${encodeURIComponent(link)}` };
95
+ },
96
+ offers: (words) => {
97
+ const q = words.join(" ").trim();
98
+ if (!q)
99
+ usage('offers takes what you need done: autonomo offers "summarize a document"');
100
+ return { slug: "ostrapad-offer-finder", path: `/offers?q=${encodeURIComponent(q)}` };
101
+ },
102
+ };
103
+ const price = (api) => {
104
+ const parts = [api.price.usdg !== undefined && `${api.price.usdg} USDG`, api.price.autonomo !== undefined && `${api.price.autonomo} AUTONOMO`].filter(Boolean);
105
+ return parts.join(" or ") || "see quote";
106
+ };
107
+ function exitFor(e) {
108
+ if (e instanceof UsageError)
109
+ return EXIT.usage;
110
+ if (!(e instanceof AutonomoError))
111
+ return EXIT.failed;
112
+ if (e.code === "over_limit")
113
+ return EXIT.overLimit;
114
+ // cannot_pay covers both a limit and an empty wallet; the limit ones say "cap" or "budget".
115
+ if (e.code === "cannot_pay")
116
+ return /per-call cap|budget/.test(e.message) && !/the wallet holds/.test(e.message) ? EXIT.overLimit : EXIT.cannotPay;
117
+ if (e.code === "no_wallet" || e.code === "bad_key")
118
+ return EXIT.cannotPay;
119
+ if (e.code === "payment_refused" || e.code === "paid_not_served")
120
+ return EXIT.refused;
121
+ if (e.code === "not_found")
122
+ return EXIT.notFound;
123
+ return EXIT.failed;
124
+ }
125
+ /** Runs one command and returns its exit code. `argv` is what follows the program name. */
126
+ export async function run(argv, io) {
127
+ let json = argv.includes("--json");
128
+ try {
129
+ const { values, positionals } = parseArgs({
130
+ args: argv,
131
+ allowPositionals: true,
132
+ options: {
133
+ max: { type: "string" },
134
+ daily: { type: "string" },
135
+ "max-autonomo": { type: "string" },
136
+ "daily-autonomo": { type: "string" },
137
+ "pay-with": { type: "string" },
138
+ borro: { type: "string" },
139
+ slug: { type: "string" },
140
+ path: { type: "string" },
141
+ "dry-run": { type: "boolean" },
142
+ yes: { type: "boolean", short: "y" },
143
+ json: { type: "boolean" },
144
+ help: { type: "boolean", short: "h" },
145
+ version: { type: "boolean", short: "v" },
146
+ key: { type: "string" },
147
+ "private-key": { type: "string" },
148
+ },
149
+ });
150
+ json = Boolean(values.json);
151
+ if (values.key !== undefined || values["private-key"] !== undefined) {
152
+ usage("The wallet key isn't taken as a flag, so it stays out of your shell history. Set AUTONOMO_WALLET_KEY instead.");
153
+ }
154
+ if (values.version)
155
+ return io.out(VERSION), EXIT.ok;
156
+ const [command, ...rest] = positionals;
157
+ if (values.help || command === undefined || command === "help")
158
+ return io.out(HELP), EXIT.ok;
159
+ const amount = (flag) => {
160
+ const v = values[flag];
161
+ if (v !== undefined && !AMOUNT.test(v))
162
+ usage(`--${flag} takes an amount in whole tokens, e.g. --${flag} 0.05.`);
163
+ return v;
164
+ };
165
+ const limits = {
166
+ perCall: { usdg: amount("max") ?? DEFAULT_LIMITS.perCall.usdg, autonomo: amount("max-autonomo") ?? DEFAULT_LIMITS.perCall.autonomo },
167
+ daily: { usdg: amount("daily") ?? DEFAULT_LIMITS.daily.usdg, autonomo: amount("daily-autonomo") ?? DEFAULT_LIMITS.daily.autonomo },
168
+ };
169
+ const payWith = values["pay-with"] ?? "auto";
170
+ if (payWith !== "usdg" && payWith !== "autonomo" && payWith !== "auto")
171
+ usage("--pay-with takes usdg, autonomo or auto.");
172
+ const borro = values.borro ?? io.env.AUTONOMO_BORRO_ACCOUNT;
173
+ if (borro !== undefined && !ADDRESS.test(borro))
174
+ usage("--borro takes the credit line's address.");
175
+ const store = io.spendStore ?? fileStore();
176
+ const client = (withWallet) => new Autonomo({
177
+ privateKey: withWallet ? io.env.AUTONOMO_WALLET_KEY || undefined : undefined,
178
+ payWith: payWith,
179
+ limits,
180
+ borro: borro ? { account: borro } : undefined,
181
+ spendStore: store,
182
+ fetch: io.fetch,
183
+ });
184
+ const print = (value) => io.out(typeof value === "string" ? value : JSON.stringify(value, null, 2));
185
+ if (command === "find") {
186
+ const [words = "", input] = rest;
187
+ let apis;
188
+ if (input !== undefined) {
189
+ apis = await client(false).find(words, input);
190
+ }
191
+ else {
192
+ // Browsing: with no input to fit, show every API whose text matches, including the ones that need an input.
193
+ const terms = words.toLowerCase().split(/[^a-z0-9$]+/).filter((w) => w.length > 2);
194
+ const hits = (a) => terms.filter((w) => `${a.slug} ${a.name} ${a.description}`.toLowerCase().includes(w)).length;
195
+ apis = (await client(false).find()).filter((a) => terms.length === 0 || hits(a) > 0).sort((x, y) => hits(y) - hits(x));
196
+ }
197
+ if (json)
198
+ return print(apis), EXIT.ok;
199
+ if (apis.length === 0)
200
+ return io.err(`No API matches "${words}". Run autonomo find to see them all.`), EXIT.notFound;
201
+ for (const a of apis)
202
+ io.out(`${a.slug} ${price(a)}${a.verified ? " verified" : ""}\n ${a.name}: ${a.description}\n ${a.path}`);
203
+ return EXIT.ok;
204
+ }
205
+ if (command === "spend") {
206
+ const day = new Date().toISOString().slice(0, 10);
207
+ const spent = await store.get(day);
208
+ const row = (t) => ({ spent: formatUnits(spent[t] ?? 0n, TOKENS[t].decimals), daily: limits.daily[t], perCall: limits.perCall[t] });
209
+ const report = { day, wallet: client(true).address, usdg: row("usdg"), autonomo: row("autonomo") };
210
+ if (json)
211
+ return print(report), EXIT.ok;
212
+ io.out(`Today (${day}, UTC)`);
213
+ io.out(` USDG ${report.usdg.spent} of ${report.usdg.daily} spent, up to ${report.usdg.perCall} a call`);
214
+ io.out(` AUTONOMO ${report.autonomo.spent} of ${report.autonomo.daily} spent, up to ${report.autonomo.perCall} a call`);
215
+ io.out(` Wallet ${report.wallet ?? "none set (AUTONOMO_WALLET_KEY)"}`);
216
+ return EXIT.ok;
217
+ }
218
+ // Everything else is a call: a shortcut, or `call` / `quote` with words.
219
+ let words = "";
220
+ let opts;
221
+ const quoteOnly = command === "quote" || Boolean(values["dry-run"]);
222
+ if (command === "call" || command === "quote") {
223
+ if (values.slug) {
224
+ opts = { slug: values.slug, path: values.path, input: rest[0] };
225
+ }
226
+ else {
227
+ if (values.path)
228
+ usage("--path needs --slug, the API it belongs to.");
229
+ if (!rest[0])
230
+ usage(`${command} takes what you need in words: autonomo ${command} "is this token safe" 0x…`);
231
+ if (rest.length > 2)
232
+ usage(`Put the words in quotes: autonomo ${command} "is this token safe" 0x…`);
233
+ words = rest[0];
234
+ opts = { input: rest[1] };
235
+ }
236
+ }
237
+ else if (Object.hasOwn(SHORTCUTS, command)) {
238
+ if (values.slug || values.path)
239
+ usage(`${command} already names its API. Use call with --slug and --path instead.`);
240
+ opts = SHORTCUTS[command](rest);
241
+ }
242
+ else {
243
+ return usage(`Unknown command "${command}". Run autonomo --help.`);
244
+ }
245
+ if (quoteOnly) {
246
+ const q = await client(false).quote(words, opts);
247
+ if (json)
248
+ return print(q), EXIT.ok;
249
+ io.out(`${q.api.name} (${q.api.slug})\n ${q.url}`);
250
+ for (const o of q.offers)
251
+ io.out(` ${o.amount} ${o.token}${o.private ? " private, through Gloam" : ""}`);
252
+ return EXIT.ok;
253
+ }
254
+ const autonomo = client(true);
255
+ if (!autonomo.address) {
256
+ throw new AutonomoError("No wallet set. Put the agent wallet's private key in AUTONOMO_WALLET_KEY, or add --dry-run to see the price.", "no_wallet");
257
+ }
258
+ if (io.interactive && !values.yes) {
259
+ const q = await autonomo.quote(words, opts);
260
+ const prices = q.offers.filter((o) => !o.private).map((o) => `${o.amount} ${o.token}`).join(" or ");
261
+ if (!(await io.confirm(`${q.api.name} costs ${prices}. Pay from ${autonomo.address}? [y/N] `)))
262
+ return io.err("Not paid."), EXIT.ok;
263
+ }
264
+ const { data, receipt } = await autonomo.call(words, opts);
265
+ if (json)
266
+ return print({ data, receipt }), EXIT.ok;
267
+ print(data);
268
+ io.err(`Paid ${receipt.amount} ${receipt.token} to ${receipt.api.name}${receipt.route === "borro" ? " on Borro credit" : ""}. Today: ${receipt.spentToday}.`);
269
+ if (receipt.explorer)
270
+ io.err(receipt.explorer);
271
+ return EXIT.ok;
272
+ }
273
+ catch (e) {
274
+ const code = e instanceof AutonomoError ? e.code : e instanceof UsageError || e?.code?.startsWith?.("ERR_PARSE_ARGS") ? "usage" : "failed";
275
+ const message = e instanceof Error ? e.message : String(e);
276
+ io.err(json ? JSON.stringify({ error: { code, message } }) : code === "usage" ? `${message}\nRun autonomo --help for the commands.` : message);
277
+ return code === "usage" ? EXIT.usage : exitFor(e);
278
+ }
279
+ }
280
+ const isMain = (() => {
281
+ try {
282
+ return Boolean(process.argv[1]) && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
283
+ }
284
+ catch {
285
+ return false;
286
+ }
287
+ })();
288
+ if (isMain) {
289
+ const code = await run(process.argv.slice(2), {
290
+ env: process.env,
291
+ out: (text) => console.log(text),
292
+ err: (text) => console.error(text),
293
+ interactive: Boolean(process.stdin.isTTY && process.stderr.isTTY),
294
+ confirm: async (question) => {
295
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
296
+ try {
297
+ return /^y(es)?$/i.test((await rl.question(question)).trim());
298
+ }
299
+ finally {
300
+ rl.close();
301
+ }
302
+ },
303
+ });
304
+ process.exitCode = code;
305
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Hex } from "viem";
2
- export declare const VERSION = "0.1.0";
2
+ export declare const VERSION = "0.1.1";
3
3
  export type TokenName = "usdg" | "autonomo";
4
4
  type Token = {
5
5
  name: TokenName;
@@ -8,6 +8,16 @@ type Token = {
8
8
  decimals: number;
9
9
  };
10
10
  export declare const TOKENS: Record<TokenName, Token>;
11
+ export declare const DEFAULT_LIMITS: {
12
+ perCall: {
13
+ usdg: string;
14
+ autonomo: string;
15
+ };
16
+ daily: {
17
+ usdg: string;
18
+ autonomo: string;
19
+ };
20
+ };
11
21
  export type Limits = {
12
22
  /** Most to pay for one call, in whole tokens, e.g. { usdg: "0.05" }. */
13
23
  perCall?: Partial<Record<TokenName, string>>;
@@ -113,6 +123,7 @@ export declare class AutonomoError extends Error {
113
123
  }
114
124
  /** Ranks APIs for a query and input: word matches first, then a fitting path, then verified and the track record. */
115
125
  export declare function rankApis(apis: Api[], query: string, input?: string): Api[];
126
+ export declare function fileStore(file?: string): SpendStore;
116
127
  /** Keeps spending in memory only, for tests or short-lived agents. */
117
128
  export declare function memoryStore(): SpendStore;
118
129
  export declare class Autonomo {
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import { homedir } from "node:os";
15
15
  import { dirname, join } from "node:path";
16
16
  import { createPublicClient, createWalletClient, defineChain, erc20Abi, formatUnits, getAddress, http, maxUint256, parseUnits, toHex, } from "viem";
17
17
  import { privateKeyToAccount } from "viem/accounts";
18
- export const VERSION = "0.1.0";
18
+ export const VERSION = "0.1.1";
19
19
  const NETWORK = "eip155:4663";
20
20
  const CHAIN_ID = 4663;
21
21
  const EXPLORER = "https://robinhoodchain.blockscout.com";
@@ -26,7 +26,7 @@ export const TOKENS = {
26
26
  usdg: { name: "usdg", symbol: "USDG", address: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", decimals: 6 },
27
27
  autonomo: { name: "autonomo", symbol: "AUTONOMO", address: "0xe83c85e1ead16fcfe85401e2b337fb4fb5b5cc96", decimals: 18 },
28
28
  };
29
- const DEFAULT_LIMITS = {
29
+ export const DEFAULT_LIMITS = {
30
30
  perCall: { usdg: "0.05", autonomo: "5000" },
31
31
  daily: { usdg: "1", autonomo: "10000" },
32
32
  };
@@ -103,7 +103,7 @@ export function rankApis(apis, query, input) {
103
103
  return scored.sort((x, y) => y.hits - x.hits || y.trust - x.trust).map((s) => s.a);
104
104
  }
105
105
  // ---------- Spending ----------
106
- function fileStore(file = join(homedir(), ".autonomo", "spend.json")) {
106
+ export function fileStore(file = join(homedir(), ".autonomo", "spend.json")) {
107
107
  const read = () => {
108
108
  try {
109
109
  return JSON.parse(readFileSync(file, "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autonomo-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "One call for AI agents: find the right paid API on Autonomo, pay in USDG or $AUTONOMO (privately through Gloam, or on Borro credit), within spending caps, and get a receipt.",
5
5
  "homepage": "https://autonomo.fun",
6
6
  "author": "Autonomo (https://autonomo.fun)",
@@ -43,5 +43,8 @@
43
43
  "@modelcontextprotocol/sdk": "^1.32.1",
44
44
  "@types/node": "26.6.5",
45
45
  "typescript": "7.0.2"
46
+ },
47
+ "bin": {
48
+ "autonomo": "dist/cli.js"
46
49
  }
47
50
  }