epistery 1.0.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/Architecture.md +84 -0
- package/CLI.md +291 -0
- package/LICENSE +21 -0
- package/MONGODB_GOTCHA.md +69 -0
- package/README.md +50 -0
- package/SESSION.md +98 -0
- package/cli/epistery.mjs +576 -0
- package/client/client.js +18 -0
- package/client/ethers.js +24441 -0
- package/client/ethers.min.js +1 -0
- package/client/export.js +67 -0
- package/client/status.html +707 -0
- package/client/wallet.js +213 -0
- package/client/witness.js +663 -0
- package/contracts/agent.sol +108 -0
- package/default.ini +14 -0
- package/docs/EpisteryModuleConfig.md +317 -0
- package/docs/blog-unified-config.md +125 -0
- package/hardhat.config.js +33 -0
- package/index.mjs +385 -0
- package/package.json +46 -0
- package/scripts/deploy-agent.js +33 -0
- package/scripts/verify-agent.js +39 -0
- package/src/epistery.ts +275 -0
- package/src/utils/Aqua.ts +194 -0
- package/src/utils/CliWallet.ts +334 -0
- package/src/utils/Config.ts +196 -0
- package/src/utils/Utils.ts +571 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/types.ts +114 -0
- package/test/README.md +50 -0
- package/test/index.html +13 -0
- package/test/package.json +15 -0
- package/test/server.mjs +87 -0
- package/tsconfig.json +26 -0
package/cli/epistery.mjs
ADDED
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* epistery - Unified CLI tool for Epistery
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* epistery initialize <domain> Initialize domain with wallet
|
|
8
|
+
* epistery curl [options] <url> Make authenticated HTTP request
|
|
9
|
+
* epistery info [domain] Show domain information
|
|
10
|
+
* epistery set-default <domain> Set default domain for CLI
|
|
11
|
+
*
|
|
12
|
+
* The curl subcommand automatically performs key exchange when needed.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { CliWallet } from '../dist/utils/CliWallet.js';
|
|
16
|
+
import { Utils } from '../dist/utils/Utils.js';
|
|
17
|
+
import { ethers } from 'ethers';
|
|
18
|
+
import { spawn } from 'child_process';
|
|
19
|
+
import dotenv from 'dotenv';
|
|
20
|
+
import { fileURLToPath } from 'url';
|
|
21
|
+
import { dirname, join } from 'path';
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = dirname(__filename);
|
|
25
|
+
|
|
26
|
+
// Load .env file from project root
|
|
27
|
+
dotenv.config({ path: join(__dirname, '../.env') });
|
|
28
|
+
|
|
29
|
+
function showHelp() {
|
|
30
|
+
console.log('epistery - CLI tool for Epistery authentication and requests');
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log('Usage:');
|
|
33
|
+
console.log(' epistery initialize <domain> Initialize domain with wallet');
|
|
34
|
+
console.log(' epistery curl [options] <url> Make authenticated HTTP request');
|
|
35
|
+
console.log(' epistery info [domain] Show domain information');
|
|
36
|
+
console.log(' epistery set-default <domain> Set default domain for CLI');
|
|
37
|
+
console.log(' epistery whitelist <domain> <addr> Add address to domain whitelist');
|
|
38
|
+
console.log(' epistery whitelist -d <domain> <a> Remove address from whitelist');
|
|
39
|
+
console.log(' epistery whitelist status <domain> Show domain whitelist status');
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log('curl options:');
|
|
42
|
+
console.log(' -w, --wallet <domain> Use specific domain wallet (overrides default)');
|
|
43
|
+
console.log(' -X, --request <method> HTTP method (default: GET)');
|
|
44
|
+
console.log(' -d, --data <data> Request body data');
|
|
45
|
+
console.log(' -H, --header <header> Additional headers');
|
|
46
|
+
console.log(' -b, --bot Use bot auth header (default: session cookie)');
|
|
47
|
+
console.log(' -v, --verbose Show detailed output');
|
|
48
|
+
console.log('');
|
|
49
|
+
console.log('Examples:');
|
|
50
|
+
console.log(' # Initialize a domain (creates wallet)');
|
|
51
|
+
console.log(' epistery initialize localhost');
|
|
52
|
+
console.log(' epistery initialize wiki.rootz.global');
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log(' # Set default domain');
|
|
55
|
+
console.log(' epistery set-default localhost');
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(' # Whitelist management');
|
|
58
|
+
console.log(' epistery whitelist localhost 0x1234567890123456789012345678901234567890');
|
|
59
|
+
console.log(' epistery whitelist -d localhost 0x1234567890123456789012345678901234567890');
|
|
60
|
+
console.log(' epistery whitelist status localhost');
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(' # Make requests (uses default domain)');
|
|
63
|
+
console.log(' epistery curl https://wiki.rootz.global/wiki/Home');
|
|
64
|
+
console.log(' epistery curl -X GET https://wiki.rootz.global/wiki/index');
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(' # Use specific domain');
|
|
67
|
+
console.log(' epistery curl -w localhost https://localhost:4080/session/context');
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log(' # Bot mode (sign per request, no session)');
|
|
70
|
+
console.log(' epistery curl --bot -X GET https://wiki.rootz.global/session/context');
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(' # POST request');
|
|
73
|
+
console.log(' epistery curl -X POST -d \'{"title":"Test","body":"# Test"}\' <url>');
|
|
74
|
+
console.log('');
|
|
75
|
+
console.log('Domain configs stored in: ~/.epistery/{domain}/config.ini');
|
|
76
|
+
console.log('Default domain set in: ~/.epistery/config.ini [cli] section');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function initializeDomain(domain) {
|
|
80
|
+
try {
|
|
81
|
+
console.log(`Initializing domain: ${domain}`);
|
|
82
|
+
console.log('');
|
|
83
|
+
|
|
84
|
+
const wallet = CliWallet.initialize(domain);
|
|
85
|
+
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log('✓ Domain initialized successfully');
|
|
88
|
+
console.log('');
|
|
89
|
+
console.log('Configuration saved to: ~/.epistery/' + domain + '/config.ini');
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log('Set as default with: epistery set-default ' + domain);
|
|
92
|
+
console.log('Make requests with: epistery curl https://example.com');
|
|
93
|
+
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('');
|
|
96
|
+
console.error('Error:', error.message);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function showInfo(domain) {
|
|
102
|
+
try {
|
|
103
|
+
const wallet = CliWallet.load(domain);
|
|
104
|
+
|
|
105
|
+
console.log('Domain:', wallet.getDomain());
|
|
106
|
+
console.log('Address:', wallet.address);
|
|
107
|
+
console.log('Public Key:', wallet.publicKey);
|
|
108
|
+
console.log('');
|
|
109
|
+
|
|
110
|
+
const provider = wallet.getProvider();
|
|
111
|
+
if (provider) {
|
|
112
|
+
console.log('Provider:', provider.name);
|
|
113
|
+
console.log('Chain ID:', provider.chainId);
|
|
114
|
+
console.log('RPC:', provider.rpc);
|
|
115
|
+
console.log('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log('Sessions: Stored per-server in ~/.epistery/' + wallet.getDomain() + '/sessions/');
|
|
119
|
+
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('');
|
|
122
|
+
console.error('Error:', error.message);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function setDefault(domain) {
|
|
128
|
+
try {
|
|
129
|
+
CliWallet.setDefaultDomain(domain);
|
|
130
|
+
console.log(`✓ Default domain set to: ${domain}`);
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log('Verify with: epistery info');
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('');
|
|
135
|
+
console.error('Error:', error.message);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function addToWhitelist(domain, address) {
|
|
141
|
+
try {
|
|
142
|
+
console.log(`Adding ${address} to whitelist for domain: ${domain}`);
|
|
143
|
+
console.log('');
|
|
144
|
+
|
|
145
|
+
const cliWallet = CliWallet.load(domain);
|
|
146
|
+
const domainConfig = Utils.GetDomainInfo(domain);
|
|
147
|
+
if (!domainConfig.wallet) {
|
|
148
|
+
throw new Error(`No wallet found for domain: ${domain}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const provider = cliWallet.getProvider();
|
|
152
|
+
if (!provider || !provider.rpc) {
|
|
153
|
+
throw new Error(`No provider configured for domain: ${domain}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const ethersProvider = new ethers.providers.JsonRpcProvider(provider.rpc);
|
|
157
|
+
let wallet;
|
|
158
|
+
if (domainConfig.wallet.mnemonic) {
|
|
159
|
+
wallet = ethers.Wallet.fromMnemonic(domainConfig.wallet.mnemonic).connect(ethersProvider);
|
|
160
|
+
}
|
|
161
|
+
else if (domainConfig.wallet.privateKey) {
|
|
162
|
+
wallet = new ethers.Wallet(domainConfig.wallet.privateKey, ethersProvider);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
throw new Error(`No wallet credentials found for domain: ${domain}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Validate the address to add
|
|
169
|
+
if (!ethers.utils.isAddress(address)) {
|
|
170
|
+
throw new Error(`Invalid Ethereum address: ${address}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const receipt = await Utils.AddToWhitelist(wallet, address, domain);
|
|
174
|
+
|
|
175
|
+
console.log('');
|
|
176
|
+
console.log('✓ Address added to whitelist successfully');
|
|
177
|
+
console.log('');
|
|
178
|
+
console.log(`Transaction hash: ${receipt.transactionHash}`);
|
|
179
|
+
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.error('');
|
|
183
|
+
console.error('Error:', error.message);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function removeFromWhitelist(domain, address) {
|
|
189
|
+
try {
|
|
190
|
+
console.log(`Removing ${address} from whitelist for domain: ${domain}`);
|
|
191
|
+
console.log('');
|
|
192
|
+
|
|
193
|
+
const cliWallet = CliWallet.load(domain);
|
|
194
|
+
const domainConfig = Utils.GetDomainInfo(domain);
|
|
195
|
+
if (!domainConfig.wallet) {
|
|
196
|
+
throw new Error(`No wallet found for domain: ${domain}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Get the provider and connect the wallet
|
|
200
|
+
const provider = cliWallet.getProvider();
|
|
201
|
+
if (!provider || !provider.rpc) {
|
|
202
|
+
throw new Error(`No provider configured for domain: ${domain}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const ethersProvider = new ethers.providers.JsonRpcProvider(provider.rpc);
|
|
206
|
+
let wallet;
|
|
207
|
+
if (domainConfig.wallet.mnemonic) {
|
|
208
|
+
wallet = ethers.Wallet.fromMnemonic(domainConfig.wallet.mnemonic).connect(ethersProvider);
|
|
209
|
+
}
|
|
210
|
+
else if (domainConfig.wallet.privateKey) {
|
|
211
|
+
wallet = new ethers.Wallet(domainConfig.wallet.privateKey, ethersProvider);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
throw new Error(`No wallet credentials found for domain: ${domain}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Validate the address to remove
|
|
218
|
+
if (!ethers.utils.isAddress(address)) {
|
|
219
|
+
throw new Error(`Invalid Ethereum address: ${address}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const receipt = await Utils.RemoveFromWhitelist(wallet, address, domain);
|
|
223
|
+
|
|
224
|
+
console.log('');
|
|
225
|
+
console.log('Address removed from whitelist successfully');
|
|
226
|
+
console.log('');
|
|
227
|
+
console.log(`Transaction hash: ${receipt.transactionHash}`);
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error('');
|
|
232
|
+
console.error('Error:', error.message);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function showWhitelistStatus(domain) {
|
|
238
|
+
try {
|
|
239
|
+
console.log(`Fetching whitelist status for domain: ${domain}`);
|
|
240
|
+
console.log('');
|
|
241
|
+
|
|
242
|
+
const cliWallet = CliWallet.load(domain);
|
|
243
|
+
const domainConfig = Utils.GetDomainInfo(domain);
|
|
244
|
+
if (!domainConfig.wallet) {
|
|
245
|
+
throw new Error(`No wallet found for domain: ${domain}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const provider = cliWallet.getProvider();
|
|
249
|
+
if (!provider || !provider.rpc) {
|
|
250
|
+
throw new Error(`No provider configured for domain: ${domain}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const ethersProvider = new ethers.providers.JsonRpcProvider(provider.rpc);
|
|
254
|
+
let wallet;
|
|
255
|
+
if (domainConfig.wallet.mnemonic) {
|
|
256
|
+
wallet = ethers.Wallet.fromMnemonic(domainConfig.wallet.mnemonic).connect(ethersProvider);
|
|
257
|
+
}
|
|
258
|
+
else if (domainConfig.wallet.privateKey) {
|
|
259
|
+
wallet = new ethers.Wallet(domainConfig.wallet.privateKey, ethersProvider);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
throw new Error(`No wallet credentials found for domain: ${domain}`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const whitelist = await Utils.GetWhitelist(wallet, cliWallet.address, domain);
|
|
266
|
+
|
|
267
|
+
console.log('');
|
|
268
|
+
console.log(`Whitelist for domain: ${domain}`);
|
|
269
|
+
console.log(`Owner address: ${cliWallet.address}`);
|
|
270
|
+
console.log(`Total whitelisted addresses: ${whitelist.length}`);
|
|
271
|
+
console.log('');
|
|
272
|
+
|
|
273
|
+
if (whitelist.length > 0) {
|
|
274
|
+
console.log('Whitelisted addresses:');
|
|
275
|
+
whitelist.forEach((addr, index) => {
|
|
276
|
+
console.log(` ${index + 1}. ${addr}`);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
console.log('No addresses whitelisted yet.');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
console.log('');
|
|
284
|
+
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
console.error('');
|
|
288
|
+
console.error('Error:', error.message);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function parseCurlArgs(args) {
|
|
294
|
+
const options = {
|
|
295
|
+
domain: null, // null means use default
|
|
296
|
+
method: 'GET',
|
|
297
|
+
data: null,
|
|
298
|
+
headers: [],
|
|
299
|
+
bot: true, // Use bot mode by default
|
|
300
|
+
verbose: false,
|
|
301
|
+
url: null
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
for (let i = 0; i < args.length; i++) {
|
|
305
|
+
const arg = args[i];
|
|
306
|
+
|
|
307
|
+
switch (arg) {
|
|
308
|
+
case '-w':
|
|
309
|
+
case '--wallet':
|
|
310
|
+
options.domain = args[++i];
|
|
311
|
+
break;
|
|
312
|
+
|
|
313
|
+
case '-X':
|
|
314
|
+
case '--request':
|
|
315
|
+
options.method = args[++i].toUpperCase();
|
|
316
|
+
break;
|
|
317
|
+
|
|
318
|
+
case '-d':
|
|
319
|
+
case '--data':
|
|
320
|
+
options.data = args[++i];
|
|
321
|
+
break;
|
|
322
|
+
|
|
323
|
+
case '-H':
|
|
324
|
+
case '--header':
|
|
325
|
+
options.headers.push(args[++i]);
|
|
326
|
+
break;
|
|
327
|
+
|
|
328
|
+
case '-b':
|
|
329
|
+
case '--bot':
|
|
330
|
+
options.bot = true;
|
|
331
|
+
break;
|
|
332
|
+
|
|
333
|
+
case '-v':
|
|
334
|
+
case '--verbose':
|
|
335
|
+
options.verbose = true;
|
|
336
|
+
break;
|
|
337
|
+
|
|
338
|
+
default:
|
|
339
|
+
if (!arg.startsWith('-')) {
|
|
340
|
+
options.url = arg;
|
|
341
|
+
} else {
|
|
342
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!options.url) {
|
|
348
|
+
throw new Error('URL required');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return options;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function executeCurl(curlArgs) {
|
|
355
|
+
return new Promise((resolve, reject) => {
|
|
356
|
+
const curl = spawn('curl', curlArgs);
|
|
357
|
+
|
|
358
|
+
let stdout = '';
|
|
359
|
+
let stderr = '';
|
|
360
|
+
|
|
361
|
+
curl.stdout.on('data', (data) => {
|
|
362
|
+
stdout += data.toString();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
curl.stderr.on('data', (data) => {
|
|
366
|
+
stderr += data.toString();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
curl.on('close', (code) => {
|
|
370
|
+
if (code !== 0) {
|
|
371
|
+
reject(new Error(`curl exited with code ${code}: ${stderr}`));
|
|
372
|
+
} else {
|
|
373
|
+
resolve({ stdout, stderr });
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
curl.on('error', (error) => {
|
|
378
|
+
reject(new Error(`Failed to execute curl: ${error.message}`));
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function performCurl(options) {
|
|
384
|
+
try {
|
|
385
|
+
// Load wallet (uses default if domain not specified)
|
|
386
|
+
const wallet = CliWallet.load(options.domain);
|
|
387
|
+
|
|
388
|
+
if (options.verbose) {
|
|
389
|
+
console.error(`[epistery] Domain: ${wallet.getDomain()}`);
|
|
390
|
+
console.error(`[epistery] Address: ${wallet.address}`);
|
|
391
|
+
console.error(`[epistery] Auth mode: ${options.bot ? 'bot' : 'session'}`);
|
|
392
|
+
console.error('');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Build curl command
|
|
396
|
+
const curlArgs = ['-s']; // Silent mode
|
|
397
|
+
|
|
398
|
+
if (options.verbose) {
|
|
399
|
+
curlArgs.push('-v');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// HTTP method
|
|
403
|
+
curlArgs.push('-X', options.method);
|
|
404
|
+
|
|
405
|
+
// Authentication
|
|
406
|
+
if (options.bot) {
|
|
407
|
+
// Bot mode: Use Authorization header
|
|
408
|
+
const authHeader = await wallet.createBotAuthHeader();
|
|
409
|
+
curlArgs.push('-H', `Authorization: ${authHeader}`);
|
|
410
|
+
|
|
411
|
+
} else {
|
|
412
|
+
// Session mode: Check for existing session or perform key exchange
|
|
413
|
+
// Extract base URL (protocol + host + port) for key exchange
|
|
414
|
+
const url = new URL(options.url);
|
|
415
|
+
const baseUrl = `${url.protocol}//${url.host}`;
|
|
416
|
+
|
|
417
|
+
let session = wallet.getSession(baseUrl);
|
|
418
|
+
|
|
419
|
+
if (!session) {
|
|
420
|
+
if (options.verbose) {
|
|
421
|
+
console.error('[epistery] No session found, performing key exchange...');
|
|
422
|
+
console.error(`[epistery] Connecting to: ${baseUrl}`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Perform key exchange automatically
|
|
426
|
+
const response = await wallet.performKeyExchange(baseUrl);
|
|
427
|
+
|
|
428
|
+
if (options.verbose) {
|
|
429
|
+
console.error(`[epistery] Key exchange successful`);
|
|
430
|
+
console.error(`[epistery] Server: ${response.serverAddress}`);
|
|
431
|
+
console.error(`[epistery] Authenticated: ${response.authenticated}`);
|
|
432
|
+
console.error('');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
session = wallet.getSession(baseUrl);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (session && session.cookie) {
|
|
439
|
+
curlArgs.push('-H', `Cookie: _rhonda_session=${session.cookie}`);
|
|
440
|
+
} else {
|
|
441
|
+
throw new Error('Failed to obtain session cookie');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Request body
|
|
446
|
+
if (options.data) {
|
|
447
|
+
curlArgs.push('-H', 'Content-Type: application/json');
|
|
448
|
+
curlArgs.push('-d', options.data);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Additional headers
|
|
452
|
+
for (const header of options.headers) {
|
|
453
|
+
curlArgs.push('-H', header);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// URL (last argument)
|
|
457
|
+
curlArgs.push(options.url);
|
|
458
|
+
|
|
459
|
+
if (options.verbose) {
|
|
460
|
+
console.error('[epistery] Executing: curl', curlArgs.join(' '));
|
|
461
|
+
console.error('');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const { stdout, stderr } = await executeCurl(curlArgs);
|
|
465
|
+
|
|
466
|
+
if (stderr && options.verbose) {
|
|
467
|
+
console.error(stderr);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
console.log(stdout);
|
|
471
|
+
|
|
472
|
+
} catch (error) {
|
|
473
|
+
console.error('');
|
|
474
|
+
console.error('Error:', error.message);
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async function main() {
|
|
480
|
+
const command = process.argv[2];
|
|
481
|
+
const args = process.argv.slice(3);
|
|
482
|
+
|
|
483
|
+
try {
|
|
484
|
+
switch (command) {
|
|
485
|
+
case 'initialize':
|
|
486
|
+
if (!args[0]) {
|
|
487
|
+
console.error('Error: Domain name required');
|
|
488
|
+
console.error('Usage: epistery initialize <domain>');
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
await initializeDomain(args[0]);
|
|
492
|
+
break;
|
|
493
|
+
|
|
494
|
+
case 'curl':
|
|
495
|
+
if (args.length === 0) {
|
|
496
|
+
console.error('Error: URL required');
|
|
497
|
+
console.error('Usage: epistery curl [options] <url>');
|
|
498
|
+
console.error('Run epistery --help for more information');
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
const curlOptions = parseCurlArgs(args);
|
|
502
|
+
await performCurl(curlOptions);
|
|
503
|
+
break;
|
|
504
|
+
|
|
505
|
+
case 'info':
|
|
506
|
+
await showInfo(args[0] || null);
|
|
507
|
+
break;
|
|
508
|
+
|
|
509
|
+
case 'set-default':
|
|
510
|
+
if (!args[0]) {
|
|
511
|
+
console.error('Error: Domain name required');
|
|
512
|
+
console.error('Usage: epistery set-default <domain>');
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
setDefault(args[0]);
|
|
516
|
+
break;
|
|
517
|
+
|
|
518
|
+
case 'whitelist':
|
|
519
|
+
if (args.length === 0) {
|
|
520
|
+
console.error('Error: Arguments required');
|
|
521
|
+
console.error('Usage: epistery whitelist <domain> <address>');
|
|
522
|
+
console.error(' epistery whitelist -d <domain> <address>');
|
|
523
|
+
console.error(' epistery whitelist status <domain>');
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (args[0] === 'status') {
|
|
528
|
+
if (!args[1]) {
|
|
529
|
+
console.error('Error: Domain name required');
|
|
530
|
+
console.error('Usage: epistery whitelist status <domain>');
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
await showWhitelistStatus(args[1]);
|
|
535
|
+
}
|
|
536
|
+
else if (args[0] === '-d' || args[0] === '--delete') {
|
|
537
|
+
if (!args[1] || !args[2]) {
|
|
538
|
+
console.error('Error: Domain and address required');
|
|
539
|
+
console.error('Usage: epistery whitelist -d <domain> <address>');
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
await removeFromWhitelist(args[1], args[2]);
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
if (!args[0] || !args[1]) {
|
|
547
|
+
console.error('Error: Domain and address required');
|
|
548
|
+
console.error('Usage: epistery whitelist <domain> <address>');
|
|
549
|
+
process.exit(1);
|
|
550
|
+
}
|
|
551
|
+
await addToWhitelist(args[0], args[1]);
|
|
552
|
+
}
|
|
553
|
+
break;
|
|
554
|
+
|
|
555
|
+
case 'help':
|
|
556
|
+
case '--help':
|
|
557
|
+
case '-h':
|
|
558
|
+
showHelp();
|
|
559
|
+
break;
|
|
560
|
+
|
|
561
|
+
default:
|
|
562
|
+
if (command) {
|
|
563
|
+
console.error(`Error: Unknown command '${command}'`);
|
|
564
|
+
console.error('');
|
|
565
|
+
}
|
|
566
|
+
showHelp();
|
|
567
|
+
process.exit(command ? 1 : 0);
|
|
568
|
+
}
|
|
569
|
+
} catch (error) {
|
|
570
|
+
console.error('');
|
|
571
|
+
console.error('Error:', error.message);
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
main();
|
package/client/client.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Epistery Client Library
|
|
3
|
+
*
|
|
4
|
+
* Main client library that can be included via <script> tag
|
|
5
|
+
* Provides easy access to Epistery functionality
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import Witness from './witness.js';
|
|
9
|
+
|
|
10
|
+
// Make Witness available globally when loaded as a script
|
|
11
|
+
if (typeof window !== 'undefined') {
|
|
12
|
+
window.Epistery = {
|
|
13
|
+
witness: await Witness.connect()
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { Witness };
|
|
18
|
+
export default Witness;
|