httpcat-cli 0.3.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/.github/workflows/ci.yml +3 -0
- package/.github/workflows/rc-publish.yml +6 -0
- package/.github/workflows/release.yml +102 -0
- package/.github/workflows/sync-version.yml +31 -2
- package/README.md +1408 -109
- package/additions.txt +3 -0
- package/bun.lock +260 -25
- package/dist/agent/autonomous-trader.d.ts.map +1 -0
- package/dist/agent/autonomous-trader.js +362 -0
- package/dist/agent/autonomous-trader.js.map +1 -0
- package/dist/agent/ax-agent.d.ts.map +1 -1
- package/dist/agent/ax-agent.js +356 -18
- package/dist/agent/ax-agent.js.map +1 -1
- package/dist/agent/event-client.d.ts.map +1 -0
- package/dist/agent/event-client.js +82 -0
- package/dist/agent/event-client.js.map +1 -0
- package/dist/agent/log-stream.d.ts.map +1 -0
- package/dist/agent/log-stream.js +95 -0
- package/dist/agent/log-stream.js.map +1 -0
- package/dist/agent/memory/conversation-session.d.ts.map +1 -0
- package/dist/agent/memory/conversation-session.js +232 -0
- package/dist/agent/memory/conversation-session.js.map +1 -0
- package/dist/agent/memory/conversation-store.d.ts.map +1 -0
- package/dist/agent/memory/conversation-store.js +214 -0
- package/dist/agent/memory/conversation-store.js.map +1 -0
- package/dist/agent/memory/database-schema.d.ts.map +1 -0
- package/dist/agent/memory/database-schema.js +355 -0
- package/dist/agent/memory/database-schema.js.map +1 -0
- package/dist/agent/memory/decision-tracker.d.ts.map +1 -0
- package/dist/agent/memory/decision-tracker.js +274 -0
- package/dist/agent/memory/decision-tracker.js.map +1 -0
- package/dist/agent/memory/memory-manager.d.ts.map +1 -0
- package/dist/agent/memory/memory-manager.js +187 -0
- package/dist/agent/memory/memory-manager.js.map +1 -0
- package/dist/agent/memory/types.d.ts.map +1 -0
- package/dist/agent/memory/types.js +5 -0
- package/dist/agent/memory/types.js.map +1 -0
- package/dist/agent/message-formatter.d.ts.map +1 -0
- package/dist/agent/message-formatter.js +76 -0
- package/dist/agent/message-formatter.js.map +1 -0
- package/dist/agent/position-db.d.ts.map +1 -0
- package/dist/agent/position-db.js +154 -0
- package/dist/agent/position-db.js.map +1 -0
- package/dist/agent/simple-chat-ui-static.d.ts.map +1 -0
- package/dist/agent/simple-chat-ui-static.js +129 -0
- package/dist/agent/simple-chat-ui-static.js.map +1 -0
- package/dist/agent/simple-chat-ui.d.ts.map +1 -0
- package/dist/agent/simple-chat-ui.js +90 -0
- package/dist/agent/simple-chat-ui.js.map +1 -0
- package/dist/agent/tools.d.ts.map +1 -1
- package/dist/agent/tools.js +297 -4
- package/dist/agent/tools.js.map +1 -1
- package/dist/agent/ui.d.ts.map +1 -0
- package/dist/agent/ui.js +84 -0
- package/dist/agent/ui.js.map +1 -0
- package/dist/agent/unified-runtime.d.ts.map +1 -0
- package/dist/agent/unified-runtime.js +397 -0
- package/dist/agent/unified-runtime.js.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +272 -21
- package/dist/client.js.map +1 -1
- package/dist/commands/account.d.ts.map +1 -1
- package/dist/commands/account.js +187 -33
- package/dist/commands/account.js.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +125 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/approve.d.ts.map +1 -0
- package/dist/commands/approve.js +505 -0
- package/dist/commands/approve.js.map +1 -0
- package/dist/commands/automation.d.ts.map +1 -0
- package/dist/commands/automation.js +346 -0
- package/dist/commands/automation.js.map +1 -0
- package/dist/commands/balances.d.ts.map +1 -1
- package/dist/commands/balances.js +226 -73
- package/dist/commands/balances.js.map +1 -1
- package/dist/commands/buy.d.ts.map +1 -1
- package/dist/commands/buy.js +149 -146
- package/dist/commands/buy.js.map +1 -1
- package/dist/commands/call.d.ts.map +1 -0
- package/dist/commands/call.js +51 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/cex.d.ts.map +1 -0
- package/dist/commands/cex.js +958 -0
- package/dist/commands/cex.js.map +1 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +169 -411
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/claim.d.ts.map +1 -1
- package/dist/commands/claim.js +313 -29
- package/dist/commands/claim.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +151 -43
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/gasless-swap.d.ts.map +1 -0
- package/dist/commands/gasless-swap.js +232 -0
- package/dist/commands/gasless-swap.js.map +1 -0
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +63 -7
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +131 -47
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/launchpad.d.ts.map +1 -0
- package/dist/commands/launchpad.js +708 -0
- package/dist/commands/launchpad.js.map +1 -0
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +57 -23
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/market.d.ts.map +1 -0
- package/dist/commands/market.js +960 -0
- package/dist/commands/market.js.map +1 -0
- package/dist/commands/mcp-install.d.ts.map +1 -0
- package/dist/commands/mcp-install.js +387 -0
- package/dist/commands/mcp-install.js.map +1 -0
- package/dist/commands/opps.d.ts.map +1 -0
- package/dist/commands/opps.js +409 -0
- package/dist/commands/opps.js.map +1 -0
- package/dist/commands/perps.d.ts.map +1 -0
- package/dist/commands/perps.js +248 -0
- package/dist/commands/perps.js.map +1 -0
- package/dist/commands/portfolio.d.ts.map +1 -0
- package/dist/commands/portfolio.js +679 -0
- package/dist/commands/portfolio.js.map +1 -0
- package/dist/commands/positions.d.ts.map +1 -1
- package/dist/commands/positions.js +76 -47
- package/dist/commands/positions.js.map +1 -1
- package/dist/commands/predict.d.ts.map +1 -0
- package/dist/commands/predict.js +280 -0
- package/dist/commands/predict.js.map +1 -0
- package/dist/commands/predictions.d.ts.map +1 -0
- package/dist/commands/predictions.js +486 -0
- package/dist/commands/predictions.js.map +1 -0
- package/dist/commands/risk.d.ts.map +1 -0
- package/dist/commands/risk.js +225 -0
- package/dist/commands/risk.js.map +1 -0
- package/dist/commands/security.d.ts.map +1 -0
- package/dist/commands/security.js +244 -0
- package/dist/commands/security.js.map +1 -0
- package/dist/commands/sell.d.ts.map +1 -1
- package/dist/commands/sell.js +67 -34
- package/dist/commands/sell.js.map +1 -1
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +733 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/sign.d.ts.map +1 -0
- package/dist/commands/sign.js +1048 -0
- package/dist/commands/sign.js.map +1 -0
- package/dist/commands/swap.d.ts.map +1 -0
- package/dist/commands/swap.js +744 -0
- package/dist/commands/swap.js.map +1 -0
- package/dist/commands/system.d.ts.map +1 -0
- package/dist/commands/system.js +417 -0
- package/dist/commands/system.js.map +1 -0
- package/dist/commands/tools/index.d.ts.map +1 -0
- package/dist/commands/tools/index.js +2040 -0
- package/dist/commands/tools/index.js.map +1 -0
- package/dist/commands/trade.d.ts.map +1 -0
- package/dist/commands/trade.js +237 -0
- package/dist/commands/trade.js.map +1 -0
- package/dist/commands/transactions.d.ts.map +1 -1
- package/dist/commands/transactions.js +29 -17
- package/dist/commands/transactions.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +429 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +351 -40
- package/dist/config.js.map +1 -1
- package/dist/index.js +4524 -924
- package/dist/index.js.map +1 -1
- package/dist/interactive/art.d.ts.map +1 -1
- package/dist/interactive/art.js +33 -1
- package/dist/interactive/art.js.map +1 -1
- package/dist/interactive/shell.d.ts.map +1 -1
- package/dist/interactive/shell.js +467 -2652
- package/dist/interactive/shell.js.map +1 -1
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/context.js +211 -0
- package/dist/mcp/context.js.map +1 -0
- package/dist/mcp/onboarding.d.ts.map +1 -0
- package/dist/mcp/onboarding.js +266 -0
- package/dist/mcp/onboarding.js.map +1 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +222 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +51 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +4119 -169
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/types/agent-info.d.ts.map +1 -0
- package/dist/types/agent-info.js +11 -0
- package/dist/types/agent-info.js.map +1 -0
- package/dist/ui/components/ScrollableList.d.ts.map +1 -0
- package/dist/ui/components/ScrollableList.js +72 -0
- package/dist/ui/components/ScrollableList.js.map +1 -0
- package/dist/ui/components/ThemeProvider.d.ts.map +1 -0
- package/dist/ui/components/ThemeProvider.js +87 -0
- package/dist/ui/components/ThemeProvider.js.map +1 -0
- package/dist/ui/components/ThemedBox.d.ts.map +1 -0
- package/dist/ui/components/ThemedBox.js +24 -0
- package/dist/ui/components/ThemedBox.js.map +1 -0
- package/dist/ui/components/agent/ChatHeader.d.ts.map +1 -0
- package/dist/ui/components/agent/ChatHeader.js +39 -0
- package/dist/ui/components/agent/ChatHeader.js.map +1 -0
- package/dist/ui/components/agent/Header.d.ts.map +1 -0
- package/dist/ui/components/agent/Header.js +14 -0
- package/dist/ui/components/agent/Header.js.map +1 -0
- package/dist/ui/components/agent/Input.d.ts.map +1 -0
- package/dist/ui/components/agent/Input.js +23 -0
- package/dist/ui/components/agent/Input.js.map +1 -0
- package/dist/ui/components/agent/Output.d.ts.map +1 -0
- package/dist/ui/components/agent/Output.js +23 -0
- package/dist/ui/components/agent/Output.js.map +1 -0
- package/dist/ui/components/chat/TokenChatUI.d.ts.map +1 -0
- package/dist/ui/components/chat/TokenChatUI.js +133 -0
- package/dist/ui/components/chat/TokenChatUI.js.map +1 -0
- package/dist/ui/components/shell/ShellHeader.d.ts.map +1 -0
- package/dist/ui/components/shell/ShellHeader.js +31 -0
- package/dist/ui/components/shell/ShellHeader.js.map +1 -0
- package/dist/ui/components/shell/ShellInput.d.ts.map +1 -0
- package/dist/ui/components/shell/ShellInput.js +151 -0
- package/dist/ui/components/shell/ShellInput.js.map +1 -0
- package/dist/ui/components/shell/ShellOutput.d.ts.map +1 -0
- package/dist/ui/components/shell/ShellOutput.js +8 -0
- package/dist/ui/components/shell/ShellOutput.js.map +1 -0
- package/dist/ui/hooks/useChatWebSocket.d.ts.map +1 -0
- package/dist/ui/hooks/useChatWebSocket.js +76 -0
- package/dist/ui/hooks/useChatWebSocket.js.map +1 -0
- package/dist/ui/hooks/useCommandHistory.d.ts.map +1 -0
- package/dist/ui/hooks/useCommandHistory.js +70 -0
- package/dist/ui/hooks/useCommandHistory.js.map +1 -0
- package/dist/ui/hooks/useDebounce.d.ts.map +1 -0
- package/dist/ui/hooks/useDebounce.js +17 -0
- package/dist/ui/hooks/useDebounce.js.map +1 -0
- package/dist/ui/hooks/useLogStream.d.ts.map +1 -0
- package/dist/ui/hooks/useLogStream.js +20 -0
- package/dist/ui/hooks/useLogStream.js.map +1 -0
- package/dist/ui/hooks/useVirtualScroll.d.ts.map +1 -0
- package/dist/ui/hooks/useVirtualScroll.js +70 -0
- package/dist/ui/hooks/useVirtualScroll.js.map +1 -0
- package/dist/utils/admin.d.ts.map +1 -0
- package/dist/utils/admin.js +144 -0
- package/dist/utils/admin.js.map +1 -0
- package/dist/utils/autoSetup.d.ts.map +1 -0
- package/dist/utils/autoSetup.js +252 -0
- package/dist/utils/autoSetup.js.map +1 -0
- package/dist/utils/build-constants.d.ts.map +1 -0
- package/dist/utils/build-constants.js +10 -0
- package/dist/utils/build-constants.js.map +1 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +10 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/formatting.js +46 -9
- package/dist/utils/formatting.js.map +1 -1
- package/dist/utils/llm-cli-config.d.ts.map +1 -0
- package/dist/utils/llm-cli-config.js +963 -0
- package/dist/utils/llm-cli-config.js.map +1 -0
- package/dist/utils/llm-cli-detector.d.ts.map +1 -0
- package/dist/utils/llm-cli-detector.js +202 -0
- package/dist/utils/llm-cli-detector.js.map +1 -0
- package/dist/utils/loading.d.ts.map +1 -1
- package/dist/utils/loading.js +25 -3
- package/dist/utils/loading.js.map +1 -1
- package/dist/utils/maintenance.d.ts.map +1 -0
- package/dist/utils/maintenance.js +17 -0
- package/dist/utils/maintenance.js.map +1 -0
- package/dist/utils/mcp-config.d.ts.map +1 -0
- package/dist/utils/mcp-config.js +77 -0
- package/dist/utils/mcp-config.js.map +1 -0
- package/dist/utils/privateKeyPrompt.d.ts.map +1 -1
- package/dist/utils/privateKeyPrompt.js +308 -129
- package/dist/utils/privateKeyPrompt.js.map +1 -1
- package/dist/utils/process-cleanup.d.ts.map +1 -0
- package/dist/utils/process-cleanup.js +136 -0
- package/dist/utils/process-cleanup.js.map +1 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +56 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/rpc-helpers.d.ts.map +1 -0
- package/dist/utils/rpc-helpers.js +70 -0
- package/dist/utils/rpc-helpers.js.map +1 -0
- package/dist/utils/rpc-transport.d.ts.map +1 -0
- package/dist/utils/rpc-transport.js +87 -0
- package/dist/utils/rpc-transport.js.map +1 -0
- package/dist/utils/shell-setup.d.ts.map +1 -0
- package/dist/utils/shell-setup.js +531 -0
- package/dist/utils/shell-setup.js.map +1 -0
- package/dist/utils/status.d.ts.map +1 -1
- package/dist/utils/status.js +34 -5
- package/dist/utils/status.js.map +1 -1
- package/dist/utils/token-resolver.d.ts.map +1 -1
- package/dist/utils/token-resolver.js +51 -8
- package/dist/utils/token-resolver.js.map +1 -1
- package/dist/utils/x402-caller.d.ts.map +1 -0
- package/dist/utils/x402-caller.js +17 -0
- package/dist/utils/x402-caller.js.map +1 -0
- package/docs/README.md +28 -0
- package/docs/agent/README.md +18 -0
- package/docs/api/README.md +41 -0
- package/docs/cli/README.md +42 -0
- package/docs/guides/README.md +26 -0
- package/docs/implementation/README.md +18 -0
- package/docs/planning/README.md +19 -0
- package/docs/testing/README.md +15 -0
- package/docs/ux/README.md +16 -0
- package/issues.txt +2 -0
- package/package.json +24 -9
- package/scripts/cat-spin.sh +417 -0
- package/scripts/deprecate-rc-versions.js +58 -0
- package/scripts/inject-build-constants.js +43 -0
- package/scripts/monitor-foobar.js +117 -0
- package/swap.logs +61 -0
- package/swapping.txt +108 -0
- package/test.txt +12 -0
- package/tests/fixtures/test-data.json +16 -0
- package/Screenshot 2025-12-21 at 8.56.02/342/200/257PM.png +0 -0
package/dist/commands/chat.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
|
|
4
|
-
import blessed from "neo-blessed";
|
|
3
|
+
import { createTokenChatUI } from "../ui/components/chat/TokenChatUI.js";
|
|
5
4
|
import { formatAddress, formatTokenAmount, formatCurrency, } from "../utils/formatting.js";
|
|
6
5
|
import { handleError } from "../utils/errors.js";
|
|
7
6
|
import { config } from "../config.js";
|
|
@@ -10,9 +9,9 @@ import { privateKeyToAccount } from "viem/accounts";
|
|
|
10
9
|
import { buyToken } from "./buy.js";
|
|
11
10
|
import { sellToken, parseTokenAmount } from "./sell.js";
|
|
12
11
|
import { getTokenInfo } from "./info.js";
|
|
13
|
-
const CHAT_JOIN_ENTRYPOINT = "
|
|
14
|
-
const CHAT_MESSAGE_ENTRYPOINT = "
|
|
15
|
-
const CHAT_RENEW_LEASE_ENTRYPOINT = "
|
|
12
|
+
const CHAT_JOIN_ENTRYPOINT = "chat/join";
|
|
13
|
+
const CHAT_MESSAGE_ENTRYPOINT = "chat/message";
|
|
14
|
+
const CHAT_RENEW_LEASE_ENTRYPOINT = "chat/renew-lease";
|
|
16
15
|
const LEASE_DURATION_MS = 10 * 60 * 1000; // 10 minutes
|
|
17
16
|
/**
|
|
18
17
|
* Normalize WebSocket URL based on agent URL
|
|
@@ -146,17 +145,16 @@ function formatMessageText(msg, isOwn = false) {
|
|
|
146
145
|
const authorShort = msg.authorShort || formatAddress(msg.author, 6);
|
|
147
146
|
return `${chalk.dim(`[${timeStr}]`)} ${authorColor(authorShort)}: ${messageColor(msg.message)}`;
|
|
148
147
|
}
|
|
149
|
-
function displayMessage(msg, isOwn = false, isPending = false,
|
|
148
|
+
function displayMessage(msg, isOwn = false, isPending = false, chatUI) {
|
|
150
149
|
// Skip if we've already displayed this message
|
|
151
150
|
if (displayedMessageIds.has(msg.messageId)) {
|
|
152
151
|
return;
|
|
153
152
|
}
|
|
154
153
|
displayedMessageIds.add(msg.messageId);
|
|
155
154
|
const messageText = formatMessageText(msg, isOwn);
|
|
156
|
-
if (
|
|
157
|
-
// Use
|
|
158
|
-
|
|
159
|
-
messageLogBox.setScrollPerc(100); // Auto-scroll to bottom
|
|
155
|
+
if (chatUI) {
|
|
156
|
+
// Use Ink chat UI
|
|
157
|
+
chatUI.log(messageText);
|
|
160
158
|
}
|
|
161
159
|
else {
|
|
162
160
|
// Simple console.log when no UI active (JSON mode)
|
|
@@ -207,56 +205,11 @@ function formatSellResultCompact(result) {
|
|
|
207
205
|
`new price ${chalk.cyan(formatCurrency(result.newPrice))}, ` +
|
|
208
206
|
`graduation ${chalk.magenta(graduationStatus)}`);
|
|
209
207
|
}
|
|
210
|
-
function
|
|
211
|
-
const title = tokenName
|
|
212
|
-
? `💬 httpcat Chat: ${tokenName}`
|
|
213
|
-
: "💬 httpcat Chat Stream";
|
|
214
|
-
// Get terminal width, default to 80 if not available
|
|
215
|
-
// Account for padding (left + right = 2)
|
|
216
|
-
const terminalWidth = process.stdout.columns || 80;
|
|
217
|
-
const boxWidth = headerBox.width;
|
|
218
|
-
const availableWidth = typeof boxWidth === "number" ? boxWidth - 2 : terminalWidth - 2;
|
|
219
|
-
const separatorWidth = Math.max(20, Math.min(availableWidth, terminalWidth - 2));
|
|
220
|
-
// Use safe separator that won't overflow
|
|
221
|
-
const separator = "═".repeat(separatorWidth);
|
|
222
|
-
const lineSeparator = "─".repeat(separatorWidth);
|
|
223
|
-
// Build content line by line - use plain text (blessed will handle wrapping)
|
|
224
|
-
const lines = [];
|
|
225
|
-
lines.push(title);
|
|
226
|
-
lines.push(separator);
|
|
227
|
-
lines.push("");
|
|
228
|
-
if (isConnected) {
|
|
229
|
-
lines.push("✅ Connected to chat stream");
|
|
230
|
-
lines.push("");
|
|
231
|
-
}
|
|
232
|
-
lines.push("💰 Entry fee: $0.01 USDC (10 min lease)");
|
|
233
|
-
lines.push("💬 Per message: $0.01 USDC");
|
|
234
|
-
if (leaseInfo) {
|
|
235
|
-
const timeRemaining = formatTimeRemaining(leaseInfo.leaseExpiresAt);
|
|
236
|
-
const isExpired = leaseInfo.leaseExpiresAt.getTime() <= Date.now();
|
|
237
|
-
const timePrefix = isExpired ? "⚠️ " : "⏱️ ";
|
|
238
|
-
lines.push(`${timePrefix}Lease expires in: ${timeRemaining}`);
|
|
239
|
-
}
|
|
240
|
-
lines.push("");
|
|
241
|
-
lines.push("💡 Type your message and press Enter to send");
|
|
242
|
-
lines.push("💡 Type /exit or Ctrl+C to quit");
|
|
243
|
-
lines.push("💡 Type /renew to renew your lease");
|
|
244
|
-
if (tokenName) {
|
|
245
|
-
lines.push("💡 Type /buy <amount> to buy tokens");
|
|
246
|
-
lines.push("💡 Type /sell <amount> to sell tokens");
|
|
247
|
-
}
|
|
248
|
-
lines.push("");
|
|
249
|
-
lines.push(lineSeparator);
|
|
250
|
-
// Clear and set content to prevent overlapping
|
|
251
|
-
headerBox.setContent(lines.join("\n"));
|
|
252
|
-
}
|
|
253
|
-
async function ensureLeaseValid(client, userAddress, leaseInfo, messageLogBox, screen) {
|
|
208
|
+
async function ensureLeaseValid(client, userAddress, leaseInfo, chatUI) {
|
|
254
209
|
if (!leaseInfo || leaseInfo.leaseExpiresAt.getTime() <= Date.now()) {
|
|
255
210
|
// Lease expired or doesn't exist, renew it
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
messageLogBox.setScrollPerc(100);
|
|
259
|
-
screen?.render();
|
|
211
|
+
if (chatUI) {
|
|
212
|
+
chatUI.log(chalk.yellow("⏱️ Lease expired. Renewing..."));
|
|
260
213
|
}
|
|
261
214
|
else {
|
|
262
215
|
clearLine();
|
|
@@ -270,26 +223,21 @@ async function ensureLeaseValid(client, userAddress, leaseInfo, messageLogBox, s
|
|
|
270
223
|
}
|
|
271
224
|
return leaseInfo;
|
|
272
225
|
}
|
|
273
|
-
export async function startChatStream(client, jsonMode = false, tokenIdentifier, inputFormat = "text") {
|
|
226
|
+
export async function startChatStream(client, jsonMode = false, tokenIdentifier, inputFormat = "text", returnToShell = false) {
|
|
274
227
|
let leaseInfo = null;
|
|
275
228
|
let userAddress;
|
|
276
229
|
let tokenAddress; // Track token address from join response
|
|
277
230
|
let ws = null;
|
|
278
231
|
let wsUrl = null; // Store websocket URL for reconnection
|
|
279
232
|
let leaseCheckInterval = null;
|
|
280
|
-
let headerUpdateInterval = null;
|
|
281
233
|
let isExiting = false;
|
|
282
234
|
let isSending = false; // Track sending state across handlers
|
|
283
235
|
let currentInput = ""; // Track current input across handlers
|
|
284
236
|
let pendingMessages = new Map(); // messageId -> tempMessageId (shared between send and receive)
|
|
285
237
|
let pulseIntervals = new Map(); // tempMessageId -> interval
|
|
286
238
|
let isCleaningUp = false; // Flag to prevent intervals from writing during cleanup
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
let screen = null;
|
|
290
|
-
let headerBox = null;
|
|
291
|
-
let messageLogBox = null;
|
|
292
|
-
let inputBox = null;
|
|
239
|
+
// Ink UI component (only used in non-JSON mode)
|
|
240
|
+
let chatUI = null;
|
|
293
241
|
// Get userAddress from private key first (required for all chat operations)
|
|
294
242
|
try {
|
|
295
243
|
const privateKey = config.getPrivateKey();
|
|
@@ -308,6 +256,7 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
308
256
|
throw new Error("Failed to get user address from private key. Please check your configuration.");
|
|
309
257
|
}
|
|
310
258
|
// Join chat
|
|
259
|
+
let joinResult;
|
|
311
260
|
try {
|
|
312
261
|
if (!jsonMode) {
|
|
313
262
|
if (tokenIdentifier) {
|
|
@@ -317,7 +266,7 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
317
266
|
console.log(chalk.dim("Joining general chat room..."));
|
|
318
267
|
}
|
|
319
268
|
}
|
|
320
|
-
|
|
269
|
+
joinResult = await joinChat(client, userAddress, tokenIdentifier, jsonMode);
|
|
321
270
|
leaseInfo = {
|
|
322
271
|
leaseId: joinResult.leaseId,
|
|
323
272
|
leaseExpiresAt: new Date(joinResult.leaseExpiresAt),
|
|
@@ -353,10 +302,11 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
353
302
|
// Helper function to attach websocket handlers
|
|
354
303
|
const attachWebSocketHandlers = (websocket) => {
|
|
355
304
|
websocket.on("open", () => {
|
|
356
|
-
// Connection established - update header if using
|
|
357
|
-
if (!jsonMode &&
|
|
358
|
-
|
|
359
|
-
|
|
305
|
+
// Connection established - update header if using Ink UI
|
|
306
|
+
if (!jsonMode && chatUI && leaseInfo) {
|
|
307
|
+
chatUI.updateHeader({
|
|
308
|
+
leaseExpiresAt: leaseInfo.leaseExpiresAt,
|
|
309
|
+
});
|
|
360
310
|
}
|
|
361
311
|
});
|
|
362
312
|
websocket.on("message", async (data) => {
|
|
@@ -406,33 +356,19 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
406
356
|
}
|
|
407
357
|
// Small delay to ensure intervals have stopped
|
|
408
358
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
409
|
-
// Stop pulsing and clear the grey pulsing input line
|
|
410
|
-
if (inputBox) {
|
|
411
|
-
// Clear input box
|
|
412
|
-
inputBox.clearValue();
|
|
413
|
-
screen?.render();
|
|
414
|
-
}
|
|
415
359
|
// Display the message (only if not already displayed)
|
|
416
|
-
// In JSON mode, displayMessage should never be called, but add explicit check
|
|
417
360
|
if (!displayedMessageIds.has(msg.messageId)) {
|
|
418
|
-
displayMessage(msg, isOwn, false,
|
|
361
|
+
displayMessage(msg, isOwn, false, chatUI || undefined);
|
|
419
362
|
}
|
|
420
|
-
// Re-enable input
|
|
363
|
+
// Re-enable input
|
|
421
364
|
isSending = false;
|
|
422
365
|
currentInput = "";
|
|
423
366
|
isCleaningUp = false; // Clear cleanup flag
|
|
424
|
-
if (inputBox && screen) {
|
|
425
|
-
// Clear input box and refocus
|
|
426
|
-
inputBox.clearValue();
|
|
427
|
-
inputBox.focus();
|
|
428
|
-
screen.render();
|
|
429
|
-
}
|
|
430
367
|
}
|
|
431
368
|
else {
|
|
432
369
|
// Not our message, just display it normally
|
|
433
|
-
// In JSON mode, displayMessage should never be called, but add explicit check
|
|
434
370
|
if (!displayedMessageIds.has(msg.messageId)) {
|
|
435
|
-
displayMessage(msg, isOwn, false,
|
|
371
|
+
displayMessage(msg, isOwn, false, chatUI || undefined);
|
|
436
372
|
}
|
|
437
373
|
}
|
|
438
374
|
}
|
|
@@ -444,16 +380,15 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
444
380
|
}));
|
|
445
381
|
}
|
|
446
382
|
else {
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
messageLogBox.setScrollPerc(100);
|
|
450
|
-
screen?.render();
|
|
383
|
+
if (chatUI) {
|
|
384
|
+
chatUI.log(chalk.yellow("⏱️ Your lease has expired. Type /renew to continue chatting."));
|
|
451
385
|
}
|
|
452
386
|
}
|
|
453
387
|
leaseInfo = null;
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
|
|
388
|
+
if (chatUI) {
|
|
389
|
+
chatUI.updateHeader({
|
|
390
|
+
leaseExpiresAt: undefined,
|
|
391
|
+
});
|
|
457
392
|
}
|
|
458
393
|
}
|
|
459
394
|
else if (event.type === "error") {
|
|
@@ -464,10 +399,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
464
399
|
}));
|
|
465
400
|
}
|
|
466
401
|
else {
|
|
467
|
-
if (
|
|
468
|
-
|
|
469
|
-
messageLogBox.setScrollPerc(100);
|
|
470
|
-
screen?.render();
|
|
402
|
+
if (chatUI) {
|
|
403
|
+
chatUI.log(chalk.red(`❌ Error: ${event.error || "Unknown error"}`));
|
|
471
404
|
}
|
|
472
405
|
}
|
|
473
406
|
}
|
|
@@ -488,10 +421,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
488
421
|
}
|
|
489
422
|
else {
|
|
490
423
|
const errorMsg = error.message || "Unknown WebSocket error";
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
messageLogBox.setScrollPerc(100);
|
|
494
|
-
screen?.render();
|
|
424
|
+
if (chatUI) {
|
|
425
|
+
chatUI.log(chalk.red(`❌ WebSocket error: ${errorMsg}`));
|
|
495
426
|
}
|
|
496
427
|
}
|
|
497
428
|
});
|
|
@@ -518,9 +449,10 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
518
449
|
message = `⚠️ Lease validation failed on server. Your lease appears valid. Type /renew to get a fresh lease.`;
|
|
519
450
|
}
|
|
520
451
|
leaseInfo = null; // Mark lease as invalid
|
|
521
|
-
if (
|
|
522
|
-
|
|
523
|
-
|
|
452
|
+
if (chatUI) {
|
|
453
|
+
chatUI.updateHeader({
|
|
454
|
+
leaseExpiresAt: undefined,
|
|
455
|
+
});
|
|
524
456
|
}
|
|
525
457
|
}
|
|
526
458
|
else if (code === 1006) {
|
|
@@ -529,10 +461,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
529
461
|
else {
|
|
530
462
|
message = `⚠️ WebSocket connection closed (code: ${code}): ${reasonStr}`;
|
|
531
463
|
}
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
messageLogBox.setScrollPerc(100);
|
|
535
|
-
screen?.render();
|
|
464
|
+
if (chatUI) {
|
|
465
|
+
chatUI.log(chalk.yellow(message));
|
|
536
466
|
}
|
|
537
467
|
}
|
|
538
468
|
});
|
|
@@ -549,12 +479,9 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
549
479
|
if (retryCount > 0) {
|
|
550
480
|
// Exponential backoff: 500ms, 1000ms, 2000ms
|
|
551
481
|
const backoffDelay = Math.min(500 * Math.pow(2, retryCount - 1), 2000);
|
|
482
|
+
// Note: UI not created yet, so can't log to it
|
|
552
483
|
if (!jsonMode) {
|
|
553
|
-
|
|
554
|
-
messageLogBox.log(chalk.yellow(`🔄 Retrying WebSocket connection (attempt ${retryCount + 1}/${maxRetries})...`));
|
|
555
|
-
messageLogBox.setScrollPerc(100);
|
|
556
|
-
screen?.render();
|
|
557
|
-
}
|
|
484
|
+
console.log(chalk.yellow(`🔄 Retrying WebSocket connection (attempt ${retryCount + 1}/${maxRetries})...`));
|
|
558
485
|
}
|
|
559
486
|
await new Promise((resolve) => setTimeout(resolve, backoffDelay));
|
|
560
487
|
}
|
|
@@ -610,10 +537,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
610
537
|
});
|
|
611
538
|
connected = true;
|
|
612
539
|
// Connection established successfully
|
|
613
|
-
if (!jsonMode &&
|
|
614
|
-
|
|
615
|
-
messageLogBox.setScrollPerc(100);
|
|
616
|
-
screen?.render();
|
|
540
|
+
if (!jsonMode && retryCount > 0) {
|
|
541
|
+
console.log(chalk.green("✅ WebSocket connected successfully"));
|
|
617
542
|
}
|
|
618
543
|
}
|
|
619
544
|
catch (error) {
|
|
@@ -632,12 +557,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
632
557
|
}));
|
|
633
558
|
}
|
|
634
559
|
else {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
messageLogBox.log(chalk.yellow("💡 Try typing /renew to get a fresh lease"));
|
|
638
|
-
messageLogBox.setScrollPerc(100);
|
|
639
|
-
screen?.render();
|
|
640
|
-
}
|
|
560
|
+
console.log(chalk.red(`❌ ${finalError}`));
|
|
561
|
+
console.log(chalk.yellow("💡 Try typing /renew to get a fresh lease"));
|
|
641
562
|
}
|
|
642
563
|
throw new Error(finalError);
|
|
643
564
|
}
|
|
@@ -656,135 +577,52 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
656
577
|
message: "Your lease has expired. Use /renew to continue chatting.",
|
|
657
578
|
}));
|
|
658
579
|
}
|
|
659
|
-
else if (
|
|
660
|
-
|
|
661
|
-
messageLogBox.setScrollPerc(100);
|
|
662
|
-
screen?.render();
|
|
580
|
+
else if (chatUI) {
|
|
581
|
+
chatUI.log(chalk.yellow("⏱️ Your lease has expired. Type /renew to continue chatting."));
|
|
663
582
|
}
|
|
664
583
|
leaseInfo = null;
|
|
665
|
-
if (
|
|
666
|
-
|
|
667
|
-
|
|
584
|
+
if (chatUI) {
|
|
585
|
+
chatUI.updateHeader({
|
|
586
|
+
leaseExpiresAt: undefined,
|
|
587
|
+
});
|
|
668
588
|
}
|
|
669
589
|
}
|
|
670
590
|
}, 30000);
|
|
671
|
-
// Set up
|
|
591
|
+
// Set up Ink UI (only in interactive mode)
|
|
672
592
|
if (!jsonMode) {
|
|
673
|
-
// Check if terminal supports
|
|
593
|
+
// Check if terminal supports TTY
|
|
674
594
|
if (!process.stdout.isTTY) {
|
|
675
595
|
throw new Error("Chat requires an interactive terminal (TTY). Use --json mode for non-interactive usage.");
|
|
676
596
|
}
|
|
677
|
-
//
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
headerBox = blessed.box({
|
|
689
|
-
top: 0,
|
|
690
|
-
left: 0,
|
|
691
|
-
width: "100%",
|
|
692
|
-
height: headerHeight,
|
|
693
|
-
content: "",
|
|
694
|
-
tags: false, // Disable tags to avoid rendering issues
|
|
695
|
-
wrap: true,
|
|
696
|
-
scrollable: false,
|
|
697
|
-
alwaysScroll: false,
|
|
698
|
-
padding: {
|
|
699
|
-
left: 1,
|
|
700
|
-
right: 1,
|
|
701
|
-
top: 0,
|
|
702
|
-
bottom: 0,
|
|
703
|
-
},
|
|
704
|
-
style: {
|
|
705
|
-
fg: "white",
|
|
706
|
-
bg: "black",
|
|
707
|
-
},
|
|
708
|
-
});
|
|
709
|
-
// Create message log box (scrollable, middle section)
|
|
710
|
-
messageLogBox = blessed.log({
|
|
711
|
-
top: headerHeight,
|
|
712
|
-
left: 0,
|
|
713
|
-
width: "100%",
|
|
714
|
-
height: `100%-${headerHeight + inputHeight}`,
|
|
715
|
-
tags: true,
|
|
716
|
-
scrollable: true,
|
|
717
|
-
alwaysScroll: true,
|
|
718
|
-
scrollbar: {
|
|
719
|
-
ch: " ",
|
|
720
|
-
inverse: true,
|
|
721
|
-
},
|
|
722
|
-
style: {
|
|
723
|
-
fg: "white",
|
|
724
|
-
bg: "black",
|
|
725
|
-
},
|
|
726
|
-
});
|
|
727
|
-
// Create input box (fixed at bottom)
|
|
728
|
-
inputBox = blessed.textbox({
|
|
729
|
-
bottom: 0,
|
|
730
|
-
left: 0,
|
|
731
|
-
width: "100%",
|
|
732
|
-
height: inputHeight,
|
|
733
|
-
content: "",
|
|
734
|
-
inputOnFocus: true,
|
|
735
|
-
tags: true,
|
|
736
|
-
keys: true,
|
|
737
|
-
style: {
|
|
738
|
-
fg: "cyan",
|
|
739
|
-
bg: "black",
|
|
740
|
-
focus: {
|
|
741
|
-
fg: "white",
|
|
742
|
-
bg: "blue",
|
|
743
|
-
},
|
|
744
|
-
},
|
|
745
|
-
});
|
|
746
|
-
// Append widgets to screen
|
|
747
|
-
screen.append(headerBox);
|
|
748
|
-
screen.append(messageLogBox);
|
|
749
|
-
screen.append(inputBox);
|
|
750
|
-
// Initial render
|
|
751
|
-
screen.render();
|
|
752
|
-
// Initial header update
|
|
753
|
-
updateHeaderBox(headerBox, leaseInfo, tokenIdentifier, false);
|
|
754
|
-
screen.render();
|
|
755
|
-
// Display last messages
|
|
756
|
-
if (joinResult.lastMessages.length > 0) {
|
|
757
|
-
const sortedMessages = [...joinResult.lastMessages].sort((a, b) => {
|
|
758
|
-
const timeA = new Date(a.timestamp).getTime();
|
|
759
|
-
const timeB = new Date(b.timestamp).getTime();
|
|
760
|
-
return timeA - timeB; // Oldest first
|
|
761
|
-
});
|
|
762
|
-
sortedMessages.forEach((msg) => {
|
|
763
|
-
displayedMessageIds.add(msg.messageId);
|
|
764
|
-
const isOwn = msg.author === userAddress;
|
|
765
|
-
// This is inside !jsonMode block, but add explicit check for safety
|
|
766
|
-
displayMessage(msg, isOwn, false, messageLogBox || undefined);
|
|
767
|
-
});
|
|
597
|
+
// Get token info for header
|
|
598
|
+
let tokenSymbol;
|
|
599
|
+
if (tokenIdentifier) {
|
|
600
|
+
try {
|
|
601
|
+
const tokenInfo = await getTokenInfo(client, tokenIdentifier, userAddress, true);
|
|
602
|
+
tokenSymbol = tokenInfo.symbol;
|
|
603
|
+
}
|
|
604
|
+
catch {
|
|
605
|
+
// Use identifier if we can't get symbol
|
|
606
|
+
tokenSymbol = tokenIdentifier;
|
|
607
|
+
}
|
|
768
608
|
}
|
|
769
|
-
//
|
|
770
|
-
|
|
771
|
-
|
|
609
|
+
// Format initial messages
|
|
610
|
+
const initialMessages = joinResult.lastMessages
|
|
611
|
+
.sort((a, b) => {
|
|
612
|
+
const timeA = new Date(a.timestamp).getTime();
|
|
613
|
+
const timeB = new Date(b.timestamp).getTime();
|
|
614
|
+
return timeA - timeB; // Oldest first
|
|
615
|
+
})
|
|
616
|
+
.map((msg) => {
|
|
617
|
+
displayedMessageIds.add(msg.messageId);
|
|
618
|
+
const isOwn = msg.author === userAddress;
|
|
619
|
+
return formatMessageText(msg, isOwn);
|
|
772
620
|
});
|
|
773
|
-
//
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
updateHeaderBox(headerBox, leaseInfo, tokenIdentifier, true);
|
|
777
|
-
screen.render();
|
|
778
|
-
}
|
|
779
|
-
}, 1000);
|
|
780
|
-
// Handle input submission
|
|
781
|
-
inputBox.on("submit", async (value) => {
|
|
782
|
-
const trimmed = value.trim();
|
|
621
|
+
// Message handler callback
|
|
622
|
+
const handleMessage = async (text) => {
|
|
623
|
+
const trimmed = text.trim();
|
|
783
624
|
// Ignore input while sending
|
|
784
625
|
if (isSending || !trimmed) {
|
|
785
|
-
inputBox?.clearValue();
|
|
786
|
-
inputBox?.focus();
|
|
787
|
-
screen?.render();
|
|
788
626
|
return;
|
|
789
627
|
}
|
|
790
628
|
// Handle commands
|
|
@@ -796,11 +634,10 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
796
634
|
isExiting = true;
|
|
797
635
|
if (leaseCheckInterval)
|
|
798
636
|
clearInterval(leaseCheckInterval);
|
|
799
|
-
if (headerUpdateInterval)
|
|
800
|
-
clearInterval(headerUpdateInterval);
|
|
801
637
|
if (ws)
|
|
802
638
|
ws.close();
|
|
803
|
-
|
|
639
|
+
if (chatUI)
|
|
640
|
+
await chatUI.destroy();
|
|
804
641
|
console.log();
|
|
805
642
|
printCat("sleeping");
|
|
806
643
|
console.log(chalk.cyan("Chat disconnected. Goodbye! 👋"));
|
|
@@ -808,22 +645,21 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
808
645
|
return;
|
|
809
646
|
case "/renew": {
|
|
810
647
|
try {
|
|
811
|
-
|
|
812
|
-
|
|
648
|
+
if (chatUI) {
|
|
649
|
+
chatUI.log(chalk.yellow("⏱️ Renewing lease..."));
|
|
650
|
+
}
|
|
813
651
|
const renewal = await renewLease(client, userAddress, leaseInfo?.leaseId);
|
|
814
652
|
leaseInfo = {
|
|
815
653
|
leaseId: renewal.leaseId,
|
|
816
654
|
leaseExpiresAt: new Date(renewal.leaseExpiresAt),
|
|
817
655
|
};
|
|
818
|
-
// Update header
|
|
819
|
-
if (
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
messageLogBox.setScrollPerc(100);
|
|
826
|
-
screen?.render();
|
|
656
|
+
// Update header
|
|
657
|
+
if (chatUI) {
|
|
658
|
+
chatUI.updateHeader({
|
|
659
|
+
leaseExpiresAt: leaseInfo.leaseExpiresAt,
|
|
660
|
+
});
|
|
661
|
+
chatUI.log(chalk.green(`✅ Lease renewed! Expires in ${formatTimeRemaining(leaseInfo.leaseExpiresAt)}`));
|
|
662
|
+
chatUI.log(chalk.yellow("🔄 Reconnecting to chat stream..."));
|
|
827
663
|
}
|
|
828
664
|
// Close old connection properly
|
|
829
665
|
if (ws) {
|
|
@@ -837,9 +673,6 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
837
673
|
ws = null;
|
|
838
674
|
}
|
|
839
675
|
// Wait for database transaction to commit and old connection to fully close
|
|
840
|
-
// The server validates the lease in the open handler, so we need to ensure
|
|
841
|
-
// the database update is visible before connecting
|
|
842
|
-
// Increased delay to 2 seconds for better database consistency
|
|
843
676
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
844
677
|
// Reconnect with new lease - with retry logic
|
|
845
678
|
if (wsUrl) {
|
|
@@ -857,10 +690,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
857
690
|
try {
|
|
858
691
|
if (retryCount > 0) {
|
|
859
692
|
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount - 1), 5000);
|
|
860
|
-
if (
|
|
861
|
-
|
|
862
|
-
messageLogBox.setScrollPerc(100);
|
|
863
|
-
screen?.render();
|
|
693
|
+
if (chatUI) {
|
|
694
|
+
chatUI.log(chalk.yellow(`🔄 Retry ${retryCount}/${maxRetries} in ${backoffDelay}ms...`));
|
|
864
695
|
}
|
|
865
696
|
await new Promise((resolve) => setTimeout(resolve, backoffDelay));
|
|
866
697
|
}
|
|
@@ -916,14 +747,12 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
916
747
|
});
|
|
917
748
|
connected = true;
|
|
918
749
|
// Connection established
|
|
919
|
-
if (
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
messageLogBox.setScrollPerc(100);
|
|
750
|
+
if (chatUI) {
|
|
751
|
+
chatUI.updateHeader({
|
|
752
|
+
leaseExpiresAt: leaseInfo?.leaseExpiresAt,
|
|
753
|
+
});
|
|
754
|
+
chatUI.log(chalk.green("✅ Reconnected to chat stream"));
|
|
925
755
|
}
|
|
926
|
-
screen?.render();
|
|
927
756
|
}
|
|
928
757
|
catch (error) {
|
|
929
758
|
retryCount++;
|
|
@@ -933,114 +762,77 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
933
762
|
throw new Error(`Failed to reconnect after ${maxRetries} attempts: ${errorMsg}`);
|
|
934
763
|
}
|
|
935
764
|
// Log retry attempt
|
|
936
|
-
if (
|
|
937
|
-
|
|
938
|
-
messageLogBox.setScrollPerc(100);
|
|
939
|
-
screen?.render();
|
|
765
|
+
if (chatUI) {
|
|
766
|
+
chatUI.log(chalk.yellow(`⚠️ Connection attempt ${retryCount} failed: ${errorMsg}`));
|
|
940
767
|
}
|
|
941
768
|
}
|
|
942
769
|
}
|
|
943
770
|
}
|
|
944
|
-
inputBox?.clearValue();
|
|
945
|
-
inputBox?.focus();
|
|
946
|
-
screen?.render();
|
|
947
771
|
}
|
|
948
772
|
catch (error) {
|
|
949
773
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
950
|
-
if (
|
|
951
|
-
|
|
952
|
-
messageLogBox.setScrollPerc(100);
|
|
774
|
+
if (chatUI) {
|
|
775
|
+
chatUI.log(chalk.red(`❌ ${errorMsg}`));
|
|
953
776
|
}
|
|
954
|
-
inputBox?.clearValue();
|
|
955
|
-
inputBox?.focus();
|
|
956
|
-
screen?.render();
|
|
957
777
|
}
|
|
958
778
|
return;
|
|
959
779
|
}
|
|
960
780
|
case "/buy": {
|
|
961
781
|
// Only allow in token-specific chats
|
|
962
782
|
if (!tokenIdentifier) {
|
|
963
|
-
if (
|
|
964
|
-
|
|
965
|
-
messageLogBox.setScrollPerc(100);
|
|
966
|
-
screen?.render();
|
|
783
|
+
if (chatUI) {
|
|
784
|
+
chatUI.log(chalk.yellow("⚠️ /buy command only works in token-specific chats"));
|
|
967
785
|
}
|
|
968
|
-
inputBox?.clearValue();
|
|
969
|
-
inputBox?.focus();
|
|
970
|
-
screen?.render();
|
|
971
786
|
return;
|
|
972
787
|
}
|
|
973
788
|
try {
|
|
974
789
|
const parts = trimmed.split(" ");
|
|
975
790
|
if (parts.length < 2) {
|
|
976
|
-
if (
|
|
977
|
-
|
|
978
|
-
messageLogBox.setScrollPerc(100);
|
|
979
|
-
screen?.render();
|
|
791
|
+
if (chatUI) {
|
|
792
|
+
chatUI.log(chalk.yellow("⚠️ Usage: /buy <amount> (e.g., /buy 0.05)"));
|
|
980
793
|
}
|
|
981
|
-
inputBox?.clearValue();
|
|
982
|
-
inputBox?.focus();
|
|
983
|
-
screen?.render();
|
|
984
794
|
return;
|
|
985
795
|
}
|
|
986
796
|
const amount = parts[1];
|
|
987
|
-
|
|
988
|
-
|
|
797
|
+
if (chatUI) {
|
|
798
|
+
chatUI.log(chalk.yellow("💰 Buying tokens..."));
|
|
799
|
+
}
|
|
989
800
|
const network = client.getNetwork();
|
|
990
801
|
const isTestMode = network === "eip155:84532" || network === "eip155:11155111" || network.includes("sepolia");
|
|
991
802
|
const result = await buyToken(client, tokenIdentifier, amount, isTestMode, true // silent mode
|
|
992
803
|
);
|
|
993
|
-
if (
|
|
994
|
-
|
|
995
|
-
messageLogBox.setScrollPerc(100);
|
|
996
|
-
screen?.render();
|
|
804
|
+
if (chatUI) {
|
|
805
|
+
chatUI.log(formatBuyResultCompact(result));
|
|
997
806
|
}
|
|
998
|
-
inputBox?.clearValue();
|
|
999
|
-
inputBox?.focus();
|
|
1000
|
-
screen?.render();
|
|
1001
807
|
}
|
|
1002
808
|
catch (error) {
|
|
1003
809
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1004
|
-
if (
|
|
1005
|
-
|
|
1006
|
-
messageLogBox.setScrollPerc(100);
|
|
1007
|
-
screen?.render();
|
|
810
|
+
if (chatUI) {
|
|
811
|
+
chatUI.log(chalk.red(`❌ Buy failed: ${errorMsg}`));
|
|
1008
812
|
}
|
|
1009
|
-
inputBox?.clearValue();
|
|
1010
|
-
inputBox?.focus();
|
|
1011
|
-
screen?.render();
|
|
1012
813
|
}
|
|
1013
814
|
return;
|
|
1014
815
|
}
|
|
1015
816
|
case "/sell": {
|
|
1016
817
|
// Only allow in token-specific chats
|
|
1017
818
|
if (!tokenIdentifier) {
|
|
1018
|
-
if (
|
|
1019
|
-
|
|
1020
|
-
messageLogBox.setScrollPerc(100);
|
|
1021
|
-
screen?.render();
|
|
819
|
+
if (chatUI) {
|
|
820
|
+
chatUI.log(chalk.yellow("⚠️ /sell command only works in token-specific chats"));
|
|
1022
821
|
}
|
|
1023
|
-
inputBox?.clearValue();
|
|
1024
|
-
inputBox?.focus();
|
|
1025
|
-
screen?.render();
|
|
1026
822
|
return;
|
|
1027
823
|
}
|
|
1028
824
|
try {
|
|
1029
825
|
const parts = trimmed.split(" ");
|
|
1030
826
|
if (parts.length < 2) {
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1033
|
-
messageLogBox.setScrollPerc(100);
|
|
1034
|
-
screen?.render();
|
|
827
|
+
if (chatUI) {
|
|
828
|
+
chatUI.log(chalk.yellow("⚠️ Usage: /sell <amount|all|percentage> (e.g., /sell 0.05, /sell all, /sell 50%)"));
|
|
1035
829
|
}
|
|
1036
|
-
inputBox?.clearValue();
|
|
1037
|
-
inputBox?.focus();
|
|
1038
|
-
screen?.render();
|
|
1039
830
|
return;
|
|
1040
831
|
}
|
|
1041
832
|
const amountStr = parts[1];
|
|
1042
|
-
|
|
1043
|
-
|
|
833
|
+
if (chatUI) {
|
|
834
|
+
chatUI.log(chalk.yellow("💸 Selling tokens..."));
|
|
835
|
+
}
|
|
1044
836
|
// Get token info to check balance
|
|
1045
837
|
if (!userAddress) {
|
|
1046
838
|
throw new Error("User address not available");
|
|
@@ -1055,50 +847,30 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1055
847
|
const tokenAmount = parseTokenAmount(amountStr, tokenInfo.userPosition.tokensOwned);
|
|
1056
848
|
const result = await sellToken(client, tokenIdentifier, tokenAmount, true // silent mode
|
|
1057
849
|
);
|
|
1058
|
-
if (
|
|
1059
|
-
|
|
1060
|
-
messageLogBox.setScrollPerc(100);
|
|
1061
|
-
screen?.render();
|
|
850
|
+
if (chatUI) {
|
|
851
|
+
chatUI.log(formatSellResultCompact(result));
|
|
1062
852
|
}
|
|
1063
|
-
inputBox?.clearValue();
|
|
1064
|
-
inputBox?.focus();
|
|
1065
|
-
screen?.render();
|
|
1066
853
|
}
|
|
1067
854
|
catch (error) {
|
|
1068
855
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1069
|
-
if (
|
|
1070
|
-
|
|
1071
|
-
messageLogBox.setScrollPerc(100);
|
|
1072
|
-
screen?.render();
|
|
856
|
+
if (chatUI) {
|
|
857
|
+
chatUI.log(chalk.red(`❌ Sell failed: ${errorMsg}`));
|
|
1073
858
|
}
|
|
1074
|
-
inputBox?.clearValue();
|
|
1075
|
-
inputBox?.focus();
|
|
1076
|
-
screen?.render();
|
|
1077
859
|
}
|
|
1078
860
|
return;
|
|
1079
861
|
}
|
|
1080
862
|
case "/help":
|
|
1081
|
-
if (
|
|
863
|
+
if (chatUI) {
|
|
1082
864
|
const helpText = tokenIdentifier
|
|
1083
865
|
? chalk.dim("Commands: /exit, /quit, /renew, /buy, /sell, /help")
|
|
1084
866
|
: chalk.dim("Commands: /exit, /quit, /renew, /help");
|
|
1085
|
-
|
|
1086
|
-
messageLogBox.setScrollPerc(100);
|
|
1087
|
-
screen?.render();
|
|
867
|
+
chatUI.log(helpText);
|
|
1088
868
|
}
|
|
1089
|
-
inputBox?.clearValue();
|
|
1090
|
-
inputBox?.focus();
|
|
1091
|
-
screen?.render();
|
|
1092
869
|
return;
|
|
1093
870
|
default:
|
|
1094
|
-
if (
|
|
1095
|
-
|
|
1096
|
-
messageLogBox.setScrollPerc(100);
|
|
1097
|
-
screen?.render();
|
|
871
|
+
if (chatUI) {
|
|
872
|
+
chatUI.log(chalk.yellow(`Unknown command: ${cmd}. Type /help for commands.`));
|
|
1098
873
|
}
|
|
1099
|
-
inputBox?.clearValue();
|
|
1100
|
-
inputBox?.focus();
|
|
1101
|
-
screen?.render();
|
|
1102
874
|
return;
|
|
1103
875
|
}
|
|
1104
876
|
}
|
|
@@ -1114,53 +886,42 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1114
886
|
? "closed"
|
|
1115
887
|
: "unknown"
|
|
1116
888
|
: "not initialized";
|
|
1117
|
-
if (
|
|
1118
|
-
|
|
1119
|
-
messageLogBox.setScrollPerc(100);
|
|
1120
|
-
screen?.render();
|
|
889
|
+
if (chatUI) {
|
|
890
|
+
chatUI.log(chalk.red(`❌ WebSocket is not connected (state: ${stateMsg}). Please wait for connection or type /renew.`));
|
|
1121
891
|
}
|
|
1122
|
-
inputBox?.clearValue();
|
|
1123
|
-
inputBox?.focus();
|
|
1124
|
-
screen?.render();
|
|
1125
892
|
return;
|
|
1126
893
|
}
|
|
1127
894
|
// Check if lease is valid
|
|
1128
895
|
if (!leaseInfo || leaseInfo.leaseExpiresAt.getTime() <= Date.now()) {
|
|
1129
|
-
if (
|
|
1130
|
-
|
|
1131
|
-
messageLogBox.setScrollPerc(100);
|
|
1132
|
-
screen?.render();
|
|
896
|
+
if (chatUI) {
|
|
897
|
+
chatUI.log(chalk.yellow("⏱️ Your lease has expired. Type /renew to continue chatting."));
|
|
1133
898
|
}
|
|
1134
|
-
inputBox?.clearValue();
|
|
1135
|
-
inputBox?.focus();
|
|
1136
|
-
screen?.render();
|
|
1137
899
|
return;
|
|
1138
900
|
}
|
|
1139
901
|
// Send message
|
|
1140
902
|
isSending = true;
|
|
1141
903
|
currentInput = trimmed;
|
|
1142
|
-
// Show pulsing animation
|
|
904
|
+
// Show pulsing animation
|
|
1143
905
|
let pulseCount = 0;
|
|
1144
906
|
let pulseInterval = null;
|
|
1145
907
|
const tempMessageId = `pending-${Date.now()}-${Math.random()}`;
|
|
1146
908
|
const updatePulse = () => {
|
|
1147
|
-
if (
|
|
909
|
+
if (isCleaningUp)
|
|
1148
910
|
return;
|
|
1149
911
|
pulseCount++;
|
|
1150
912
|
const pulseChar = pulseCount % 2 === 0 ? "●" : "○";
|
|
1151
|
-
|
|
1152
|
-
screen?.render();
|
|
913
|
+
// Note: Ink doesn't support directly updating input, so we just track state
|
|
1153
914
|
};
|
|
1154
915
|
// Initial pulse
|
|
1155
|
-
updatePulse();
|
|
1156
916
|
pulseInterval = setInterval(updatePulse, 500);
|
|
1157
917
|
pulseIntervals.set(tempMessageId, pulseInterval);
|
|
1158
918
|
try {
|
|
1159
919
|
// Ensure lease is valid before sending
|
|
1160
|
-
leaseInfo = await ensureLeaseValid(client, userAddress, leaseInfo,
|
|
1161
|
-
if (
|
|
1162
|
-
|
|
1163
|
-
|
|
920
|
+
leaseInfo = await ensureLeaseValid(client, userAddress, leaseInfo, chatUI || undefined);
|
|
921
|
+
if (chatUI) {
|
|
922
|
+
chatUI.updateHeader({
|
|
923
|
+
leaseExpiresAt: leaseInfo.leaseExpiresAt,
|
|
924
|
+
});
|
|
1164
925
|
}
|
|
1165
926
|
// Double-check websocket is still connected after lease renewal
|
|
1166
927
|
// WebSocket.OPEN = 1
|
|
@@ -1168,7 +929,6 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1168
929
|
throw new Error("WebSocket connection lost. Please wait for reconnection.");
|
|
1169
930
|
}
|
|
1170
931
|
const result = await sendChatMessage(client, trimmed, leaseInfo.leaseId, userAddress, tokenAddress);
|
|
1171
|
-
userAddress = result.author;
|
|
1172
932
|
// Track this message
|
|
1173
933
|
pendingMessages.set(result.messageId, tempMessageId);
|
|
1174
934
|
pendingMessages.set(`text:${trimmed}`, tempMessageId);
|
|
@@ -1181,38 +941,46 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1181
941
|
pulseIntervals.delete(tempMessageId);
|
|
1182
942
|
}
|
|
1183
943
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1184
|
-
if (
|
|
1185
|
-
|
|
1186
|
-
messageLogBox.setScrollPerc(100);
|
|
944
|
+
if (chatUI) {
|
|
945
|
+
chatUI.log(chalk.red(`❌ ${errorMsg}`));
|
|
1187
946
|
}
|
|
1188
|
-
inputBox?.clearValue();
|
|
1189
|
-
inputBox?.focus();
|
|
1190
947
|
isSending = false;
|
|
1191
948
|
currentInput = "";
|
|
1192
|
-
screen?.render();
|
|
1193
949
|
}
|
|
1194
|
-
}
|
|
1195
|
-
//
|
|
1196
|
-
|
|
950
|
+
};
|
|
951
|
+
// Exit handler
|
|
952
|
+
const handleExit = async () => {
|
|
1197
953
|
isExiting = true;
|
|
1198
954
|
isSending = false;
|
|
1199
955
|
pulseIntervals.forEach((interval) => clearInterval(interval));
|
|
1200
956
|
pulseIntervals.clear();
|
|
1201
957
|
if (leaseCheckInterval)
|
|
1202
958
|
clearInterval(leaseCheckInterval);
|
|
1203
|
-
if (headerUpdateInterval)
|
|
1204
|
-
clearInterval(headerUpdateInterval);
|
|
1205
959
|
if (ws)
|
|
1206
960
|
ws.close();
|
|
1207
|
-
|
|
961
|
+
if (chatUI)
|
|
962
|
+
await chatUI.destroy();
|
|
1208
963
|
console.log();
|
|
1209
964
|
printCat("sleeping");
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
965
|
+
if (returnToShell) {
|
|
966
|
+
console.log(chalk.cyan("Returning to shell..."));
|
|
967
|
+
// Don't exit - return to shell
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
console.log(chalk.cyan("Chat disconnected. Goodbye! 👋"));
|
|
971
|
+
process.exit(0);
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
// Create Ink UI
|
|
975
|
+
chatUI = createTokenChatUI(handleMessage, handleExit, {
|
|
976
|
+
network: client.getNetwork(),
|
|
977
|
+
account: userAddress,
|
|
978
|
+
tokenSymbol,
|
|
979
|
+
tokenAddress,
|
|
980
|
+
leaseExpiresAt: leaseInfo.leaseExpiresAt,
|
|
981
|
+
}, initialMessages);
|
|
982
|
+
// Wait for the chat UI to exit (blocks until user exits)
|
|
983
|
+
await chatUI.waitUntilExit();
|
|
1216
984
|
}
|
|
1217
985
|
else {
|
|
1218
986
|
// JSON mode: read messages from stdin and output JSON to stdout
|
|
@@ -1272,8 +1040,6 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1272
1040
|
isExiting = true;
|
|
1273
1041
|
if (leaseCheckInterval)
|
|
1274
1042
|
clearInterval(leaseCheckInterval);
|
|
1275
|
-
if (headerUpdateInterval)
|
|
1276
|
-
clearInterval(headerUpdateInterval);
|
|
1277
1043
|
if (ws)
|
|
1278
1044
|
ws.close();
|
|
1279
1045
|
console.log(JSON.stringify({ type: "exiting" }));
|
|
@@ -1430,7 +1196,7 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1430
1196
|
// Send message
|
|
1431
1197
|
isSending = true;
|
|
1432
1198
|
try {
|
|
1433
|
-
leaseInfo = await ensureLeaseValid(client, userAddress, leaseInfo, undefined
|
|
1199
|
+
leaseInfo = await ensureLeaseValid(client, userAddress, leaseInfo, undefined);
|
|
1434
1200
|
if (!ws || ws.readyState !== 1) {
|
|
1435
1201
|
throw new Error("WebSocket connection lost. Please wait for reconnection.");
|
|
1436
1202
|
}
|
|
@@ -1462,11 +1228,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1462
1228
|
isExiting = true;
|
|
1463
1229
|
if (leaseCheckInterval)
|
|
1464
1230
|
clearInterval(leaseCheckInterval);
|
|
1465
|
-
if (headerUpdateInterval)
|
|
1466
|
-
clearInterval(headerUpdateInterval);
|
|
1467
1231
|
if (ws)
|
|
1468
1232
|
ws.close();
|
|
1469
|
-
// Don't try to use readline in JSON mode - it should never exist
|
|
1470
1233
|
process.exit(0);
|
|
1471
1234
|
});
|
|
1472
1235
|
process.stdin.on("error", (error) => {
|
|
@@ -1482,11 +1245,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1482
1245
|
isExiting = true;
|
|
1483
1246
|
if (leaseCheckInterval)
|
|
1484
1247
|
clearInterval(leaseCheckInterval);
|
|
1485
|
-
if (headerUpdateInterval)
|
|
1486
|
-
clearInterval(headerUpdateInterval);
|
|
1487
1248
|
if (ws)
|
|
1488
1249
|
ws.close();
|
|
1489
|
-
// Don't try to use readline in JSON mode - it should never exist
|
|
1490
1250
|
console.log(JSON.stringify({ type: "exiting" }));
|
|
1491
1251
|
process.exit(0);
|
|
1492
1252
|
});
|
|
@@ -1497,8 +1257,6 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1497
1257
|
try {
|
|
1498
1258
|
if (leaseCheckInterval)
|
|
1499
1259
|
clearInterval(leaseCheckInterval);
|
|
1500
|
-
if (headerUpdateInterval)
|
|
1501
|
-
clearInterval(headerUpdateInterval);
|
|
1502
1260
|
pulseIntervals.forEach((interval) => clearInterval(interval));
|
|
1503
1261
|
pulseIntervals.clear();
|
|
1504
1262
|
if (ws !== null) {
|
|
@@ -1514,8 +1272,8 @@ export async function startChatStream(client, jsonMode = false, tokenIdentifier,
|
|
|
1514
1272
|
// Ignore cleanup errors
|
|
1515
1273
|
}
|
|
1516
1274
|
}
|
|
1517
|
-
if (
|
|
1518
|
-
|
|
1275
|
+
if (chatUI) {
|
|
1276
|
+
await chatUI.destroy();
|
|
1519
1277
|
}
|
|
1520
1278
|
}
|
|
1521
1279
|
catch (cleanupError) {
|