Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,075 changes: 2,055 additions & 20 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "txtcode",
"version": "0.1.0",
"description": "Remote IDE control via WhatsApp/Telegram/Discord - Code from anywhere using AI-powered messaging",
"description": "Remote IDE control via WhatsApp/Telegram/Discord/Slack/Teams/Signal - Code from anywhere using AI-powered messaging",
"keywords": [
"ai",
"claude",
Expand All @@ -11,10 +11,13 @@
"gemini",
"ide",
"messaging-ide",
"microsoft-teams",
"mobile-coding",
"openai",
"remote",
"remote-development",
"signal",
"slack",
"telegram",
"text-based-coding",
"whatsapp"
Expand All @@ -41,7 +44,9 @@
"dependencies": {
"@anthropic-ai/sdk": "^0.74.0",
"@google/generative-ai": "^0.24.1",
"@slack/bolt": "^4.6.0",
"@whiskeysockets/baileys": "^7.0.0-rc.9",
"botbuilder": "^4.23.3",
"chalk": "^4.1.2",
"discord.js": "^14.25.1",
"dotenv": "^16.3.1",
Expand Down
131 changes: 131 additions & 0 deletions src/cli/commands/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,11 +555,22 @@ export async function authCommand() {
{ name: "WhatsApp", value: "whatsapp" },
{ name: "Telegram", value: "telegram" },
{ name: "Discord", value: "discord" },
{ name: "Slack", value: "slack" },
{ name: "Microsoft Teams", value: "teams" },
{ name: "Signal", value: "signal" },
],
});

let telegramToken = "";
let discordToken = "";
let slackBotToken = "";
let slackAppToken = "";
let slackSigningSecret = "";
let teamsAppId = "";
let teamsAppPassword = "";
let teamsTenantId = "";
let signalPhoneNumber = "";
let signalCliRestUrl = "";

