pryvy 0.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
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 ADDED
@@ -0,0 +1,106 @@
1
+ # pryvy
2
+
3
+ CLI and MCP server for [Privy](https://www.privy.io) embedded wallets & auth.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g pryvy
9
+ ```
10
+
11
+ Or run directly:
12
+
13
+ ```bash
14
+ npx pryvy --help
15
+ ```
16
+
17
+ ## Setup
18
+
19
+ On first run with no arguments, pryvy prompts for your App ID and App Secret interactively. Or configure them explicitly:
20
+
21
+ ```bash
22
+ pryvy configure app-id <your-app-id>
23
+ pryvy configure app-secret <your-app-secret>
24
+ ```
25
+
26
+ You can also set them via environment variables:
27
+
28
+ ```bash
29
+ export PRIVY_APP_ID=...
30
+ export PRIVY_APP_SECRET=...
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```bash
36
+ # create a user with an email
37
+ pryvy users create --email alice@example.com
38
+
39
+ # create a user with an embedded ethereum wallet
40
+ pryvy users create --email alice@example.com --create-wallet ethereum
41
+
42
+ # list all users
43
+ pryvy users list
44
+
45
+ # create a standalone wallet
46
+ pryvy wallets create --chain-type ethereum
47
+
48
+ # check wallet balance
49
+ pryvy wallets get-balance <wallet-id> --asset usdc --chain base
50
+
51
+ # sign a message
52
+ pryvy signing personal-sign <wallet-id> --message "hello world"
53
+
54
+ # send a transaction
55
+ pryvy signing send-tx <wallet-id> \
56
+ --caip2 eip155:1 --to 0x... --value 0x0
57
+
58
+ # set default output format
59
+ pryvy configure format json
60
+ ```
61
+
62
+ ## Commands
63
+
64
+ | Group | Commands |
65
+ |---|---|
66
+ | `users` | `create`, `get`, `list`, `delete`, `search`, `add-metadata`, `pregenerate-wallets` |
67
+ | `wallets` | `create`, `get`, `list`, `get-balance`, `get-by-address`, `batch-create` |
68
+ | `signing` | `personal-sign`, `send-tx`, `sign-tx`, `sign-typed-data`, `sign-7702`, `sign-user-op`, `sol-sign-tx`, `sol-sign-message` |
69
+ | `transactions` | `get` |
70
+ | `policies` | `create`, `get`, `list`, `update`, `delete` |
71
+ | `configure` | `app-id`, `app-secret`, `format`, `show` |
72
+
73
+ All commands support `--format toon|json|yaml|md|jsonl` and `--help`.
74
+
75
+ ## Agent Setup
76
+
77
+ Give your AI agent (Claude Code, Amp, Cursor, Copilot, etc.) full access to Privy:
78
+
79
+ ```bash
80
+ npx pryvy mcp add # register as MCP server — gives agents direct tool access
81
+ npx pryvy skills add # install skill files — gives agents context on available commands
82
+ ```
83
+
84
+ That's it. Your agent can now run commands like "create a user with an email" or "sign a message with a wallet".
85
+
86
+ You can also run the MCP server directly in stdio mode:
87
+
88
+ ```bash
89
+ pryvy --mcp
90
+ ```
91
+
92
+ ## Development
93
+
94
+ ```bash
95
+ bun install
96
+ bun run build # tsc + chmod
97
+ bun run typecheck # tsc --noEmit
98
+ ```
99
+
100
+ ## Built with
101
+
102
+ [incur](https://github.com/wevm/incur) — one CLI router that gives you a CLI, MCP server, and agent skills for free.
103
+
104
+ ## License
105
+
106
+ MIT
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { getAppId, getAppSecret, writeConfig } from './core/client.js';
3
+ const args = process.argv.slice(2);
4
+ const hasArgs = args.length > 0;
5
+ if (!hasArgs && (!getAppId() || !getAppSecret())) {
6
+ const { createInterface } = await import('node:readline/promises');
7
+ console.error(`
8
+ Welcome to pryvy — Privy embedded wallets & auth CLI
9
+
10
+ To get started, you need your Privy App ID and App Secret.
11
+ Get them at https://dashboard.privy.io
12
+ `);
13
+ if (process.stdin.isTTY) {
14
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
15
+ const appId = (await rl.question(' Enter your App ID: ')).trim();
16
+ const appSecret = (await rl.question(' Enter your App Secret: ')).trim();
17
+ rl.close();
18
+ if (!appId || !appSecret) {
19
+ console.error('\n Missing credentials. You can set them later:\n\n pryvy configure app-id <your-app-id>\n pryvy configure app-secret <your-app-secret>\n');
20
+ process.exit(1);
21
+ }
22
+ writeConfig({ app_id: appId, app_secret: appSecret });
23
+ console.error('\n ✓ Saved to ~/.config/pryvy/config.json');
24
+ console.error('\n You\'re all set! Run pryvy --help to see available commands.\n');
25
+ }
26
+ else {
27
+ console.error(' Run: pryvy configure app-id <your-app-id>');
28
+ console.error(' pryvy configure app-secret <your-app-secret>');
29
+ console.error(' Or: export PRIVY_APP_ID=... && export PRIVY_APP_SECRET=...\n');
30
+ process.exit(1);
31
+ }
32
+ }
33
+ else {
34
+ const { default: cli } = await import('./cli.js');
35
+ cli.serve();
36
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { Cli } from 'incur';
2
+ declare const cli: Cli.Cli<{}, undefined, undefined>;
3
+ export default cli;
package/dist/cli.js ADDED
@@ -0,0 +1,386 @@
1
+ import { Cli, z } from 'incur';
2
+ import { createUser, getUser, listUsers, deleteUser, searchUsers, addCustomMetadata, pregenerateWallets } from './core/users.js';
3
+ import { createWallet, getWallet, listWallets, getWalletBalance, getWalletByAddress, batchCreateWallets } from './core/wallets.js';
4
+ import { personalSign, sendTransaction, signTransaction, signTypedData, sign7702Authorization, signUserOperation, solSignTransaction, solSignMessage } from './core/signing.js';
5
+ import { getTransaction } from './core/transactions.js';
6
+ import { createPolicy, getPolicy, listPolicies, updatePolicy, deletePolicy } from './core/policies.js';
7
+ import { writeConfig, getAppId, getAppSecret, getDefaultFormat } from './core/client.js';
8
+ import pkg from '../package.json' with { type: 'json' };
9
+ const cli = Cli.create('pryvy', {
10
+ version: pkg.version,
11
+ description: 'Privy embedded wallets & auth CLI. Built with incur.',
12
+ format: getDefaultFormat(),
13
+ sync: {
14
+ suggestions: [
15
+ 'create a user with an email',
16
+ 'create an ethereum wallet',
17
+ 'list all wallets',
18
+ 'sign a message with a wallet',
19
+ 'send a transaction',
20
+ ],
21
+ },
22
+ });
23
+ // --- users subcommand group ---
24
+ const users = Cli.create('users', { description: 'Manage Privy users.' });
25
+ users.command('create', {
26
+ description: 'Create a new user with linked accounts',
27
+ options: z.object({
28
+ email: z.string().optional().describe('Email address to link'),
29
+ phone: z.string().optional().describe('Phone number to link'),
30
+ wallet: z.string().optional().describe('Wallet address to link'),
31
+ walletChain: z.string().optional().describe('Chain type for linked wallet (ethereum, solana)'),
32
+ createWallet: z.string().optional().describe('Create embedded wallet (ethereum, solana, etc.)'),
33
+ }),
34
+ async run(c) {
35
+ const linked_accounts = [];
36
+ if (c.options.email)
37
+ linked_accounts.push({ type: 'email', address: c.options.email });
38
+ if (c.options.phone)
39
+ linked_accounts.push({ type: 'phone', number: c.options.phone });
40
+ if (c.options.wallet)
41
+ linked_accounts.push({ type: 'wallet', address: c.options.wallet, chain_type: c.options.walletChain ?? 'ethereum' });
42
+ const body = { linked_accounts };
43
+ if (c.options.createWallet)
44
+ body.wallets = [{ chain_type: c.options.createWallet }];
45
+ return createUser(body);
46
+ },
47
+ });
48
+ users.command('get', {
49
+ description: 'Get a user by ID',
50
+ args: z.object({ id: z.string().describe('User ID') }),
51
+ async run(c) { return getUser(c.args.id); },
52
+ });
53
+ users.command('list', {
54
+ description: 'Get all users',
55
+ options: z.object({
56
+ cursor: z.string().optional().describe('Pagination cursor'),
57
+ limit: z.string().optional().describe('Max results (up to 100)'),
58
+ }),
59
+ async run(c) {
60
+ const params = {};
61
+ if (c.options.cursor)
62
+ params.cursor = c.options.cursor;
63
+ if (c.options.limit)
64
+ params.limit = c.options.limit;
65
+ return listUsers(Object.keys(params).length ? params : undefined);
66
+ },
67
+ });
68
+ users.command('delete', {
69
+ description: 'Delete a user by ID',
70
+ args: z.object({ id: z.string().describe('User ID') }),
71
+ async run(c) { return deleteUser(c.args.id); },
72
+ });
73
+ users.command('search', {
74
+ description: 'Search users by term, emails, phone numbers, or wallet addresses',
75
+ options: z.object({
76
+ term: z.string().optional().describe('Search term'),
77
+ email: z.string().optional().describe('Email to search for'),
78
+ phone: z.string().optional().describe('Phone number to search for'),
79
+ wallet: z.string().optional().describe('Wallet address to search for'),
80
+ }),
81
+ async run(c) {
82
+ if (c.options.term)
83
+ return searchUsers({ searchTerm: c.options.term });
84
+ const body = {};
85
+ if (c.options.email)
86
+ body.emails = [c.options.email];
87
+ if (c.options.phone)
88
+ body.phoneNumbers = [c.options.phone];
89
+ if (c.options.wallet)
90
+ body.walletAddresses = [c.options.wallet];
91
+ return searchUsers(body);
92
+ },
93
+ });
94
+ users.command('add-metadata', {
95
+ description: 'Add custom metadata to a user',
96
+ args: z.object({ id: z.string().describe('User ID') }),
97
+ options: z.object({
98
+ json: z.string().describe('JSON string of custom metadata'),
99
+ }),
100
+ async run(c) {
101
+ return addCustomMetadata(c.args.id, JSON.parse(c.options.json));
102
+ },
103
+ });
104
+ users.command('pregenerate-wallets', {
105
+ description: 'Pregenerate embedded wallets for a user',
106
+ args: z.object({ id: z.string().describe('User ID') }),
107
+ options: z.object({
108
+ chainType: z.string().default('ethereum').describe('Chain type (ethereum, solana, etc.)'),
109
+ }),
110
+ async run(c) {
111
+ return pregenerateWallets(c.args.id, { chain_type: c.options.chainType });
112
+ },
113
+ });
114
+ cli.command(users);
115
+ // --- wallets subcommand group ---
116
+ const wallets = Cli.create('wallets', { description: 'Manage Privy wallets.' });
117
+ wallets.command('create', {
118
+ description: 'Create a wallet',
119
+ options: z.object({
120
+ chainType: z.string().describe('Chain type (ethereum, solana, cosmos, stellar, sui, aptos, etc.)'),
121
+ userId: z.string().optional().describe('Owner user ID'),
122
+ policyId: z.string().optional().describe('Policy ID to attach'),
123
+ }),
124
+ alias: { chainType: 'c', userId: 'u' },
125
+ async run(c) {
126
+ const body = { chain_type: c.options.chainType };
127
+ if (c.options.userId)
128
+ body.owner_id = c.options.userId;
129
+ if (c.options.policyId)
130
+ body.policy_ids = [c.options.policyId];
131
+ return createWallet(body);
132
+ },
133
+ });
134
+ wallets.command('get', {
135
+ description: 'Get a wallet by ID',
136
+ args: z.object({ id: z.string().describe('Wallet ID') }),
137
+ async run(c) { return getWallet(c.args.id); },
138
+ });
139
+ wallets.command('list', {
140
+ description: 'Get all wallets',
141
+ options: z.object({
142
+ cursor: z.string().optional().describe('Pagination cursor'),
143
+ limit: z.string().optional().describe('Max results (up to 100)'),
144
+ chainType: z.string().optional().describe('Filter by chain type'),
145
+ userId: z.string().optional().describe('Filter by user ID'),
146
+ }),
147
+ async run(c) {
148
+ const params = {};
149
+ if (c.options.cursor)
150
+ params.cursor = c.options.cursor;
151
+ if (c.options.limit)
152
+ params.limit = c.options.limit;
153
+ if (c.options.chainType)
154
+ params.chain_type = c.options.chainType;
155
+ if (c.options.userId)
156
+ params.user_id = c.options.userId;
157
+ return listWallets(Object.keys(params).length ? params : undefined);
158
+ },
159
+ });
160
+ wallets.command('get-balance', {
161
+ description: 'Get wallet balance',
162
+ args: z.object({ id: z.string().describe('Wallet ID') }),
163
+ options: z.object({
164
+ asset: z.string().optional().describe('Asset filter (usdc, eth, sol, etc.)'),
165
+ chain: z.string().optional().describe('Chain filter (ethereum, base, solana, etc.)'),
166
+ currency: z.string().optional().describe('Include fiat value (usd, eur)'),
167
+ }),
168
+ async run(c) {
169
+ const params = {};
170
+ if (c.options.asset)
171
+ params.asset = c.options.asset;
172
+ if (c.options.chain)
173
+ params.chain = c.options.chain;
174
+ if (c.options.currency)
175
+ params.include_currency = c.options.currency;
176
+ return getWalletBalance(c.args.id, Object.keys(params).length ? params : undefined);
177
+ },
178
+ });
179
+ wallets.command('get-by-address', {
180
+ description: 'Look up a wallet by blockchain address',
181
+ args: z.object({ address: z.string().describe('Blockchain address') }),
182
+ async run(c) { return getWalletByAddress(c.args.address); },
183
+ });
184
+ wallets.command('batch-create', {
185
+ description: 'Create multiple wallets in a single request',
186
+ options: z.object({
187
+ chainType: z.string().describe('Chain type for all wallets'),
188
+ count: z.string().default('1').describe('Number of wallets to create'),
189
+ userId: z.string().optional().describe('Owner user ID'),
190
+ }),
191
+ async run(c) {
192
+ const wallets = Array.from({ length: parseInt(c.options.count) }, () => ({
193
+ chain_type: c.options.chainType,
194
+ ...(c.options.userId ? { owner_id: c.options.userId } : {}),
195
+ }));
196
+ return batchCreateWallets({ wallets });
197
+ },
198
+ });
199
+ cli.command(wallets);
200
+ // --- signing subcommand group ---
201
+ const signing = Cli.create('signing', { description: 'Sign messages and transactions with Privy wallets.' });
202
+ signing.command('personal-sign', {
203
+ description: 'Sign a message with personal_sign',
204
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
205
+ options: z.object({
206
+ message: z.string().describe('Message to sign'),
207
+ encoding: z.string().optional().describe('Message encoding (utf-8, hex)'),
208
+ }),
209
+ async run(c) { return personalSign(c.args.walletId, c.options.message, c.options.encoding); },
210
+ });
211
+ signing.command('send-tx', {
212
+ description: 'Sign and send an Ethereum transaction',
213
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
214
+ options: z.object({
215
+ caip2: z.string().describe('CAIP-2 chain ID (e.g. eip155:1, eip155:11155111)'),
216
+ to: z.string().describe('Destination address'),
217
+ value: z.string().optional().describe('Value in hex wei'),
218
+ data: z.string().optional().describe('Transaction data (hex)'),
219
+ sponsor: z.boolean().default(false).describe('Enable gas sponsorship'),
220
+ }),
221
+ async run(c) {
222
+ const tx = { to: c.options.to };
223
+ if (c.options.value)
224
+ tx.value = c.options.value;
225
+ if (c.options.data)
226
+ tx.data = c.options.data;
227
+ return sendTransaction(c.args.walletId, c.options.caip2, tx, c.options.sponsor || undefined);
228
+ },
229
+ });
230
+ signing.command('sign-tx', {
231
+ description: 'Sign an Ethereum transaction without broadcasting',
232
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
233
+ options: z.object({
234
+ caip2: z.string().describe('CAIP-2 chain ID'),
235
+ to: z.string().describe('Destination address'),
236
+ value: z.string().optional().describe('Value in hex wei'),
237
+ data: z.string().optional().describe('Transaction data (hex)'),
238
+ }),
239
+ async run(c) {
240
+ const tx = { to: c.options.to };
241
+ if (c.options.value)
242
+ tx.value = c.options.value;
243
+ if (c.options.data)
244
+ tx.data = c.options.data;
245
+ return signTransaction(c.args.walletId, c.options.caip2, tx);
246
+ },
247
+ });
248
+ signing.command('sign-typed-data', {
249
+ description: 'Sign EIP-712 typed data',
250
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
251
+ options: z.object({
252
+ json: z.string().describe('JSON string of typed data (domain, types, message, primary_type)'),
253
+ }),
254
+ async run(c) { return signTypedData(c.args.walletId, JSON.parse(c.options.json)); },
255
+ });
256
+ signing.command('sign-7702', {
257
+ description: 'Sign an EIP-7702 authorization',
258
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
259
+ options: z.object({
260
+ caip2: z.string().describe('CAIP-2 chain ID'),
261
+ json: z.string().describe('JSON string of authorization struct'),
262
+ }),
263
+ async run(c) { return sign7702Authorization(c.args.walletId, c.options.caip2, JSON.parse(c.options.json)); },
264
+ });
265
+ signing.command('sign-user-op', {
266
+ description: 'Sign a user operation',
267
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
268
+ options: z.object({
269
+ caip2: z.string().describe('CAIP-2 chain ID'),
270
+ json: z.string().describe('JSON string of user operation'),
271
+ }),
272
+ async run(c) { return signUserOperation(c.args.walletId, c.options.caip2, JSON.parse(c.options.json)); },
273
+ });
274
+ signing.command('sol-sign-tx', {
275
+ description: 'Sign a Solana transaction',
276
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
277
+ options: z.object({
278
+ transaction: z.string().describe('Base64-encoded serialized transaction'),
279
+ }),
280
+ async run(c) { return solSignTransaction(c.args.walletId, c.options.transaction); },
281
+ });
282
+ signing.command('sol-sign-message', {
283
+ description: 'Sign a Solana message',
284
+ args: z.object({ walletId: z.string().describe('Wallet ID') }),
285
+ options: z.object({
286
+ message: z.string().describe('Message to sign'),
287
+ encoding: z.string().optional().describe('Message encoding (utf-8, base64)'),
288
+ }),
289
+ async run(c) { return solSignMessage(c.args.walletId, c.options.message, c.options.encoding); },
290
+ });
291
+ cli.command(signing);
292
+ // --- transactions subcommand group ---
293
+ const transactions = Cli.create('transactions', { description: 'Query transactions.' });
294
+ transactions.command('get', {
295
+ description: 'Get a transaction by ID',
296
+ args: z.object({ id: z.string().describe('Transaction ID') }),
297
+ async run(c) { return getTransaction(c.args.id); },
298
+ });
299
+ cli.command(transactions);
300
+ // --- policies subcommand group ---
301
+ const policies = Cli.create('policies', { description: 'Manage wallet policies.' });
302
+ policies.command('create', {
303
+ description: 'Create a policy',
304
+ options: z.object({
305
+ json: z.string().describe('JSON string of policy configuration'),
306
+ }),
307
+ async run(c) { return createPolicy(JSON.parse(c.options.json)); },
308
+ });
309
+ policies.command('get', {
310
+ description: 'Get a policy by ID',
311
+ args: z.object({ id: z.string().describe('Policy ID') }),
312
+ async run(c) { return getPolicy(c.args.id); },
313
+ });
314
+ policies.command('list', {
315
+ description: 'List all policies',
316
+ options: z.object({
317
+ cursor: z.string().optional().describe('Pagination cursor'),
318
+ limit: z.string().optional().describe('Max results'),
319
+ }),
320
+ async run(c) {
321
+ const params = {};
322
+ if (c.options.cursor)
323
+ params.cursor = c.options.cursor;
324
+ if (c.options.limit)
325
+ params.limit = c.options.limit;
326
+ return listPolicies(Object.keys(params).length ? params : undefined);
327
+ },
328
+ });
329
+ policies.command('update', {
330
+ description: 'Update a policy',
331
+ args: z.object({ id: z.string().describe('Policy ID') }),
332
+ options: z.object({
333
+ json: z.string().describe('JSON string of policy updates'),
334
+ }),
335
+ async run(c) { return updatePolicy(c.args.id, JSON.parse(c.options.json)); },
336
+ });
337
+ policies.command('delete', {
338
+ description: 'Delete a policy',
339
+ args: z.object({ id: z.string().describe('Policy ID') }),
340
+ async run(c) { return deletePolicy(c.args.id); },
341
+ });
342
+ cli.command(policies);
343
+ // --- configure subcommand group ---
344
+ const configure = Cli.create('configure', { description: 'Manage CLI configuration.' });
345
+ configure.command('app-id', {
346
+ description: 'Save your Privy App ID',
347
+ args: z.object({ appId: z.string().describe('Privy App ID') }),
348
+ async run(c) {
349
+ writeConfig({ app_id: c.args.appId });
350
+ return { saved: true, config: '~/.config/pryvy/config.json' };
351
+ },
352
+ });
353
+ configure.command('app-secret', {
354
+ description: 'Save your Privy App Secret',
355
+ args: z.object({ appSecret: z.string().describe('Privy App Secret') }),
356
+ async run(c) {
357
+ writeConfig({ app_secret: c.args.appSecret });
358
+ return { saved: true, config: '~/.config/pryvy/config.json' };
359
+ },
360
+ });
361
+ configure.command('format', {
362
+ description: 'Set the default output format',
363
+ args: z.object({ format: z.enum(['toon', 'json', 'yaml', 'md', 'jsonl']).describe('Output format') }),
364
+ async run(c) {
365
+ writeConfig({ format: c.args.format });
366
+ return { saved: true, format: c.args.format };
367
+ },
368
+ });
369
+ configure.command('show', {
370
+ description: 'Show current configuration',
371
+ async run() {
372
+ const appId = getAppId();
373
+ const appSecret = getAppSecret();
374
+ if (!appId || !appSecret)
375
+ return { error: 'No credentials configured. Run: pryvy configure app-id <id> && pryvy configure app-secret <secret>' };
376
+ const source = process.env.PRIVY_APP_ID ? 'env vars' : '~/.config/pryvy/config.json';
377
+ return {
378
+ app_id: appId,
379
+ app_secret: appSecret.slice(0, 8) + '...' + appSecret.slice(-4),
380
+ format: getDefaultFormat() ?? 'toon',
381
+ source,
382
+ };
383
+ },
384
+ });
385
+ cli.command(configure);
386
+ export default cli;
@@ -0,0 +1,21 @@
1
+ /** Write a key-value pair to the config file. */
2
+ export declare function writeConfig(data: Record<string, string>): void;
3
+ /** Resolve the App ID: env var takes precedence, then config file. */
4
+ export declare function getAppId(): string;
5
+ /** Resolve the App Secret: env var takes precedence, then config file. */
6
+ export declare function getAppSecret(): string;
7
+ /** Get the saved default output format. */
8
+ export declare function getDefaultFormat(): string | undefined;
9
+ /** Returns the full URL for a Privy API path. */
10
+ export declare const url: (path: string) => string;
11
+ /** Thin fetch wrapper for the Privy API. */
12
+ export declare const privy: {
13
+ /** GET a Privy API endpoint, with optional query params. */
14
+ get: (path: string, params?: Record<string, string>) => Promise<any>;
15
+ /** POST to a Privy API endpoint. */
16
+ post: (path: string, body: Record<string, unknown>) => Promise<any>;
17
+ /** PATCH a Privy API endpoint. */
18
+ patch: (path: string, body: Record<string, unknown>) => Promise<any>;
19
+ /** DELETE a Privy API endpoint. */
20
+ delete: (path: string) => Promise<any>;
21
+ };
@@ -0,0 +1,65 @@
1
+ import { readFileSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ const CONFIG_DIR = join(homedir(), '.config', 'pryvy');
5
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
6
+ function readConfig() {
7
+ try {
8
+ return JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ /** Write a key-value pair to the config file. */
15
+ export function writeConfig(data) {
16
+ mkdirSync(CONFIG_DIR, { recursive: true });
17
+ writeFileSync(CONFIG_FILE, JSON.stringify({ ...readConfig(), ...data }, null, 2) + '\n');
18
+ }
19
+ /** Resolve the App ID: env var takes precedence, then config file. */
20
+ export function getAppId() {
21
+ return process.env.PRIVY_APP_ID ?? readConfig().app_id ?? '';
22
+ }
23
+ /** Resolve the App Secret: env var takes precedence, then config file. */
24
+ export function getAppSecret() {
25
+ return process.env.PRIVY_APP_SECRET ?? readConfig().app_secret ?? '';
26
+ }
27
+ /** Get the saved default output format. */
28
+ export function getDefaultFormat() {
29
+ return readConfig().format;
30
+ }
31
+ const BASE_URL = 'https://api.privy.io/v1';
32
+ /** Returns the full URL for a Privy API path. */
33
+ export const url = (path) => `${BASE_URL}${path}`;
34
+ function request(method, path, body) {
35
+ const appId = getAppId();
36
+ const appSecret = getAppSecret();
37
+ if (!appId || !appSecret)
38
+ throw new Error('No Privy credentials configured. Run: pryvy configure app-id <id> && pryvy configure app-secret <secret>');
39
+ const h = {
40
+ 'Authorization': `Basic ${Buffer.from(`${appId}:${appSecret}`).toString('base64')}`,
41
+ 'privy-app-id': appId,
42
+ 'Content-Type': 'application/json',
43
+ };
44
+ if (method === 'POST' || method === 'PUT' || method === 'PATCH')
45
+ h['privy-idempotency-key'] = crypto.randomUUID();
46
+ return fetch(url(path), {
47
+ method,
48
+ headers: h,
49
+ ...(body ? { body: JSON.stringify(body) } : {}),
50
+ }).then(res => res.json());
51
+ }
52
+ /** Thin fetch wrapper for the Privy API. */
53
+ export const privy = {
54
+ /** GET a Privy API endpoint, with optional query params. */
55
+ get: (path, params) => {
56
+ const qs = params ? '?' + new URLSearchParams(params).toString() : '';
57
+ return request('GET', path + qs);
58
+ },
59
+ /** POST to a Privy API endpoint. */
60
+ post: (path, body) => request('POST', path, body),
61
+ /** PATCH a Privy API endpoint. */
62
+ patch: (path, body) => request('PATCH', path, body),
63
+ /** DELETE a Privy API endpoint. */
64
+ delete: (path) => request('DELETE', path),
65
+ };
@@ -0,0 +1,10 @@
1
+ /** Create a policy. */
2
+ export declare const createPolicy: (data: Record<string, unknown>) => Promise<any>;
3
+ /** Get a policy by ID. */
4
+ export declare const getPolicy: (id: string) => Promise<any>;
5
+ /** List all policies. */
6
+ export declare const listPolicies: (params?: Record<string, string>) => Promise<any>;
7
+ /** Update a policy by ID. */
8
+ export declare const updatePolicy: (id: string, data: Record<string, unknown>) => Promise<any>;
9
+ /** Delete a policy by ID. */
10
+ export declare const deletePolicy: (id: string) => Promise<any>;
@@ -0,0 +1,11 @@
1
+ import { privy } from './client.js';
2
+ /** Create a policy. */
3
+ export const createPolicy = (data) => privy.post('/policies', data);
4
+ /** Get a policy by ID. */
5
+ export const getPolicy = (id) => privy.get(`/policies/${id}`);
6
+ /** List all policies. */
7
+ export const listPolicies = (params) => privy.get('/policies', params);
8
+ /** Update a policy by ID. */
9
+ export const updatePolicy = (id, data) => privy.patch(`/policies/${id}`, data);
10
+ /** Delete a policy by ID. */
11
+ export const deletePolicy = (id) => privy.delete(`/policies/${id}`);
@@ -0,0 +1,16 @@
1
+ /** Sign a message with personal_sign. */
2
+ export declare const personalSign: (walletId: string, message: string, encoding?: string) => Promise<any>;
3
+ /** Send a transaction with eth_sendTransaction. */
4
+ export declare const sendTransaction: (walletId: string, caip2: string, transaction: Record<string, unknown>, sponsor?: boolean) => Promise<any>;
5
+ /** Sign a transaction with eth_signTransaction. */
6
+ export declare const signTransaction: (walletId: string, caip2: string, transaction: Record<string, unknown>) => Promise<any>;
7
+ /** Sign typed data with eth_signTypedData_v4. */
8
+ export declare const signTypedData: (walletId: string, typedData: Record<string, unknown>) => Promise<any>;
9
+ /** Sign an EIP-7702 authorization. */
10
+ export declare const sign7702Authorization: (walletId: string, caip2: string, authorization: Record<string, unknown>) => Promise<any>;
11
+ /** Sign a user operation with eth_signUserOperation. */
12
+ export declare const signUserOperation: (walletId: string, caip2: string, userOperation: Record<string, unknown>) => Promise<any>;
13
+ /** Sign a Solana transaction. */
14
+ export declare const solSignTransaction: (walletId: string, transaction: string) => Promise<any>;
15
+ /** Sign a Solana message. */
16
+ export declare const solSignMessage: (walletId: string, message: string, encoding?: string) => Promise<any>;
@@ -0,0 +1,19 @@
1
+ import { privy } from './client.js';
2
+ /** Send an RPC request to a wallet. */
3
+ const rpc = (walletId, body) => privy.post(`/wallets/${walletId}/rpc`, body);
4
+ /** Sign a message with personal_sign. */
5
+ export const personalSign = (walletId, message, encoding) => rpc(walletId, { method: 'personal_sign', params: { message, ...(encoding ? { encoding } : {}) } });
6
+ /** Send a transaction with eth_sendTransaction. */
7
+ export const sendTransaction = (walletId, caip2, transaction, sponsor) => rpc(walletId, { method: 'eth_sendTransaction', caip2, params: { transaction }, ...(sponsor ? { sponsor: true } : {}) });
8
+ /** Sign a transaction with eth_signTransaction. */
9
+ export const signTransaction = (walletId, caip2, transaction) => rpc(walletId, { method: 'eth_signTransaction', caip2, params: { transaction } });
10
+ /** Sign typed data with eth_signTypedData_v4. */
11
+ export const signTypedData = (walletId, typedData) => rpc(walletId, { method: 'eth_signTypedData_v4', params: { typed_data: typedData } });
12
+ /** Sign an EIP-7702 authorization. */
13
+ export const sign7702Authorization = (walletId, caip2, authorization) => rpc(walletId, { method: 'eth_sign7702Authorization', caip2, params: { authorization } });
14
+ /** Sign a user operation with eth_signUserOperation. */
15
+ export const signUserOperation = (walletId, caip2, userOperation) => rpc(walletId, { method: 'eth_signUserOperation', caip2, params: { user_operation: userOperation } });
16
+ /** Sign a Solana transaction. */
17
+ export const solSignTransaction = (walletId, transaction) => rpc(walletId, { method: 'signTransaction', params: { transaction } });
18
+ /** Sign a Solana message. */
19
+ export const solSignMessage = (walletId, message, encoding) => rpc(walletId, { method: 'signMessage', params: { message, ...(encoding ? { encoding } : {}) } });
@@ -0,0 +1,2 @@
1
+ /** Get a transaction by ID. */
2
+ export declare const getTransaction: (id: string) => Promise<any>;
@@ -0,0 +1,3 @@
1
+ import { privy } from './client.js';
2
+ /** Get a transaction by ID. */
3
+ export const getTransaction = (id) => privy.get(`/transactions/${id}`);
@@ -0,0 +1,14 @@
1
+ /** Create a new user with linked accounts. */
2
+ export declare const createUser: (data: Record<string, unknown>) => Promise<any>;
3
+ /** Get a user by ID. */
4
+ export declare const getUser: (id: string) => Promise<any>;
5
+ /** List all users. */
6
+ export declare const listUsers: (params?: Record<string, string>) => Promise<any>;
7
+ /** Delete a user by ID. */
8
+ export declare const deleteUser: (id: string) => Promise<any>;
9
+ /** Search users by term, emails, phone numbers, or wallet addresses. */
10
+ export declare const searchUsers: (data: Record<string, unknown>) => Promise<any>;
11
+ /** Add custom metadata to a user. */
12
+ export declare const addCustomMetadata: (id: string, data: Record<string, unknown>) => Promise<any>;
13
+ /** Pregenerate embedded wallets for a user. */
14
+ export declare const pregenerateWallets: (id: string, data: Record<string, unknown>) => Promise<any>;
@@ -0,0 +1,15 @@
1
+ import { privy } from './client.js';
2
+ /** Create a new user with linked accounts. */
3
+ export const createUser = (data) => privy.post('/users', data);
4
+ /** Get a user by ID. */
5
+ export const getUser = (id) => privy.get(`/users/${id}`);
6
+ /** List all users. */
7
+ export const listUsers = (params) => privy.get('/users', params);
8
+ /** Delete a user by ID. */
9
+ export const deleteUser = (id) => privy.delete(`/users/${id}`);
10
+ /** Search users by term, emails, phone numbers, or wallet addresses. */
11
+ export const searchUsers = (data) => privy.post('/users/search', data);
12
+ /** Add custom metadata to a user. */
13
+ export const addCustomMetadata = (id, data) => privy.patch(`/users/${id}/custom_metadata`, data);
14
+ /** Pregenerate embedded wallets for a user. */
15
+ export const pregenerateWallets = (id, data) => privy.post(`/users/${id}/wallets`, data);
@@ -0,0 +1,12 @@
1
+ /** Create a wallet. */
2
+ export declare const createWallet: (data: Record<string, unknown>) => Promise<any>;
3
+ /** Get a wallet by ID. */
4
+ export declare const getWallet: (id: string) => Promise<any>;
5
+ /** List all wallets. */
6
+ export declare const listWallets: (params?: Record<string, string>) => Promise<any>;
7
+ /** Get wallet balance. */
8
+ export declare const getWalletBalance: (id: string, params?: Record<string, string>) => Promise<any>;
9
+ /** Get a wallet by address. */
10
+ export declare const getWalletByAddress: (address: string) => Promise<any>;
11
+ /** Create wallets in batch. */
12
+ export declare const batchCreateWallets: (data: Record<string, unknown>) => Promise<any>;
@@ -0,0 +1,13 @@
1
+ import { privy } from './client.js';
2
+ /** Create a wallet. */
3
+ export const createWallet = (data) => privy.post('/wallets', data);
4
+ /** Get a wallet by ID. */
5
+ export const getWallet = (id) => privy.get(`/wallets/${id}`);
6
+ /** List all wallets. */
7
+ export const listWallets = (params) => privy.get('/wallets', params);
8
+ /** Get wallet balance. */
9
+ export const getWalletBalance = (id, params) => privy.get(`/wallets/${id}/balance`, params);
10
+ /** Get a wallet by address. */
11
+ export const getWalletByAddress = (address) => privy.post('/wallets/address', { address });
12
+ /** Create wallets in batch. */
13
+ export const batchCreateWallets = (data) => privy.post('/wallets/batch', data);
@@ -0,0 +1 @@
1
+ export { privy } from './core/client.js';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { privy } from './core/client.js';
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "pryvy",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Privy embedded wallets & auth CLI.",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "privy",
9
+ "wallets",
10
+ "auth",
11
+ "cli",
12
+ "mcp",
13
+ "embedded-wallets",
14
+ "signing",
15
+ "web3"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/struong/pryvy.git"
20
+ },
21
+ "bin": {
22
+ "pryvy": "./dist/bin.js"
23
+ },
24
+ "exports": {
25
+ ".": "./dist/index.js"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc && chmod +x dist/bin.js",
32
+ "typecheck": "tsc --noEmit",
33
+ "changeset:version": "changeset version",
34
+ "changeset:publish": "bun run build && changeset publish"
35
+ },
36
+ "dependencies": {
37
+ "incur": "^0.3.13"
38
+ },
39
+ "devDependencies": {
40
+ "@changesets/changelog-github": "^0.6.0",
41
+ "@changesets/cli": "^2.30.0",
42
+ "@types/node": "^22.0.0",
43
+ "typescript": "^5.7.0"
44
+ }
45
+ }