@nullpay/mcp 1.0.0 → 1.0.3

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 CHANGED
@@ -1,49 +1,216 @@
1
1
  # @nullpay/mcp
2
2
 
3
- NullPay MCP (Model Context Protocol) server for conversational invoice and payment flows.
3
+ NullPay MCP is a local Model Context Protocol server for Claude that lets users create invoices, pay invoices, and inspect transactions through chat.
4
4
 
5
- This server allows AI agents to interact with the NullPay protocol, enabling them to create invoices, track payments, and manage merchant flows directly through chat.
5
+ The package is designed so the user only needs to provide their own wallet credentials:
6
6
 
7
- ## Features
7
+ - `NULLPAY_MAIN_ADDRESS`
8
+ - `NULLPAY_MAIN_PRIVATE_KEY`
9
+ - `NULLPAY_MAIN_PASSWORD`
8
10
 
9
- - **Tool-based Interaction**: Exposes tools for creating NullPay invoices.
10
- - **Privacy First**: Built on top of the Aleo blockchain with Zero-Knowledge Proofs.
11
- - **Stdio Transport**: Compatible with MCP clients like Claude Desktop.
11
+ NullPay-owned infrastructure stays inside the package or on the backend:
12
12
 
13
- ## Installation
13
+ - default backend API: `https://nullpay-backend-ib5q4.ondigitalocean.app/api`
14
+ - default public app URL: `https://nullpay.app`
15
+ - relayer private key: backend only
16
+
17
+ ## How It Works
18
+
19
+ 1. The user runs the setup wizard.
20
+ 2. The wizard asks whether to install into Claude Code or Claude Desktop.
21
+ 3. The wizard asks for the user's address, private key, and password.
22
+ 4. The wizard writes the MCP config file automatically on the user's machine.
23
+ 5. Claude starts the local NullPay MCP server with `@nullpay/mcp server`.
24
+ 6. The MCP server talks to the NullPay backend and keeps the user's private-key operations local.
25
+
26
+ ## Install For End Users
27
+
28
+ Run the setup wizard:
29
+
30
+ ```bash
31
+ npx -y @nullpay/mcp
32
+ ```
33
+
34
+ You can also call the setup command explicitly:
14
35
 
15
36
  ```bash
16
- npm install @nullpay/mcp
37
+ npx -y @nullpay/mcp setup
17
38
  ```
18
39
 
19
- ## Usage
40
+ The wizard writes the required MCP config entry automatically.
20
41
 
21
- ### As an MCP Server
42
+ ## Manual Claude Configuration
22
43
 
23
- Add the following to your MCP client configuration (e.g., `claude_desktop_config.json`):
44
+ The setup wizard is the recommended path, but you can also add the MCP server manually.
45
+
46
+ ### Claude Desktop
24
47
 
25
48
  ```json
26
49
  {
27
50
  "mcpServers": {
28
51
  "nullpay": {
29
52
  "command": "npx",
30
- "args": ["-y", "@nullpay/mcp"],
53
+ "args": ["-y", "@nullpay/mcp", "server"],
54
+ "env": {
55
+ "NULLPAY_MAIN_ADDRESS": "aleo1...",
56
+ "NULLPAY_MAIN_PRIVATE_KEY": "APrivateKey1...",
57
+ "NULLPAY_MAIN_PASSWORD": "your-password"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ On Windows, Claude Desktop may need:
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "nullpay": {
70
+ "command": "cmd",
71
+ "args": ["/c", "npx", "-y", "@nullpay/mcp", "server"],
31
72
  "env": {
32
- "NULLPAY_BACKEND_URL": "https://your-api.com/api",
33
- "NULLPAY_MCP_SHARED_SECRET": "your-secret"
73
+ "NULLPAY_MAIN_ADDRESS": "aleo1...",
74
+ "NULLPAY_MAIN_PRIVATE_KEY": "APrivateKey1...",
75
+ "NULLPAY_MAIN_PASSWORD": "your-password"
34
76
  }
35
77
  }
36
78
  }
37
79
  }
38
80
  ```
39
81
 
40
- ## Configuration
82
+ ### Claude Code
83
+
84
+ The setup wizard can also write the Claude Code config automatically. If needed, the same `server` command is used there too.
85
+
86
+ After setup, restart Claude and verify the MCP server is available.
87
+
88
+ ## Commands
89
+
90
+ ### Published package
91
+
92
+ Start the interactive installer:
93
+
94
+ ```bash
95
+ npx -y @nullpay/mcp
96
+ ```
97
+
98
+ Run the MCP server directly:
41
99
 
