pop-pay 0.2.0 → 0.3.1
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 +71 -322
- package/dashboard/dashboard.css +193 -0
- package/dashboard/dashboard.js +135 -0
- package/dashboard/index.html +89 -0
- package/dist/cli-dashboard.d.ts +3 -0
- package/dist/cli-dashboard.d.ts.map +1 -0
- package/dist/cli-dashboard.js +28 -0
- package/dist/cli-dashboard.js.map +1 -0
- package/dist/cli-main.d.ts +7 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +81 -0
- package/dist/cli-main.js.map +1 -0
- package/dist/dashboard.d.ts +9 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +122 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/engine/injector.d.ts.map +1 -1
- package/dist/engine/injector.js +60 -10
- package/dist/engine/injector.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
2
|
+
const todaySpendingEl = document.getElementById('today-spending');
|
|
3
|
+
const remainingBudgetEl = document.getElementById('remaining-budget');
|
|
4
|
+
const utilizationPctEl = document.getElementById('utilization-pct');
|
|
5
|
+
const utilizationFillEl = document.getElementById('utilization-fill');
|
|
6
|
+
const sealsBody = document.getElementById('seals-body');
|
|
7
|
+
const rejectedBody = document.getElementById('rejected-body');
|
|
8
|
+
const refreshBtn = document.getElementById('refresh-btn');
|
|
9
|
+
const maxBudgetInput = document.getElementById('max-daily-budget');
|
|
10
|
+
const saveSettingsBtn = document.getElementById('save-settings');
|
|
11
|
+
|
|
12
|
+
let sealsData = [];
|
|
13
|
+
|
|
14
|
+
const formatCurrency = (amount) => {
|
|
15
|
+
return new Intl.NumberFormat('en-US', {
|
|
16
|
+
style: 'currency',
|
|
17
|
+
currency: 'USD',
|
|
18
|
+
}).format(amount);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const fetchData = async () => {
|
|
22
|
+
try {
|
|
23
|
+
const [budgetRes, sealsRes, rejectedRes] = await Promise.all([
|
|
24
|
+
fetch('/api/budget/today'),
|
|
25
|
+
fetch('/api/seals'),
|
|
26
|
+
fetch('/api/seals?status=rejected')
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const budget = await budgetRes.json();
|
|
30
|
+
sealsData = await sealsRes.json();
|
|
31
|
+
const rejected = await rejectedRes.json();
|
|
32
|
+
|
|
33
|
+
updateBudget(budget);
|
|
34
|
+
renderSeals(sealsData);
|
|
35
|
+
renderRejected(rejected);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('Failed to fetch dashboard data:', error);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const updateBudget = (data) => {
|
|
42
|
+
const { spent, max, remaining } = data;
|
|
43
|
+
todaySpendingEl.textContent = formatCurrency(spent);
|
|
44
|
+
remainingBudgetEl.textContent = formatCurrency(remaining);
|
|
45
|
+
|
|
46
|
+
const utilization = max > 0 ? (spent / max) * 100 : 0;
|
|
47
|
+
utilizationPctEl.textContent = `${utilization.toFixed(1)}%`;
|
|
48
|
+
utilizationFillEl.style.width = `${Math.min(utilization, 100)}%`;
|
|
49
|
+
|
|
50
|
+
if (utilization >= 90) {
|
|
51
|
+
utilizationFillEl.style.backgroundColor = 'var(--danger-red)';
|
|
52
|
+
} else if (utilization >= 70) {
|
|
53
|
+
utilizationFillEl.style.backgroundColor = 'var(--warning-amber)';
|
|
54
|
+
} else {
|
|
55
|
+
utilizationFillEl.style.backgroundColor = 'var(--accent-green)';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
maxBudgetInput.value = max;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const renderSeals = (seals) => {
|
|
62
|
+
sealsBody.innerHTML = '';
|
|
63
|
+
seals.forEach(seal => {
|
|
64
|
+
const row = document.createElement('tr');
|
|
65
|
+
row.innerHTML = `
|
|
66
|
+
<td>${seal.seal_id}</td>
|
|
67
|
+
<td>${formatCurrency(seal.amount)}</td>
|
|
68
|
+
<td>${seal.vendor}</td>
|
|
69
|
+
<td style="color: ${getStatusColor(seal.status)}">${seal.status}</td>
|
|
70
|
+
<td>${seal.masked_card || 'N/A'}</td>
|
|
71
|
+
<td>${new Date(seal.timestamp).toLocaleString()}</td>
|
|
72
|
+
`;
|
|
73
|
+
sealsBody.appendChild(row);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const renderRejected = (rejected) => {
|
|
78
|
+
rejectedBody.innerHTML = '';
|
|
79
|
+
rejected.forEach(seal => {
|
|
80
|
+
const row = document.createElement('tr');
|
|
81
|
+
row.innerHTML = `
|
|
82
|
+
<td>${seal.seal_id}</td>
|
|
83
|
+
<td>${formatCurrency(seal.amount)}</td>
|
|
84
|
+
<td>${seal.vendor}</td>
|
|
85
|
+
<td>${seal.status}</td>
|
|
86
|
+
<td>${new Date(seal.timestamp).toLocaleString()}</td>
|
|
87
|
+
`;
|
|
88
|
+
rejectedBody.appendChild(row);
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getStatusColor = (status) => {
|
|
93
|
+
switch (status.toLowerCase()) {
|
|
94
|
+
case 'issued': return 'var(--accent-green)';
|
|
95
|
+
case 'used': return 'var(--text-secondary)';
|
|
96
|
+
case 'rejected': return 'var(--danger-red)';
|
|
97
|
+
default: return 'var(--text-primary)';
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const saveSettings = async () => {
|
|
102
|
+
const maxBudget = parseFloat(maxBudgetInput.value);
|
|
103
|
+
if (isNaN(maxBudget)) return;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await fetch('/api/settings/max_daily_budget', {
|
|
107
|
+
method: 'PUT',
|
|
108
|
+
headers: { 'Content-Type': 'application/json' },
|
|
109
|
+
body: JSON.stringify({ value: maxBudget.toString() })
|
|
110
|
+
});
|
|
111
|
+
fetchData();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('Failed to save settings:', error);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Sorting logic
|
|
118
|
+
document.querySelectorAll('#seals-table th[data-sort]').forEach(th => {
|
|
119
|
+
th.addEventListener('click', () => {
|
|
120
|
+
const prop = th.dataset.sort;
|
|
121
|
+
const sorted = [...sealsData].sort((a, b) => {
|
|
122
|
+
if (a[prop] < b[prop]) return -1;
|
|
123
|
+
if (a[prop] > b[prop]) return 1;
|
|
124
|
+
return 0;
|
|
125
|
+
});
|
|
126
|
+
renderSeals(sorted);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
refreshBtn.addEventListener('click', fetchData);
|
|
131
|
+
saveSettingsBtn.addEventListener('click', saveSettings);
|
|
132
|
+
|
|
133
|
+
// Initial load
|
|
134
|
+
fetchData();
|
|
135
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>The Vault - Pop Pay Dashboard</title>
|
|
7
|
+
<link rel="stylesheet" href="dashboard.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<header>
|
|
11
|
+
<div class="container">
|
|
12
|
+
<h1>THE VAULT <span class="subtitle">// POP PAY TERMINAL</span></h1>
|
|
13
|
+
<button id="refresh-btn" class="btn">REFRESH_DATA</button>
|
|
14
|
+
</div>
|
|
15
|
+
</header>
|
|
16
|
+
|
|
17
|
+
<main class="container">
|
|
18
|
+
<section class="metrics">
|
|
19
|
+
<div class="card">
|
|
20
|
+
<h3>TODAY_SPENDING</h3>
|
|
21
|
+
<p id="today-spending" class="value">$0.00</p>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="card">
|
|
24
|
+
<h3>REMAINING_BUDGET</h3>
|
|
25
|
+
<p id="remaining-budget" class="value">$0.00</p>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="card">
|
|
28
|
+
<h3>UTILIZATION</h3>
|
|
29
|
+
<p id="utilization-pct" class="value">0%</p>
|
|
30
|
+
<div class="progress-bar">
|
|
31
|
+
<div id="utilization-fill" class="progress-fill"></div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<section class="settings card">
|
|
37
|
+
<h3>BUDGET_SETTINGS</h3>
|
|
38
|
+
<div class="input-group">
|
|
39
|
+
<label for="max-daily-budget">MAX_DAILY_BUDGET ($):</label>
|
|
40
|
+
<input type="number" id="max-daily-budget" value="500" step="10">
|
|
41
|
+
<button id="save-settings" class="btn btn-small">UPDATE</button>
|
|
42
|
+
</div>
|
|
43
|
+
</section>
|
|
44
|
+
|
|
45
|
+
<section class="seals">
|
|
46
|
+
<h3>ISSUED_SEALS</h3>
|
|
47
|
+
<div class="table-container">
|
|
48
|
+
<table id="seals-table">
|
|
49
|
+
<thead>
|
|
50
|
+
<tr>
|
|
51
|
+
<th data-sort="seal_id">SEAL_ID</th>
|
|
52
|
+
<th data-sort="amount">AMOUNT</th>
|
|
53
|
+
<th data-sort="vendor">VENDOR</th>
|
|
54
|
+
<th data-sort="status">STATUS</th>
|
|
55
|
+
<th data-sort="masked_card">CARD_MASK</th>
|
|
56
|
+
<th data-sort="timestamp">TIMESTAMP</th>
|
|
57
|
+
</tr>
|
|
58
|
+
</thead>
|
|
59
|
+
<tbody id="seals-body">
|
|
60
|
+
<!-- Data injected here -->
|
|
61
|
+
</tbody>
|
|
62
|
+
</table>
|
|
63
|
+
</div>
|
|
64
|
+
</section>
|
|
65
|
+
|
|
66
|
+
<section class="rejected">
|
|
67
|
+
<h3>REJECTION_LOG</h3>
|
|
68
|
+
<div class="table-container">
|
|
69
|
+
<table id="rejected-table">
|
|
70
|
+
<thead>
|
|
71
|
+
<tr>
|
|
72
|
+
<th>SEAL_ID</th>
|
|
73
|
+
<th>AMOUNT</th>
|
|
74
|
+
<th>VENDOR</th>
|
|
75
|
+
<th>REASON</th>
|
|
76
|
+
<th>TIMESTAMP</th>
|
|
77
|
+
</tr>
|
|
78
|
+
</thead>
|
|
79
|
+
<tbody id="rejected-body">
|
|
80
|
+
<!-- Data injected here -->
|
|
81
|
+
</tbody>
|
|
82
|
+
</table>
|
|
83
|
+
</div>
|
|
84
|
+
</section>
|
|
85
|
+
</main>
|
|
86
|
+
|
|
87
|
+
<script src="dashboard.js"></script>
|
|
88
|
+
</body>
|
|
89
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-dashboard.d.ts","sourceRoot":"","sources":["../src/cli-dashboard.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const dashboard_js_1 = require("./dashboard.js");
|
|
5
|
+
function parseArgs() {
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const options = {
|
|
8
|
+
port: 3210,
|
|
9
|
+
dbPath: "pop_state.db"
|
|
10
|
+
};
|
|
11
|
+
for (let i = 0; i < args.length; i++) {
|
|
12
|
+
if (args[i] === "--port" && args[i + 1]) {
|
|
13
|
+
options.port = parseInt(args[i + 1], 10);
|
|
14
|
+
i++;
|
|
15
|
+
}
|
|
16
|
+
else if (args[i] === "--db" && args[i + 1]) {
|
|
17
|
+
options.dbPath = args[i + 1];
|
|
18
|
+
i++;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return options;
|
|
22
|
+
}
|
|
23
|
+
const options = parseArgs();
|
|
24
|
+
(0, dashboard_js_1.main)(options).catch(err => {
|
|
25
|
+
console.error("Failed to start dashboard:", err);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=cli-dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-dashboard.js","sourceRoot":"","sources":["../src/cli-dashboard.ts"],"names":[],"mappings":";;;AACA,iDAAsC;AAEtC,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,cAAc;KACvB,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzC,CAAC,EAAE,CAAC;QACN,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7B,CAAC,EAAE,CAAC;QACN,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;AAC5B,IAAA,mBAAI,EAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-main.d.ts","sourceRoot":"","sources":["../src/cli-main.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
|
package/dist/cli-main.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* pop-pay CLI dispatcher.
|
|
5
|
+
* Routes subcommands to the appropriate module.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
function getVersion() {
|
|
11
|
+
try {
|
|
12
|
+
const pkgPath = (0, node_path_1.join)(__dirname, "..", "package.json");
|
|
13
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)(pkgPath, "utf8"));
|
|
14
|
+
return pkg.version;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return "unknown";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function showHelp() {
|
|
21
|
+
console.log(`pop-pay v${getVersion()} — Semantic Payment Guardrail for AI Agents
|
|
22
|
+
|
|
23
|
+
Usage: pop-pay <command> [options]
|
|
24
|
+
|
|
25
|
+
Commands:
|
|
26
|
+
launch-mcp Start the MCP server (stdio transport)
|
|
27
|
+
launch Launch Chrome with CDP remote debugging
|
|
28
|
+
init-vault Initialize the encrypted credential vault
|
|
29
|
+
unlock Unlock the vault for the current session
|
|
30
|
+
dashboard Start the monitoring dashboard
|
|
31
|
+
|
|
32
|
+
Options:
|
|
33
|
+
-v, --version Show version
|
|
34
|
+
-h, --help Show this help message`);
|
|
35
|
+
}
|
|
36
|
+
async function main() {
|
|
37
|
+
const subcommand = process.argv[2];
|
|
38
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
39
|
+
showHelp();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (subcommand === "--version" || subcommand === "-v") {
|
|
43
|
+
console.log(getVersion());
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
switch (subcommand) {
|
|
47
|
+
case "launch-mcp":
|
|
48
|
+
process.argv.splice(2, 1);
|
|
49
|
+
await import("./mcp-server.js");
|
|
50
|
+
break;
|
|
51
|
+
case "launch":
|
|
52
|
+
case "pop-launch":
|
|
53
|
+
process.argv.splice(2, 1);
|
|
54
|
+
await import("./cli.js");
|
|
55
|
+
break;
|
|
56
|
+
case "init-vault":
|
|
57
|
+
case "pop-init-vault":
|
|
58
|
+
process.argv.splice(2, 1);
|
|
59
|
+
await import("./cli-vault.js");
|
|
60
|
+
break;
|
|
61
|
+
case "unlock":
|
|
62
|
+
case "pop-unlock":
|
|
63
|
+
// Keep "unlock" in argv — cli-vault.ts detects it via process.argv.includes("unlock")
|
|
64
|
+
process.argv[2] = "unlock";
|
|
65
|
+
await import("./cli-vault.js");
|
|
66
|
+
break;
|
|
67
|
+
case "dashboard":
|
|
68
|
+
process.argv.splice(2, 1);
|
|
69
|
+
await import("./cli-dashboard.js");
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
console.error(`Unknown command: ${subcommand}\n`);
|
|
73
|
+
showHelp();
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
main().catch((err) => {
|
|
78
|
+
console.error("pop-pay:", err.message ?? err);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=cli-main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-main.js","sourceRoot":"","sources":["../src/cli-main.ts"],"names":[],"mappings":";;AAEA;;;GAGG;;AAEH,qCAAuC;AACvC,yCAAiC;AAEjC,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,gBAAI,EAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACtD,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,CAAC,GAAG,CAAC,YAAY,UAAU,EAAE;;;;;;;;;;;;;yCAaG,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEnC,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QAClE,QAAQ,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,IAAI,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,YAAY;YACf,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1B,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;YAChC,MAAM;QAER,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY;YACf,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1B,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACzB,MAAM;QAER,KAAK,YAAY,CAAC;QAClB,KAAK,gBAAgB;YACnB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1B,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;YAC/B,MAAM;QAER,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY;YACf,sFAAsF;YACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;YAC3B,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;YAC/B,MAAM;QAER,KAAK,WAAW;YACd,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1B,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YACnC,MAAM;QAER;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,UAAU,IAAI,CAAC,CAAC;YAClD,QAAQ,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
export interface DashboardOptions {
|
|
3
|
+
port: number;
|
|
4
|
+
dbPath: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function main(options: DashboardOptions & {
|
|
7
|
+
skipOpen?: boolean;
|
|
8
|
+
}): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
|
9
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAM7B,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,iFA0H5E"}
|
|
@@ -0,0 +1,122 @@
|
|
|
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.main = main;
|
|
7
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_child_process_1 = require("node:child_process");
|
|
11
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
12
|
+
async function main(options) {
|
|
13
|
+
const { port, dbPath, skipOpen } = options;
|
|
14
|
+
const db = new better_sqlite3_1.default(dbPath);
|
|
15
|
+
// Initialize tables
|
|
16
|
+
db.exec(`
|
|
17
|
+
CREATE TABLE IF NOT EXISTS daily_budget (
|
|
18
|
+
date TEXT PRIMARY KEY,
|
|
19
|
+
spent_amount REAL
|
|
20
|
+
)
|
|
21
|
+
`);
|
|
22
|
+
db.exec(`
|
|
23
|
+
CREATE TABLE IF NOT EXISTS issued_seals (
|
|
24
|
+
seal_id TEXT PRIMARY KEY,
|
|
25
|
+
amount REAL,
|
|
26
|
+
vendor TEXT,
|
|
27
|
+
status TEXT,
|
|
28
|
+
masked_card TEXT,
|
|
29
|
+
expiration_date TEXT,
|
|
30
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
31
|
+
)
|
|
32
|
+
`);
|
|
33
|
+
db.exec(`
|
|
34
|
+
CREATE TABLE IF NOT EXISTS dashboard_settings (
|
|
35
|
+
key TEXT PRIMARY KEY,
|
|
36
|
+
value TEXT
|
|
37
|
+
)
|
|
38
|
+
`);
|
|
39
|
+
const server = node_http_1.default.createServer((req, res) => {
|
|
40
|
+
const { method, url } = req;
|
|
41
|
+
const pathname = url ? new URL(url, `http://localhost:${port}`).pathname : "";
|
|
42
|
+
// Static File Serving
|
|
43
|
+
if (method === "GET" && (pathname === "/" || pathname.startsWith("/dashboard") || !pathname.startsWith("/api"))) {
|
|
44
|
+
let filePath = pathname === "/" ? "/index.html" : pathname;
|
|
45
|
+
if (filePath.startsWith("/dashboard/")) {
|
|
46
|
+
filePath = filePath.replace("/dashboard/", "/");
|
|
47
|
+
}
|
|
48
|
+
const fullPath = node_path_1.default.join(process.cwd(), "dashboard", filePath);
|
|
49
|
+
if (node_fs_1.default.existsSync(fullPath) && node_fs_1.default.statSync(fullPath).isFile()) {
|
|
50
|
+
const ext = node_path_1.default.extname(fullPath);
|
|
51
|
+
const mimeTypes = {
|
|
52
|
+
".html": "text/html",
|
|
53
|
+
".js": "application/javascript",
|
|
54
|
+
".css": "text/css",
|
|
55
|
+
".png": "image/png",
|
|
56
|
+
".jpg": "image/jpeg",
|
|
57
|
+
};
|
|
58
|
+
res.writeHead(200, { "Content-Type": mimeTypes[ext] || "text/plain" });
|
|
59
|
+
node_fs_1.default.createReadStream(fullPath).pipe(res);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// API Routes
|
|
64
|
+
if (method === "GET" && pathname === "/api/budget/today") {
|
|
65
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
66
|
+
const spentRow = db.prepare("SELECT spent_amount FROM daily_budget WHERE date = ?").get(today);
|
|
67
|
+
const spent = spentRow?.spent_amount ?? 0;
|
|
68
|
+
const maxRow = db.prepare("SELECT value FROM dashboard_settings WHERE key = 'max_daily_budget'").get();
|
|
69
|
+
const max = maxRow ? parseFloat(maxRow.value) : 500;
|
|
70
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
71
|
+
res.end(JSON.stringify({ spent, max, remaining: max - spent }));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (method === "GET" && pathname === "/api/seals") {
|
|
75
|
+
const searchParams = new URL(url, `http://localhost:${port}`).searchParams;
|
|
76
|
+
const statusFilter = searchParams.get("status");
|
|
77
|
+
let seals;
|
|
78
|
+
if (statusFilter) {
|
|
79
|
+
seals = db.prepare("SELECT * FROM issued_seals WHERE LOWER(status) = LOWER(?) ORDER BY timestamp DESC").all(statusFilter);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
seals = db.prepare("SELECT * FROM issued_seals ORDER BY timestamp DESC").all();
|
|
83
|
+
}
|
|
84
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
85
|
+
res.end(JSON.stringify(seals));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (method === "PUT" && pathname.startsWith("/api/settings/")) {
|
|
89
|
+
const key = pathname.replace("/api/settings/", "");
|
|
90
|
+
let body = "";
|
|
91
|
+
req.on("data", chunk => body += chunk);
|
|
92
|
+
req.on("end", () => {
|
|
93
|
+
try {
|
|
94
|
+
const { value } = JSON.parse(body);
|
|
95
|
+
db.prepare("INSERT INTO dashboard_settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?")
|
|
96
|
+
.run(key, value.toString(), value.toString());
|
|
97
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
98
|
+
res.end(JSON.stringify({ key, value }));
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
res.writeHead(400);
|
|
102
|
+
res.end("Invalid JSON");
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
res.writeHead(404);
|
|
108
|
+
res.end("Not Found");
|
|
109
|
+
});
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
server.listen(port, () => {
|
|
112
|
+
const url = `http://localhost:${port}`;
|
|
113
|
+
if (!skipOpen) {
|
|
114
|
+
console.log(`Dashboard running at ${url}`);
|
|
115
|
+
const start = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
116
|
+
(0, node_child_process_1.exec)(`${start} ${url}`);
|
|
117
|
+
}
|
|
118
|
+
resolve(server);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injector.d.ts","sourceRoot":"","sources":["../../src/engine/injector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsDH,eAAO,MAAM,qBAAqB,UAUjC,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAS5B,CAAC;AAEF,eAAO,MAAM,aAAa,UAUzB,CAAC;AAKF,eAAO,MAAM,oBAAoB,UAMhC,CAAC;AAEF,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAQ5B,CAAC;AAEF,eAAO,MAAM,aAAa,UAQzB,CAAC;AAEF,eAAO,MAAM,eAAe,UAM3B,CAAC;AAEF,eAAO,MAAM,eAAe,UAQ3B,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAQxC,CAAC;AAEF,eAAO,MAAM,iBAAiB,UAM7B,CAAC;AAEF,eAAO,MAAM,eAAe,UAQ3B,CAAC;AAEF,eAAO,MAAM,cAAc,UAO1B,CAAC;AAyBF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyB1D;AA6BD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC5E;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACzC;AAKD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,GACrB,MAAM,GAAG,IAAI,CA8Df;AA2ED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAU;gBAEd,MAAM,GAAE,MAAgC,EAAE,QAAQ,GAAE,OAAe;IAQzE,iBAAiB,CAAC,IAAI,EAAE;QAC5B,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqEtB,iBAAiB,CAAC,IAAI,EAAE;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,eAAe,CAAC;IA0CtB,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;YA2DpD,cAAc;YAqCd,oBAAoB;YAoDpB,iBAAiB;YA8BjB,mBAAmB;YA8FnB,gBAAgB;YAwChB,YAAY;YAyEZ,gBAAgB;YAqChB,iBAAiB;
|
|
1
|
+
{"version":3,"file":"injector.d.ts","sourceRoot":"","sources":["../../src/engine/injector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsDH,eAAO,MAAM,qBAAqB,UAUjC,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAS5B,CAAC;AAEF,eAAO,MAAM,aAAa,UAUzB,CAAC;AAKF,eAAO,MAAM,oBAAoB,UAMhC,CAAC;AAEF,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAQ5B,CAAC;AAEF,eAAO,MAAM,aAAa,UAQzB,CAAC;AAEF,eAAO,MAAM,eAAe,UAM3B,CAAC;AAEF,eAAO,MAAM,eAAe,UAQ3B,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAQxC,CAAC;AAEF,eAAO,MAAM,iBAAiB,UAM7B,CAAC;AAEF,eAAO,MAAM,eAAe,UAQ3B,CAAC;AAEF,eAAO,MAAM,cAAc,UAO1B,CAAC;AAyBF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyB1D;AA6BD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC5E;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACzC;AAKD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,GACrB,MAAM,GAAG,IAAI,CA8Df;AA2ED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAU;gBAEd,MAAM,GAAE,MAAgC,EAAE,QAAQ,GAAE,OAAe;IAQzE,iBAAiB,CAAC,IAAI,EAAE;QAC5B,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqEtB,iBAAiB,CAAC,IAAI,EAAE;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,eAAe,CAAC;IA0CtB,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;YA2DpD,cAAc;YAqCd,oBAAoB;YAoDpB,iBAAiB;YA8BjB,mBAAmB;YA8FnB,gBAAgB;YAwChB,YAAY;YAyEZ,gBAAgB;YAqChB,iBAAiB;IA2H/B,OAAO,CAAC,eAAe;YAkBT,cAAc;IAgD5B,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CAI9C"}
|
package/dist/engine/injector.js
CHANGED
|
@@ -852,19 +852,69 @@ class PopBrowserInjector {
|
|
|
852
852
|
else
|
|
853
853
|
failed.push(`${name} (value='${value}')`);
|
|
854
854
|
};
|
|
855
|
-
|
|
856
|
-
|
|
855
|
+
// Fill ORDER matters: input fields first, then select dropdowns last.
|
|
856
|
+
// Reason: filling inputs can trigger framework re-renders (React, Zoho)
|
|
857
|
+
// which reset previously selected dropdowns. Selects go last to survive.
|
|
858
|
+
const fieldConfigs = [
|
|
859
|
+
{ selectors: exports.FIRST_NAME_SELECTORS, value: info.firstName, name: "first_name" },
|
|
860
|
+
{ selectors: exports.LAST_NAME_SELECTORS, value: info.lastName, name: "last_name" },
|
|
861
|
+
{ selectors: exports.STREET_SELECTORS, value: info.street, name: "street" },
|
|
862
|
+
{ selectors: exports.CITY_SELECTORS, value: info.city, name: "city" },
|
|
863
|
+
{ selectors: exports.STATE_SELECTORS, value: state, name: "state" },
|
|
864
|
+
{ selectors: exports.COUNTRY_SELECTORS, value: info.country, name: "country" },
|
|
865
|
+
{ selectors: exports.ZIP_SELECTORS, value: info.zip, name: "zip" },
|
|
866
|
+
{ selectors: exports.EMAIL_SELECTORS, value: info.email, name: "email" },
|
|
867
|
+
];
|
|
857
868
|
// Full name fallback
|
|
858
869
|
if (info.firstName || info.lastName) {
|
|
859
870
|
const fullName = [info.firstName, info.lastName].filter(Boolean).join(" ");
|
|
860
|
-
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
871
|
+
fieldConfigs.push({ selectors: exports.FULL_NAME_SELECTORS, value: fullName, name: "full_name" });
|
|
872
|
+
}
|
|
873
|
+
// Detect tagName for each non-empty field
|
|
874
|
+
const detections = [];
|
|
875
|
+
for (const config of fieldConfigs) {
|
|
876
|
+
if (!config.value) {
|
|
877
|
+
skipped.push(config.name);
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
let tagName = null;
|
|
881
|
+
try {
|
|
882
|
+
const allSelector = config.selectors.join(", ");
|
|
883
|
+
const { result } = await client.send("Runtime.evaluate", {
|
|
884
|
+
expression: `
|
|
885
|
+
(function() {
|
|
886
|
+
const el = document.querySelector(${JSON.stringify(allSelector)});
|
|
887
|
+
return el ? el.tagName.toLowerCase() : null;
|
|
888
|
+
})()
|
|
889
|
+
`,
|
|
890
|
+
returnByValue: true,
|
|
891
|
+
});
|
|
892
|
+
tagName = result?.value || null;
|
|
893
|
+
}
|
|
894
|
+
catch {
|
|
895
|
+
// Detection failed — treat as input (non-select)
|
|
896
|
+
}
|
|
897
|
+
detections.push({ ...config, tagName });
|
|
898
|
+
}
|
|
899
|
+
const doFill = async (d) => {
|
|
900
|
+
const ok = await this.fillBillingField(client, d.selectors, d.value, d.name);
|
|
901
|
+
if (ok)
|
|
902
|
+
filled.push(d.name);
|
|
903
|
+
else
|
|
904
|
+
failed.push(`${d.name} (value='${d.value}')`);
|
|
905
|
+
};
|
|
906
|
+
// Round 1: fill all non-select fields (inputs, textareas, etc.)
|
|
907
|
+
for (const d of detections) {
|
|
908
|
+
if (d.tagName !== "select") {
|
|
909
|
+
await doFill(d);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
// Round 2: fill all select dropdowns (survive re-renders)
|
|
913
|
+
for (const d of detections) {
|
|
914
|
+
if (d.tagName === "select") {
|
|
915
|
+
await doFill(d);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
868
918
|
// Phone: country code dropdown first, then number
|
|
869
919
|
let ccFilled = false;
|
|
870
920
|
if (info.phoneCountryCode) {
|