// Complete messaging platform auth immediately
if (platform === "telegram") {
Expand Down Expand Up @@ -598,6 +609,110 @@ export async function authCommand() {
console.log();
console.log(chalk.green("Discord bot configured"));
console.log();
} else if (platform === "slack") {
console.log();
console.log(chalk.cyan("Slack Bot Setup"));
console.log();
console.log(chalk.gray("1. Go to https://api.slack.com/apps and create a new app"));
console.log(chalk.gray("2. Enable Socket Mode (Settings → Socket Mode)"));
console.log(
chalk.gray(
"3. Add Bot Token Scopes: chat:write, channels:history, groups:history, im:history, mpim:history",
),
);
console.log(chalk.gray("4. Install the app to your workspace"));
console.log(
chalk.gray(
"5. Subscribe to bot events: message.channels, message.groups, message.im, message.mpim",
),
);
console.log();

slackBotToken = await showCenteredInput({
message: "Enter Slack Bot Token (xoxb-...):",
password: true,
validate: (input) => input.length > 0 || "Bot token is required",
});
console.log();

slackAppToken = await showCenteredInput({
message: "Enter Slack App-Level Token (xapp-...):",
password: true,
validate: (input) => input.length > 0 || "App token is required",
});
console.log();

slackSigningSecret = await showCenteredInput({
message: "Enter Slack Signing Secret:",
password: true,
validate: (input) => input.length > 0 || "Signing secret is required",
});
console.log();
console.log(chalk.green("Slack bot configured"));
console.log();
} else if (platform === "teams") {
console.log();
console.log(chalk.cyan("Microsoft Teams Bot Setup"));
console.log();
console.log(chalk.gray("1. Go to https://dev.teams.microsoft.com/bots"));
console.log(chalk.gray("2. Create a new Bot registration"));
console.log(chalk.gray("3. Copy the App ID and generate a client secret"));
console.log(chalk.gray("4. Set the messaging endpoint to https://<your-domain>/api/messages"));
console.log();

teamsAppId = await showCenteredInput({
message: "Enter Teams App (Bot) ID:",
password: false,
validate: (input) => input.length > 0 || "App ID is required",
});
console.log();

teamsAppPassword = await showCenteredInput({
message: "Enter Teams App Password (Client Secret):",
password: true,
validate: (input) => input.length > 0 || "App password is required",
});
console.log();

teamsTenantId = await showCenteredInput({
message: "Enter Azure Tenant ID:",
password: false,
validate: (input) => input.length > 0 || "Tenant ID is required",
});
console.log();
console.log(chalk.green("Microsoft Teams bot configured"));
console.log();
} else if (platform === "signal") {
console.log();
console.log(chalk.cyan("Signal Bot Setup"));
console.log();
console.log(chalk.gray("Signal requires signal-cli-rest-api running as a companion service."));
console.log();
console.log(chalk.gray("Setup:"));
console.log(chalk.gray(" 1. Run signal-cli-rest-api via Docker:"));
console.log(chalk.white(" docker run -p 8080:8080 bbernhard/signal-cli-rest-api"));
console.log(chalk.gray(" 2. Register/link your phone number with signal-cli"));
console.log(chalk.gray(" 3. Provide the phone number and API URL below"));
console.log();

signalPhoneNumber = await showCenteredInput({
message: "Enter Signal phone number (e.g. +1234567890):",
password: false,
validate: (input) => input.startsWith("+") || "Phone number must start with +",
});
console.log();

signalCliRestUrl = await showCenteredInput({
message: "Enter signal-cli-rest-api URL (default: http://localhost:8080):",
password: false,
validate: () => true,
});
if (!signalCliRestUrl.trim()) {
signalCliRestUrl = "http://localhost:8080";
}
console.log();
console.log(chalk.green("Signal bot configured"));
console.log();
} else {
console.log();
console.log(chalk.cyan("WhatsApp Setup"));
Expand Down Expand Up @@ -633,6 +748,8 @@ export async function authCommand() {
console.log(chalk.cyan("Recommended alternatives:"));
console.log(chalk.white(" • Telegram - More stable and reliable"));
console.log(chalk.white(" • Discord - Also very stable"));
console.log(chalk.white(" • Slack - Great for workspace integration"));
console.log(chalk.white(" • Signal - Privacy-focused alternative"));
console.log();
console.log(chalk.gray("Would you like to restart and choose a different platform?"));
} else {
Expand Down Expand Up @@ -677,6 +794,20 @@ export async function authCommand() {
if (discordToken) {
await setBotToken("discord", discordToken);
}
if (slackBotToken) {
await setBotToken("slack-bot", slackBotToken);
await setBotToken("slack-app", slackAppToken);
await setBotToken("slack-signing", slackSigningSecret);
}
if (teamsAppId) {
await setBotToken("teams-app-id", teamsAppId);
await setBotToken("teams-app-password", teamsAppPassword);
await setBotToken("teams-tenant-id", teamsTenantId);
}
if (signalPhoneNumber) {
await setBotToken("signal-phone", signalPhoneNumber);
await setBotToken("signal-api-url", signalCliRestUrl);
}
} catch {
console.log(chalk.red("\n[ERROR] Failed to store credentials in keychain"));
console.log(chalk.yellow("Falling back to encrypted file storage...\n"));
Expand Down
66 changes: 48 additions & 18 deletions src/cli/commands/start.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import chalk from "chalk";
import { AgentCore } from "../../core/agent";
import { DiscordBot } from "../../platforms/discord";
import { SignalBot } from "../../platforms/signal";
import { SlackBot } from "../../platforms/slack";
import { TeamsBot } from "../../platforms/teams";
import { TelegramBot } from "../../platforms/telegram";
import { WhatsAppBot } from "../../platforms/whatsapp";
import { logger } from "../../shared/logger";
Expand All @@ -9,6 +12,19 @@ import { getApiKey, getBotToken } from "../../utils/keychain";
import { centerLog } from "../tui";
import { loadConfig } from "./auth";

async function loadPlatformToken(name: string, keychainKey: string): Promise<string> {
const token = (await getBotToken(keychainKey)) || "";
if (!token) {
console.log();
centerLog(chalk.red(`[ERROR] Failed to retrieve ${name} token from keychain`));
console.log();
centerLog(chalk.yellow("Please run authentication"));
console.log();
process.exit(1);
}
return token;
}

export async function startCommand(_options: { daemon?: boolean }) {
const rawConfig = loadConfig();

Expand Down Expand Up @@ -41,25 +57,30 @@ export async function startCommand(_options: { daemon?: boolean }) {
let discordToken = "";

if (config.platform === "telegram") {
telegramToken = (await getBotToken("telegram")) || "";
if (!telegramToken) {
console.log();
centerLog(chalk.red("[ERROR] Failed to retrieve Telegram token from keychain"));
console.log();
centerLog(chalk.yellow("Please run authentication"));
console.log();
process.exit(1);
}
telegramToken = await loadPlatformToken("Telegram", "telegram");
} else if (config.platform === "discord") {
discordToken = (await getBotToken("discord")) || "";
if (!discordToken) {
console.log();
centerLog(chalk.red("[ERROR] Failed to retrieve Discord token from keychain"));
console.log();
centerLog(chalk.yellow("Please run authentication"));
console.log();
process.exit(1);
}
discordToken = await loadPlatformToken("Discord", "discord");
} else if (config.platform === "slack") {
process.env.SLACK_BOT_TOKEN = await loadPlatformToken("Slack Bot", "slack-bot");
process.env.SLACK_APP_TOKEN = await loadPlatformToken("Slack App", "slack-app");
process.env.SLACK_SIGNING_SECRET = await loadPlatformToken(
"Slack Signing Secret",
"slack-signing",
);
} else if (config.platform === "teams") {
process.env.TEAMS_APP_ID = await loadPlatformToken("Teams App ID", "teams-app-id");
process.env.TEAMS_APP_PASSWORD = await loadPlatformToken(
"Teams App Password",
"teams-app-password",
);
process.env.TEAMS_TENANT_ID = await loadPlatformToken("Teams Tenant ID", "teams-tenant-id");
} else if (config.platform === "signal") {
process.env.SIGNAL_PHONE_NUMBER = await loadPlatformToken(
"Signal Phone Number",
"signal-phone",
);
const signalApiUrl = (await getBotToken("signal-api-url")) || "http://localhost:8080";
process.env.SIGNAL_CLI_REST_URL = signalApiUrl;
}

process.env.PLATFORM = config.platform;
Expand Down Expand Up @@ -87,6 +108,15 @@ export async function startCommand(_options: { daemon?: boolean }) {
} else if (config.platform === "discord") {
const bot = new DiscordBot(agent);
await bot.start();
} else if (config.platform === "slack") {
const bot = new SlackBot(agent);
await bot.start();
} else if (config.platform === "teams") {
const bot = new TeamsBot(agent);
await bot.start();
} else if (config.platform === "signal") {
const bot = new SignalBot(agent);
await bot.start();
} else {
logger.error("Invalid platform specified");
process.exit(1);
Expand Down
Loading