42
- The server requires the following environment variables:
100
+ ```bash
101
+ npx -y @nullpay/mcp server
102
+ ```
103
+
104
+ Show help:
105
+
106
+ ```bash
107
+ npx -y @nullpay/mcp --help
108
+ ```
109
+
110
+ ### Local development
111
+
112
+ From `packages/nullpay-mcp`:
43
113
 
44
- - `NULLPAY_BACKEND_URL`: The base URL of your NullPay backend instance.
45
- - `NULLPAY_MCP_SHARED_SECRET`: A shared secret to authenticate with the backend.
114
+ ```bash
115
+ npm install
116
+ npm run build
117
+ node dist/cli.js
118
+ ```
119
+
120
+ Run the local MCP server directly:
121
+
122
+ ```bash
123
+ node dist/cli.js server
124
+ ```
125
+
126
+ Run in TypeScript during development:
127
+
128
+ ```bash
129
+ npm run dev
130
+ ```
131
+
132
+ ## Tooling And Login Model
133
+
134
+ The MCP server exposes four tools:
135
+
136
+ - `login`
137
+ - `create_invoice`
138
+ - `pay_invoice`
139
+ - `get_transaction_info`
140
+
141
+ ### `login`
142
+
143
+ `login` creates or resumes the NullPay session inside the MCP process.
144
+
145
+ Normal path:
146
+
147
+ - uses address + password from tool input or env
148
+ - loads the backend profile
149
+ - validates the password by decrypting stored wallet data
150
+ - restores burner wallet metadata when present
151
+
152
+ Recovery path:
153
+
154
+ - if password is missing but `NULLPAY_MAIN_PRIVATE_KEY` is available
155
+ - the server can recover the backed-up password from on-chain backup records
156
+ - if a full burner backup exists on-chain, it restores the burner wallet automatically
157
+
158
+ ### `create_invoice`
159
+
160
+ - uses the active wallet address
161
+ - calls the NullPay backend relayer endpoint
162
+ - waits for the invoice hash to resolve from Aleo mapping
163
+ - stores the invoice row in the backend
164
+ - returns a payment link
165
+
166
+ ### `pay_invoice`
167
+
168
+ - accepts either a full NullPay payment link or an invoice hash
169
+ - prefers the full payment link so merchant address, amount, salt, token, and session id are available immediately
170
+ - builds the payment authorization locally with the user's wallet key
171
+ - sends the authorization to the backend sponsor endpoint
172
+
173
+ ### `get_transaction_info`
174
+
175
+ - fetches invoice rows from the backend
176
+ - enriches private record-backed data locally when the main private key is available
177
+
178
+ ## Credential Model
179
+
180
+ User-provided values:
181
+
182
+ - `NULLPAY_MAIN_ADDRESS`
183
+ - `NULLPAY_MAIN_PRIVATE_KEY`
184
+ - `NULLPAY_MAIN_PASSWORD`
185
+
186
+ Bundled or backend-side values:
187
+
188
+ - `NULLPAY_BACKEND_URL` defaults internally to production
189
+ - `NULLPAY_PUBLIC_BASE_URL` defaults internally to production
190
+ - relayer secrets stay on the backend
191
+
192
+ ## Security Notes
193
+
194
+ - The user's private key is intended to stay local to the MCP process.
195
+ - The config file used by Claude contains sensitive wallet credentials and should be treated like a secret file.
196
+ - The MCP server never returns decrypted private keys in tool output.
197
+ - Password recovery from on-chain records only works when the main private key is available locally.
198
+
199
+ ## Publishing
200
+
201
+ From `packages/nullpay-mcp`:
202
+
203
+ ```bash
204
+ npm run build
205
+ npm publish --access public
206
+ ```
207
+
208
+ If npm 2FA is enabled:
209
+
210
+ ```bash
211
+ npm publish --access public --otp=123456
212
+ ```
46
213
 
47
214
  ## License
48
215
 
49
- MIT
216
+ MIT
package/dist/aleo.d.ts CHANGED
@@ -53,8 +53,24 @@ export declare function createInvoiceDbRecord(args: {
53
53
  }[] | null;
54
54
  for_sdk: boolean;
55
55
  };
