voice-wallet 1.0.31
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/agent.js +185 -0
- package/dist/app.js +287 -0
- package/dist/blockchain.js +364 -0
- package/dist/cli.js +63 -0
- package/dist/config.js +136 -0
- package/package.json +67 -0
- package/readme.md +111 -0
package/dist/agent.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
3
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
4
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
7
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
8
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
9
|
+
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
10
|
+
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
11
|
+
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
12
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
13
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
14
|
+
import Groq from 'groq-sdk';
|
|
15
|
+
export var AgentService = /*#__PURE__*/function () {
|
|
16
|
+
function AgentService(apiKey) {
|
|
17
|
+
_classCallCheck(this, AgentService);
|
|
18
|
+
this.groq = new Groq({
|
|
19
|
+
apiKey: apiKey
|
|
20
|
+
});
|
|
21
|
+
this.conversationHistory = [];
|
|
22
|
+
this.pendingAction = null;
|
|
23
|
+
}
|
|
24
|
+
return _createClass(AgentService, [{
|
|
25
|
+
key: "parseIntent",
|
|
26
|
+
value: function () {
|
|
27
|
+
var _parseIntent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(transcript) {
|
|
28
|
+
var context,
|
|
29
|
+
systemPrompt,
|
|
30
|
+
contextMessage,
|
|
31
|
+
_context$pendingActio,
|
|
32
|
+
completion,
|
|
33
|
+
responseText,
|
|
34
|
+
parsedIntent,
|
|
35
|
+
_args = arguments,
|
|
36
|
+
_t;
|
|
37
|
+
return _regenerator().w(function (_context) {
|
|
38
|
+
while (1) switch (_context.p = _context.n) {
|
|
39
|
+
case 0:
|
|
40
|
+
context = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};
|
|
41
|
+
_context.p = 1;
|
|
42
|
+
systemPrompt = "You are a DeFi voice assistant that parses user commands and returns structured JSON.\n\nYour job is to analyze the user's voice transcript and determine what blockchain action they want to perform.\n\nPossible actions:\n1. \"send\" - Send native cryptocurrency to an address or ENS name\n2. \"send_erc20\" - Send ERC20 tokens using EIP-7702\n3. \"deploy_token\" - Deploy a new ERC20 token\n4. \"check_balance\" - Check wallet balance\n5. \"help\" - Show available commands\n6. \"need_more_info\" - User provided incomplete information\n7. \"unknown\" - Command not recognized\n\nFor \"send\" actions, extract:\n- recipient: address or ENS name\n- amount: amount to send (as string)\n\nFor \"send_erc20\" actions, extract:\n- tokenAddress: ERC20 token contract address\n- recipient: address or ENS name\n- amount: amount to send (as string)\n- decimals: token decimals (default 18)\n\nFor \"deploy_token\" actions, extract:\n- name: token name\n- symbol: token symbol\n- initialSupply: initial supply amount\n\nIf the user provides INCOMPLETE information, return action=\"need_more_info\" with:\n- missingFields: array of missing fields\n- providedFields: object with fields they DID provide\n- question: what to ask the user next\n\nIMPORTANT: Return ONLY valid JSON. No markdown, no explanations, just pure JSON.\n\nResponse format:\n{\n\t\"action\": \"send\" | \"send_erc20\" | \"deploy_token\" | \"check_balance\" | \"help\" | \"need_more_info\" | \"unknown\",\n\t\"params\": {\n\t\t// Action-specific parameters\n\t},\n\t\"confidence\": 0.0 to 1.0,\n\t\"rawCommand\": \"original user command\",\n\t\"missingFields\": [\"field1\", \"field2\"] // only for need_more_info,\n\t\"question\": \"What information do you need?\" // only for need_more_info\n}"; // Build context message if there's pending action
|
|
43
|
+
contextMessage = '';
|
|
44
|
+
if (context.pendingAction) {
|
|
45
|
+
contextMessage = "\n\nCONTEXT: User is completing a previous command.\nPrevious action: ".concat(context.pendingAction.action, "\nAlready provided: ").concat(JSON.stringify(context.pendingAction.providedFields || {}), "\nMissing fields: ").concat((_context$pendingActio = context.pendingAction.missingFields) === null || _context$pendingActio === void 0 ? void 0 : _context$pendingActio.join(', '), "\n\nUse the user's new input to fill in the missing fields and complete the action.");
|
|
46
|
+
}
|
|
47
|
+
_context.n = 2;
|
|
48
|
+
return this.groq.chat.completions.create({
|
|
49
|
+
messages: [{
|
|
50
|
+
role: "system",
|
|
51
|
+
content: systemPrompt
|
|
52
|
+
}, {
|
|
53
|
+
role: "user",
|
|
54
|
+
content: "Parse this command: \"".concat(transcript, "\"").concat(contextMessage)
|
|
55
|
+
}],
|
|
56
|
+
model: "meta-llama/llama-4-scout-17b-16e-instruct",
|
|
57
|
+
temperature: 0.1,
|
|
58
|
+
response_format: {
|
|
59
|
+
type: "json_object"
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
case 2:
|
|
63
|
+
completion = _context.v;
|
|
64
|
+
responseText = completion.choices[0].message.content;
|
|
65
|
+
parsedIntent = JSON.parse(responseText); // If we have a pending action and user provided more info, merge it
|
|
66
|
+
if (context.pendingAction && parsedIntent.action !== 'need_more_info') {
|
|
67
|
+
parsedIntent.params = _objectSpread(_objectSpread({}, context.pendingAction.providedFields), parsedIntent.params);
|
|
68
|
+
}
|
|
69
|
+
return _context.a(2, parsedIntent);
|
|
70
|
+
case 3:
|
|
71
|
+
_context.p = 3;
|
|
72
|
+
_t = _context.v;
|
|
73
|
+
console.error('Intent parsing error:', _t);
|
|
74
|
+
return _context.a(2, {
|
|
75
|
+
action: 'unknown',
|
|
76
|
+
params: {},
|
|
77
|
+
confidence: 0,
|
|
78
|
+
rawCommand: transcript,
|
|
79
|
+
error: _t.message
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}, _callee, this, [[1, 3]]);
|
|
83
|
+
}));
|
|
84
|
+
function parseIntent(_x) {
|
|
85
|
+
return _parseIntent.apply(this, arguments);
|
|
86
|
+
}
|
|
87
|
+
return parseIntent;
|
|
88
|
+
}()
|
|
89
|
+
}, {
|
|
90
|
+
key: "askFollowUp",
|
|
91
|
+
value: function () {
|
|
92
|
+
var _askFollowUp = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(missingFields, providedFields, action) {
|
|
93
|
+
var systemPrompt, userMessage, completion, _t2;
|
|
94
|
+
return _regenerator().w(function (_context2) {
|
|
95
|
+
while (1) switch (_context2.p = _context2.n) {
|
|
96
|
+
case 0:
|
|
97
|
+
_context2.p = 0;
|
|
98
|
+
systemPrompt = "You are a friendly DeFi assistant helping users complete blockchain transactions.\n\nThe user started a command but didn't provide all information. Ask them a natural, conversational question to get the missing information.\n\nBe specific and helpful. For example:\n- If missing token name: \"What would you like to name your token?\"\n- If missing recipient: \"Who would you like to send this to? You can use an address or ENS name like vitalik.eth\"\n- If missing amount: \"How much would you like to send?\"\n\nKeep it friendly and under 2 sentences.";
|
|
99
|
+
userMessage = "Action: ".concat(action, "\nAlready have: ").concat(JSON.stringify(providedFields), "\nMissing: ").concat(missingFields.join(', '), "\n\nAsk the user for the missing information in a natural way.");
|
|
100
|
+
_context2.n = 1;
|
|
101
|
+
return this.groq.chat.completions.create({
|
|
102
|
+
messages: [{
|
|
103
|
+
role: "system",
|
|
104
|
+
content: systemPrompt
|
|
105
|
+
}, {
|
|
106
|
+
role: "user",
|
|
107
|
+
content: userMessage
|
|
108
|
+
}],
|
|
109
|
+
model: "meta-llama/llama-4-scout-17b-16e-instruct",
|
|
110
|
+
temperature: 0.7,
|
|
111
|
+
max_tokens: 100
|
|
112
|
+
});
|
|
113
|
+
case 1:
|
|
114
|
+
completion = _context2.v;
|
|
115
|
+
return _context2.a(2, completion.choices[0].message.content.trim());
|
|
116
|
+
case 2:
|
|
117
|
+
_context2.p = 2;
|
|
118
|
+
_t2 = _context2.v;
|
|
119
|
+
return _context2.a(2, "I need a bit more information. What's the ".concat(missingFields[0], "?"));
|
|
120
|
+
}
|
|
121
|
+
}, _callee2, this, [[0, 2]]);
|
|
122
|
+
}));
|
|
123
|
+
function askFollowUp(_x2, _x3, _x4) {
|
|
124
|
+
return _askFollowUp.apply(this, arguments);
|
|
125
|
+
}
|
|
126
|
+
return askFollowUp;
|
|
127
|
+
}()
|
|
128
|
+
}, {
|
|
129
|
+
key: "generateResponse",
|
|
130
|
+
value: function () {
|
|
131
|
+
var _generateResponse = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(action, result) {
|
|
132
|
+
var systemPrompt, userMessage, completion, _t3;
|
|
133
|
+
return _regenerator().w(function (_context3) {
|
|
134
|
+
while (1) switch (_context3.p = _context3.n) {
|
|
135
|
+
case 0:
|
|
136
|
+
_context3.p = 0;
|
|
137
|
+
systemPrompt = "You are a friendly DeFi assistant. \n\t\t\t\nGenerate a natural, conversational response based on the action result. \nKeep responses concise (1-2 sentences max). \nIf there's a transaction URL, mention it but don't include the full URL in your text.";
|
|
138
|
+
userMessage = "Action: ".concat(action, "\nResult: ").concat(JSON.stringify(result, null, 2), "\n\nGenerate a friendly response for the user.");
|
|
139
|
+
_context3.n = 1;
|
|
140
|
+
return this.groq.chat.completions.create({
|
|
141
|
+
messages: [{
|
|
142
|
+
role: "system",
|
|
143
|
+
content: systemPrompt
|
|
144
|
+
}, {
|
|
145
|
+
role: "user",
|
|
146
|
+
content: userMessage
|
|
147
|
+
}],
|
|
148
|
+
model: "meta-llama/llama-4-scout-17b-16e-instruct",
|
|
149
|
+
temperature: 0.7,
|
|
150
|
+
max_tokens: 150
|
|
151
|
+
});
|
|
152
|
+
case 1:
|
|
153
|
+
completion = _context3.v;
|
|
154
|
+
return _context3.a(2, completion.choices[0].message.content.trim());
|
|
155
|
+
case 2:
|
|
156
|
+
_context3.p = 2;
|
|
157
|
+
_t3 = _context3.v;
|
|
158
|
+
return _context3.a(2, "Action completed! ".concat(result.hash ? "Transaction: ".concat(result.hash) : ''));
|
|
159
|
+
}
|
|
160
|
+
}, _callee3, this, [[0, 2]]);
|
|
161
|
+
}));
|
|
162
|
+
function generateResponse(_x5, _x6) {
|
|
163
|
+
return _generateResponse.apply(this, arguments);
|
|
164
|
+
}
|
|
165
|
+
return generateResponse;
|
|
166
|
+
}()
|
|
167
|
+
}, {
|
|
168
|
+
key: "getHelpMessage",
|
|
169
|
+
value: function () {
|
|
170
|
+
var _getHelpMessage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
171
|
+
return _regenerator().w(function (_context4) {
|
|
172
|
+
while (1) switch (_context4.n) {
|
|
173
|
+
case 0:
|
|
174
|
+
return _context4.a(2, "\n\uD83C\uDF99\uFE0F Voice DeFi Commands:\n\n\uD83D\uDCB8 Send Native Crypto:\n \"Send 0.001 ETH to vitalik.eth\"\n \"Transfer 0.5 BNB to 0x123...\"\n\n\uD83E\uDE99 Send ERC20 Tokens (EIP-7702):\n \"Send 100 USDC tokens from 0xTokenAddr to djpai.eth\"\n \"Transfer 50 tokens from 0xABC... to 0x123...\"\n\n\uD83D\uDE80 Deploy Token:\n \"Launch a token called MyToken with symbol MTK and 1000000 supply\"\n \"Create a token named DogeCoin symbol DOGE with 1 billion supply\"\n\n\uD83D\uDCB0 Check Balance:\n \"What's my balance?\"\n \"How much do I have?\"\n\n\u2753 Help:\n \"Help\" or \"What can you do?\"\n\nPress 's' to start recording your command!\n");
|
|
175
|
+
}
|
|
176
|
+
}, _callee4);
|
|
177
|
+
}));
|
|
178
|
+
function getHelpMessage() {
|
|
179
|
+
return _getHelpMessage.apply(this, arguments);
|
|
180
|
+
}
|
|
181
|
+
return getHelpMessage;
|
|
182
|
+
}()
|
|
183
|
+
}]);
|
|
184
|
+
}();
|
|
185
|
+
export default AgentService;
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { Box, Text, useInput, useApp } from 'ink';
|
|
3
|
+
import recorder from 'node-record-lpcm16';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import Groq from 'groq-sdk';
|
|
8
|
+
export default function App({
|
|
9
|
+
apiKey,
|
|
10
|
+
model = 'whisper-large-v3-turbo',
|
|
11
|
+
language
|
|
12
|
+
}) {
|
|
13
|
+
const {
|
|
14
|
+
exit
|
|
15
|
+
} = useApp();
|
|
16
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
17
|
+
const [transcript, setTranscript] = useState('');
|
|
18
|
+
const [status, setStatus] = useState('Initializing...');
|
|
19
|
+
const [error, setError] = useState('');
|
|
20
|
+
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
21
|
+
const [recordingDuration, setRecordingDuration] = useState(0);
|
|
22
|
+
const [agentResponse, setAgentResponse] = useState();
|
|
23
|
+
const recordingRef = useRef(null);
|
|
24
|
+
const audioFileRef = useRef(null);
|
|
25
|
+
const durationIntervalRef = useRef(null);
|
|
26
|
+
const groqRef = useRef(null);
|
|
27
|
+
|
|
28
|
+
// Initialize Groq
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
try {
|
|
31
|
+
groqRef.current = new Groq({
|
|
32
|
+
apiKey: apiKey
|
|
33
|
+
});
|
|
34
|
+
setStatus(`Ready. Press "s" to start recording (Model: ${model})`);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
setError(`Failed to initialize Groq: ${err.message}`);
|
|
37
|
+
setStatus('Error');
|
|
38
|
+
}
|
|
39
|
+
return () => {
|
|
40
|
+
stopRecording();
|
|
41
|
+
cleanupAudioFile();
|
|
42
|
+
};
|
|
43
|
+
}, [apiKey, model]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!transcript) return;
|
|
46
|
+
const run = async () => {
|
|
47
|
+
try {
|
|
48
|
+
if (!groqRef.current) {
|
|
49
|
+
setError("Groq ref not set");
|
|
50
|
+
setStatus("Error");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const completion = await groqRef.current.chat.completions.create({
|
|
54
|
+
messages: [{
|
|
55
|
+
role: "system",
|
|
56
|
+
content: "You are an assistant who also roasts everytime."
|
|
57
|
+
}, {
|
|
58
|
+
role: "user",
|
|
59
|
+
content: transcript
|
|
60
|
+
}],
|
|
61
|
+
model: "openai/gpt-oss-20b"
|
|
62
|
+
});
|
|
63
|
+
const content = completion?.choices?.[0]?.message?.content || "Something went wrong";
|
|
64
|
+
setAgentResponse(content);
|
|
65
|
+
setStatus(`Press "s" to start another recording`);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
setAgentResponse("Error: " + err.message);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
run();
|
|
71
|
+
}, [transcript]);
|
|
72
|
+
const startRecording = () => {
|
|
73
|
+
try {
|
|
74
|
+
const tempDir = os.tmpdir();
|
|
75
|
+
const audioFileName = `voice-defi-${Date.now()}.wav`;
|
|
76
|
+
audioFileRef.current = path.join(tempDir, audioFileName);
|
|
77
|
+
const file = fs.createWriteStream(audioFileRef.current, {
|
|
78
|
+
encoding: 'binary'
|
|
79
|
+
});
|
|
80
|
+
recordingRef.current = recorder.record({
|
|
81
|
+
sampleRate: 16000,
|
|
82
|
+
channels: 1,
|
|
83
|
+
audioType: 'wav'
|
|
84
|
+
});
|
|
85
|
+
recordingRef.current.stream().pipe(file);
|
|
86
|
+
setIsRecording(true);
|
|
87
|
+
setStatus('🔴 Recording... Press "q" to stop');
|
|
88
|
+
setRecordingDuration(0);
|
|
89
|
+
setError('');
|
|
90
|
+
durationIntervalRef.current = setInterval(() => {
|
|
91
|
+
setRecordingDuration(prev => prev + 1);
|
|
92
|
+
}, 1000);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
setError(`Recorcding error: ${err.message}`);
|
|
95
|
+
setStatus('Error');
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const stopRecording = () => {
|
|
99
|
+
if (recordingRef.current) {
|
|
100
|
+
recordingRef.current.stop();
|
|
101
|
+
recordingRef.current = null;
|
|
102
|
+
}
|
|
103
|
+
if (durationIntervalRef.current) {
|
|
104
|
+
clearInterval(durationIntervalRef.current);
|
|
105
|
+
durationIntervalRef.current = null;
|
|
106
|
+
}
|
|
107
|
+
setIsRecording(false);
|
|
108
|
+
};
|
|
109
|
+
const transcribeAudio = async () => {
|
|
110
|
+
if (!audioFileRef.current || !groqRef.current) {
|
|
111
|
+
setError('No audio file to transcribe');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
setIsTranscribing(true);
|
|
115
|
+
setStatus(`🔄 Transcribing with Groq ${model}...`);
|
|
116
|
+
try {
|
|
117
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
118
|
+
if (!fs.existsSync(audioFileRef.current)) {
|
|
119
|
+
throw new Error('Audio file not found');
|
|
120
|
+
}
|
|
121
|
+
const stats = fs.statSync(audioFileRef.current);
|
|
122
|
+
if (stats.size < 1000) {
|
|
123
|
+
throw new Error('Audio file is too small - no speech detected');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Groq uses same API as OpenAI for transcriptions
|
|
127
|
+
const transcription = await groqRef.current.audio.transcriptions.create({
|
|
128
|
+
file: fs.createReadStream(audioFileRef.current),
|
|
129
|
+
model: model,
|
|
130
|
+
language: language || undefined,
|
|
131
|
+
response_format: 'json',
|
|
132
|
+
temperature: 0.0
|
|
133
|
+
});
|
|
134
|
+
if (transcription.text && transcription.text.trim()) {
|
|
135
|
+
setTranscript(transcription.text.trim());
|
|
136
|
+
setStatus('✅ Transcription complete! Press "s" for new recording');
|
|
137
|
+
setError('');
|
|
138
|
+
} else {
|
|
139
|
+
setError('No speech detected in audio');
|
|
140
|
+
setStatus('No speech detected. Press "s" to try again');
|
|
141
|
+
}
|
|
142
|
+
} catch (err) {
|
|
143
|
+
// Handle Groq-specific errors
|
|
144
|
+
if (err.status === 401) {
|
|
145
|
+
setError('Invalid API key. Check your Groq API key.');
|
|
146
|
+
} else if (err.status === 429) {
|
|
147
|
+
setError('Rate limit exceeded. Please wait and try again.');
|
|
148
|
+
} else if (err.message?.includes('File size')) {
|
|
149
|
+
setError('Audio file too large (max 25 MB on free tier).');
|
|
150
|
+
} else {
|
|
151
|
+
setError(`Transcription error: ${err.message}`);
|
|
152
|
+
}
|
|
153
|
+
setStatus('Error during transcription');
|
|
154
|
+
} finally {
|
|
155
|
+
setIsTranscribing(false);
|
|
156
|
+
cleanupAudioFile();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const cleanupAudioFile = () => {
|
|
160
|
+
if (audioFileRef.current && fs.existsSync(audioFileRef.current)) {
|
|
161
|
+
try {
|
|
162
|
+
fs.unlinkSync(audioFileRef.current);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
// Ignore
|
|
165
|
+
}
|
|
166
|
+
audioFileRef.current = null;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const formatDuration = seconds => {
|
|
170
|
+
const mins = Math.floor(seconds / 60);
|
|
171
|
+
const secs = seconds % 60;
|
|
172
|
+
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
173
|
+
};
|
|
174
|
+
useInput((input, key) => {
|
|
175
|
+
if (input === 's' && !isRecording && !isTranscribing) {
|
|
176
|
+
setTranscript('');
|
|
177
|
+
setAgentResponse('');
|
|
178
|
+
setError('');
|
|
179
|
+
setStatus(`🔴 Recording... Press "q" to stop`);
|
|
180
|
+
startRecording();
|
|
181
|
+
} else if (input === 'q' && isRecording) {
|
|
182
|
+
stopRecording();
|
|
183
|
+
transcribeAudio();
|
|
184
|
+
} else if (input === 'x' || key.ctrl && input === 'c') {
|
|
185
|
+
stopRecording();
|
|
186
|
+
cleanupAudioFile();
|
|
187
|
+
exit();
|
|
188
|
+
} else if (input === 'c' && !isRecording && !isTranscribing) {
|
|
189
|
+
setTranscript('');
|
|
190
|
+
setError('');
|
|
191
|
+
setStatus(`Ready. Press "s" to start recording (Model: ${model})`);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
195
|
+
flexDirection: "column",
|
|
196
|
+
padding: 1
|
|
197
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
198
|
+
marginBottom: 1
|
|
199
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
200
|
+
bold: true,
|
|
201
|
+
color: "cyan"
|
|
202
|
+
}, "\uD83C\uDFA4 Voice DeFi - Groq Transcription CLI")), /*#__PURE__*/React.createElement(Box, {
|
|
203
|
+
marginBottom: 1
|
|
204
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
205
|
+
color: error ? 'red' : isRecording ? 'green' : isTranscribing ? 'yellow' : 'blue'
|
|
206
|
+
}, error || status)), /*#__PURE__*/React.createElement(Box, {
|
|
207
|
+
marginBottom: 1,
|
|
208
|
+
flexDirection: "column"
|
|
209
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
210
|
+
bold: true,
|
|
211
|
+
underline: true
|
|
212
|
+
}, "Controls:"), /*#__PURE__*/React.createElement(Text, null, " ", /*#__PURE__*/React.createElement(Text, {
|
|
213
|
+
color: "green"
|
|
214
|
+
}, "s"), " - Start recording"), /*#__PURE__*/React.createElement(Text, null, " ", /*#__PURE__*/React.createElement(Text, {
|
|
215
|
+
color: "red"
|
|
216
|
+
}, "q"), " - Stop recording & transcribe"), /*#__PURE__*/React.createElement(Text, null, " ", /*#__PURE__*/React.createElement(Text, {
|
|
217
|
+
color: "blue"
|
|
218
|
+
}, "c"), " - Clear transcript"), /*#__PURE__*/React.createElement(Text, null, " ", /*#__PURE__*/React.createElement(Text, {
|
|
219
|
+
color: "yellow"
|
|
220
|
+
}, "x"), " - Exit (or Ctrl+C)")), /*#__PURE__*/React.createElement(Box, {
|
|
221
|
+
marginBottom: 1,
|
|
222
|
+
flexDirection: "column"
|
|
223
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
224
|
+
bold: true,
|
|
225
|
+
underline: true
|
|
226
|
+
}, "Model Info:"), /*#__PURE__*/React.createElement(Text, null, " Model: ", /*#__PURE__*/React.createElement(Text, {
|
|
227
|
+
color: "cyan"
|
|
228
|
+
}, model)), /*#__PURE__*/React.createElement(Text, null, " Language: ", /*#__PURE__*/React.createElement(Text, {
|
|
229
|
+
color: "cyan"
|
|
230
|
+
}, language || 'Auto-detect')), /*#__PURE__*/React.createElement(Text, null, " Provider: ", /*#__PURE__*/React.createElement(Text, {
|
|
231
|
+
color: "magenta"
|
|
232
|
+
}, "Groq"))), isRecording && /*#__PURE__*/React.createElement(Box, {
|
|
233
|
+
marginBottom: 1,
|
|
234
|
+
borderStyle: "round",
|
|
235
|
+
borderColor: "red",
|
|
236
|
+
padding: 1
|
|
237
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
238
|
+
flexDirection: "column"
|
|
239
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
240
|
+
color: "red",
|
|
241
|
+
bold: true
|
|
242
|
+
}, "\u25CF RECORDING"), /*#__PURE__*/React.createElement(Text, null, "Duration: ", formatDuration(recordingDuration)), /*#__PURE__*/React.createElement(Text, {
|
|
243
|
+
dimColor: true
|
|
244
|
+
}, "Press 'q' to stop and transcribe"))), isTranscribing && /*#__PURE__*/React.createElement(Box, {
|
|
245
|
+
marginBottom: 1,
|
|
246
|
+
borderStyle: "round",
|
|
247
|
+
borderColor: "yellow",
|
|
248
|
+
padding: 1
|
|
249
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
250
|
+
color: "yellow"
|
|
251
|
+
}, "\u23F3 Transcribing audio with Groq ", model, "...")), transcript && !isTranscribing && /*#__PURE__*/React.createElement(Box, {
|
|
252
|
+
marginTop: 1,
|
|
253
|
+
flexDirection: "column"
|
|
254
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
255
|
+
bold: true,
|
|
256
|
+
underline: true,
|
|
257
|
+
color: "green"
|
|
258
|
+
}, "\uD83D\uDCDD Transcript:"), /*#__PURE__*/React.createElement(Box, {
|
|
259
|
+
marginTop: 1,
|
|
260
|
+
borderStyle: "round",
|
|
261
|
+
borderColor: "green",
|
|
262
|
+
padding: 1
|
|
263
|
+
}, /*#__PURE__*/React.createElement(Text, null, transcript))), agentResponse && /*#__PURE__*/React.createElement(Box, {
|
|
264
|
+
marginTop: 1,
|
|
265
|
+
flexDirection: "column"
|
|
266
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
267
|
+
bold: true,
|
|
268
|
+
underline: true,
|
|
269
|
+
color: "blue"
|
|
270
|
+
}, "Agent Response:"), /*#__PURE__*/React.createElement(Box, {
|
|
271
|
+
marginTop: 1,
|
|
272
|
+
borderStyle: "round",
|
|
273
|
+
borderColor: "blue",
|
|
274
|
+
padding: 1
|
|
275
|
+
}, /*#__PURE__*/React.createElement(Text, null, agentResponse))), !isRecording && !isTranscribing && /*#__PURE__*/React.createElement(Box, {
|
|
276
|
+
marginTop: 1
|
|
277
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
278
|
+
dimColor: true
|
|
279
|
+
}, "Press 's' to start a new recording.")), error && /*#__PURE__*/React.createElement(Box, {
|
|
280
|
+
marginTop: 1,
|
|
281
|
+
borderStyle: "round",
|
|
282
|
+
borderColor: "red",
|
|
283
|
+
padding: 1
|
|
284
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
285
|
+
color: "red"
|
|
286
|
+
}, "\u274C ", error)));
|
|
287
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
3
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
4
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
5
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
6
|
+
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
7
|
+
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
8
|
+
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
9
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
10
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
11
|
+
import { createPublicClient, createWalletClient, http, parseEther, formatEther } from "viem";
|
|
12
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
13
|
+
import { bscTestnet, sepolia } from "viem/chains";
|
|
14
|
+
import { ethers } from "ethers";
|
|
15
|
+
import { createSmartAccountClient } from "permissionless";
|
|
16
|
+
import { toSimpleSmartAccount } from "permissionless/accounts";
|
|
17
|
+
import { bundlerActions } from "viem/account-abstraction";
|
|
18
|
+
|
|
19
|
+
/* -------------------------------------------------------------------------- */
|
|
20
|
+
/* NETWORK CONFIG */
|
|
21
|
+
/* -------------------------------------------------------------------------- */
|
|
22
|
+
|
|
23
|
+
var NETWORKS = {
|
|
24
|
+
bnb_testnet: {
|
|
25
|
+
chain: bscTestnet,
|
|
26
|
+
rpcUrl: "https://data-seed-prebsc-1-s1.binance.org:8545",
|
|
27
|
+
explorer: "https://testnet.bscscan.com",
|
|
28
|
+
nativeCurrency: "tBNB",
|
|
29
|
+
entryPoint: "0x66f0c4C9a21B78D4D92358D087176964982e1c21"
|
|
30
|
+
},
|
|
31
|
+
sepolia: {
|
|
32
|
+
chain: sepolia,
|
|
33
|
+
rpcUrl: "https://eth-sepolia.g.alchemy.com/v2/demo",
|
|
34
|
+
explorer: "https://sepolia.etherscan.io",
|
|
35
|
+
nativeCurrency: "ETH",
|
|
36
|
+
entryPoint: "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/* -------------------------------------------------------------------------- */
|
|
41
|
+
/* BLOCKCHAIN SERVICE */
|
|
42
|
+
/* -------------------------------------------------------------------------- */
|
|
43
|
+
|
|
44
|
+
export var BlockchainService = /*#__PURE__*/function () {
|
|
45
|
+
function BlockchainService(privateKey) {
|
|
46
|
+
var network = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "bnb_testnet";
|
|
47
|
+
var bundlerUrl = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
48
|
+
_classCallCheck(this, BlockchainService);
|
|
49
|
+
this.network = NETWORKS[network];
|
|
50
|
+
this.privateKey = privateKey;
|
|
51
|
+
this.account = privateKeyToAccount(privateKey);
|
|
52
|
+
this.bundlerUrl = bundlerUrl;
|
|
53
|
+
this.publicClient = createPublicClient({
|
|
54
|
+
chain: this.network.chain,
|
|
55
|
+
transport: http(this.network.rpcUrl)
|
|
56
|
+
});
|
|
57
|
+
this.walletClient = createWalletClient({
|
|
58
|
+
account: this.account,
|
|
59
|
+
chain: this.network.chain,
|
|
60
|
+
transport: http(this.network.rpcUrl)
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ENS Resolver (supports mixed-case ENS like Vitalik.ETH)
|
|
64
|
+
this.ensProvider = new ethers.JsonRpcProvider("https://sepolia.infura.io/v3/2WCbZ8YpmuPxUtM6PzbFOfY5k4B");
|
|
65
|
+
this.smartAccountEnabled = false;
|
|
66
|
+
if (bundlerUrl) {
|
|
67
|
+
this.initSmartAccount();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* ---------------------------------------------------------------------- */
|
|
72
|
+
/* INIT SMART ACCOUNT (EIP-7702) */
|
|
73
|
+
/* ---------------------------------------------------------------------- */
|
|
74
|
+
return _createClass(BlockchainService, [{
|
|
75
|
+
key: "initSmartAccount",
|
|
76
|
+
value: function initSmartAccount() {
|
|
77
|
+
try {
|
|
78
|
+
this.bundlerClient = this.publicClient.extend(bundlerActions({
|
|
79
|
+
entryPoint: this.network.entryPoint
|
|
80
|
+
}));
|
|
81
|
+
this.smartAccountEnabled = true;
|
|
82
|
+
console.log("✓ Smart account (EIP-7702) initialized");
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error("⚠️ Smart account init failed:", err.message);
|
|
85
|
+
this.smartAccountEnabled = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ---------------------------------------------------------------------- */
|
|
90
|
+
/* UTILITIES */
|
|
91
|
+
/* ---------------------------------------------------------------------- */
|
|
92
|
+
}, {
|
|
93
|
+
key: "getAddress",
|
|
94
|
+
value: (function () {
|
|
95
|
+
var _getAddress = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
96
|
+
return _regenerator().w(function (_context) {
|
|
97
|
+
while (1) switch (_context.n) {
|
|
98
|
+
case 0:
|
|
99
|
+
return _context.a(2, this.account.address);
|
|
100
|
+
}
|
|
101
|
+
}, _callee, this);
|
|
102
|
+
}));
|
|
103
|
+
function getAddress() {
|
|
104
|
+
return _getAddress.apply(this, arguments);
|
|
105
|
+
}
|
|
106
|
+
return getAddress;
|
|
107
|
+
}())
|
|
108
|
+
}, {
|
|
109
|
+
key: "getBalance",
|
|
110
|
+
value: function () {
|
|
111
|
+
var _getBalance = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
112
|
+
var bal;
|
|
113
|
+
return _regenerator().w(function (_context2) {
|
|
114
|
+
while (1) switch (_context2.n) {
|
|
115
|
+
case 0:
|
|
116
|
+
_context2.n = 1;
|
|
117
|
+
return this.publicClient.getBalance({
|
|
118
|
+
address: this.account.address
|
|
119
|
+
});
|
|
120
|
+
case 1:
|
|
121
|
+
bal = _context2.v;
|
|
122
|
+
return _context2.a(2, formatEther(bal));
|
|
123
|
+
}
|
|
124
|
+
}, _callee2, this);
|
|
125
|
+
}));
|
|
126
|
+
function getBalance() {
|
|
127
|
+
return _getBalance.apply(this, arguments);
|
|
128
|
+
}
|
|
129
|
+
return getBalance;
|
|
130
|
+
}() /* ----------------------------- ENS RESOLUTION -------------------------- */
|
|
131
|
+
}, {
|
|
132
|
+
key: "resolveENS",
|
|
133
|
+
value: (function () {
|
|
134
|
+
var _resolveENS = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(value) {
|
|
135
|
+
var v, resolved, _t;
|
|
136
|
+
return _regenerator().w(function (_context3) {
|
|
137
|
+
while (1) switch (_context3.p = _context3.n) {
|
|
138
|
+
case 0:
|
|
139
|
+
if (value) {
|
|
140
|
+
_context3.n = 1;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
throw new Error("No address or ENS provided");
|
|
144
|
+
case 1:
|
|
145
|
+
if (!(typeof value !== "string")) {
|
|
146
|
+
_context3.n = 2;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
throw new Error("Address/ENS must be a string");
|
|
150
|
+
case 2:
|
|
151
|
+
v = value.trim(); // If not ENS, return unchanged
|
|
152
|
+
if (v.toLowerCase().endsWith(".eth")) {
|
|
153
|
+
_context3.n = 3;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
return _context3.a(2, v);
|
|
157
|
+
case 3:
|
|
158
|
+
_context3.p = 3;
|
|
159
|
+
_context3.n = 4;
|
|
160
|
+
return this.ensProvider.resolveName(v);
|
|
161
|
+
case 4:
|
|
162
|
+
resolved = _context3.v;
|
|
163
|
+
if (resolved) {
|
|
164
|
+
_context3.n = 5;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
throw new Error("Failed to resolve ENS: ".concat(v));
|
|
168
|
+
case 5:
|
|
169
|
+
return _context3.a(2, resolved);
|
|
170
|
+
case 6:
|
|
171
|
+
_context3.p = 6;
|
|
172
|
+
_t = _context3.v;
|
|
173
|
+
throw new Error("ENS resolution failed: ".concat(_t.message));
|
|
174
|
+
case 7:
|
|
175
|
+
return _context3.a(2);
|
|
176
|
+
}
|
|
177
|
+
}, _callee3, this, [[3, 6]]);
|
|
178
|
+
}));
|
|
179
|
+
function resolveENS(_x) {
|
|
180
|
+
return _resolveENS.apply(this, arguments);
|
|
181
|
+
}
|
|
182
|
+
return resolveENS;
|
|
183
|
+
}() /* ----------------------------- AMOUNT PARSER -------------------------- */)
|
|
184
|
+
}, {
|
|
185
|
+
key: "_normalizeAmount",
|
|
186
|
+
value: function _normalizeAmount(amount) {
|
|
187
|
+
if (amount == null) throw new Error("Amount required");
|
|
188
|
+
|
|
189
|
+
// Convert BigInt → string
|
|
190
|
+
if (typeof amount === "bigint") return amount.toString();
|
|
191
|
+
|
|
192
|
+
// Convert numbers → string
|
|
193
|
+
if (typeof amount === "number") {
|
|
194
|
+
if (!isFinite(amount)) throw new Error("Invalid amount");
|
|
195
|
+
return String(amount);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// If object w/ toString
|
|
199
|
+
if (_typeof(amount) === "object" && typeof amount.toString === "function") {
|
|
200
|
+
amount = amount.toString();
|
|
201
|
+
}
|
|
202
|
+
if (typeof amount !== "string") {
|
|
203
|
+
throw new Error("Amount must be string or number");
|
|
204
|
+
}
|
|
205
|
+
var s = amount.trim();
|
|
206
|
+
|
|
207
|
+
// Remove commas "1,000.5"
|
|
208
|
+
s = s.replace(/,/g, "");
|
|
209
|
+
|
|
210
|
+
// Remove ETH suffix
|
|
211
|
+
s = s.replace(/eth/gi, "").trim();
|
|
212
|
+
|
|
213
|
+
// ".5" → "0.5"
|
|
214
|
+
if (s.startsWith(".")) s = "0".concat(s);
|
|
215
|
+
|
|
216
|
+
// Validate numeric
|
|
217
|
+
if (!/^[-+]?\d+(\.\d+)?([eE][-+]?\d+)?$/.test(s)) {
|
|
218
|
+
throw new Error("Invalid amount");
|
|
219
|
+
}
|
|
220
|
+
return s;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* ---------------------------------------------------------------------- */
|
|
224
|
+
/* STANDARD TRANSACTION */
|
|
225
|
+
/* ---------------------------------------------------------------------- */
|
|
226
|
+
}, {
|
|
227
|
+
key: "sendTransaction",
|
|
228
|
+
value: (function () {
|
|
229
|
+
var _sendTransaction = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(to, amount) {
|
|
230
|
+
var resolvedTo, clean, value, hash, receipt, _t2;
|
|
231
|
+
return _regenerator().w(function (_context4) {
|
|
232
|
+
while (1) switch (_context4.p = _context4.n) {
|
|
233
|
+
case 0:
|
|
234
|
+
_context4.n = 1;
|
|
235
|
+
return this.resolveENS(to);
|
|
236
|
+
case 1:
|
|
237
|
+
resolvedTo = _context4.v;
|
|
238
|
+
clean = this._normalizeAmount(amount);
|
|
239
|
+
_context4.p = 2;
|
|
240
|
+
value = parseEther(clean);
|
|
241
|
+
_context4.n = 4;
|
|
242
|
+
break;
|
|
243
|
+
case 3:
|
|
244
|
+
_context4.p = 3;
|
|
245
|
+
_t2 = _context4.v;
|
|
246
|
+
throw new Error("Invalid amount: ".concat(_t2.message));
|
|
247
|
+
case 4:
|
|
248
|
+
_context4.n = 5;
|
|
249
|
+
return this.walletClient.sendTransaction({
|
|
250
|
+
to: resolvedTo,
|
|
251
|
+
value: value
|
|
252
|
+
});
|
|
253
|
+
case 5:
|
|
254
|
+
hash = _context4.v;
|
|
255
|
+
_context4.n = 6;
|
|
256
|
+
return this.publicClient.waitForTransactionReceipt({
|
|
257
|
+
hash: hash
|
|
258
|
+
});
|
|
259
|
+
case 6:
|
|
260
|
+
receipt = _context4.v;
|
|
261
|
+
return _context4.a(2, {
|
|
262
|
+
hash: hash,
|
|
263
|
+
explorerUrl: "".concat(this.network.explorer, "/tx/").concat(hash),
|
|
264
|
+
success: receipt.status === "success",
|
|
265
|
+
type: "Standard Transaction"
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}, _callee4, this, [[2, 3]]);
|
|
269
|
+
}));
|
|
270
|
+
function sendTransaction(_x2, _x3) {
|
|
271
|
+
return _sendTransaction.apply(this, arguments);
|
|
272
|
+
}
|
|
273
|
+
return sendTransaction;
|
|
274
|
+
}()
|
|
275
|
+
/* ---------------------------------------------------------------------- */
|
|
276
|
+
/* EIP-7702 USER OPERATION */
|
|
277
|
+
/* ---------------------------------------------------------------------- */
|
|
278
|
+
)
|
|
279
|
+
}, {
|
|
280
|
+
key: "sendViaSmartAccount",
|
|
281
|
+
value: (function () {
|
|
282
|
+
var _sendViaSmartAccount = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(to, amount) {
|
|
283
|
+
var resolvedTo, clean, value, simpleAccount, smartClient, userOpHash, receipt, _t3;
|
|
284
|
+
return _regenerator().w(function (_context5) {
|
|
285
|
+
while (1) switch (_context5.p = _context5.n) {
|
|
286
|
+
case 0:
|
|
287
|
+
if (this.smartAccountEnabled) {
|
|
288
|
+
_context5.n = 1;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
throw new Error("Smart account not initialized");
|
|
292
|
+
case 1:
|
|
293
|
+
_context5.n = 2;
|
|
294
|
+
return this.resolveENS(to);
|
|
295
|
+
case 2:
|
|
296
|
+
resolvedTo = _context5.v;
|
|
297
|
+
clean = this._normalizeAmount(amount);
|
|
298
|
+
_context5.p = 3;
|
|
299
|
+
value = parseEther(clean);
|
|
300
|
+
_context5.n = 5;
|
|
301
|
+
break;
|
|
302
|
+
case 4:
|
|
303
|
+
_context5.p = 4;
|
|
304
|
+
_t3 = _context5.v;
|
|
305
|
+
throw new Error("Invalid amount: ".concat(_t3.message));
|
|
306
|
+
case 5:
|
|
307
|
+
_context5.n = 6;
|
|
308
|
+
return toSimpleSmartAccount({
|
|
309
|
+
client: this.publicClient,
|
|
310
|
+
owner: this.account,
|
|
311
|
+
entryPoint: {
|
|
312
|
+
address: this.network.entryPoint,
|
|
313
|
+
version: "0.8"
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
case 6:
|
|
317
|
+
simpleAccount = _context5.v;
|
|
318
|
+
smartClient = createSmartAccountClient({
|
|
319
|
+
account: simpleAccount,
|
|
320
|
+
chain: this.network.chain,
|
|
321
|
+
bundlerTransport: http(this.bundlerUrl),
|
|
322
|
+
entryPoint: {
|
|
323
|
+
address: this.network.entryPoint,
|
|
324
|
+
version: "0.8"
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
_context5.n = 7;
|
|
328
|
+
return smartClient.sendUserOperation({
|
|
329
|
+
calls: [{
|
|
330
|
+
to: resolvedTo,
|
|
331
|
+
value: value,
|
|
332
|
+
data: "0x"
|
|
333
|
+
}]
|
|
334
|
+
});
|
|
335
|
+
case 7:
|
|
336
|
+
userOpHash = _context5.v;
|
|
337
|
+
_context5.n = 8;
|
|
338
|
+
return this.bundlerClient.waitForUserOperationReceipt({
|
|
339
|
+
hash: userOpHash
|
|
340
|
+
});
|
|
341
|
+
case 8:
|
|
342
|
+
receipt = _context5.v;
|
|
343
|
+
return _context5.a(2, {
|
|
344
|
+
hash: receipt.receipt.transactionHash,
|
|
345
|
+
userOpHash: userOpHash,
|
|
346
|
+
explorerUrl: "".concat(this.network.explorer, "/tx/").concat(receipt.receipt.transactionHash),
|
|
347
|
+
success: receipt.success,
|
|
348
|
+
type: "EIP-7702 Smart Account"
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}, _callee5, this, [[3, 4]]);
|
|
352
|
+
}));
|
|
353
|
+
function sendViaSmartAccount(_x4, _x5) {
|
|
354
|
+
return _sendViaSmartAccount.apply(this, arguments);
|
|
355
|
+
}
|
|
356
|
+
return sendViaSmartAccount;
|
|
357
|
+
}() /* ---------------------------------------------------------------------- */)
|
|
358
|
+
}, {
|
|
359
|
+
key: "getExplorerUrl",
|
|
360
|
+
value: function getExplorerUrl(hash) {
|
|
361
|
+
return "".concat(this.network.explorer, "/tx/").concat(hash);
|
|
362
|
+
}
|
|
363
|
+
}]);
|
|
364
|
+
}();
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import meow from 'meow';
|
|
5
|
+
import App from './app.js';
|
|
6
|
+
const cli = meow(`
|
|
7
|
+
Usage
|
|
8
|
+
$ voice-defi
|
|
9
|
+
|
|
10
|
+
Commands
|
|
11
|
+
Start the CLI and use keyboard controls:
|
|
12
|
+
s - Start recording
|
|
13
|
+
q - Stop recording and transcribe
|
|
14
|
+
c - Clear transcript
|
|
15
|
+
x - Exit
|
|
16
|
+
|
|
17
|
+
Options
|
|
18
|
+
--api-key Your Groq API key (required)
|
|
19
|
+
--model Transcription model [default: whisper-large-v3-turbo]
|
|
20
|
+
--language Language code (optional, auto-detect by default)
|
|
21
|
+
|
|
22
|
+
Examples
|
|
23
|
+
$ voice-defi --api-key=gsk_...
|
|
24
|
+
$ voice-defi --api-key=gsk_... --model=whisper-large-v3
|
|
25
|
+
$ voice-defi --api-key=gsk_... --language=en
|
|
26
|
+
|
|
27
|
+
Get your API key from: https://console.groq.com/keys
|
|
28
|
+
|
|
29
|
+
Available models:
|
|
30
|
+
whisper-large-v3-turbo - Faster, cheaper ($0.04/hour)
|
|
31
|
+
whisper-large-v3 - More accurate ($0.111/hour)
|
|
32
|
+
`, {
|
|
33
|
+
importMeta: import.meta,
|
|
34
|
+
flags: {
|
|
35
|
+
apiKey: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
isRequired: false
|
|
38
|
+
},
|
|
39
|
+
model: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
default: 'whisper-large-v3-turbo'
|
|
42
|
+
},
|
|
43
|
+
language: {
|
|
44
|
+
type: 'string'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const apiKey = cli.flags.apiKey || process.env.GROQ_API_KEY;
|
|
49
|
+
if (!apiKey) {
|
|
50
|
+
console.error('❌ Error: --api-key is required (or set GROQ_API_KEY environment variable)');
|
|
51
|
+
console.log('Get your API key from: https://console.groq.com/keys');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
const validModels = ['whisper-large-v3-turbo', 'whisper-large-v3'];
|
|
55
|
+
if (!validModels.includes(cli.flags.model)) {
|
|
56
|
+
console.error(`⚠️ Warning: Model "${cli.flags.model}" may not be supported.`);
|
|
57
|
+
console.log(`Recommended models: ${validModels.join(', ')}`);
|
|
58
|
+
}
|
|
59
|
+
render(/*#__PURE__*/React.createElement(App, {
|
|
60
|
+
apiKey: apiKey,
|
|
61
|
+
model: cli.flags.model,
|
|
62
|
+
language: cli.flags.language
|
|
63
|
+
}));
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
3
|
+
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
4
|
+
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
6
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
+
import dotenv from 'dotenv';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
|
|
12
|
+
// Resolve file paths
|
|
13
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
var __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Project-level .env path
|
|
17
|
+
var envPath = join(__dirname, '..', '.env');
|
|
18
|
+
|
|
19
|
+
// Load .env at startup
|
|
20
|
+
if (fs.existsSync(envPath)) {
|
|
21
|
+
dotenv.config({
|
|
22
|
+
path: envPath
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
// Create one if missing
|
|
26
|
+
fs.writeFileSync(envPath, '');
|
|
27
|
+
dotenv.config({
|
|
28
|
+
path: envPath
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Helper: update .env file safely
|
|
33
|
+
function updateEnvFile(key, value) {
|
|
34
|
+
var content = fs.readFileSync(envPath, 'utf-8');
|
|
35
|
+
var newEntry = "".concat(key, "=").concat(value, "\n");
|
|
36
|
+
|
|
37
|
+
// Key exists → replace it
|
|
38
|
+
if (content.includes("".concat(key, "="))) {
|
|
39
|
+
var regex = new RegExp("".concat(key, "=.*"), 'g');
|
|
40
|
+
content = content.replace(regex, "".concat(key, "=").concat(value));
|
|
41
|
+
} else {
|
|
42
|
+
// Key missing → append to bottom
|
|
43
|
+
content += newEntry;
|
|
44
|
+
}
|
|
45
|
+
fs.writeFileSync(envPath, content);
|
|
46
|
+
}
|
|
47
|
+
var ConfigManager = /*#__PURE__*/function () {
|
|
48
|
+
function ConfigManager() {
|
|
49
|
+
_classCallCheck(this, ConfigManager);
|
|
50
|
+
}
|
|
51
|
+
return _createClass(ConfigManager, [{
|
|
52
|
+
key: "getGroqApiKey",
|
|
53
|
+
value:
|
|
54
|
+
// Groq API Key
|
|
55
|
+
function getGroqApiKey() {
|
|
56
|
+
return process.env.GROQ_API_KEY || null;
|
|
57
|
+
}
|
|
58
|
+
}, {
|
|
59
|
+
key: "setGroqApiKey",
|
|
60
|
+
value: function setGroqApiKey(apiKey) {
|
|
61
|
+
updateEnvFile('GROQ_API_KEY', apiKey);
|
|
62
|
+
process.env.GROQ_API_KEY = apiKey;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Private Key
|
|
66
|
+
}, {
|
|
67
|
+
key: "getPrivateKey",
|
|
68
|
+
value: function getPrivateKey() {
|
|
69
|
+
var key = process.env.PRIVATE_KEY || null;
|
|
70
|
+
if (key && !key.startsWith('0x')) {
|
|
71
|
+
key = "0x".concat(key);
|
|
72
|
+
}
|
|
73
|
+
return key;
|
|
74
|
+
}
|
|
75
|
+
}, {
|
|
76
|
+
key: "setPrivateKey",
|
|
77
|
+
value: function setPrivateKey(privateKey) {
|
|
78
|
+
var formatted = privateKey.startsWith('0x') ? privateKey : "0x".concat(privateKey);
|
|
79
|
+
updateEnvFile('PRIVATE_KEY', formatted);
|
|
80
|
+
process.env.PRIVATE_KEY = formatted;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Network
|
|
84
|
+
}, {
|
|
85
|
+
key: "getNetwork",
|
|
86
|
+
value: function getNetwork() {
|
|
87
|
+
return process.env.NETWORK || 'bnb_testnet';
|
|
88
|
+
}
|
|
89
|
+
}, {
|
|
90
|
+
key: "setNetwork",
|
|
91
|
+
value: function setNetwork(network) {
|
|
92
|
+
updateEnvFile('NETWORK', network);
|
|
93
|
+
process.env.NETWORK = network;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Bundler URL
|
|
97
|
+
}, {
|
|
98
|
+
key: "getBundlerUrl",
|
|
99
|
+
value: function getBundlerUrl() {
|
|
100
|
+
return process.env.BUNDLER_URL || null;
|
|
101
|
+
}
|
|
102
|
+
}, {
|
|
103
|
+
key: "setBundlerUrl",
|
|
104
|
+
value: function setBundlerUrl(url) {
|
|
105
|
+
updateEnvFile('BUNDLER_URL', url);
|
|
106
|
+
process.env.BUNDLER_URL = url;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Setup check
|
|
110
|
+
}, {
|
|
111
|
+
key: "isSetupComplete",
|
|
112
|
+
value: function isSetupComplete() {
|
|
113
|
+
return !!(this.getGroqApiKey() && this.getPrivateKey());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Reset .env completely
|
|
117
|
+
}, {
|
|
118
|
+
key: "clearAll",
|
|
119
|
+
value: function clearAll() {
|
|
120
|
+
fs.writeFileSync(envPath, '');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Debug info
|
|
124
|
+
}, {
|
|
125
|
+
key: "getAllConfig",
|
|
126
|
+
value: function getAllConfig() {
|
|
127
|
+
return {
|
|
128
|
+
hasGroqKey: !!this.getGroqApiKey(),
|
|
129
|
+
hasPrivateKey: !!this.getPrivateKey(),
|
|
130
|
+
network: this.getNetwork(),
|
|
131
|
+
hasBundlerUrl: !!this.getBundlerUrl()
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}]);
|
|
135
|
+
}();
|
|
136
|
+
export default new ConfigManager();
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "voice-wallet",
|
|
3
|
+
"version": "1.0.31",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"bin": "dist/cli.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=16"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "babel --out-dir=dist source",
|
|
12
|
+
"dev": "babel --out-dir=dist --watch source",
|
|
13
|
+
"test": "prettier --check . && xo && ava"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"groq-sdk": "^0.35.0",
|
|
20
|
+
"ink": "^4.4.1",
|
|
21
|
+
"meow": "^13.2.0",
|
|
22
|
+
"node-record-lpcm16": "^1.0.1",
|
|
23
|
+
"openai": "^6.9.0",
|
|
24
|
+
"react": "^18.2.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@babel/cli": "^7.21.0",
|
|
28
|
+
"@babel/preset-react": "^7.18.6",
|
|
29
|
+
"@vdemedes/prettier-config": "^2.0.1",
|
|
30
|
+
"ava": "^5.2.0",
|
|
31
|
+
"chalk": "^5.2.0",
|
|
32
|
+
"eslint-config-xo-react": "^0.27.0",
|
|
33
|
+
"eslint-plugin-react": "^7.32.2",
|
|
34
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
35
|
+
"import-jsx": "^5.0.0",
|
|
36
|
+
"ink-testing-library": "^3.0.0",
|
|
37
|
+
"prettier": "^2.8.7",
|
|
38
|
+
"xo": "^0.53.1"
|
|
39
|
+
},
|
|
40
|
+
"ava": {
|
|
41
|
+
"environmentVariables": {
|
|
42
|
+
"NODE_NO_WARNINGS": "1"
|
|
43
|
+
},
|
|
44
|
+
"nodeArguments": [
|
|
45
|
+
"--loader=import-jsx"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"xo": {
|
|
49
|
+
"extends": "xo-react",
|
|
50
|
+
"prettier": true,
|
|
51
|
+
"rules": {
|
|
52
|
+
"react/prop-types": "off"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"prettier": "@vdemedes/prettier-config",
|
|
56
|
+
"babel": {
|
|
57
|
+
"presets": [
|
|
58
|
+
"@babel/preset-react"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"opm": {
|
|
62
|
+
"signature": "0x1194da07d3bda70fbd815d7fb1a5e6cc9f89f00749a3c9fbfcb74bcda379a5c97c1fd16f8c925ac188456fee7614e0537caeb14df1d6fbd517945d5b11fd74cc1b",
|
|
63
|
+
"author": "0x541cC10D295671697FF7e8c841aF097ED0ea3802",
|
|
64
|
+
"ensName": "",
|
|
65
|
+
"checksum": "0xd23d684bfac5a18a1143b0da3a8a12d3c818945c1076309830113a0a9377184a"
|
|
66
|
+
}
|
|
67
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# voice-wallet
|
|
2
|
+
|
|
3
|
+
### Overview
|
|
4
|
+
|
|
5
|
+
BNB Voice Wallet is a voice controlled agent wallet for Web3. It allows users to send transactions, check balances and interact with the blockchain using natural voice commands. The goal is to provide a more accessible and intuitive experience while keeping actions secure and intent driven. This package makes it possible to run the wallet locally or integrate it into other agent systems.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g bnb-voice-wallet
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
or use it directly in a project:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install bnb-voice-wallet
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
NPM package:
|
|
22
|
+
[https://www.npmjs.com/package/bnb-voice-wallet](https://www.npmjs.com/package/bnb-voice-wallet)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### Environment Setup
|
|
27
|
+
|
|
28
|
+
Create a `.env` file in the project root:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
GROQ_API_KEY=gsk_YourGroqKey
|
|
32
|
+
PRIVATE_KEY=0xYourPrivateKey
|
|
33
|
+
NETWORK=bnb_testnet
|
|
34
|
+
BUNDLER_URL=https://your-bundler.example
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If you want separate environments, you can also create:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
.env.development
|
|
41
|
+
.env.production
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The CLI automatically loads `.env` if present.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### How to Run
|
|
49
|
+
|
|
50
|
+
Once installed globally:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
voice-wallet
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
You can then:
|
|
57
|
+
|
|
58
|
+
* speak or type commands
|
|
59
|
+
* send crypto to ENS or hex addresses
|
|
60
|
+
* request balances
|
|
61
|
+
* interact with the agent
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
Can you send 0.001 ETH to vitalik.eth
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
or:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
What is my balance
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### Project Structure
|
|
78
|
+
|
|
79
|
+
* `cli.js` handles the voice interface and command parsing
|
|
80
|
+
* `app.js` coordinates speech, AI and wallet operations
|
|
81
|
+
* `blockchain.js` manages ENS resolution and transactions
|
|
82
|
+
* `config.js` loads environment variables and manages local config
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### Prerequisite: SoX
|
|
87
|
+
|
|
88
|
+
This project requires SoX for audio recording.
|
|
89
|
+
|
|
90
|
+
**macOS**
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
brew install sox
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Windows**
|
|
97
|
+
Download from: [https://sourceforge.net/projects/sox/files/sox/](https://sourceforge.net/projects/sox/files/sox/)
|
|
98
|
+
Add the SoX `bin` directory to your system PATH.
|
|
99
|
+
|
|
100
|
+
**Linux (Debian/Ubuntu)**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
sudo apt-get update
|
|
104
|
+
sudo apt-get install sox libsox-fmt-all
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
To confirm installation:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
sox --version
|
|
111
|
+
```
|