@manifest-network/manifest-mcp-node 0.1.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 +163 -0
- package/dist/bootstrap.d.ts +28 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +82 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/chain.d.ts +1 -0
- package/dist/chain.js +13 -0
- package/dist/chain.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +46 -0
- package/dist/config.js.map +1 -0
- package/dist/fred.d.ts +1 -0
- package/dist/fred.js +13 -0
- package/dist/fred.js.map +1 -0
- package/dist/keyfileWallet.d.ts +24 -0
- package/dist/keyfileWallet.d.ts.map +1 -0
- package/dist/keyfileWallet.js +116 -0
- package/dist/keyfileWallet.js.map +1 -0
- package/dist/keygen.d.ts +6 -0
- package/dist/keygen.d.ts.map +1 -0
- package/dist/keygen.js +163 -0
- package/dist/keygen.js.map +1 -0
- package/dist/lease.d.ts +1 -0
- package/dist/lease.js +13 -0
- package/dist/lease.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @manifest-network/manifest-mcp-node
|
|
2
|
+
|
|
3
|
+
Node.js CLI entry points for the Manifest MCP servers with stdio transport and encrypted keyfile wallet.
|
|
4
|
+
|
|
5
|
+
Provides three binaries:
|
|
6
|
+
- **`manifest-mcp-chain`** -- Chain MCP server (5 tools: queries, transactions, module discovery)
|
|
7
|
+
- **`manifest-mcp-lease`** -- Lease MCP server (6 tools: credit balance, funding, lease queries, SKUs, providers)
|
|
8
|
+
- **`manifest-mcp-fred`** -- Fred MCP server (8 tools: catalog, deployment, status, logs, restart, update, diagnostics, releases)
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# From the monorepo root
|
|
14
|
+
npm install
|
|
15
|
+
npm run build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Wallet setup
|
|
19
|
+
|
|
20
|
+
All three servers need a wallet to sign transactions. Choose one of the options below.
|
|
21
|
+
|
|
22
|
+
### Option A -- Generate a new keyfile (recommended)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx manifest-mcp-chain keygen
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
All CLIs share the same keyfile (`~/.manifest/key.json`), so any of the three commands works for `keygen` and `import`. You will be prompted for an encryption password. The keyfile is written with mode `0600`.
|
|
29
|
+
|
|
30
|
+
### Option B -- Import an existing mnemonic
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx manifest-mcp-chain import
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
You will be prompted for your mnemonic (any valid BIP-39 length: 12, 15, 18, 21, or 24 words) and an encryption password. The wallet is derived from the mnemonic, encrypted, and stored in the same keyfile location. The raw mnemonic is not retained.
|
|
37
|
+
|
|
38
|
+
### Option C -- Mnemonic via environment variable (fallback)
|
|
39
|
+
|
|
40
|
+
Set `COSMOS_MNEMONIC` in your `.env` or shell environment. This is used only when no keyfile exists.
|
|
41
|
+
|
|
42
|
+
### Wallet resolution order
|
|
43
|
+
|
|
44
|
+
1. If the keyfile exists at the path specified by `MANIFEST_KEY_FILE` (default `~/.manifest/key.json`), use it
|
|
45
|
+
2. Otherwise, if `COSMOS_MNEMONIC` is set, use it
|
|
46
|
+
3. Exit with an error if neither is available
|
|
47
|
+
|
|
48
|
+
## CLI reference
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
manifest-mcp-chain Start the chain MCP server (stdio)
|
|
52
|
+
manifest-mcp-chain keygen Generate a new encrypted keyfile
|
|
53
|
+
manifest-mcp-chain import Import a mnemonic into an encrypted keyfile
|
|
54
|
+
|
|
55
|
+
manifest-mcp-lease Start the lease MCP server (stdio)
|
|
56
|
+
manifest-mcp-lease keygen Generate a new encrypted keyfile
|
|
57
|
+
manifest-mcp-lease import Import a mnemonic into an encrypted keyfile
|
|
58
|
+
|
|
59
|
+
manifest-mcp-fred Start the fred MCP server (stdio)
|
|
60
|
+
manifest-mcp-fred keygen Generate a new encrypted keyfile
|
|
61
|
+
manifest-mcp-fred import Import a mnemonic into an encrypted keyfile
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## MCP client integration
|
|
65
|
+
|
|
66
|
+
### Claude Desktop
|
|
67
|
+
|
|
68
|
+
Add the following to your `claude_desktop_config.json`:
|
|
69
|
+
|
|
70
|
+
```jsonc
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"manifest-chain": {
|
|
74
|
+
"command": "npx",
|
|
75
|
+
"args": ["manifest-mcp-chain"],
|
|
76
|
+
"env": {
|
|
77
|
+
"COSMOS_CHAIN_ID": "manifest-ledger-beta",
|
|
78
|
+
"COSMOS_RPC_URL": "https://nodes.chandrastation.com/rpc/manifest/",
|
|
79
|
+
"COSMOS_GAS_PRICE": "0.01umfx",
|
|
80
|
+
"MANIFEST_KEY_PASSWORD": "your-keyfile-password"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"manifest-lease": {
|
|
84
|
+
"command": "npx",
|
|
85
|
+
"args": ["manifest-mcp-lease"],
|
|
86
|
+
"env": {
|
|
87
|
+
"COSMOS_CHAIN_ID": "manifest-ledger-beta",
|
|
88
|
+
"COSMOS_RPC_URL": "https://nodes.chandrastation.com/rpc/manifest/",
|
|
89
|
+
"COSMOS_GAS_PRICE": "0.01umfx",
|
|
90
|
+
"MANIFEST_KEY_PASSWORD": "your-keyfile-password"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"manifest-fred": {
|
|
94
|
+
"command": "npx",
|
|
95
|
+
"args": ["manifest-mcp-fred"],
|
|
96
|
+
"env": {
|
|
97
|
+
"COSMOS_CHAIN_ID": "manifest-ledger-beta",
|
|
98
|
+
"COSMOS_RPC_URL": "https://nodes.chandrastation.com/rpc/manifest/",
|
|
99
|
+
"COSMOS_GAS_PRICE": "0.01umfx",
|
|
100
|
+
"MANIFEST_KEY_PASSWORD": "your-keyfile-password"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If you use a mnemonic instead of a keyfile, replace `MANIFEST_KEY_PASSWORD` with `COSMOS_MNEMONIC`.
|
|
108
|
+
|
|
109
|
+
## Environment variables
|
|
110
|
+
|
|
111
|
+
| Variable | Required | Default | Description |
|
|
112
|
+
|----------|----------|---------|-------------|
|
|
113
|
+
| `COSMOS_CHAIN_ID` | Yes | -- | Chain ID (e.g. `manifest-ledger-beta`) |
|
|
114
|
+
| `COSMOS_RPC_URL` | One of `COSMOS_RPC_URL` or `COSMOS_REST_URL` required | -- | RPC endpoint URL (HTTPS required; HTTP allowed for localhost) |
|
|
115
|
+
| `COSMOS_GAS_PRICE` | Required when `COSMOS_RPC_URL` is set | -- | Gas price with denom (e.g. `0.01umfx`) |
|
|
116
|
+
| `COSMOS_REST_URL` | One of `COSMOS_RPC_URL` or `COSMOS_REST_URL` required | -- | LCD/REST endpoint URL for query-only mode |
|
|
117
|
+
| `COSMOS_ADDRESS_PREFIX` | No | `manifest` | Bech32 address prefix |
|
|
118
|
+
| `MANIFEST_KEY_FILE` | No | `~/.manifest/key.json` | Path to the encrypted keyfile |
|
|
119
|
+
| `MANIFEST_KEY_PASSWORD` | No | -- | Password to decrypt the keyfile |
|
|
120
|
+
| `COSMOS_MNEMONIC` | No | -- | BIP-39 mnemonic (fallback when no keyfile exists) |
|
|
121
|
+
| `LOG_LEVEL` | No | `warn` | Log level: `debug`, `info`, `warn`, `error`, or `silent` |
|
|
122
|
+
|
|
123
|
+
Set `COSMOS_RPC_URL` + `COSMOS_GAS_PRICE` for full access (queries + transactions). Set `COSMOS_REST_URL` alone for query-only mode (LCD/REST). When both are set, `COSMOS_REST_URL` is preferred for queries.
|
|
124
|
+
|
|
125
|
+
`COSMOS_CHAIN_ID` and at least one endpoint URL are only required when starting an MCP server, not for `keygen` or `import`.
|
|
126
|
+
|
|
127
|
+
## Chain server tools (5)
|
|
128
|
+
|
|
129
|
+
| Tool | Description |
|
|
130
|
+
|------|-------------|
|
|
131
|
+
| `get_account_info` | Get account address for the configured key |
|
|
132
|
+
| `cosmos_query` | Execute any Cosmos SDK query command |
|
|
133
|
+
| `cosmos_tx` | Execute any Cosmos SDK transaction |
|
|
134
|
+
| `list_modules` | List all available query and transaction modules |
|
|
135
|
+
| `list_module_subcommands` | List available subcommands for a specific module |
|
|
136
|
+
|
|
137
|
+
## Lease server tools (6)
|
|
138
|
+
|
|
139
|
+
| Tool | Description |
|
|
140
|
+
|------|-------------|
|
|
141
|
+
| `credit_balance` | Query on-chain credit balance |
|
|
142
|
+
| `fund_credit` | Send tokens to the billing account |
|
|
143
|
+
| `leases_by_tenant` | List leases for the current account by state |
|
|
144
|
+
| `close_lease` | Close a lease on-chain |
|
|
145
|
+
| `get_skus` | List available SKUs |
|
|
146
|
+
| `get_providers` | List available providers |
|
|
147
|
+
|
|
148
|
+
## Fred server tools (8)
|
|
149
|
+
|
|
150
|
+
| Tool | Description |
|
|
151
|
+
|------|-------------|
|
|
152
|
+
| `browse_catalog` | Browse available providers and service tiers with health checks |
|
|
153
|
+
| `deploy_app` | Deploy a new application (create lease + deploy container) |
|
|
154
|
+
| `app_status` | Get detailed status for a deployed app by lease UUID |
|
|
155
|
+
| `get_logs` | Get logs for a deployed app by lease UUID |
|
|
156
|
+
| `restart_app` | Restart a deployed app via the provider |
|
|
157
|
+
| `update_app` | Update a deployed app with a new manifest |
|
|
158
|
+
| `app_diagnostics` | Get provision diagnostics for a deployed app |
|
|
159
|
+
| `app_releases` | Get release/version history for a deployed app |
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { WalletProvider, createValidatedConfig } from "@manifest-network/manifest-mcp-core";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
|
|
4
|
+
//#region src/bootstrap.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for bootstrapping a CLI entry point.
|
|
7
|
+
*/
|
|
8
|
+
interface BootstrapConfig {
|
|
9
|
+
/** CLI binary name shown in usage text (e.g. "manifest-mcp-chain") */
|
|
10
|
+
readonly cliName: string;
|
|
11
|
+
/** Human-readable server label for the startup log (e.g. "chain") */
|
|
12
|
+
readonly label: string;
|
|
13
|
+
/** Factory that creates the MCP server and returns its underlying Server */
|
|
14
|
+
readonly createServer: (opts: {
|
|
15
|
+
config: ReturnType<typeof createValidatedConfig>;
|
|
16
|
+
walletProvider: WalletProvider;
|
|
17
|
+
}) => Server;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Shared bootstrap for all three CLI entry points (chain, lease, fred).
|
|
21
|
+
*
|
|
22
|
+
* Handles subcommand dispatch, config loading, wallet resolution,
|
|
23
|
+
* transport setup, and top-level error handling.
|
|
24
|
+
*/
|
|
25
|
+
declare function bootstrap(cfg: BootstrapConfig): void;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { BootstrapConfig, bootstrap };
|
|
28
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","names":[],"sources":["../src/bootstrap.ts"],"mappings":";;;;;;AA6BA;UAAiB,eAAA;;WAEN,OAAA;EAKC;EAAA,SAHD,KAAA;EAKH;EAAA,SAHG,YAAA,GAAe,IAAA;IACtB,MAAA,EAAQ,UAAA,QAAkB,qBAAA;IAC1B,cAAA,EAAgB,cAAA;EAAA,MACZ,MAAA;AAAA;;;;;;;iBA0DQ,SAAA,CAAU,GAAA,EAAK,eAAA"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { loadConfig } from "./config.js";
|
|
2
|
+
import { KeyfileWalletProvider } from "./keyfileWallet.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { ManifestMCPError, MnemonicWalletProvider, createValidatedConfig, sanitizeForLogging } from "@manifest-network/manifest-mcp-core";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
//#region src/bootstrap.ts
|
|
7
|
+
/** Thrown after process.exit() to halt control flow when exit is mocked. */
|
|
8
|
+
var ExitError = class extends Error {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.name = "ExitError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function exit(code) {
|
|
15
|
+
process.exit(code);
|
|
16
|
+
throw new ExitError();
|
|
17
|
+
}
|
|
18
|
+
function handleSubcommand(cliName, label, subcommand) {
|
|
19
|
+
if (subcommand === "keygen") return import("./keygen.js").then(({ runKeygen }) => runKeygen());
|
|
20
|
+
if (subcommand === "import") return import("./keygen.js").then(({ runImport }) => runImport());
|
|
21
|
+
console.error(`Unknown subcommand: "${subcommand}"\n\nUsage:
|
|
22
|
+
${cliName} Start the ${label} MCP server\n ${cliName} keygen Generate a new encrypted keyfile\n ${cliName} import Import a mnemonic into an encrypted keyfile\n`);
|
|
23
|
+
exit(1);
|
|
24
|
+
}
|
|
25
|
+
function resolveWallet(env, config, cliName) {
|
|
26
|
+
if (existsSync(env.keyfilePath)) {
|
|
27
|
+
console.error(`Using encrypted keyfile wallet from ${env.keyfilePath}`);
|
|
28
|
+
return new KeyfileWalletProvider(env.keyfilePath, env.addressPrefix, env.keyPassword);
|
|
29
|
+
}
|
|
30
|
+
if (env.mnemonic) {
|
|
31
|
+
console.error("Using mnemonic wallet from COSMOS_MNEMONIC");
|
|
32
|
+
return new MnemonicWalletProvider(config, env.mnemonic);
|
|
33
|
+
}
|
|
34
|
+
console.error(`No wallet found. Either:
|
|
35
|
+
1. Run "${cliName} keygen" to generate an encrypted keyfile at ${env.keyfilePath}\n 2. Set the COSMOS_MNEMONIC environment variable`);
|
|
36
|
+
exit(1);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Shared bootstrap for all three CLI entry points (chain, lease, fred).
|
|
40
|
+
*
|
|
41
|
+
* Handles subcommand dispatch, config loading, wallet resolution,
|
|
42
|
+
* transport setup, and top-level error handling.
|
|
43
|
+
*/
|
|
44
|
+
function bootstrap(cfg) {
|
|
45
|
+
async function main() {
|
|
46
|
+
const subcommand = process.argv[2];
|
|
47
|
+
if (subcommand) {
|
|
48
|
+
await handleSubcommand(cfg.cliName, cfg.label, subcommand);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const env = loadConfig();
|
|
52
|
+
const config = createValidatedConfig({
|
|
53
|
+
chainId: env.chainId,
|
|
54
|
+
rpcUrl: env.rpcUrl,
|
|
55
|
+
gasPrice: env.gasPrice,
|
|
56
|
+
restUrl: env.restUrl,
|
|
57
|
+
addressPrefix: env.addressPrefix
|
|
58
|
+
});
|
|
59
|
+
const walletProvider = resolveWallet(env, config, cfg.cliName);
|
|
60
|
+
if (walletProvider.connect) await walletProvider.connect();
|
|
61
|
+
const server = cfg.createServer({
|
|
62
|
+
config,
|
|
63
|
+
walletProvider
|
|
64
|
+
});
|
|
65
|
+
const transport = new StdioServerTransport();
|
|
66
|
+
await server.connect(transport);
|
|
67
|
+
console.error(`Manifest MCP ${cfg.label} server running on stdio`);
|
|
68
|
+
}
|
|
69
|
+
main().catch((error) => {
|
|
70
|
+
if (error instanceof ExitError) return;
|
|
71
|
+
if (error instanceof ManifestMCPError) console.error(`Fatal error [${error.code}]: ${sanitizeForLogging(error.message)}`);
|
|
72
|
+
else {
|
|
73
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
74
|
+
console.error(`Fatal error: ${sanitizeForLogging(msg)}`);
|
|
75
|
+
}
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { bootstrap };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","names":[],"sources":["../src/bootstrap.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport {\n createValidatedConfig,\n ManifestMCPError,\n MnemonicWalletProvider,\n sanitizeForLogging,\n type WalletProvider,\n} from '@manifest-network/manifest-mcp-core';\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './config.js';\nimport { KeyfileWalletProvider } from './keyfileWallet.js';\n\n/** Thrown after process.exit() to halt control flow when exit is mocked. */\nclass ExitError extends Error {\n constructor() {\n super();\n this.name = 'ExitError';\n }\n}\n\nfunction exit(code: number): never {\n process.exit(code);\n throw new ExitError();\n}\n\n/**\n * Configuration for bootstrapping a CLI entry point.\n */\nexport interface BootstrapConfig {\n /** CLI binary name shown in usage text (e.g. \"manifest-mcp-chain\") */\n readonly cliName: string;\n /** Human-readable server label for the startup log (e.g. \"chain\") */\n readonly label: string;\n /** Factory that creates the MCP server and returns its underlying Server */\n readonly createServer: (opts: {\n config: ReturnType<typeof createValidatedConfig>;\n walletProvider: WalletProvider;\n }) => Server;\n}\n\nfunction handleSubcommand(\n cliName: string,\n label: string,\n subcommand: string,\n): Promise<void> {\n if (subcommand === 'keygen') {\n return import('./keygen.js').then(({ runKeygen }) => runKeygen());\n }\n if (subcommand === 'import') {\n return import('./keygen.js').then(({ runImport }) => runImport());\n }\n\n console.error(\n `Unknown subcommand: \"${subcommand}\"\\n\\n` +\n 'Usage:\\n' +\n ` ${cliName} Start the ${label} MCP server\\n` +\n ` ${cliName} keygen Generate a new encrypted keyfile\\n` +\n ` ${cliName} import Import a mnemonic into an encrypted keyfile\\n`,\n );\n exit(1);\n}\n\nfunction resolveWallet(\n env: ReturnType<typeof loadConfig>,\n config: ReturnType<typeof createValidatedConfig>,\n cliName: string,\n): WalletProvider {\n if (existsSync(env.keyfilePath)) {\n console.error(`Using encrypted keyfile wallet from ${env.keyfilePath}`);\n return new KeyfileWalletProvider(\n env.keyfilePath,\n env.addressPrefix,\n env.keyPassword,\n );\n }\n\n if (env.mnemonic) {\n console.error('Using mnemonic wallet from COSMOS_MNEMONIC');\n return new MnemonicWalletProvider(config, env.mnemonic);\n }\n\n console.error(\n 'No wallet found. Either:\\n' +\n ` 1. Run \"${cliName} keygen\" to generate an encrypted keyfile at ${env.keyfilePath}\\n` +\n ' 2. Set the COSMOS_MNEMONIC environment variable',\n );\n exit(1);\n}\n\n/**\n * Shared bootstrap for all three CLI entry points (chain, lease, fred).\n *\n * Handles subcommand dispatch, config loading, wallet resolution,\n * transport setup, and top-level error handling.\n */\nexport function bootstrap(cfg: BootstrapConfig): void {\n async function main(): Promise<void> {\n const subcommand = process.argv[2];\n if (subcommand) {\n await handleSubcommand(cfg.cliName, cfg.label, subcommand);\n return;\n }\n\n const env = loadConfig();\n\n const config = createValidatedConfig({\n chainId: env.chainId,\n rpcUrl: env.rpcUrl,\n gasPrice: env.gasPrice,\n restUrl: env.restUrl,\n addressPrefix: env.addressPrefix,\n });\n\n const walletProvider = resolveWallet(env, config, cfg.cliName);\n\n if (walletProvider.connect) {\n await walletProvider.connect();\n }\n\n const server = cfg.createServer({ config, walletProvider });\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(`Manifest MCP ${cfg.label} server running on stdio`);\n }\n\n main().catch((error) => {\n if (error instanceof ExitError) return;\n if (error instanceof ManifestMCPError) {\n console.error(\n `Fatal error [${error.code}]: ${sanitizeForLogging(error.message) as string}`,\n );\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`Fatal error: ${sanitizeForLogging(msg) as string}`);\n }\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;AAcA,IAAM,YAAN,cAAwB,MAAM;CAC5B,cAAc;AACZ,SAAO;AACP,OAAK,OAAO;;;AAIhB,SAAS,KAAK,MAAqB;AACjC,SAAQ,KAAK,KAAK;AAClB,OAAM,IAAI,WAAW;;AAkBvB,SAAS,iBACP,SACA,OACA,YACe;AACf,KAAI,eAAe,SACjB,QAAO,OAAO,eAAe,MAAM,EAAE,gBAAgB,WAAW,CAAC;AAEnE,KAAI,eAAe,SACjB,QAAO,OAAO,eAAe,MAAM,EAAE,gBAAgB,WAAW,CAAC;AAGnE,SAAQ,MACN,wBAAwB,WAAW;IAE5B,QAAQ,0BAA0B,MAAM,iBACxC,QAAQ,oDACR,QAAQ,6DAChB;AACD,MAAK,EAAE;;AAGT,SAAS,cACP,KACA,QACA,SACgB;AAChB,KAAI,WAAW,IAAI,YAAY,EAAE;AAC/B,UAAQ,MAAM,uCAAuC,IAAI,cAAc;AACvE,SAAO,IAAI,sBACT,IAAI,aACJ,IAAI,eACJ,IAAI,YACL;;AAGH,KAAI,IAAI,UAAU;AAChB,UAAQ,MAAM,6CAA6C;AAC3D,SAAO,IAAI,uBAAuB,QAAQ,IAAI,SAAS;;AAGzD,SAAQ,MACN;YACe,QAAQ,+CAA+C,IAAI,YAAY,qDAEvF;AACD,MAAK,EAAE;;;;;;;;AAST,SAAgB,UAAU,KAA4B;CACpD,eAAe,OAAsB;EACnC,MAAM,aAAa,QAAQ,KAAK;AAChC,MAAI,YAAY;AACd,SAAM,iBAAiB,IAAI,SAAS,IAAI,OAAO,WAAW;AAC1D;;EAGF,MAAM,MAAM,YAAY;EAExB,MAAM,SAAS,sBAAsB;GACnC,SAAS,IAAI;GACb,QAAQ,IAAI;GACZ,UAAU,IAAI;GACd,SAAS,IAAI;GACb,eAAe,IAAI;GACpB,CAAC;EAEF,MAAM,iBAAiB,cAAc,KAAK,QAAQ,IAAI,QAAQ;AAE9D,MAAI,eAAe,QACjB,OAAM,eAAe,SAAS;EAGhC,MAAM,SAAS,IAAI,aAAa;GAAE;GAAQ;GAAgB,CAAC;EAC3D,MAAM,YAAY,IAAI,sBAAsB;AAC5C,QAAM,OAAO,QAAQ,UAAU;AAE/B,UAAQ,MAAM,gBAAgB,IAAI,MAAM,0BAA0B;;AAGpE,OAAM,CAAC,OAAO,UAAU;AACtB,MAAI,iBAAiB,UAAW;AAChC,MAAI,iBAAiB,iBACnB,SAAQ,MACN,gBAAgB,MAAM,KAAK,KAAK,mBAAmB,MAAM,QAAQ,GAClE;OACI;GACL,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAClE,WAAQ,MAAM,gBAAgB,mBAAmB,IAAI,GAAa;;AAEpE,UAAQ,KAAK,EAAE;GACf"}
|
package/dist/chain.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/chain.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { bootstrap } from "./bootstrap.js";
|
|
3
|
+
import { ChainMCPServer } from "@manifest-network/manifest-mcp-chain";
|
|
4
|
+
//#region src/chain.ts
|
|
5
|
+
bootstrap({
|
|
6
|
+
cliName: "manifest-mcp-chain",
|
|
7
|
+
label: "chain",
|
|
8
|
+
createServer: (opts) => new ChainMCPServer(opts).getServer()
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export {};
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain.js","names":[],"sources":["../src/chain.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { ChainMCPServer } from '@manifest-network/manifest-mcp-chain';\nimport { bootstrap } from './bootstrap.js';\n\nbootstrap({\n cliName: 'manifest-mcp-chain',\n label: 'chain',\n createServer: (opts) => new ChainMCPServer(opts).getServer(),\n});\n"],"mappings":";;;;AAIA,UAAU;CACR,SAAS;CACT,OAAO;CACP,eAAe,SAAS,IAAI,eAAe,KAAK,CAAC,WAAW;CAC7D,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/config.d.ts
|
|
2
|
+
interface NodeMCPConfig {
|
|
3
|
+
readonly chainId: string;
|
|
4
|
+
readonly rpcUrl?: string;
|
|
5
|
+
readonly gasPrice?: string;
|
|
6
|
+
readonly restUrl?: string;
|
|
7
|
+
readonly addressPrefix: string;
|
|
8
|
+
readonly mnemonic?: string;
|
|
9
|
+
readonly keyfilePath: string;
|
|
10
|
+
readonly keyPassword?: string;
|
|
11
|
+
}
|
|
12
|
+
declare function loadKeyfileConfig(): Pick<NodeMCPConfig, 'addressPrefix' | 'keyfilePath'>;
|
|
13
|
+
declare function loadConfig(): NodeMCPConfig;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { NodeMCPConfig, loadConfig, loadKeyfileConfig };
|
|
16
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"mappings":";UAMiB,aAAA;EAAA,SACN,OAAA;EAAA,SACA,MAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;EAAA,SACA,aAAA;EAAA,SACA,QAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;AAAA;AAAA,iBA0BK,iBAAA,CAAA,GAAqB,IAAA,CACnC,aAAA;AAAA,iBAcc,UAAA,CAAA,GAAc,aAAA"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
//#region src/config.ts
|
|
5
|
+
dotenv.config();
|
|
6
|
+
function getEnvRequired(key) {
|
|
7
|
+
const value = process.env[key];
|
|
8
|
+
if (!value) throw new Error(`Environment variable ${key} is not set. Please check your .env file or environment.`);
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
function getEnvOptional(key, defaultValue) {
|
|
12
|
+
const value = process.env[key];
|
|
13
|
+
if (value === void 0 || value === "") return defaultValue;
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function resolvePath(p) {
|
|
17
|
+
if (p.startsWith("~/")) return join(homedir(), p.slice(2));
|
|
18
|
+
return p;
|
|
19
|
+
}
|
|
20
|
+
function loadKeyfileConfig() {
|
|
21
|
+
return {
|
|
22
|
+
addressPrefix: getEnvOptional("COSMOS_ADDRESS_PREFIX", "manifest"),
|
|
23
|
+
keyfilePath: resolvePath(getEnvOptional("MANIFEST_KEY_FILE", join(homedir(), ".manifest", "key.json")))
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function loadConfig() {
|
|
27
|
+
const rpcUrl = process.env.COSMOS_RPC_URL || void 0;
|
|
28
|
+
const gasPrice = process.env.COSMOS_GAS_PRICE || void 0;
|
|
29
|
+
const restUrl = process.env.COSMOS_REST_URL || void 0;
|
|
30
|
+
if (!rpcUrl && !restUrl) throw new Error("At least one of COSMOS_RPC_URL or COSMOS_REST_URL must be set. Set COSMOS_RPC_URL for full access (queries + transactions) or COSMOS_REST_URL for query-only mode.");
|
|
31
|
+
if (rpcUrl && !gasPrice) throw new Error("COSMOS_GAS_PRICE is required when COSMOS_RPC_URL is set (e.g., COSMOS_GAS_PRICE=\"0.025umfx\").");
|
|
32
|
+
return {
|
|
33
|
+
chainId: getEnvRequired("COSMOS_CHAIN_ID"),
|
|
34
|
+
rpcUrl,
|
|
35
|
+
gasPrice,
|
|
36
|
+
restUrl,
|
|
37
|
+
addressPrefix: getEnvOptional("COSMOS_ADDRESS_PREFIX", "manifest"),
|
|
38
|
+
mnemonic: process.env.COSMOS_MNEMONIC,
|
|
39
|
+
keyfilePath: resolvePath(getEnvOptional("MANIFEST_KEY_FILE", join(homedir(), ".manifest", "key.json"))),
|
|
40
|
+
keyPassword: process.env.MANIFEST_KEY_PASSWORD
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { loadConfig, loadKeyfileConfig };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\nexport interface NodeMCPConfig {\n readonly chainId: string;\n readonly rpcUrl?: string;\n readonly gasPrice?: string;\n readonly restUrl?: string;\n readonly addressPrefix: string;\n readonly mnemonic?: string;\n readonly keyfilePath: string;\n readonly keyPassword?: string;\n}\n\nfunction getEnvRequired(key: string): string {\n const value = process.env[key];\n if (!value) {\n throw new Error(\n `Environment variable ${key} is not set. Please check your .env file or environment.`,\n );\n }\n return value;\n}\n\nfunction getEnvOptional(key: string, defaultValue: string): string {\n const value = process.env[key];\n if (value === undefined || value === '') return defaultValue;\n return value;\n}\n\nfunction resolvePath(p: string): string {\n if (p.startsWith('~/')) {\n return join(homedir(), p.slice(2));\n }\n return p;\n}\n\nexport function loadKeyfileConfig(): Pick<\n NodeMCPConfig,\n 'addressPrefix' | 'keyfilePath'\n> {\n return {\n addressPrefix: getEnvOptional('COSMOS_ADDRESS_PREFIX', 'manifest'),\n keyfilePath: resolvePath(\n getEnvOptional(\n 'MANIFEST_KEY_FILE',\n join(homedir(), '.manifest', 'key.json'),\n ),\n ),\n };\n}\n\nexport function loadConfig(): NodeMCPConfig {\n const rpcUrl = process.env.COSMOS_RPC_URL || undefined;\n const gasPrice = process.env.COSMOS_GAS_PRICE || undefined;\n const restUrl = process.env.COSMOS_REST_URL || undefined;\n\n // At least one endpoint is required\n if (!rpcUrl && !restUrl) {\n throw new Error(\n 'At least one of COSMOS_RPC_URL or COSMOS_REST_URL must be set. ' +\n 'Set COSMOS_RPC_URL for full access (queries + transactions) or COSMOS_REST_URL for query-only mode.',\n );\n }\n\n if (rpcUrl && !gasPrice) {\n throw new Error(\n 'COSMOS_GAS_PRICE is required when COSMOS_RPC_URL is set (e.g., COSMOS_GAS_PRICE=\"0.025umfx\").',\n );\n }\n\n return {\n chainId: getEnvRequired('COSMOS_CHAIN_ID'),\n rpcUrl,\n gasPrice,\n restUrl,\n addressPrefix: getEnvOptional('COSMOS_ADDRESS_PREFIX', 'manifest'),\n mnemonic: process.env.COSMOS_MNEMONIC,\n keyfilePath: resolvePath(\n getEnvOptional(\n 'MANIFEST_KEY_FILE',\n join(homedir(), '.manifest', 'key.json'),\n ),\n ),\n keyPassword: process.env.MANIFEST_KEY_PASSWORD,\n };\n}\n"],"mappings":";;;;AAIA,OAAO,QAAQ;AAaf,SAAS,eAAe,KAAqB;CAC3C,MAAM,QAAQ,QAAQ,IAAI;AAC1B,KAAI,CAAC,MACH,OAAM,IAAI,MACR,wBAAwB,IAAI,0DAC7B;AAEH,QAAO;;AAGT,SAAS,eAAe,KAAa,cAA8B;CACjE,MAAM,QAAQ,QAAQ,IAAI;AAC1B,KAAI,UAAU,KAAA,KAAa,UAAU,GAAI,QAAO;AAChD,QAAO;;AAGT,SAAS,YAAY,GAAmB;AACtC,KAAI,EAAE,WAAW,KAAK,CACpB,QAAO,KAAK,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC;AAEpC,QAAO;;AAGT,SAAgB,oBAGd;AACA,QAAO;EACL,eAAe,eAAe,yBAAyB,WAAW;EAClE,aAAa,YACX,eACE,qBACA,KAAK,SAAS,EAAE,aAAa,WAAW,CACzC,CACF;EACF;;AAGH,SAAgB,aAA4B;CAC1C,MAAM,SAAS,QAAQ,IAAI,kBAAkB,KAAA;CAC7C,MAAM,WAAW,QAAQ,IAAI,oBAAoB,KAAA;CACjD,MAAM,UAAU,QAAQ,IAAI,mBAAmB,KAAA;AAG/C,KAAI,CAAC,UAAU,CAAC,QACd,OAAM,IAAI,MACR,qKAED;AAGH,KAAI,UAAU,CAAC,SACb,OAAM,IAAI,MACR,kGACD;AAGH,QAAO;EACL,SAAS,eAAe,kBAAkB;EAC1C;EACA;EACA;EACA,eAAe,eAAe,yBAAyB,WAAW;EAClE,UAAU,QAAQ,IAAI;EACtB,aAAa,YACX,eACE,qBACA,KAAK,SAAS,EAAE,aAAa,WAAW,CACzC,CACF;EACD,aAAa,QAAQ,IAAI;EAC1B"}
|
package/dist/fred.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/fred.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { bootstrap } from "./bootstrap.js";
|
|
3
|
+
import { FredMCPServer } from "@manifest-network/manifest-mcp-fred";
|
|
4
|
+
//#region src/fred.ts
|
|
5
|
+
bootstrap({
|
|
6
|
+
cliName: "manifest-mcp-fred",
|
|
7
|
+
label: "fred",
|
|
8
|
+
createServer: (opts) => new FredMCPServer(opts).getServer()
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export {};
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=fred.js.map
|
package/dist/fred.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fred.js","names":[],"sources":["../src/fred.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { FredMCPServer } from '@manifest-network/manifest-mcp-fred';\nimport { bootstrap } from './bootstrap.js';\n\nbootstrap({\n cliName: 'manifest-mcp-fred',\n label: 'fred',\n createServer: (opts) => new FredMCPServer(opts).getServer(),\n});\n"],"mappings":";;;;AAIA,UAAU;CACR,SAAS;CACT,OAAO;CACP,eAAe,SAAS,IAAI,cAAc,KAAK,CAAC,WAAW;CAC5D,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SignArbitraryResult, WalletProvider } from "@manifest-network/manifest-mcp-core";
|
|
2
|
+
import { OfflineSigner } from "@cosmjs/proto-signing";
|
|
3
|
+
|
|
4
|
+
//#region src/keyfileWallet.d.ts
|
|
5
|
+
declare class KeyfileWalletProvider implements WalletProvider {
|
|
6
|
+
private keyfilePath;
|
|
7
|
+
private addressPrefix;
|
|
8
|
+
private password;
|
|
9
|
+
private wallet;
|
|
10
|
+
private aminoWallet;
|
|
11
|
+
private address;
|
|
12
|
+
private disconnected;
|
|
13
|
+
private initPromise;
|
|
14
|
+
constructor(keyfilePath: string, addressPrefix: string, password?: string);
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
private doConnect;
|
|
17
|
+
getAddress(): Promise<string>;
|
|
18
|
+
getSigner(): Promise<OfflineSigner>;
|
|
19
|
+
disconnect(): Promise<void>;
|
|
20
|
+
signArbitrary(address: string, data: string): Promise<SignArbitraryResult>;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { KeyfileWalletProvider };
|
|
24
|
+
//# sourceMappingURL=keyfileWallet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyfileWallet.d.ts","names":[],"sources":["../src/keyfileWallet.ts"],"mappings":";;;;cAgBa,qBAAA,YAAiC,cAAA;EAAA,QACpC,WAAA;EAAA,QACA,aAAA;EAAA,QACA,QAAA;EAAA,QACA,MAAA;EAAA,QACA,WAAA;EAAA,QACA,OAAA;EAAA,QACA,YAAA;EAAA,QAGA,WAAA;cAEI,WAAA,UAAqB,aAAA,UAAuB,QAAA;EAMlD,OAAA,CAAA,GAAW,OAAA;EAAA,QAcH,SAAA;EA4JR,UAAA,CAAA,GAAc,OAAA;EAYd,SAAA,CAAA,GAAa,OAAA,CAAQ,aAAA;EAYrB,UAAA,CAAA,GAAc,OAAA;EASd,aAAA,CACJ,OAAA,UACA,IAAA,WACC,OAAA,CAAQ,mBAAA;AAAA"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { ManifestMCPError, ManifestMCPErrorCode, logger, signArbitraryWithAmino } from "@manifest-network/manifest-mcp-core";
|
|
3
|
+
import { Secp256k1HdWallet } from "@cosmjs/amino";
|
|
4
|
+
import { fromBech32 } from "@cosmjs/encoding";
|
|
5
|
+
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
|
|
6
|
+
//#region src/keyfileWallet.ts
|
|
7
|
+
var KeyfileWalletProvider = class {
|
|
8
|
+
constructor(keyfilePath, addressPrefix, password) {
|
|
9
|
+
this.wallet = null;
|
|
10
|
+
this.aminoWallet = null;
|
|
11
|
+
this.address = null;
|
|
12
|
+
this.disconnected = false;
|
|
13
|
+
this.initPromise = null;
|
|
14
|
+
this.keyfilePath = keyfilePath;
|
|
15
|
+
this.addressPrefix = addressPrefix;
|
|
16
|
+
this.password = password;
|
|
17
|
+
}
|
|
18
|
+
async connect() {
|
|
19
|
+
if (this.disconnected) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_NOT_CONNECTED, "Wallet has been disconnected. Create a new KeyfileWalletProvider instance to reconnect.");
|
|
20
|
+
if (this.wallet) return;
|
|
21
|
+
if (this.initPromise) return this.initPromise;
|
|
22
|
+
this.initPromise = this.doConnect();
|
|
23
|
+
return this.initPromise;
|
|
24
|
+
}
|
|
25
|
+
async doConnect() {
|
|
26
|
+
try {
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = readFileSync(this.keyfilePath, "utf-8");
|
|
30
|
+
} catch (err) {
|
|
31
|
+
const code = err.code;
|
|
32
|
+
if (code === "ENOENT") throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Keyfile not found at ${this.keyfilePath}. Run "<cli> keygen" to generate one, or "<cli> import" to import an existing mnemonic (where <cli> is manifest-mcp-chain, manifest-mcp-lease, or manifest-mcp-fred). Check MANIFEST_KEY_FILE if the path is wrong.`);
|
|
33
|
+
if (code === "EACCES") throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Permission denied reading keyfile at ${this.keyfilePath}. Check file permissions (expected mode 0600).`);
|
|
34
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Failed to read keyfile at ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const mode = statSync(this.keyfilePath).mode & 511;
|
|
38
|
+
if (mode & 63) logger.warn(`Keyfile ${this.keyfilePath} has permissions 0${mode.toString(8)}; recommended 0600. Other users on this system may be able to read your private key. Fix with: chmod 600 ${this.keyfilePath}`);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
logger.debug(`Could not check keyfile permissions for ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
41
|
+
}
|
|
42
|
+
let data;
|
|
43
|
+
try {
|
|
44
|
+
data = JSON.parse(raw);
|
|
45
|
+
} catch {
|
|
46
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Keyfile at ${this.keyfilePath} contains invalid JSON. The file may be corrupted. Regenerate with "<cli> keygen" or import an existing mnemonic with "<cli> import" (any manifest-mcp-* CLI).`);
|
|
47
|
+
}
|
|
48
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Keyfile at ${this.keyfilePath} does not contain a valid JSON object. Expected a CosmJS encrypted wallet or a JSON object with a "mnemonic" field.`);
|
|
49
|
+
const obj = data;
|
|
50
|
+
if (obj.type !== void 0) {
|
|
51
|
+
if (!this.password?.trim()) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, "Keyfile is encrypted but no password provided. Set MANIFEST_KEY_PASSWORD to the password used when the keyfile was created.");
|
|
52
|
+
try {
|
|
53
|
+
this.wallet = await DirectSecp256k1HdWallet.deserialize(raw, this.password);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Failed to decrypt keyfile at ${this.keyfilePath}. Verify that MANIFEST_KEY_PASSWORD is correct. (${err instanceof Error ? err.message : String(err)})`);
|
|
56
|
+
}
|
|
57
|
+
} else if (obj.mnemonic) {
|
|
58
|
+
if (typeof obj.mnemonic !== "string") throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Keyfile at ${this.keyfilePath} has a "mnemonic" field that is not a string. Expected a BIP-39 mnemonic phrase.`);
|
|
59
|
+
logger.warn(`Keyfile at ${this.keyfilePath} contains an unencrypted mnemonic. Consider encrypting with "<cli> import" (any manifest-mcp-* CLI).`);
|
|
60
|
+
try {
|
|
61
|
+
this.wallet = await DirectSecp256k1HdWallet.fromMnemonic(obj.mnemonic, { prefix: this.addressPrefix });
|
|
62
|
+
} catch (err) {
|
|
63
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.INVALID_MNEMONIC, `Invalid mnemonic in keyfile at ${this.keyfilePath}. The stored mnemonic may be corrupted. (${err instanceof Error ? err.message : String(err)})`);
|
|
64
|
+
}
|
|
65
|
+
} else throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Unrecognized keyfile format in ${this.keyfilePath}. Expected a CosmJS encrypted wallet or a JSON object with a "mnemonic" field.`);
|
|
66
|
+
try {
|
|
67
|
+
this.aminoWallet = await Secp256k1HdWallet.fromMnemonic(this.wallet.mnemonic, { prefix: this.addressPrefix });
|
|
68
|
+
} catch (err) {
|
|
69
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Failed to initialize amino signing wallet from keyfile at ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
}
|
|
71
|
+
const accounts = await this.wallet.getAccounts().catch((err) => {
|
|
72
|
+
throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Failed to derive accounts from keyfile at ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
73
|
+
});
|
|
74
|
+
if (accounts.length === 0) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, "No accounts derived from keyfile");
|
|
75
|
+
this.address = accounts[0].address;
|
|
76
|
+
const actualPrefix = fromBech32(this.address).prefix;
|
|
77
|
+
if (actualPrefix !== this.addressPrefix) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_CONNECTION_FAILED, `Keyfile address prefix mismatch: keyfile produced "${actualPrefix}" but config expects "${this.addressPrefix}". Regenerate the keyfile with the correct COSMOS_ADDRESS_PREFIX.`);
|
|
78
|
+
this.password = void 0;
|
|
79
|
+
this.initPromise = null;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
this.initPromise = null;
|
|
82
|
+
this.wallet = null;
|
|
83
|
+
this.aminoWallet = null;
|
|
84
|
+
this.address = null;
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async getAddress() {
|
|
89
|
+
await this.connect();
|
|
90
|
+
if (!this.address) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_NOT_CONNECTED, "Wallet failed to initialize");
|
|
91
|
+
return this.address;
|
|
92
|
+
}
|
|
93
|
+
async getSigner() {
|
|
94
|
+
await this.connect();
|
|
95
|
+
if (!this.wallet) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_NOT_CONNECTED, "Wallet failed to initialize");
|
|
96
|
+
return this.wallet;
|
|
97
|
+
}
|
|
98
|
+
async disconnect() {
|
|
99
|
+
this.wallet = null;
|
|
100
|
+
this.aminoWallet = null;
|
|
101
|
+
this.address = null;
|
|
102
|
+
this.password = void 0;
|
|
103
|
+
this.disconnected = true;
|
|
104
|
+
this.initPromise = null;
|
|
105
|
+
}
|
|
106
|
+
async signArbitrary(address, data) {
|
|
107
|
+
await this.connect();
|
|
108
|
+
if (!this.aminoWallet) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_NOT_CONNECTED, "Amino wallet failed to initialize");
|
|
109
|
+
if (!this.address) throw new ManifestMCPError(ManifestMCPErrorCode.WALLET_NOT_CONNECTED, "Wallet address not initialized");
|
|
110
|
+
return signArbitraryWithAmino(this.aminoWallet, this.address, address, data);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
//#endregion
|
|
114
|
+
export { KeyfileWalletProvider };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=keyfileWallet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyfileWallet.js","names":[],"sources":["../src/keyfileWallet.ts"],"sourcesContent":["import { readFileSync, statSync } from 'node:fs';\nimport { Secp256k1HdWallet } from '@cosmjs/amino';\nimport { fromBech32 } from '@cosmjs/encoding';\nimport {\n DirectSecp256k1HdWallet,\n type OfflineSigner,\n} from '@cosmjs/proto-signing';\nimport {\n logger,\n ManifestMCPError,\n ManifestMCPErrorCode,\n type SignArbitraryResult,\n signArbitraryWithAmino,\n type WalletProvider,\n} from '@manifest-network/manifest-mcp-core';\n\nexport class KeyfileWalletProvider implements WalletProvider {\n private keyfilePath: string;\n private addressPrefix: string;\n private password: string | undefined;\n private wallet: DirectSecp256k1HdWallet | null = null;\n private aminoWallet: Secp256k1HdWallet | null = null;\n private address: string | null = null;\n private disconnected = false;\n\n // Promise to prevent concurrent wallet initialization\n private initPromise: Promise<void> | null = null;\n\n constructor(keyfilePath: string, addressPrefix: string, password?: string) {\n this.keyfilePath = keyfilePath;\n this.addressPrefix = addressPrefix;\n this.password = password;\n }\n\n async connect(): Promise<void> {\n if (this.disconnected) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_NOT_CONNECTED,\n 'Wallet has been disconnected. Create a new KeyfileWalletProvider instance to reconnect.',\n );\n }\n if (this.wallet) return;\n if (this.initPromise) return this.initPromise;\n\n this.initPromise = this.doConnect();\n return this.initPromise;\n }\n\n private async doConnect(): Promise<void> {\n try {\n let raw: string;\n try {\n raw = readFileSync(this.keyfilePath, 'utf-8');\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Keyfile not found at ${this.keyfilePath}. Run \"<cli> keygen\" to generate one, or \"<cli> import\" to import an existing mnemonic (where <cli> is manifest-mcp-chain, manifest-mcp-lease, or manifest-mcp-fred). Check MANIFEST_KEY_FILE if the path is wrong.`,\n );\n }\n if (code === 'EACCES') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Permission denied reading keyfile at ${this.keyfilePath}. Check file permissions (expected mode 0600).`,\n );\n }\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Failed to read keyfile at ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // Warn if keyfile has overly permissive permissions\n try {\n const mode = statSync(this.keyfilePath).mode & 0o777;\n if (mode & 0o077) {\n logger.warn(\n `Keyfile ${this.keyfilePath} has permissions 0${mode.toString(8)}; recommended 0600. ` +\n `Other users on this system may be able to read your private key. ` +\n `Fix with: chmod 600 ${this.keyfilePath}`,\n );\n }\n } catch (err) {\n logger.debug(\n `Could not check keyfile permissions for ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Keyfile at ${this.keyfilePath} contains invalid JSON. The file may be corrupted. Regenerate with \"<cli> keygen\" or import an existing mnemonic with \"<cli> import\" (any manifest-mcp-* CLI).`,\n );\n }\n\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Keyfile at ${this.keyfilePath} does not contain a valid JSON object. Expected a CosmJS encrypted wallet or a JSON object with a \"mnemonic\" field.`,\n );\n }\n\n const obj = data as Record<string, unknown>;\n\n if (obj.type !== undefined) {\n if (!this.password?.trim()) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n 'Keyfile is encrypted but no password provided. Set MANIFEST_KEY_PASSWORD to the password used when the keyfile was created.',\n );\n }\n try {\n this.wallet = await DirectSecp256k1HdWallet.deserialize(\n raw,\n this.password,\n );\n } catch (err: unknown) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Failed to decrypt keyfile at ${this.keyfilePath}. Verify that MANIFEST_KEY_PASSWORD is correct. (${err instanceof Error ? err.message : String(err)})`,\n );\n }\n } else if (obj.mnemonic) {\n if (typeof obj.mnemonic !== 'string') {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Keyfile at ${this.keyfilePath} has a \"mnemonic\" field that is not a string. Expected a BIP-39 mnemonic phrase.`,\n );\n }\n logger.warn(\n `Keyfile at ${this.keyfilePath} contains an unencrypted mnemonic. ` +\n 'Consider encrypting with \"<cli> import\" (any manifest-mcp-* CLI).',\n );\n try {\n this.wallet = await DirectSecp256k1HdWallet.fromMnemonic(\n obj.mnemonic,\n {\n prefix: this.addressPrefix,\n },\n );\n } catch (err: unknown) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.INVALID_MNEMONIC,\n `Invalid mnemonic in keyfile at ${this.keyfilePath}. The stored mnemonic may be corrupted. (${err instanceof Error ? err.message : String(err)})`,\n );\n }\n } else {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Unrecognized keyfile format in ${this.keyfilePath}. Expected a CosmJS encrypted wallet or a JSON object with a \"mnemonic\" field.`,\n );\n }\n\n try {\n this.aminoWallet = await Secp256k1HdWallet.fromMnemonic(\n this.wallet.mnemonic,\n {\n prefix: this.addressPrefix,\n },\n );\n } catch (err: unknown) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Failed to initialize amino signing wallet from keyfile at ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const accounts = await this.wallet.getAccounts().catch((err: unknown) => {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Failed to derive accounts from keyfile at ${this.keyfilePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n if (accounts.length === 0) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n 'No accounts derived from keyfile',\n );\n }\n this.address = accounts[0].address;\n const actualPrefix = fromBech32(this.address).prefix;\n if (actualPrefix !== this.addressPrefix) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_CONNECTION_FAILED,\n `Keyfile address prefix mismatch: keyfile produced \"${actualPrefix}\" but config expects \"${this.addressPrefix}\". Regenerate the keyfile with the correct COSMOS_ADDRESS_PREFIX.`,\n );\n }\n // Clear password from memory only after full initialization succeeds,\n // so that a retry after a partial failure can still use it.\n this.password = undefined;\n this.initPromise = null;\n } catch (error) {\n this.initPromise = null;\n this.wallet = null;\n this.aminoWallet = null;\n this.address = null;\n throw error;\n }\n }\n\n async getAddress(): Promise<string> {\n await this.connect();\n\n if (!this.address) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_NOT_CONNECTED,\n 'Wallet failed to initialize',\n );\n }\n return this.address;\n }\n\n async getSigner(): Promise<OfflineSigner> {\n await this.connect();\n\n if (!this.wallet) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_NOT_CONNECTED,\n 'Wallet failed to initialize',\n );\n }\n return this.wallet;\n }\n\n async disconnect(): Promise<void> {\n this.wallet = null;\n this.aminoWallet = null;\n this.address = null;\n this.password = undefined;\n this.disconnected = true;\n this.initPromise = null;\n }\n\n async signArbitrary(\n address: string,\n data: string,\n ): Promise<SignArbitraryResult> {\n await this.connect();\n\n if (!this.aminoWallet) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_NOT_CONNECTED,\n 'Amino wallet failed to initialize',\n );\n }\n\n if (!this.address) {\n throw new ManifestMCPError(\n ManifestMCPErrorCode.WALLET_NOT_CONNECTED,\n 'Wallet address not initialized',\n );\n }\n return signArbitraryWithAmino(\n this.aminoWallet,\n this.address,\n address,\n data,\n );\n }\n}\n"],"mappings":"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"}
|
package/dist/keygen.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keygen.d.ts","names":[],"sources":["../src/keygen.ts"],"mappings":";iBAqHsB,SAAA,CAAA,GAAa,OAAA;AAAA,iBAiDb,SAAA,CAAA,GAAa,OAAA"}
|
package/dist/keygen.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { loadKeyfileConfig } from "./config.js";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
6
|
+
//#region src/keygen.ts
|
|
7
|
+
function prompt(question) {
|
|
8
|
+
if (!process.stdin.isTTY) throw new Error("Interactive terminal required for key management commands. Cannot prompt for input in non-interactive mode.");
|
|
9
|
+
const rl = createInterface({
|
|
10
|
+
input: process.stdin,
|
|
11
|
+
output: process.stderr
|
|
12
|
+
});
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
let answered = false;
|
|
15
|
+
rl.question(question, (answer) => {
|
|
16
|
+
answered = true;
|
|
17
|
+
rl.close();
|
|
18
|
+
resolve(answer);
|
|
19
|
+
});
|
|
20
|
+
rl.on("close", () => {
|
|
21
|
+
if (!answered) reject(/* @__PURE__ */ new Error("Input stream closed before response was received."));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function promptPassword(question) {
|
|
26
|
+
if (!process.stdin.isTTY) throw new Error("Interactive terminal required for key management commands. Cannot prompt for input in non-interactive mode.");
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
let password = "";
|
|
29
|
+
process.stderr.write(question);
|
|
30
|
+
process.stdin.setRawMode(true);
|
|
31
|
+
process.stdin.resume();
|
|
32
|
+
process.stdin.setEncoding("utf8");
|
|
33
|
+
const cleanup = () => {
|
|
34
|
+
process.stdin.setRawMode(false);
|
|
35
|
+
process.stdin.pause();
|
|
36
|
+
process.stdin.removeListener("data", onData);
|
|
37
|
+
process.stdin.removeListener("error", onError);
|
|
38
|
+
process.stderr.write("\n");
|
|
39
|
+
};
|
|
40
|
+
const onError = (err) => {
|
|
41
|
+
cleanup();
|
|
42
|
+
reject(/* @__PURE__ */ new Error(`stdin error during password prompt: ${err.message}`));
|
|
43
|
+
};
|
|
44
|
+
const onData = (ch) => {
|
|
45
|
+
if (ch === "\r" || ch === "\n") {
|
|
46
|
+
cleanup();
|
|
47
|
+
resolve(password);
|
|
48
|
+
} else if (ch === "") {
|
|
49
|
+
cleanup();
|
|
50
|
+
reject(/* @__PURE__ */ new Error("Input stream closed before password was entered."));
|
|
51
|
+
} else if (ch === "") {
|
|
52
|
+
cleanup();
|
|
53
|
+
process.exit(130);
|
|
54
|
+
} else if (ch === "" || ch === "\b") {
|
|
55
|
+
if (password.length > 0) password = [...password].slice(0, -1).join("");
|
|
56
|
+
} else if (ch >= " ") password += ch;
|
|
57
|
+
};
|
|
58
|
+
process.stdin.on("data", onData);
|
|
59
|
+
process.stdin.on("error", onError);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async function writeKeyfile(wallet, keyfilePath, password) {
|
|
63
|
+
let serialized;
|
|
64
|
+
try {
|
|
65
|
+
serialized = await wallet.serialize(password);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
throw new Error(`Failed to encrypt wallet: ${err instanceof Error ? err.message : String(err)}`);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
mkdirSync(dirname(keyfilePath), {
|
|
71
|
+
recursive: true,
|
|
72
|
+
mode: 448
|
|
73
|
+
});
|
|
74
|
+
writeFileSync(keyfilePath, serialized, { mode: 384 });
|
|
75
|
+
} catch (err) {
|
|
76
|
+
throw new Error(`Failed to write keyfile to ${keyfilePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function confirmOverwrite(keyfilePath) {
|
|
80
|
+
if (existsSync(keyfilePath)) {
|
|
81
|
+
if ((await prompt(`Keyfile already exists at ${keyfilePath}. Overwrite? (yes/no): `)).toLowerCase() !== "yes") {
|
|
82
|
+
console.error("Aborted. Existing keyfile was not modified.");
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function runKeygen() {
|
|
88
|
+
const config = loadKeyfileConfig();
|
|
89
|
+
const prefix = config.addressPrefix;
|
|
90
|
+
const keyfilePath = config.keyfilePath;
|
|
91
|
+
await confirmOverwrite(keyfilePath);
|
|
92
|
+
const password = await promptPassword("Enter password for keyfile encryption: ");
|
|
93
|
+
if (!password) {
|
|
94
|
+
console.error("Error: password cannot be empty.");
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
if (password.length < 8) {
|
|
98
|
+
console.error("Error: password must be at least 8 characters.");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
if (password !== await promptPassword("Confirm password: ")) {
|
|
102
|
+
console.error("Error: passwords do not match.");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
let wallet;
|
|
106
|
+
try {
|
|
107
|
+
wallet = await DirectSecp256k1HdWallet.generate(24, { prefix });
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error(`Failed to generate wallet: ${err instanceof Error ? err.message : String(err)}`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
await writeKeyfile(wallet, keyfilePath, password);
|
|
113
|
+
console.error(`Keyfile written to ${keyfilePath}`);
|
|
114
|
+
try {
|
|
115
|
+
const accounts = await wallet.getAccounts();
|
|
116
|
+
if (accounts.length > 0) console.error(`Address: ${accounts[0].address}`);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.error(`Note: could not derive address for display (${err instanceof Error ? err.message : String(err)}), but the keyfile was written successfully.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function runImport() {
|
|
122
|
+
const config = loadKeyfileConfig();
|
|
123
|
+
const prefix = config.addressPrefix;
|
|
124
|
+
const keyfilePath = config.keyfilePath;
|
|
125
|
+
await confirmOverwrite(keyfilePath);
|
|
126
|
+
const mnemonic = await promptPassword("Enter mnemonic (hidden): ");
|
|
127
|
+
if (!mnemonic.trim()) {
|
|
128
|
+
console.error("Error: mnemonic cannot be empty.");
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const password = await promptPassword("Enter password for keyfile encryption: ");
|
|
132
|
+
if (!password) {
|
|
133
|
+
console.error("Error: password cannot be empty.");
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
if (password.length < 8) {
|
|
137
|
+
console.error("Error: password must be at least 8 characters.");
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
if (password !== await promptPassword("Confirm password: ")) {
|
|
141
|
+
console.error("Error: passwords do not match.");
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
let wallet;
|
|
145
|
+
try {
|
|
146
|
+
wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic.trim(), { prefix });
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.error(`Invalid mnemonic: ${err instanceof Error ? err.message : String(err)}\nPlease verify your mnemonic phrase has the correct number of words (12, 15, 18, 21, or 24) and all words are valid BIP-39 words.`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
await writeKeyfile(wallet, keyfilePath, password);
|
|
152
|
+
console.error(`Keyfile written to ${keyfilePath}`);
|
|
153
|
+
try {
|
|
154
|
+
const accounts = await wallet.getAccounts();
|
|
155
|
+
if (accounts.length > 0) console.error(`Address: ${accounts[0].address}`);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
console.error(`Note: could not derive address for display (${err instanceof Error ? err.message : String(err)}), but the keyfile was written successfully.`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { runImport, runKeygen };
|
|
162
|
+
|
|
163
|
+
//# sourceMappingURL=keygen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keygen.js","names":[],"sources":["../src/keygen.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { createInterface } from 'node:readline';\nimport { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';\nimport { loadKeyfileConfig } from './config.js';\n\nfunction prompt(question: string): Promise<string> {\n if (!process.stdin.isTTY) {\n throw new Error(\n 'Interactive terminal required for key management commands. Cannot prompt for input in non-interactive mode.',\n );\n }\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n return new Promise((resolve, reject) => {\n let answered = false;\n rl.question(question, (answer) => {\n answered = true;\n rl.close();\n resolve(answer);\n });\n rl.on('close', () => {\n if (!answered) {\n reject(new Error('Input stream closed before response was received.'));\n }\n });\n });\n}\n\nfunction promptPassword(question: string): Promise<string> {\n if (!process.stdin.isTTY) {\n throw new Error(\n 'Interactive terminal required for key management commands. Cannot prompt for input in non-interactive mode.',\n );\n }\n return new Promise((resolve, reject) => {\n let password = '';\n process.stderr.write(question);\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.setEncoding('utf8');\n\n const cleanup = (): void => {\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.stdin.removeListener('data', onData);\n process.stdin.removeListener('error', onError);\n process.stderr.write('\\n');\n };\n\n const onError = (err: Error): void => {\n cleanup();\n reject(new Error(`stdin error during password prompt: ${err.message}`));\n };\n\n const onData = (ch: string): void => {\n if (ch === '\\r' || ch === '\\n') {\n cleanup();\n resolve(password);\n } else if (ch === '\\u0004') {\n // Ctrl+D (EOF) — reject instead of resolving with partial input\n cleanup();\n reject(new Error('Input stream closed before password was entered.'));\n } else if (ch === '\\u0003') {\n cleanup();\n process.exit(130);\n } else if (ch === '\\u007f' || ch === '\\b') {\n if (password.length > 0) {\n password = [...password].slice(0, -1).join('');\n }\n } else if (ch >= ' ') {\n password += ch;\n }\n };\n\n process.stdin.on('data', onData);\n process.stdin.on('error', onError);\n });\n}\n\nasync function writeKeyfile(\n wallet: DirectSecp256k1HdWallet,\n keyfilePath: string,\n password: string,\n): Promise<void> {\n let serialized: string;\n try {\n serialized = await wallet.serialize(password);\n } catch (err: unknown) {\n throw new Error(\n `Failed to encrypt wallet: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n try {\n mkdirSync(dirname(keyfilePath), { recursive: true, mode: 0o700 });\n writeFileSync(keyfilePath, serialized, { mode: 0o600 });\n } catch (err: unknown) {\n throw new Error(\n `Failed to write keyfile to ${keyfilePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nasync function confirmOverwrite(keyfilePath: string): Promise<void> {\n if (existsSync(keyfilePath)) {\n const answer = await prompt(\n `Keyfile already exists at ${keyfilePath}. Overwrite? (yes/no): `,\n );\n if (answer.toLowerCase() !== 'yes') {\n console.error('Aborted. Existing keyfile was not modified.');\n process.exit(0);\n }\n }\n}\n\nexport async function runKeygen(): Promise<void> {\n const config = loadKeyfileConfig();\n const prefix = config.addressPrefix;\n const keyfilePath = config.keyfilePath;\n\n await confirmOverwrite(keyfilePath);\n\n const password = await promptPassword(\n 'Enter password for keyfile encryption: ',\n );\n if (!password) {\n console.error('Error: password cannot be empty.');\n process.exit(1);\n }\n if (password.length < 8) {\n console.error('Error: password must be at least 8 characters.');\n process.exit(1);\n }\n const confirmPassword = await promptPassword('Confirm password: ');\n if (password !== confirmPassword) {\n console.error('Error: passwords do not match.');\n process.exit(1);\n }\n\n let wallet: DirectSecp256k1HdWallet;\n try {\n wallet = await DirectSecp256k1HdWallet.generate(24, { prefix });\n } catch (err: unknown) {\n console.error(\n `Failed to generate wallet: ${err instanceof Error ? err.message : String(err)}`,\n );\n process.exit(1);\n }\n\n await writeKeyfile(wallet, keyfilePath, password);\n console.error(`Keyfile written to ${keyfilePath}`);\n\n try {\n const accounts = await wallet.getAccounts();\n if (accounts.length > 0) {\n console.error(`Address: ${accounts[0].address}`);\n }\n } catch (err) {\n console.error(\n `Note: could not derive address for display (${err instanceof Error ? err.message : String(err)}), but the keyfile was written successfully.`,\n );\n }\n}\n\nexport async function runImport(): Promise<void> {\n const config = loadKeyfileConfig();\n const prefix = config.addressPrefix;\n const keyfilePath = config.keyfilePath;\n\n await confirmOverwrite(keyfilePath);\n\n const mnemonic = await promptPassword('Enter mnemonic (hidden): ');\n if (!mnemonic.trim()) {\n console.error('Error: mnemonic cannot be empty.');\n process.exit(1);\n }\n\n const password = await promptPassword(\n 'Enter password for keyfile encryption: ',\n );\n if (!password) {\n console.error('Error: password cannot be empty.');\n process.exit(1);\n }\n if (password.length < 8) {\n console.error('Error: password must be at least 8 characters.');\n process.exit(1);\n }\n const confirmPassword = await promptPassword('Confirm password: ');\n if (password !== confirmPassword) {\n console.error('Error: passwords do not match.');\n process.exit(1);\n }\n\n let wallet: DirectSecp256k1HdWallet;\n try {\n wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic.trim(), {\n prefix,\n });\n } catch (err: unknown) {\n console.error(\n `Invalid mnemonic: ${err instanceof Error ? err.message : String(err)}\\n` +\n 'Please verify your mnemonic phrase has the correct number of words (12, 15, 18, 21, or 24) ' +\n 'and all words are valid BIP-39 words.',\n );\n process.exit(1);\n }\n\n await writeKeyfile(wallet, keyfilePath, password);\n console.error(`Keyfile written to ${keyfilePath}`);\n\n try {\n const accounts = await wallet.getAccounts();\n if (accounts.length > 0) {\n console.error(`Address: ${accounts[0].address}`);\n }\n } catch (err) {\n console.error(\n `Note: could not derive address for display (${err instanceof Error ? err.message : String(err)}), but the keyfile was written successfully.`,\n );\n }\n}\n"],"mappings":";;;;;;AAMA,SAAS,OAAO,UAAmC;AACjD,KAAI,CAAC,QAAQ,MAAM,MACjB,OAAM,IAAI,MACR,8GACD;CAEH,MAAM,KAAK,gBAAgB;EACzB,OAAO,QAAQ;EACf,QAAQ,QAAQ;EACjB,CAAC;AACF,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,WAAW;AACf,KAAG,SAAS,WAAW,WAAW;AAChC,cAAW;AACX,MAAG,OAAO;AACV,WAAQ,OAAO;IACf;AACF,KAAG,GAAG,eAAe;AACnB,OAAI,CAAC,SACH,wBAAO,IAAI,MAAM,oDAAoD,CAAC;IAExE;GACF;;AAGJ,SAAS,eAAe,UAAmC;AACzD,KAAI,CAAC,QAAQ,MAAM,MACjB,OAAM,IAAI,MACR,8GACD;AAEH,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,WAAW;AACf,UAAQ,OAAO,MAAM,SAAS;AAC9B,UAAQ,MAAM,WAAW,KAAK;AAC9B,UAAQ,MAAM,QAAQ;AACtB,UAAQ,MAAM,YAAY,OAAO;EAEjC,MAAM,gBAAsB;AAC1B,WAAQ,MAAM,WAAW,MAAM;AAC/B,WAAQ,MAAM,OAAO;AACrB,WAAQ,MAAM,eAAe,QAAQ,OAAO;AAC5C,WAAQ,MAAM,eAAe,SAAS,QAAQ;AAC9C,WAAQ,OAAO,MAAM,KAAK;;EAG5B,MAAM,WAAW,QAAqB;AACpC,YAAS;AACT,0BAAO,IAAI,MAAM,uCAAuC,IAAI,UAAU,CAAC;;EAGzE,MAAM,UAAU,OAAqB;AACnC,OAAI,OAAO,QAAQ,OAAO,MAAM;AAC9B,aAAS;AACT,YAAQ,SAAS;cACR,OAAO,KAAU;AAE1B,aAAS;AACT,2BAAO,IAAI,MAAM,mDAAmD,CAAC;cAC5D,OAAO,KAAU;AAC1B,aAAS;AACT,YAAQ,KAAK,IAAI;cACR,OAAO,OAAY,OAAO;QAC/B,SAAS,SAAS,EACpB,YAAW,CAAC,GAAG,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG;cAEvC,MAAM,IACf,aAAY;;AAIhB,UAAQ,MAAM,GAAG,QAAQ,OAAO;AAChC,UAAQ,MAAM,GAAG,SAAS,QAAQ;GAClC;;AAGJ,eAAe,aACb,QACA,aACA,UACe;CACf,IAAI;AACJ,KAAI;AACF,eAAa,MAAM,OAAO,UAAU,SAAS;UACtC,KAAc;AACrB,QAAM,IAAI,MACR,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC9E;;AAEH,KAAI;AACF,YAAU,QAAQ,YAAY,EAAE;GAAE,WAAW;GAAM,MAAM;GAAO,CAAC;AACjE,gBAAc,aAAa,YAAY,EAAE,MAAM,KAAO,CAAC;UAChD,KAAc;AACrB,QAAM,IAAI,MACR,8BAA8B,YAAY,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC/F;;;AAIL,eAAe,iBAAiB,aAAoC;AAClE,KAAI,WAAW,YAAY;OACV,MAAM,OACnB,6BAA6B,YAAY,yBAC1C,EACU,aAAa,KAAK,OAAO;AAClC,WAAQ,MAAM,8CAA8C;AAC5D,WAAQ,KAAK,EAAE;;;;AAKrB,eAAsB,YAA2B;CAC/C,MAAM,SAAS,mBAAmB;CAClC,MAAM,SAAS,OAAO;CACtB,MAAM,cAAc,OAAO;AAE3B,OAAM,iBAAiB,YAAY;CAEnC,MAAM,WAAW,MAAM,eACrB,0CACD;AACD,KAAI,CAAC,UAAU;AACb,UAAQ,MAAM,mCAAmC;AACjD,UAAQ,KAAK,EAAE;;AAEjB,KAAI,SAAS,SAAS,GAAG;AACvB,UAAQ,MAAM,iDAAiD;AAC/D,UAAQ,KAAK,EAAE;;AAGjB,KAAI,aADoB,MAAM,eAAe,qBAAqB,EAChC;AAChC,UAAQ,MAAM,iCAAiC;AAC/C,UAAQ,KAAK,EAAE;;CAGjB,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,wBAAwB,SAAS,IAAI,EAAE,QAAQ,CAAC;UACxD,KAAc;AACrB,UAAQ,MACN,8BAA8B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC/E;AACD,UAAQ,KAAK,EAAE;;AAGjB,OAAM,aAAa,QAAQ,aAAa,SAAS;AACjD,SAAQ,MAAM,sBAAsB,cAAc;AAElD,KAAI;EACF,MAAM,WAAW,MAAM,OAAO,aAAa;AAC3C,MAAI,SAAS,SAAS,EACpB,SAAQ,MAAM,YAAY,SAAS,GAAG,UAAU;UAE3C,KAAK;AACZ,UAAQ,MACN,+CAA+C,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,8CACjG;;;AAIL,eAAsB,YAA2B;CAC/C,MAAM,SAAS,mBAAmB;CAClC,MAAM,SAAS,OAAO;CACtB,MAAM,cAAc,OAAO;AAE3B,OAAM,iBAAiB,YAAY;CAEnC,MAAM,WAAW,MAAM,eAAe,4BAA4B;AAClE,KAAI,CAAC,SAAS,MAAM,EAAE;AACpB,UAAQ,MAAM,mCAAmC;AACjD,UAAQ,KAAK,EAAE;;CAGjB,MAAM,WAAW,MAAM,eACrB,0CACD;AACD,KAAI,CAAC,UAAU;AACb,UAAQ,MAAM,mCAAmC;AACjD,UAAQ,KAAK,EAAE;;AAEjB,KAAI,SAAS,SAAS,GAAG;AACvB,UAAQ,MAAM,iDAAiD;AAC/D,UAAQ,KAAK,EAAE;;AAGjB,KAAI,aADoB,MAAM,eAAe,qBAAqB,EAChC;AAChC,UAAQ,MAAM,iCAAiC;AAC/C,UAAQ,KAAK,EAAE;;CAGjB,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,wBAAwB,aAAa,SAAS,MAAM,EAAE,EACnE,QACD,CAAC;UACK,KAAc;AACrB,UAAQ,MACN,qBAAqB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,oIAGvE;AACD,UAAQ,KAAK,EAAE;;AAGjB,OAAM,aAAa,QAAQ,aAAa,SAAS;AACjD,SAAQ,MAAM,sBAAsB,cAAc;AAElD,KAAI;EACF,MAAM,WAAW,MAAM,OAAO,aAAa;AAC3C,MAAI,SAAS,SAAS,EACpB,SAAQ,MAAM,YAAY,SAAS,GAAG,UAAU;UAE3C,KAAK;AACZ,UAAQ,MACN,+CAA+C,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,8CACjG"}
|
package/dist/lease.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/lease.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { bootstrap } from "./bootstrap.js";
|
|
3
|
+
import { LeaseMCPServer } from "@manifest-network/manifest-mcp-lease";
|
|
4
|
+
//#region src/lease.ts
|
|
5
|
+
bootstrap({
|
|
6
|
+
cliName: "manifest-mcp-lease",
|
|
7
|
+
label: "lease",
|
|
8
|
+
createServer: (opts) => new LeaseMCPServer(opts).getServer()
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export {};
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=lease.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease.js","names":[],"sources":["../src/lease.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { LeaseMCPServer } from '@manifest-network/manifest-mcp-lease';\nimport { bootstrap } from './bootstrap.js';\n\nbootstrap({\n cliName: 'manifest-mcp-lease',\n label: 'lease',\n createServer: (opts) => new LeaseMCPServer(opts).getServer(),\n});\n"],"mappings":";;;;AAIA,UAAU;CACR,SAAS;CACT,OAAO;CACP,eAAe,SAAS,IAAI,eAAe,KAAK,CAAC,WAAW;CAC7D,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@manifest-network/manifest-mcp-node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Node.js MCP servers for Manifest Network with stdio transport and keyfile wallet",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"manifest-mcp-chain": "dist/chain.js",
|
|
8
|
+
"manifest-mcp-lease": "dist/lease.js",
|
|
9
|
+
"manifest-mcp-fred": "dist/fred.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsdown",
|
|
13
|
+
"lint": "tsc --noEmit",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"cosmos",
|
|
20
|
+
"blockchain",
|
|
21
|
+
"manifest",
|
|
22
|
+
"stdio"
|
|
23
|
+
],
|
|
24
|
+
"author": "Felix C. Morency",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/manifest-network/manifest-mcp-mono.git",
|
|
29
|
+
"directory": "packages/node"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/manifest-network/manifest-mcp-mono#readme",
|
|
32
|
+
"bugs": "https://github.com/manifest-network/manifest-mcp-mono/issues",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@cosmjs/amino": "0.32.4",
|
|
44
|
+
"@cosmjs/encoding": "0.32.4",
|
|
45
|
+
"@cosmjs/proto-signing": "0.32.4",
|
|
46
|
+
"@manifest-network/manifest-mcp-core": "^0.1.0",
|
|
47
|
+
"@manifest-network/manifest-mcp-chain": "^0.1.0",
|
|
48
|
+
"@manifest-network/manifest-mcp-lease": "^0.1.0",
|
|
49
|
+
"@manifest-network/manifest-mcp-fred": "^0.1.0",
|
|
50
|
+
"@modelcontextprotocol/sdk": "1.27.1",
|
|
51
|
+
"dotenv": "^17.2.3"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "22.15.29",
|
|
55
|
+
"typescript": "5.9.3",
|
|
56
|
+
"vitest": "4.1.0"
|
|
57
|
+
}
|
|
58
|
+
}
|