56
+ export interface ParsedBurnerBackupRecord {
57
+ owner: string;
58
+ burnerAddress: string;
59
+ passwordPart: string;
60
+ pkParts: string[];
61
+ plaintext: string;
62
+ }
63
+ export interface RecoveredOnChainWalletBackup {
64
+ password: string;
65
+ burnerAddress?: string;
66
+ encryptedBurnerKey?: string;
67
+ source: 'password_only' | 'full_burner';
68
+ }
56
69
  export declare function parseOwnedInvoiceRecord(plaintext: string): ParsedOwnedInvoiceRecord | null;
70
+ export declare function parseBurnerBackupRecord(plaintext: string): ParsedBurnerBackupRecord | null;
57
71
  export declare function fetchOwnedInvoiceRecords(privateKey: string): Promise<ParsedOwnedInvoiceRecord[]>;
72
+ export declare function fetchOwnedBurnerBackupRecords(privateKey: string): Promise<ParsedBurnerBackupRecord[]>;
73
+ export declare function recoverOnChainWalletBackup(privateKey: string, ownerAddress: string): Promise<RecoveredOnChainWalletBackup | null>;
58
74
  export declare function fetchOwnedInvoiceRecordByHash(privateKey: string, invoiceHash: string): Promise<ParsedOwnedInvoiceRecord | null>;
59
75
  export declare function enrichInvoiceWithRecordAmount(invoice: InvoiceRecord, privateKey?: string | null): Promise<InvoiceRecord>;
