@agirails/sdk 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +11 -2
- package/dist/ACTPClient.js.map +1 -1
- package/dist/cli/commands/deploy-check.d.ts +24 -0
- package/dist/cli/commands/deploy-check.d.ts.map +1 -0
- package/dist/cli/commands/deploy-check.js +316 -0
- package/dist/cli/commands/deploy-check.js.map +1 -0
- package/dist/cli/commands/deploy-env.d.ts +19 -0
- package/dist/cli/commands/deploy-env.d.ts.map +1 -0
- package/dist/cli/commands/deploy-env.js +123 -0
- package/dist/cli/commands/deploy-env.js.map +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +15 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/publish.js +1 -1
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.js +1 -1
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/config.d.ts +12 -0
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js +61 -1
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/config/publishPipeline.d.ts.map +1 -1
- package/dist/config/publishPipeline.js +5 -3
- package/dist/config/publishPipeline.js.map +1 -1
- package/dist/level0/request.js +1 -1
- package/dist/level0/request.js.map +1 -1
- package/dist/level1/Agent.js +1 -1
- package/dist/level1/Agent.js.map +1 -1
- package/dist/wallet/keystore.d.ts +10 -3
- package/dist/wallet/keystore.d.ts.map +1 -1
- package/dist/wallet/keystore.js +91 -11
- package/dist/wallet/keystore.js.map +1 -1
- package/package.json +1 -1
- package/src/ACTPClient.ts +13 -2
- package/src/cli/commands/deploy-check.ts +364 -0
- package/src/cli/commands/deploy-env.ts +120 -0
- package/src/cli/commands/init.ts +15 -1
- package/src/cli/commands/publish.ts +1 -1
- package/src/cli/commands/register.ts +1 -1
- package/src/cli/index.ts +6 -0
- package/src/cli/utils/config.ts +68 -0
- package/src/config/publishPipeline.ts +5 -3
- package/src/level0/request.ts +1 -1
- package/src/level1/Agent.ts +1 -1
- package/src/wallet/keystore.ts +116 -11
package/src/wallet/keystore.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Keystore auto-resolution for ACTP wallets.
|
|
3
3
|
*
|
|
4
|
-
* Resolution order:
|
|
5
|
-
* 1. ACTP_PRIVATE_KEY env var (
|
|
6
|
-
* 2.
|
|
7
|
-
* 3.
|
|
4
|
+
* Resolution order (AIP-13):
|
|
5
|
+
* 1. ACTP_PRIVATE_KEY env var (policy-gated: mainnet/unknown = hard fail)
|
|
6
|
+
* 2. ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD (deployment-safe, preferred)
|
|
7
|
+
* 3. .actp/keystore.json decrypted with ACTP_KEY_PASSWORD
|
|
8
|
+
* 4. undefined (caller decides what to do)
|
|
8
9
|
*/
|
|
9
10
|
import * as fs from 'fs';
|
|
10
11
|
import * as path from 'path';
|
|
11
12
|
import { Wallet } from 'ethers';
|
|
13
|
+
import { sdkLogger } from '../utils/Logger';
|
|
12
14
|
|
|
13
15
|
/** 30-minute TTL for cached private keys */
|
|
14
16
|
const CACHE_TTL_MS = 30 * 60 * 1000;
|
|
@@ -19,12 +21,20 @@ interface CacheEntry {
|
|
|
19
21
|
expiresAt: number;
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
export interface ResolvePrivateKeyOptions {
|
|
25
|
+
/** Network mode: 'mainnet', 'testnet', or 'mock'. Controls ACTP_PRIVATE_KEY policy. */
|
|
26
|
+
network?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
// Cache keyed by resolved keystorePath to support multiple stateDirectories
|
|
23
30
|
const _cache = new Map<string, CacheEntry>();
|
|
24
31
|
|
|
25
32
|
// Separate cache for env-var-resolved key (no path dependency)
|
|
26
33
|
let _envCache: CacheEntry | null = null;
|
|
27
34
|
|
|
35
|
+
// Separate cache for base64-resolved key (no path dependency)
|
|
36
|
+
let _base64Cache: CacheEntry | null = null;
|
|
37
|
+
|
|
28
38
|
function isExpired(entry: CacheEntry): boolean {
|
|
29
39
|
return Date.now() >= entry.expiresAt;
|
|
30
40
|
}
|
|
@@ -59,14 +69,58 @@ function validateRawKey(raw: string, source: string): string {
|
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
/**
|
|
62
|
-
*
|
|
72
|
+
* Determine the effective network for ACTP_PRIVATE_KEY policy.
|
|
73
|
+
* Falls back to ACTP_NETWORK env var. Null means unknown (fail-closed).
|
|
74
|
+
*/
|
|
75
|
+
function getEffectiveNetwork(options?: ResolvePrivateKeyOptions): string | null {
|
|
76
|
+
return options?.network ?? process.env.ACTP_NETWORK ?? null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Enforce ACTP_PRIVATE_KEY policy based on network (AIP-13).
|
|
81
|
+
* - mainnet/unknown: hard fail
|
|
82
|
+
* - testnet: warn once (on first resolution, not cache hits)
|
|
83
|
+
* - mock: silent
|
|
84
|
+
*/
|
|
85
|
+
function enforcePrivateKeyPolicy(network: string | null): void {
|
|
86
|
+
if (network === 'mock') return;
|
|
87
|
+
|
|
88
|
+
if (network === 'testnet') {
|
|
89
|
+
// Warn once (only on first resolution — _envCache is null)
|
|
90
|
+
if (_envCache === null) {
|
|
91
|
+
sdkLogger.warn(
|
|
92
|
+
'ACTP_PRIVATE_KEY is deprecated. Use ACTP_KEYSTORE_BASE64 instead.\n' +
|
|
93
|
+
'Run: actp deploy:env'
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// mainnet or unknown (null) — fail-closed
|
|
100
|
+
const networkLabel = network === 'mainnet' ? 'production' : 'unknown network (fail-closed)';
|
|
101
|
+
throw new Error(
|
|
102
|
+
`ACTP_PRIVATE_KEY is not allowed in ${networkLabel}. Use ACTP_KEYSTORE_BASE64 instead.\n` +
|
|
103
|
+
'Run: actp deploy:env\n' +
|
|
104
|
+
'If this is testnet, set ACTP_NETWORK=testnet'
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Auto-resolve private key: env var → base64 keystore → file keystore → undefined.
|
|
63
110
|
* Never logs or prints the key itself.
|
|
111
|
+
*
|
|
112
|
+
* @param stateDirectory - Directory containing .actp/ (defaults to cwd)
|
|
113
|
+
* @param options - Options including network for ACTP_PRIVATE_KEY policy
|
|
64
114
|
*/
|
|
65
115
|
export async function resolvePrivateKey(
|
|
66
|
-
stateDirectory?: string
|
|
116
|
+
stateDirectory?: string,
|
|
117
|
+
options?: ResolvePrivateKeyOptions
|
|
67
118
|
): Promise<string | undefined> {
|
|
68
|
-
// 1.
|
|
119
|
+
// 1. ACTP_PRIVATE_KEY (highest priority, policy-gated)
|
|
69
120
|
if (process.env.ACTP_PRIVATE_KEY) {
|
|
121
|
+
const network = getEffectiveNetwork(options);
|
|
122
|
+
enforcePrivateKeyPolicy(network);
|
|
123
|
+
|
|
70
124
|
if (_envCache && !isExpired(_envCache)) return _envCache.key;
|
|
71
125
|
|
|
72
126
|
const key = validateRawKey(process.env.ACTP_PRIVATE_KEY, 'ACTP_PRIVATE_KEY env var');
|
|
@@ -75,7 +129,54 @@ export async function resolvePrivateKey(
|
|
|
75
129
|
return key;
|
|
76
130
|
}
|
|
77
131
|
|
|
78
|
-
// 2.
|
|
132
|
+
// 2. ACTP_KEYSTORE_BASE64 (deployment-safe, preferred for production)
|
|
133
|
+
if (process.env.ACTP_KEYSTORE_BASE64) {
|
|
134
|
+
if (_base64Cache && !isExpired(_base64Cache)) return _base64Cache.key;
|
|
135
|
+
|
|
136
|
+
const raw = process.env.ACTP_KEYSTORE_BASE64.replace(/\s/g, '');
|
|
137
|
+
let decoded: string;
|
|
138
|
+
try {
|
|
139
|
+
decoded = Buffer.from(raw, 'base64').toString('utf-8');
|
|
140
|
+
} catch {
|
|
141
|
+
throw new Error(
|
|
142
|
+
'ACTP_KEYSTORE_BASE64 is not valid base64.\n' +
|
|
143
|
+
'Run: actp deploy:env'
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
JSON.parse(decoded);
|
|
149
|
+
} catch {
|
|
150
|
+
throw new Error(
|
|
151
|
+
'ACTP_KEYSTORE_BASE64 is not valid encrypted keystore JSON.\n' +
|
|
152
|
+
'Run: actp deploy:env'
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const password = process.env.ACTP_KEY_PASSWORD;
|
|
157
|
+
if (!password) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
'ACTP_KEYSTORE_BASE64 is set but ACTP_KEY_PASSWORD is not set.\n' +
|
|
160
|
+
'Set it: export ACTP_KEY_PASSWORD="your-password"'
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let wallet: Wallet;
|
|
165
|
+
try {
|
|
166
|
+
wallet = (await Wallet.fromEncryptedJson(decoded, password)) as Wallet;
|
|
167
|
+
} catch (err) {
|
|
168
|
+
// Sanitize: do not leak keystore content in error messages
|
|
169
|
+
const message = err instanceof Error ? err.message : 'unknown error';
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Failed to decrypt ACTP_KEYSTORE_BASE64: ${message}`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
_base64Cache = { key: wallet.privateKey, address: wallet.address, expiresAt: Date.now() + CACHE_TTL_MS };
|
|
176
|
+
return wallet.privateKey;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 3. Resolve keystore path
|
|
79
180
|
if (stateDirectory) {
|
|
80
181
|
validateStateDirectory(stateDirectory);
|
|
81
182
|
}
|
|
@@ -84,12 +185,12 @@ export async function resolvePrivateKey(
|
|
|
84
185
|
: path.join(process.cwd(), '.actp');
|
|
85
186
|
const keystorePath = path.resolve(actpDir, 'keystore.json');
|
|
86
187
|
|
|
87
|
-
//
|
|
188
|
+
// 4. Cache hit (keyed by resolved path, with TTL)
|
|
88
189
|
const cached = _cache.get(keystorePath);
|
|
89
190
|
if (cached && !isExpired(cached)) return cached.key;
|
|
90
191
|
if (cached) _cache.delete(keystorePath); // expired
|
|
91
192
|
|
|
92
|
-
//
|
|
193
|
+
// 5. Keystore file
|
|
93
194
|
if (!fs.existsSync(keystorePath)) return undefined;
|
|
94
195
|
|
|
95
196
|
const password = process.env.ACTP_KEY_PASSWORD;
|
|
@@ -109,12 +210,15 @@ export async function resolvePrivateKey(
|
|
|
109
210
|
|
|
110
211
|
/**
|
|
111
212
|
* Get cached address from last resolvePrivateKey() call.
|
|
112
|
-
* Works for
|
|
213
|
+
* Works for env-var, base64, and keystore resolution paths.
|
|
113
214
|
*/
|
|
114
215
|
export function getCachedAddress(stateDirectory?: string): string | undefined {
|
|
115
216
|
// Env var path
|
|
116
217
|
if (_envCache && !isExpired(_envCache)) return _envCache.address;
|
|
117
218
|
|
|
219
|
+
// Base64 path
|
|
220
|
+
if (_base64Cache && !isExpired(_base64Cache)) return _base64Cache.address;
|
|
221
|
+
|
|
118
222
|
// Keystore path — look up by resolved path
|
|
119
223
|
const actpDir = stateDirectory
|
|
120
224
|
? path.join(stateDirectory, '.actp')
|
|
@@ -132,4 +236,5 @@ export function getCachedAddress(stateDirectory?: string): string | undefined {
|
|
|
132
236
|
export function _clearCache(): void {
|
|
133
237
|
_cache.clear();
|
|
134
238
|
_envCache = null;
|
|
239
|
+
_base64Cache = null;
|
|
135
240
|
}
|