@portkey/ca-agent-skills 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Portkey
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,270 @@
1
+ # Portkey CA Agent Skills
2
+
3
+ > AI Agent toolkit for [Portkey Wallet](https://portkey.finance) on the [aelf](https://aelf.com) blockchain — Email registration, login, transfers, guardian management, and generic contract calls.
4
+
5
+ [中文文档](./README.zh-CN.md)
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ ca-agent-skills/
11
+ ├── index.ts # SDK entry — direct import for LangChain / LlamaIndex
12
+ ├── src/
13
+ │ ├── core/ # Pure business logic (no I/O side effects)
14
+ │ │ ├── account.ts # checkAccount, getGuardianList, getHolderInfo, getChainInfo
15
+ │ │ ├── auth.ts # sendVerificationCode, verifyCode, registerWallet, recoverWallet
16
+ │ │ ├── assets.ts # getTokenBalance, getTokenList, getNftCollections, getNftItems, getTokenPrice
17
+ │ │ ├── transfer.ts # sameChainTransfer, crossChainTransfer, recoverStuckTransfer
18
+ │ │ ├── guardian.ts # addGuardian, removeGuardian
19
+ │ │ ├── contract.ts # managerForwardCall, callContractViewMethod
20
+ │ │ └── keystore.ts # Encrypted wallet persistence (save, unlock, lock)
21
+ │ └── mcp/
22
+ │ └── server.ts # MCP adapter — for Claude Desktop, Cursor, GPT, etc.
23
+ ├── portkey_query_skill.ts # CLI adapter — query commands
24
+ ├── portkey_auth_skill.ts # CLI adapter — registration & login commands
25
+ ├── portkey_tx_skill.ts # CLI adapter — transfer & guardian commands
26
+ ├── cli-helpers.ts # CLI output helpers
27
+ ├── bin/
28
+ │ └── setup.ts # One-command setup for AI platforms
29
+ ├── lib/
30
+ │ ├── config.ts # Network config, env overrides
31
+ │ ├── types.ts # TypeScript interfaces & enums
32
+ │ ├── aelf-client.ts # aelf-sdk wrapper (wallet, contract, signing)
33
+ │ └── http.ts # HTTP client for Portkey backend API
34
+ └── __tests__/ # Unit / Integration / E2E tests
35
+ ```
36
+
37
+ **Core + Adapters pattern:** Three adapters (MCP, CLI, SDK) call the same Core functions — zero duplicated logic.
38
+
39
+ ## Features
40
+
41
+ | # | Category | Capability | MCP Tool | CLI Command | SDK Function |
42
+ |---|----------|-----------|----------|-------------|--------------|
43
+ | 1 | Account | Check email registration | `portkey_check_account` | `check-account` | `checkAccount` |
44
+ | 2 | Account | Get guardian list | `portkey_get_guardian_list` | `guardian-list` | `getGuardianList` |
45
+ | 3 | Account | Get CA holder info | `portkey_get_holder_info` | `holder-info` | `getHolderInfo` |
46
+ | 4 | Account | Get chain info | `portkey_get_chain_info` | `chain-info` | `getChainInfo` |
47
+ | 5 | Auth | Get verifier server | `portkey_get_verifier` | `get-verifier` | `getVerifierServer` |
48
+ | 6 | Auth | Send verification code | `portkey_send_code` | `send-code` | `sendVerificationCode` |
49
+ | 7 | Auth | Verify code | `portkey_verify_code` | `verify-code` | `verifyCode` |
50
+ | 8 | Auth | Register wallet | `portkey_register` | `register` | `registerWallet` |
51
+ | 9 | Auth | Recover wallet (login) | `portkey_recover` | `recover` | `recoverWallet` |
52
+ | 10 | Auth | Check status | `portkey_check_status` | `check-status` | `checkRegisterOrRecoveryStatus` |
53
+ | 11 | Assets | Token balance | `portkey_balance` | `balance` | `getTokenBalance` |
54
+ | 12 | Assets | Token list | `portkey_token_list` | `token-list` | `getTokenList` |
55
+ | 13 | Assets | NFT collections | `portkey_nft_collections` | `nft-collections` | `getNftCollections` |
56
+ | 14 | Assets | NFT items | `portkey_nft_items` | `nft-items` | `getNftItems` |
57
+ | 15 | Assets | Token price | `portkey_token_price` | `token-price` | `getTokenPrice` |
58
+ | 16 | Transfer | Same-chain transfer | `portkey_transfer` | `transfer` | `sameChainTransfer` |
59
+ | 17 | Transfer | Cross-chain transfer | `portkey_cross_chain_transfer` | `cross-chain-transfer` | `crossChainTransfer` |
60
+ | 18 | Transfer | Transaction result | `portkey_tx_result` | `tx-result` | `getTransactionResult` |
61
+ | 19 | Transfer | Recover stuck transfer | `portkey_recover_stuck_transfer` | `recover-stuck-transfer` | `recoverStuckTransfer` |
62
+ | 20 | Guardian | Add guardian | `portkey_add_guardian` | `add-guardian` | `addGuardian` |
63
+ | 21 | Guardian | Remove guardian | `portkey_remove_guardian` | `remove-guardian` | `removeGuardian` |
64
+ | 22 | Contract | ManagerForwardCall | `portkey_forward_call` | `forward-call` | `managerForwardCall` |
65
+ | 23 | Contract | View method call | `portkey_view_call` | `view-call` | `callContractViewMethod` |
66
+ | 24 | Wallet | Create wallet | `portkey_create_wallet` | `create-wallet` | `createWallet` |
67
+ | 25 | Wallet | Save keystore | `portkey_save_keystore` | `save-keystore` | `saveKeystore` |
68
+ | 26 | Wallet | Unlock wallet | `portkey_unlock` | `unlock` | `unlockWallet` |
69
+ | 27 | Wallet | Lock wallet | `portkey_lock` | `lock` | `lockWallet` |
70
+ | 28 | Wallet | Wallet status | `portkey_wallet_status` | `wallet-status` | `getWalletStatus` |
71
+
72
+ ## Wallet Persistence (Keystore)
73
+
74
+ Manager private keys are encrypted and stored locally using aelf-sdk's keystore scheme (scrypt + AES-128-CTR).
75
+
76
+ **Storage location:** `~/.portkey/ca/{network}.keystore.json`
77
+
78
+ ### First-time setup (after registration/recovery)
79
+
80
+ ```bash
81
+ # AI flow: create_wallet → register → check_status → save_keystore(password)
82
+ # The wallet is auto-unlocked after saving.
83
+ ```
84
+
85
+ ### New conversation
86
+
87
+ ```bash
88
+ # AI calls portkey_wallet_status to check if keystore exists
89
+ # If locked, asks user for password → portkey_unlock(password)
90
+ # Write operations now work automatically
91
+ ```
92
+
93
+ ### Manual CLI usage
94
+
95
+ ```bash
96
+ # Save keystore
97
+ bun run portkey_auth_skill.ts save-keystore \
98
+ --password "your-password" \
99
+ --private-key "hex-key" \
100
+ --mnemonic "word1 word2 ..." \
101
+ --ca-hash "xxx" --ca-address "ELF_xxx_AELF"
102
+
103
+ # Unlock
104
+ bun run portkey_auth_skill.ts unlock --password "your-password"
105
+
106
+ # Check status
107
+ bun run portkey_auth_skill.ts wallet-status
108
+
109
+ # Lock
110
+ bun run portkey_auth_skill.ts lock
111
+ ```
112
+
113
+ ### How it works
114
+
115
+ 1. **Save** — encrypts the Manager private key + mnemonic with a user-provided password, writes to `~/.portkey/ca/`
116
+ 2. **Unlock** — decrypts the keystore, loads the wallet into memory for the current process
117
+ 3. **Lock** — clears the private key from memory
118
+ 4. **Write operations** — automatically use the unlocked wallet; falls back to `PORTKEY_PRIVATE_KEY` env var if no keystore is unlocked
119
+
120
+ ## Prerequisites
121
+
122
+ - [Bun](https://bun.sh) >= 1.0
123
+ - An aelf wallet private key or an unlocked keystore (for write operations only)
124
+
125
+ ## Quick Start
126
+
127
+ ### 1. Install
128
+
129
+ ```bash
130
+ bun add @portkey/ca-agent-skills
131
+
132
+ # Or clone locally
133
+ git clone https://github.com/AwakenFinance/ca-agent-skills.git
134
+ cd ca-agent-skills
135
+ bun install
136
+ ```
137
+
138
+ ### 2. Configure
139
+
140
+ ```bash
141
+ cp .env.example .env
142
+ # Edit .env — add your PORTKEY_PRIVATE_KEY (only for write operations)
143
+ ```
144
+
145
+ ### 3. One-Command Setup
146
+
147
+ ```bash
148
+ # Claude Desktop
149
+ bun run bin/setup.ts claude
150
+
151
+ # Cursor (project-level)
152
+ bun run bin/setup.ts cursor
153
+
154
+ # Cursor (global)
155
+ bun run bin/setup.ts cursor --global
156
+
157
+ # OpenClaw — output config to stdout
158
+ bun run bin/setup.ts openclaw
159
+
160
+ # OpenClaw — merge into existing config
161
+ bun run bin/setup.ts openclaw --config-path ./my-openclaw.json
162
+
163
+ # Check status (Claude, Cursor, OpenClaw)
164
+ bun run bin/setup.ts list
165
+
166
+ # Remove
167
+ bun run bin/setup.ts uninstall claude
168
+ bun run bin/setup.ts uninstall cursor
169
+ bun run bin/setup.ts uninstall openclaw --config-path ./my-openclaw.json
170
+ ```
171
+
172
+ ## Usage
173
+
174
+ ### MCP (Claude Desktop / Cursor)
175
+
176
+ Add to your MCP config (`mcp-config.example.json`):
177
+
178
+ ```json
179
+ {
180
+ "mcpServers": {
181
+ "ca-agent-skills": {
182
+ "command": "bun",
183
+ "args": ["run", "/path/to/ca-agent-skills/src/mcp/server.ts"],
184
+ "env": {
185
+ "PORTKEY_PRIVATE_KEY": "your_private_key_here",
186
+ "PORTKEY_NETWORK": "mainnet"
187
+ }
188
+ }
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### OpenClaw
194
+
195
+ The `openclaw.json` in the project root defines 13 CLI-based tools for OpenClaw. Use `bun run bin/setup.ts openclaw` to generate or merge the config.
196
+
197
+ ### CLI
198
+
199
+ ```bash
200
+ # Check if email is registered
201
+ bun run portkey_query_skill.ts check-account --email user@example.com
202
+
203
+ # Get chain info
204
+ bun run portkey_query_skill.ts chain-info
205
+
206
+ # Create wallet
207
+ bun run portkey_auth_skill.ts create-wallet
208
+
209
+ # Transfer tokens (requires PORTKEY_PRIVATE_KEY env)
210
+ bun run portkey_tx_skill.ts transfer --ca-hash xxx --token-contract xxx --symbol ELF --to xxx --amount 100000000 --chain-id AELF
211
+ ```
212
+
213
+ ### SDK
214
+
215
+ ```typescript
216
+ import { getConfig, checkAccount, createWallet, getTokenBalance } from '@portkey/ca-agent-skills';
217
+
218
+ const config = getConfig({ network: 'mainnet' });
219
+
220
+ // Check account
221
+ const account = await checkAccount(config, { email: 'user@example.com' });
222
+
223
+ // Create wallet
224
+ const wallet = createWallet();
225
+
226
+ // Get balance
227
+ const balance = await getTokenBalance(config, {
228
+ caAddress: 'xxx',
229
+ chainId: 'AELF',
230
+ symbol: 'ELF',
231
+ });
232
+ ```
233
+
234
+ ## Network
235
+
236
+ | Network | Chain IDs | API URL |
237
+ |---------|-----------|---------|
238
+ | mainnet (default) | AELF, tDVV | `https://aa-portkey.portkey.finance` |
239
+ | testnet | AELF, tDVW | `https://aa-portkey-test.portkey.finance` |
240
+
241
+ ## Environment Variables
242
+
243
+ | Variable | Required | Default | Description |
244
+ |----------|----------|---------|-------------|
245
+ | `PORTKEY_PRIVATE_KEY` | Fallback | — | Manager wallet private key (fallback if keystore not unlocked) |
246
+ | `PORTKEY_NETWORK` | No | `mainnet` | `mainnet` or `testnet` |
247
+ | `PORTKEY_API_URL` | No | Per network | Override API endpoint |
248
+ | `PORTKEY_GRAPHQL_URL` | No | Per network | Override GraphQL endpoint |
249
+
250
+ ## Testing
251
+
252
+ ```bash
253
+ bun test # All tests
254
+ bun run test:unit # Unit tests only
255
+ bun run test:integration # Integration (requires network)
256
+ bun run test:e2e # E2E (requires private key)
257
+ ```
258
+
259
+ ## Security
260
+
261
+ - Never commit your `.env` file (git-ignored by default)
262
+ - Private keys are only needed for write operations (transfer, guardian management, contract calls)
263
+ - **Keystore encryption**: Manager private keys are encrypted with scrypt (N=8192) + AES-128-CTR via aelf-sdk's keystore module. Files are stored with `0600` permissions.
264
+ - **In-memory lifecycle**: private keys exist in memory only while unlocked; `portkey_lock` clears them immediately
265
+ - When using MCP, the keystore password only exists in the AI conversation context — it is never written to disk
266
+ - `PORTKEY_PRIVATE_KEY` env var is supported as a fallback but keystore is the recommended approach
267
+
268
+ ## License
269
+
270
+ MIT
@@ -0,0 +1,265 @@
1
+ # Portkey CA Agent Skills
2
+
3
+ > [Portkey Wallet](https://portkey.finance) 的 AI Agent 工具包,基于 [aelf](https://aelf.com) 区块链 — 支持 Email 注册/登录、转账、Guardian 管理和通用合约调用。
4
+
5
+ [English](./README.md)
6
+
7
+ ## 架构
8
+
9
+ ```
10
+ ca-agent-skills/
11
+ ├── index.ts # SDK 入口 — 供 LangChain / LlamaIndex 直接 import
12
+ ├── src/
13
+ │ ├── core/ # 纯业务逻辑(无副作用)
14
+ │ │ ├── account.ts # 账户查询
15
+ │ │ ├── auth.ts # 注册/登录/验证
16
+ │ │ ├── assets.ts # 资产查询(Token、NFT、价格)
17
+ │ │ ├── transfer.ts # 同链/跨链转账、卡单恢复
18
+ │ │ ├── guardian.ts # Guardian 管理
19
+ │ │ ├── contract.ts # 通用合约调用(ManagerForwardCall)
20
+ │ │ └── keystore.ts # 钱包加密持久化(save、unlock、lock)
21
+ │ └── mcp/
22
+ │ └── server.ts # MCP 适配器 — Claude Desktop / Cursor / GPT
23
+ ├── portkey_query_skill.ts # CLI — 查询命令
24
+ ├── portkey_auth_skill.ts # CLI — 注册/登录命令
25
+ ├── portkey_tx_skill.ts # CLI — 交易/Guardian 命令
26
+ ├── bin/setup.ts # 一键配置工具
27
+ └── lib/ # 基础设施(config、types、aelf-sdk 封装、HTTP 客户端)
28
+ ```
29
+
30
+ **核心模式:** 三个适配器(MCP / CLI / SDK)调用同一套 Core 函数,零重复逻辑。
31
+
32
+ ## 功能清单
33
+
34
+ | # | 分类 | 功能 | MCP Tool | SDK 函数 |
35
+ |---|------|------|----------|----------|
36
+ | 1 | 账户 | 检查 Email 是否注册 | `portkey_check_account` | `checkAccount` |
37
+ | 2 | 账户 | 获取 Guardian 列表 | `portkey_get_guardian_list` | `getGuardianList` |
38
+ | 3 | 账户 | 获取 CA Holder 信息 | `portkey_get_holder_info` | `getHolderInfo` |
39
+ | 4 | 账户 | 获取链信息 | `portkey_get_chain_info` | `getChainInfo` |
40
+ | 5 | 验证 | 获取 Verifier | `portkey_get_verifier` | `getVerifierServer` |
41
+ | 6 | 验证 | 发送验证码 | `portkey_send_code` | `sendVerificationCode` |
42
+ | 7 | 验证 | 校验验证码 | `portkey_verify_code` | `verifyCode` |
43
+ | 8 | 注册 | 注册 CA 钱包 | `portkey_register` | `registerWallet` |
44
+ | 9 | 登录 | 恢复/登录 CA 钱包 | `portkey_recover` | `recoverWallet` |
45
+ | 10 | 状态 | 查询注册/恢复状态 | `portkey_check_status` | `checkRegisterOrRecoveryStatus` |
46
+ | 11 | 资产 | 查询 Token 余额 | `portkey_balance` | `getTokenBalance` |
47
+ | 12 | 资产 | Token 列表 | `portkey_token_list` | `getTokenList` |
48
+ | 13 | 资产 | NFT 集合 | `portkey_nft_collections` | `getNftCollections` |
49
+ | 14 | 资产 | NFT 项目 | `portkey_nft_items` | `getNftItems` |
50
+ | 15 | 资产 | Token 价格 | `portkey_token_price` | `getTokenPrice` |
51
+ | 16 | 转账 | 同链转账 | `portkey_transfer` | `sameChainTransfer` |
52
+ | 17 | 转账 | 跨链转账 | `portkey_cross_chain_transfer` | `crossChainTransfer` |
53
+ | 18 | 转账 | 查询交易结果 | `portkey_tx_result` | `getTransactionResult` |
54
+ | 19 | 转账 | 跨链卡单恢复 | `portkey_recover_stuck_transfer` | `recoverStuckTransfer` |
55
+ | 20 | Guardian | 添加 Guardian | `portkey_add_guardian` | `addGuardian` |
56
+ | 21 | Guardian | 移除 Guardian | `portkey_remove_guardian` | `removeGuardian` |
57
+ | 22 | 合约 | 通用 ManagerForwardCall | `portkey_forward_call` | `managerForwardCall` |
58
+ | 23 | 合约 | 只读合约调用 | `portkey_view_call` | `callContractViewMethod` |
59
+ | 24 | 钱包 | 创建钱包 | `portkey_create_wallet` | `createWallet` |
60
+ | 25 | 钱包 | 保存 Keystore | `portkey_save_keystore` | `saveKeystore` |
61
+ | 26 | 钱包 | 解锁钱包 | `portkey_unlock` | `unlockWallet` |
62
+ | 27 | 钱包 | 锁定钱包 | `portkey_lock` | `lockWallet` |
63
+ | 28 | 钱包 | 钱包状态 | `portkey_wallet_status` | `getWalletStatus` |
64
+
65
+ ## 钱包持久化(Keystore)
66
+
67
+ Manager 私钥使用 aelf-sdk 内置的 keystore 方案(scrypt + AES-128-CTR)加密存储到本地。
68
+
69
+ **存储路径:** `~/.portkey/ca/{network}.keystore.json`
70
+
71
+ ### 首次设置(注册/恢复成功后)
72
+
73
+ ```bash
74
+ # AI 流程:create_wallet → register → check_status → save_keystore(密码)
75
+ # 保存后自动解锁,当前对话可直接使用。
76
+ ```
77
+
78
+ ### 新对话
79
+
80
+ ```bash
81
+ # AI 调用 portkey_wallet_status 检查是否存在 keystore
82
+ # 如果已锁定,向用户索要密码 → portkey_unlock(密码)
83
+ # 之后写操作自动生效
84
+ ```
85
+
86
+ ### CLI 手动操作
87
+
88
+ ```bash
89
+ # 保存 keystore
90
+ bun run portkey_auth_skill.ts save-keystore \
91
+ --password "你的密码" \
92
+ --private-key "hex私钥" \
93
+ --mnemonic "助记词" \
94
+ --ca-hash "xxx" --ca-address "ELF_xxx_AELF"
95
+
96
+ # 解锁
97
+ bun run portkey_auth_skill.ts unlock --password "你的密码"
98
+
99
+ # 查看状态
100
+ bun run portkey_auth_skill.ts wallet-status
101
+
102
+ # 锁定
103
+ bun run portkey_auth_skill.ts lock
104
+ ```
105
+
106
+ ### 工作原理
107
+
108
+ 1. **Save** — 用用户密码加密 Manager 私钥 + 助记词,写入 `~/.portkey/ca/`
109
+ 2. **Unlock** — 解密 keystore,将钱包加载到进程内存
110
+ 3. **Lock** — 清除内存中的私钥
111
+ 4. **写操作** — 优先使用已解锁的钱包;如果没有解锁的 keystore,fallback 到 `PORTKEY_PRIVATE_KEY` 环境变量
112
+
113
+ ## 前置条件
114
+
115
+ - [Bun](https://bun.sh) >= 1.0
116
+ - aelf 钱包私钥或已解锁的 keystore(仅写操作需要)
117
+
118
+ ## 快速开始
119
+
120
+ ```bash
121
+ # 安装
122
+ bun add @portkey/ca-agent-skills
123
+
124
+ # 配置
125
+ cp .env.example .env
126
+ # 编辑 .env,添加 PORTKEY_PRIVATE_KEY(仅写操作需要)
127
+
128
+ # 一键配置到 AI 平台
129
+ bun run bin/setup.ts claude # Claude Desktop
130
+ bun run bin/setup.ts cursor # Cursor(项目级)
131
+ bun run bin/setup.ts cursor --global # Cursor(全局)
132
+ bun run bin/setup.ts openclaw # OpenClaw — 输出配置到 stdout
133
+ bun run bin/setup.ts openclaw --config-path ./my-openclaw.json # 合并到已有配置
134
+
135
+ # 查看配置状态(Claude / Cursor / OpenClaw)
136
+ bun run bin/setup.ts list
137
+
138
+ # 卸载
139
+ bun run bin/setup.ts uninstall claude
140
+ bun run bin/setup.ts uninstall cursor
141
+ bun run bin/setup.ts uninstall openclaw --config-path ./my-openclaw.json
142
+ ```
143
+
144
+ ## SDK 使用示例
145
+
146
+ ```typescript
147
+ import { getConfig, checkAccount, createWallet, getTokenBalance } from '@portkey/ca-agent-skills';
148
+
149
+ const config = getConfig({ network: 'mainnet' });
150
+
151
+ // 检查账户
152
+ const account = await checkAccount(config, { email: 'user@example.com' });
153
+ console.log(account.isRegistered, account.originChainId);
154
+
155
+ // 创建钱包
156
+ const wallet = createWallet();
157
+ console.log(wallet.address, wallet.privateKey);
158
+
159
+ // 查询余额
160
+ const balance = await getTokenBalance(config, {
161
+ caAddress: 'xxx',
162
+ chainId: 'AELF',
163
+ symbol: 'ELF',
164
+ });
165
+ ```
166
+
167
+ ## 注册流程示例(Email)
168
+
169
+ ```typescript
170
+ import {
171
+ getConfig, createWallet, getVerifierServer,
172
+ sendVerificationCode, verifyCode, registerWallet,
173
+ checkRegisterOrRecoveryStatus, OperationType,
174
+ } from '@portkey/ca-agent-skills';
175
+
176
+ const config = getConfig({ network: 'mainnet' });
177
+
178
+ // 1. 获取 Verifier
179
+ const verifier = await getVerifierServer(config);
180
+
181
+ // 2. 发送验证码(注意:mainnet 已废弃 Register(0),统一使用 CommunityRecovery(1))
182
+ const { verifierSessionId } = await sendVerificationCode(config, {
183
+ email: 'user@example.com',
184
+ verifierId: verifier.id,
185
+ chainId: 'AELF',
186
+ operationType: OperationType.CreateCAHolder, // 注册用 1,登录用 SocialRecovery(2)
187
+ });
188
+
189
+ // 3. 用户输入验证码后校验
190
+ const { signature, verificationDoc } = await verifyCode(config, {
191
+ email: 'user@example.com',
192
+ verificationCode: '123456',
193
+ verifierId: verifier.id,
194
+ verifierSessionId,
195
+ chainId: 'AELF',
196
+ operationType: OperationType.CreateCAHolder,
197
+ });
198
+
199
+ // 4. 创建 Manager 钱包
200
+ const wallet = createWallet();
201
+
202
+ // 5. 提交注册
203
+ const { sessionId } = await registerWallet(config, {
204
+ email: 'user@example.com',
205
+ manager: wallet.address,
206
+ verifierId: verifier.id,
207
+ verificationDoc,
208
+ signature,
209
+ chainId: 'AELF',
210
+ });
211
+
212
+ // 6. 轮询状态
213
+ let status;
214
+ do {
215
+ await new Promise(r => setTimeout(r, 3000));
216
+ status = await checkRegisterOrRecoveryStatus(config, { sessionId, type: 'register' });
217
+ } while (status.status === 'pending');
218
+
219
+ console.log('CA Address:', status.caAddress);
220
+ console.log('CA Hash:', status.caHash);
221
+
222
+ // 7. 保存 keystore(加密持久化 Manager 私钥)
223
+ import { saveKeystore } from '@portkey/ca-agent-skills';
224
+ saveKeystore({
225
+ password: 'user-chosen-password',
226
+ privateKey: wallet.privateKey,
227
+ mnemonic: wallet.mnemonic,
228
+ caHash: status.caHash!,
229
+ caAddress: status.caAddress!,
230
+ originChainId: 'AELF',
231
+ network: 'mainnet',
232
+ });
233
+ // 钱包已自动解锁,后续写操作无需再设置 PORTKEY_PRIVATE_KEY
234
+ ```
235
+
236
+ ## 环境变量
237
+
238
+ | 变量 | 必需 | 默认值 | 说明 |
239
+ |------|------|--------|------|
240
+ | `PORTKEY_PRIVATE_KEY` | Fallback | — | Manager 钱包私钥(keystore 未解锁时的 fallback) |
241
+ | `PORTKEY_NETWORK` | 否 | `mainnet` | `mainnet` 或 `testnet` |
242
+ | `PORTKEY_API_URL` | 否 | 按网络 | 覆盖 API 地址 |
243
+ | `PORTKEY_GRAPHQL_URL` | 否 | 按网络 | 覆盖 GraphQL 地址 |
244
+
245
+ ## 测试
246
+
247
+ ```bash
248
+ bun test # 全部测试
249
+ bun run test:unit # 单元测试
250
+ bun run test:integration # 集成测试(需要网络)
251
+ bun run test:e2e # E2E 测试(需要私钥)
252
+ ```
253
+
254
+ ## 安全
255
+
256
+ - `.env` 文件已默认 git-ignore,不要提交
257
+ - 私钥仅写操作需要(转账、Guardian 管理、合约调用)
258
+ - **Keystore 加密**:Manager 私钥使用 scrypt(N=8192)+ AES-128-CTR 加密,文件权限 `0600`
259
+ - **内存生命周期**:私钥仅在 unlock 期间存在于内存,`portkey_lock` 立即清除
260
+ - MCP 模式下,keystore 密码仅存在于 AI 对话上下文,不会写入磁盘
261
+ - `PORTKEY_PRIVATE_KEY` 环境变量仍然支持作为 fallback,但推荐使用 keystore
262
+
263
+ ## License
264
+
265
+ MIT
@@ -0,0 +1,36 @@
1
+ import {
2
+ getPlatformPaths,
3
+ readJsonFile,
4
+ writeJsonFile,
5
+ mergeMcpConfig,
6
+ generateMcpEntry,
7
+ SERVER_NAME,
8
+ } from './utils.js';
9
+
10
+ export interface ClaudeSetupOptions {
11
+ configPath?: string;
12
+ serverPath?: string;
13
+ force?: boolean;
14
+ }
15
+
16
+ export function setupClaude(options: ClaudeSetupOptions = {}): void {
17
+ const paths = getPlatformPaths();
18
+ const configPath = options.configPath || paths.claude;
19
+
20
+ const existing = readJsonFile(configPath);
21
+ const entry = generateMcpEntry(options.serverPath);
22
+ const { config, action } = mergeMcpConfig(existing, SERVER_NAME, entry, options.force);
23
+
24
+ if (action === 'skipped') {
25
+ console.log(`[SKIP] "${SERVER_NAME}" already exists in ${configPath}`);
26
+ console.log(' Use --force to overwrite.');
27
+ return;
28
+ }
29
+
30
+ writeJsonFile(configPath, config);
31
+ console.log(`[${action.toUpperCase()}] ${SERVER_NAME} in ${configPath}`);
32
+ console.log('');
33
+ console.log('Next steps:');
34
+ console.log(' 1. Edit the config to replace <YOUR_PRIVATE_KEY> with your actual key');
35
+ console.log(' 2. Restart Claude Desktop');
36
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ getPlatformPaths,
3
+ readJsonFile,
4
+ writeJsonFile,
5
+ mergeMcpConfig,
6
+ generateMcpEntry,
7
+ SERVER_NAME,
8
+ } from './utils.js';
9
+
10
+ export interface CursorSetupOptions {
11
+ configPath?: string;
12
+ serverPath?: string;
13
+ force?: boolean;
14
+ global?: boolean;
15
+ }
16
+
17
+ export function setupCursor(options: CursorSetupOptions = {}): void {
18
+ const paths = getPlatformPaths();
19
+ const configPath =
20
+ options.configPath || (options.global ? paths.cursorGlobal : paths.cursorProject);
21
+
22
+ const existing = readJsonFile(configPath);
23
+ const entry = generateMcpEntry(options.serverPath);
24
+ const { config, action } = mergeMcpConfig(existing, SERVER_NAME, entry, options.force);
25
+
26
+ if (action === 'skipped') {
27
+ console.log(`[SKIP] "${SERVER_NAME}" already exists in ${configPath}`);
28
+ console.log(' Use --force to overwrite.');
29
+ return;
30
+ }
31
+
32
+ writeJsonFile(configPath, config);
33
+ const scope = options.global ? 'global' : 'project-level';
34
+ console.log(`[${action.toUpperCase()}] ${SERVER_NAME} (${scope}) in ${configPath}`);
35
+ console.log('');
36
+ console.log('Next steps:');
37
+ console.log(' 1. Edit the config to replace <YOUR_PRIVATE_KEY> with your actual key');
38
+ console.log(' 2. Restart Cursor or reload the MCP servers');
39
+ }
@@ -0,0 +1,39 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs';
3
+ import { getPackageRoot, readJsonFile, writeJsonFile } from './utils.js';
4
+
5
+ export interface OpenClawSetupOptions {
6
+ configPath?: string;
7
+ cwd?: string;
8
+ force?: boolean;
9
+ }
10
+
11
+ export function setupOpenClaw(options: OpenClawSetupOptions = {}): void {
12
+ const packageRoot = getPackageRoot();
13
+ const openclawSource = path.join(packageRoot, 'openclaw.json');
14
+
15
+ if (!fs.existsSync(openclawSource)) {
16
+ console.log('[ERROR] openclaw.json not found at', openclawSource);
17
+ console.log(' Please create it first.');
18
+ return;
19
+ }
20
+
21
+ const sourceConfig = readJsonFile(openclawSource);
22
+
23
+ // Replace cwd placeholders if needed
24
+ const cwd = options.cwd || packageRoot;
25
+
26
+ if (options.configPath) {
27
+ // Merge into existing config
28
+ const existing = readJsonFile(options.configPath);
29
+ const merged = {
30
+ ...existing,
31
+ tools: [...((existing.tools as unknown[]) || []), ...((sourceConfig.tools as unknown[]) || [])],
32
+ };
33
+ writeJsonFile(options.configPath, merged);
34
+ console.log(`[MERGED] OpenClaw tools into ${options.configPath}`);
35
+ } else {
36
+ // Output to stdout for piping
37
+ console.log(JSON.stringify({ ...sourceConfig, cwd }, null, 2));
38
+ }
39
+ }