60
76
  export declare function createSponsoredPaymentAuthorization(args: {
package/dist/aleo.js CHANGED
@@ -13,12 +13,16 @@ exports.invoiceTypeToNumber = invoiceTypeToNumber;
13
13
  exports.buildPaymentLink = buildPaymentLink;
14
14
  exports.createInvoiceDbRecord = createInvoiceDbRecord;
15
15
  exports.parseOwnedInvoiceRecord = parseOwnedInvoiceRecord;
16
+ exports.parseBurnerBackupRecord = parseBurnerBackupRecord;
16
17
  exports.fetchOwnedInvoiceRecords = fetchOwnedInvoiceRecords;
18
+ exports.fetchOwnedBurnerBackupRecords = fetchOwnedBurnerBackupRecords;
19
+ exports.recoverOnChainWalletBackup = recoverOnChainWalletBackup;
17
20
  exports.fetchOwnedInvoiceRecordByHash = fetchOwnedInvoiceRecordByHash;
18
21
  exports.enrichInvoiceWithRecordAmount = enrichInvoiceWithRecordAmount;
19
22
  exports.createSponsoredPaymentAuthorization = createSponsoredPaymentAuthorization;
20
23
  const crypto_1 = __importDefault(require("crypto"));
21
24
  const esm_1 = require("./esm");
25
+ const env_1 = require("./env");
22
26
  exports.PROGRAM_ID = 'zk_pay_proofs_privacy_v22.aleo';
23
27
  const FREEZELIST_PROGRAM_ID = 'test_usdcx_freezelist.aleo';
24
28
  const EXPLORER_BASE = 'https://api.explorer.provable.com/v1';
@@ -51,6 +55,30 @@ function fieldToString(fieldVal) {
51
55
  return '';
52
56
  }
53
57
  }
58
+ function fieldChunksToString(chunks) {
59
+ let result = '';
60
+ for (const chunk of chunks) {
61
+ if (!chunk || chunk === '0field' || chunk === '0') {
62
+ continue;
63
+ }
64
+ const numeric = chunk.replace('field', '').replace('u128', '').replace('u64', '');
65
+ let value;
66
+ try {
67
+ value = BigInt(numeric);
68
+ }
69
+ catch {
70
+ continue;
71
+ }
72
+ let hex = value.toString(16);
73
+ if (hex.length % 2 !== 0) {
74
+ hex = '0' + hex;
75
+ }
76
+ for (let i = 0; i < hex.length; i += 2) {
77
+ result += String.fromCharCode(Number.parseInt(hex.slice(i, i + 2), 16));
78
+ }
79
+ }
80
+ return result;
81
+ }
54
82
  function parseNumericValue(value) {
55
83
  if (!value) {
56
84
  return 0;
@@ -206,12 +234,8 @@ function resolvePaymentMode(invoice, fallbackAmount, fallbackCurrency) {
206
234
  amountSuffix: 'u64',
207
235
  };
208
236
  }
209
- function getProvableConsumerId() {
210
- return process.env.PROVABLE_CONSUMER_ID || process.env.PROVABLE_CONSUMER_KEY;
211
- }
212
237
  async function getScannerSession(privateKey) {
213
- const provableApiKey = process.env.PROVABLE_API_KEY;
214
- const consumerId = getProvableConsumerId();
238
+ const { apiKey: provableApiKey, consumerId } = (0, env_1.getProvableConfig)();
215
239
  if (!provableApiKey || !consumerId) {
216
240
  throw new Error('PROVABLE_API_KEY and PROVABLE_CONSUMER_ID/PROVABLE_CONSUMER_KEY are required for record fetching and payment automation.');
217
241
  }
@@ -308,6 +332,41 @@ function parseOwnedInvoiceRecord(plaintext) {
308
332
  return null;
309
333
  }
310
334
  }
335
+ function parseBurnerBackupRecord(plaintext) {
336
+ try {
337
+ const getVal = (key) => {
338
+ const regex = new RegExp(`(?:${key}|"${key}"):\\s*([\\w\\d\\.]+)`);
339
+ const match = plaintext.match(regex);
340
+ if (match && match[1]) {
341
+ return match[1].replace('.private', '').replace('.public', '');
342
+ }
343
+ return null;
344
+ };
345
+ const owner = getVal('owner');
346
+ const burnerAddress = getVal('burner_address');
347
+ const passwordPart = getVal('password_part');
348
+ if (!burnerAddress || !passwordPart) {
349
+ return null;
350
+ }
351
+ const pkParts = [];
352
+ for (let i = 1; i <= 10; i += 1) {
353
+ const part = getVal(`pk_part_${i}`);
354
+ if (part && part !== '0field' && part !== '0') {
355
+ pkParts.push(part);
356
+ }
357
+ }
358
+ return {
359
+ owner: owner || '',
360
+ burnerAddress,
361
+ passwordPart,
362
+ pkParts,
363
+ plaintext,
364
+ };
365
+ }
366
+ catch {
367
+ return null;
368
+ }
369
+ }
311
370
  async function fetchOwnedInvoiceRecords(privateKey) {
312
371
  const session = await getScannerSession(privateKey);
313
372
  const records = await fetchOwnedProgramRecords(session, exports.PROGRAM_ID);
@@ -328,6 +387,67 @@ async function fetchOwnedInvoiceRecords(privateKey) {
328
387
  }
329
388
  return parsed;
330
389
  }
390
+ async function fetchOwnedBurnerBackupRecords(privateKey) {
391
+ const session = await getScannerSession(privateKey);
392
+ const records = await fetchOwnedProgramRecords(session, exports.PROGRAM_ID);
393
+ const parsed = [];
394
+ for (const record of records) {
395
+ let plaintext = record.record_plaintext || record.plaintext || '';
396
+ if (!plaintext && record.record_ciphertext) {
397
+ const { RecordCiphertext } = await (0, esm_1.dynamicImport)('@provablehq/sdk');
398
+ const ciphertext = RecordCiphertext.fromString(record.record_ciphertext);
399
+ plaintext = ciphertext.decrypt(session.account.viewKey()).toString();
400
+ }
401
+ if (!plaintext)
402
+ continue;
403
+ const burnerRecord = parseBurnerBackupRecord(plaintext);
404
+ if (burnerRecord) {
405
+ parsed.push(burnerRecord);
406
+ }
407
+ }
408
+ return parsed;
409
+ }
410
+ async function recoverOnChainWalletBackup(privateKey, ownerAddress) {
411
+ const records = await fetchOwnedBurnerBackupRecords(privateKey);
412
+ let passwordOnlyMatch = null;
413
+ let fullBurnerMatch = null;
414
+ for (const record of records) {
415
+ const ownerMatches = record.owner === ownerAddress;
416
+ const passwordOnlyMatches = record.burnerAddress === ownerAddress;
417
+ if (!ownerMatches && !passwordOnlyMatches) {
418
+ continue;
419
+ }
420
+ const encryptedPayload = fieldChunksToString(record.pkParts);
421
+ const hasRealPayload = Boolean(encryptedPayload && !encryptedPayload.startsWith('0'));
422
+ if (hasRealPayload) {
423
+ fullBurnerMatch = record;
424
+ }
425
+ else if (!passwordOnlyMatch) {
426
+ passwordOnlyMatch = record;
427
+ }
428
+ }
429
+ const bestMatch = fullBurnerMatch || passwordOnlyMatch;
430
+ if (!bestMatch) {
431
+ return null;
432
+ }
433
+ const password = fieldChunksToString([bestMatch.passwordPart]);
434
+ if (!password) {
435
+ return null;
436
+ }
437
+ if (fullBurnerMatch) {
438
+ const encryptedBurnerKey = fieldChunksToString(fullBurnerMatch.pkParts);
439
+ return {
440
+ password,
441
+ burnerAddress: fullBurnerMatch.burnerAddress,
442
+ encryptedBurnerKey: encryptedBurnerKey || undefined,
443
+ source: 'full_burner',
444
+ };
445
+ }
446
+ return {
447
+ password,
448
+ source: 'password_only',
449
+ };
450
+ }
331
451
  async function fetchOwnedInvoiceRecordByHash(privateKey, invoiceHash) {
332
452
  const normalized = normalizeInvoiceHash(invoiceHash);
333
453
  const records = await fetchOwnedInvoiceRecords(privateKey);
@@ -1,8 +1,7 @@
1
1
  import { InvoiceRecord, UserProfile } from './types';
2
2
  export declare class NullPayBackendClient {
3
3
  private readonly baseUrl;
4
- private readonly mcpSecret?;
5
- constructor(baseUrl: string, mcpSecret?: string | undefined);
4
+ constructor(baseUrl: string);
6
5
  private buildUrl;
7
6
  private request;
8
7
  getUserProfile(addressHash: string): Promise<UserProfile | null>;
@@ -2,20 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NullPayBackendClient = void 0;
4
4
  function mapBackendError(path, text) {
5
- if (path === '/mcp/relay/create-invoice') {
6
- if (text.includes('NULLPAY_MCP_SHARED_SECRET is not configured')) {
7
- return 'Invoice creation is blocked because NULLPAY_MCP_SHARED_SECRET is missing on the backend. Set the same NULLPAY_MCP_SHARED_SECRET value in both the backend env and the MCP server env, then restart both processes.';
8
- }
9
- if (text.includes('Invalid MCP shared secret')) {
10
- return 'Invoice creation is blocked because the MCP shared secret does not match. Set the same NULLPAY_MCP_SHARED_SECRET value in both the backend env and the MCP server env, then restart both processes.';
11
- }
12
- }
13
5
  return text;
14
6
  }
15
7
  class NullPayBackendClient {
16
- constructor(baseUrl, mcpSecret) {
8
+ constructor(baseUrl) {
17
9
  this.baseUrl = baseUrl;
18
- this.mcpSecret = mcpSecret;
19
10
  }
20
11
  buildUrl(path) {
21
12
  return `${this.baseUrl.replace(/\/+$/, '')}${path}`;
@@ -75,10 +66,7 @@ class NullPayBackendClient {
75
66
  async relayCreateInvoice(body) {
76
67
  return await this.request('/mcp/relay/create-invoice', {
77
68
  method: 'POST',
78
- headers: {
79
- 'Content-Type': 'application/json',
80
- ...(this.mcpSecret ? { 'x-nullpay-mcp-secret': this.mcpSecret } : {})
81
- },
69
+ headers: { 'Content-Type': 'application/json' },
82
70
  body: JSON.stringify(body),
83
71
  });
84
72
  }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const server_1 = require("./server");
5
+ const setup_1 = require("./setup");
6
+ function printHelp() {
7
+ console.log('NullPay MCP');
8
+ console.log('');
9
+ console.log('Usage:');
10
+ console.log(' nullpay-mcp Start the setup wizard');
11
+ console.log(' nullpay-mcp setup Start the setup wizard');
12
+ console.log(' nullpay-mcp server Run the stdio MCP server');
13
+ }
14
+ async function main() {
15
+ const mode = (process.argv[2] || 'setup').toLowerCase();
16
+ if (mode === 'server') {
17
+ (0, server_1.startServer)();
18
+ return;
19
+ }
20
+ if (mode === 'setup') {
21
+ await (0, setup_1.runSetupWizard)();
22
+ return;
23
+ }
24
+ if (mode === '--help' || mode === '-h' || mode === 'help') {
25
+ printHelp();
26
+ return;
27
+ }
28
+ console.error(`Unknown command: ${mode}`);
29
+ printHelp();
30
+ process.exitCode = 1;
31
+ }
32
+ void main().catch((error) => {
33
+ console.error(error instanceof Error ? error.message : String(error));
34
+ process.exit(1);
35
+ });
package/dist/env.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export declare const DEFAULT_BACKEND_URL = "https://nullpay-backend-ib5q4.ondigitalocean.app/api";
2
+ export declare const DEFAULT_PUBLIC_BASE_URL = "https://nullpay.app";
3
+ export declare function parseDotEnv(content: string): Record<string, string>;
4
+ export declare function loadEnvFiles(): void;
5
+ export declare function getRuntimeConfig(): {
6
+ backendBaseUrl: string;
7
+ publicBaseUrl: string;
8
+ };
9
+ export declare function getProvableConfig(): {
10
+ apiKey: string;
11
+ consumerId: string;
12
+ };
package/dist/env.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_PUBLIC_BASE_URL = exports.DEFAULT_BACKEND_URL = void 0;
7
+ exports.parseDotEnv = parseDotEnv;
8
+ exports.loadEnvFiles = loadEnvFiles;
9
+ exports.getRuntimeConfig = getRuntimeConfig;
10
+ exports.getProvableConfig = getProvableConfig;
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ exports.DEFAULT_BACKEND_URL = 'https://nullpay-backend-ib5q4.ondigitalocean.app/api';
14
+ exports.DEFAULT_PUBLIC_BASE_URL = 'https://nullpay.app';
15
+ const DEFAULT_PROVABLE_API_KEY = 'tWR9YWkM5SVmx1u3m7My8S4p4e2s84Oe';
16
+ const DEFAULT_PROVABLE_CONSUMER_ID = '73ba1b21-d8f7-4d7f-bfd9-0408a4e183f3';
17
+ function parseDotEnv(content) {
18
+ const parsed = {};
19
+ for (const rawLine of content.split(/\r?\n/)) {
20
+ const line = rawLine.trim();
21
+ if (!line || line.startsWith('#')) {
22
+ continue;
23
+ }
24
+ const normalized = line.startsWith('export ') ? line.slice(7).trim() : line;
25
+ const separatorIndex = normalized.indexOf('=');
26
+ if (separatorIndex <= 0) {
27
+ continue;
28
+ }
29
+ const key = normalized.slice(0, separatorIndex).trim();
30
+ let value = normalized.slice(separatorIndex + 1).trim();
31
+ if (!key) {
32
+ continue;
33
+ }
34
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
35
+ value = value.slice(1, -1);
36
+ }
37
+ parsed[key] = value;
38
+ }
39
+ return parsed;
40
+ }
41
+ function loadEnvFiles() {
42
+ const packageRoot = path_1.default.resolve(__dirname, '..');
43
+ const repoRoot = path_1.default.resolve(packageRoot, '..', '..');
44
+ const candidates = [
45
+ path_1.default.resolve(process.cwd(), '.env'),
46
+ path_1.default.resolve(packageRoot, '.env'),
47
+ path_1.default.resolve(repoRoot, '.env'),
48
+ path_1.default.resolve(repoRoot, 'backend', '.env'),
49
+ ];
50
+ for (const filePath of candidates) {
51
+ if (!fs_1.default.existsSync(filePath)) {
52
+ continue;
53
+ }
54
+ const values = parseDotEnv(fs_1.default.readFileSync(filePath, 'utf8'));
55
+ for (const [key, value] of Object.entries(values)) {
56
+ if (!process.env[key]) {
57
+ process.env[key] = value;
58
+ }
59
+ }
60
+ }
61
+ }
62
+ function getRuntimeConfig() {
63
+ loadEnvFiles();
64
+ return {
65
+ backendBaseUrl: process.env.NULLPAY_BACKEND_URL || exports.DEFAULT_BACKEND_URL,
66
+ publicBaseUrl: process.env.NULLPAY_PUBLIC_BASE_URL || exports.DEFAULT_PUBLIC_BASE_URL,
67
+ };
68
+ }
69
+ function getProvableConfig() {
70
+ loadEnvFiles();
71
+ return {
72
+ apiKey: process.env.PROVABLE_API_KEY || DEFAULT_PROVABLE_API_KEY,
73
+ consumerId: process.env.PROVABLE_CONSUMER_ID || process.env.PROVABLE_CONSUMER_KEY || DEFAULT_PROVABLE_CONSUMER_ID,
74
+ };
75
+ }
package/dist/server.d.ts CHANGED
@@ -1 +1 @@
1
- export {};
1
+ export declare function startServer(): void;