Function bodies 137 total
removePid function · typescript · L54-L61 (8 LOC)src/daemon.ts
function removePid(): void {
try {
const content = fs.readFileSync(pidFilePath(), "utf-8").trim();
if (content === String(process.pid)) {
fs.unlinkSync(pidFilePath());
}
} catch (err) { logger.debug("daemon", `removePid: ${errorMessage(err)}`); }
}killOrphanDaemons function · typescript · L64-L79 (16 LOC)src/daemon.ts
export function killOrphanDaemons(): void {
try {
const out = execSync("ps -eo pid,args", { stdio: "pipe" }).toString();
const myPid = process.pid;
for (const line of out.split("\n")) {
if (!line.includes("--daemon")) continue;
if (!line.includes("remotecode") && !line.includes("index.js") && !line.includes("index.ts")) continue;
const pid = parseInt(line.trim(), 10);
if (isNaN(pid) || pid === myPid) continue;
try {
process.kill(pid, "SIGTERM");
console.log(` Killed orphan daemon (pid ${pid})`);
} catch (err) { logger.debug("daemon", `kill orphan ${pid}: ${errorMessage(err)}`); }
}
} catch (err) { logger.debug("daemon", `killOrphanDaemons: ${errorMessage(err)}`); }
}setupLogging function · typescript · L84-L122 (39 LOC)src/daemon.ts
function setupLogging(): void {
const logPath = logFilePath();
let logStream = fs.createWriteStream(logPath, { flags: "a" });
let byteCount = 0;
try {
byteCount = fs.statSync(logPath).size;
} catch { /* file may not exist yet */ }
function rotateIfNeeded(bytes: number): void {
byteCount += bytes;
if (byteCount >= LOG_MAX_BYTES) {
logStream.end();
try {
fs.renameSync(logPath, logPath + ".old");
} catch (err) { logger.debug("daemon", `log rotate: ${errorMessage(err)}`); }
logStream = fs.createWriteStream(logPath, { flags: "a" });
byteCount = 0;
patchWrite();
}
}
function patchWrite(): void {
process.stdout.write = (chunk: string | Uint8Array, ...args: unknown[]): boolean => {
const buf = Buffer.from(typeof chunk === "string" ? chunk : chunk.toString());
logStream.write(buf);
rotateIfNeeded(buf.length);
return true;
};
process.stderr.write = (chunk: string | Uint8Array, ...arrotateIfNeeded function · typescript · L92-L103 (12 LOC)src/daemon.ts
function rotateIfNeeded(bytes: number): void {
byteCount += bytes;
if (byteCount >= LOG_MAX_BYTES) {
logStream.end();
try {
fs.renameSync(logPath, logPath + ".old");
} catch (err) { logger.debug("daemon", `log rotate: ${errorMessage(err)}`); }
logStream = fs.createWriteStream(logPath, { flags: "a" });
byteCount = 0;
patchWrite();
}
}patchWrite function · typescript · L105-L119 (15 LOC)src/daemon.ts
function patchWrite(): void {
process.stdout.write = (chunk: string | Uint8Array, ...args: unknown[]): boolean => {
const buf = Buffer.from(typeof chunk === "string" ? chunk : chunk.toString());
logStream.write(buf);
rotateIfNeeded(buf.length);
return true;
};
process.stderr.write = (chunk: string | Uint8Array, ...args: unknown[]): boolean => {
const buf = Buffer.from(typeof chunk === "string" ? chunk : chunk.toString());
logStream.write(buf);
rotateIfNeeded(buf.length);
return true;
};
}registerCommands function · typescript · L125-L138 (14 LOC)src/daemon.ts
async function registerCommands(config: TelegramConfig): Promise<void> {
const commands = [
{ command: "start", description: "Welcome message and quick actions" },
{ command: "help", description: "Show help and commands" },
{ command: "sessions", description: "List local Claude Code sessions" },
{ command: "projects", description: "Browse sessions by project" },
{ command: "new", description: "New Claude session" },
{ command: "history", description: "Show last 10 turns of current session" },
{ command: "cancel", description: "Cancel the current task" },
{ command: "model", description: "Switch Claude model" },
{ command: "sync", description: "Toggle auto-sync notifications" },
];
await setMyCommands(config, commands);
}saveChatId function · typescript · L140-L145 (6 LOC)src/daemon.ts
function saveChatId(sessionsFile: string, chatId: number): void {
let lines = readEnvLines(sessionsFile);
lines = lines.filter((l) => !l.trim().startsWith("REMOTECODE_CHAT_ID="));
lines.push(`REMOTECODE_CHAT_ID=${chatId}`);
writeEnvLines(sessionsFile, lines);
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
processUpdate function · typescript · L147-L178 (32 LOC)src/daemon.ts
async function processUpdate(update: Update, ctx: HandlerContext): Promise<void> {
try {
const chatId = update.message?.chat.id || update.callback_query?.message?.chat.id;
if (chatId) saveChatId(ctx.sessionsFile, chatId);
const from = update.message?.from || update.callback_query?.from;
const msg = update.message;
const content = msg?.text
? `text="${msg.text}"`
: msg?.voice ? `voice ${msg.voice.duration || 0}s`
: msg?.audio ? `audio "${msg.audio.file_name || "unknown"}"`
: msg?.photo ? `photo${msg.caption ? ` "${msg.caption}"` : ""}`
: msg?.document ? `document "${msg.document.file_name || "unknown"}"`
: update.callback_query ? `callback "${update.callback_query.data || ""}"`
: "unknown";
logger.info("poll", `Update ${update.update_id} from=${from?.username || from?.id || "unknown"} ${content}`);
if (update.callback_query) {
await handleCallbackQuery(update.callback_query, ctx);
} else if (update.messagpollLoop function · typescript · L180-L252 (73 LOC)src/daemon.ts
async function pollLoop(telegramConfig: TelegramConfig, ctx: HandlerContext): Promise<void> {
let offset = 0;
let conflictCount = 0;
const MAX_CONFLICT_RETRIES = 3;
// Flush pending updates from before daemon start to avoid processing stale messages
try {
const pending = await getUpdates(telegramConfig, -1, 0);
if (pending.length > 0) {
offset = pending[pending.length - 1].update_id + 1;
const texts = pending
.map((u) => u.message?.text || u.callback_query?.data || "")
.filter(Boolean);
logger.info("poll", `Flushed ${pending.length} pending update(s)`);
const chatId = pending[0].message?.chat.id || pending[0].callback_query?.message?.chat.id;
if (chatId) {
const dropped = texts.length > 0 ? `\n\nDropped: ${texts.join(", ")}` : "";
await sendMessage(telegramConfig, chatId, `RemoteCode restarted.${dropped}`);
}
}
} catch (err) {
logger.debug("poll", `flush pending: ${errorMessage(err)}`);
}daemonMain function · typescript · L259-L315 (57 LOC)src/daemon.ts
export async function daemonMain(): Promise<void> {
ensureConfigDir();
setupLogging();
writePid();
const cfgPath = loadConfig();
if (cfgPath) logger.info("config", `Loaded: ${cfgPath}`);
const config = getConfig();
if (config.yolo && isPrivileged()) {
logger.error("daemon", "YOLO mode cannot run with root/sudo privileges (Claude Code restriction).");
logger.error("daemon", "Either switch to a non-root user or set REMOTECODE_YOLO=false in config.");
removePid();
process.exit(1);
}
const telegramConfig: TelegramConfig = { botToken: config.botToken };
logger.info("daemon", "RemoteCode daemon starting...");
if (config.verbose) logger.info("daemon", "Verbose logging enabled");
try {
await deleteWebhook(telegramConfig);
logger.debug("daemon", "Webhook cleared for long polling");
} catch (err) {
logger.warn("daemon", `Failed to clear webhook: ${errorMessage(err)}`);
}
try {
await registerCommands(telegramConfig);
logger.despawnDaemon function · typescript · L318-L348 (31 LOC)src/daemon.ts
export async function spawnDaemon(): Promise<void> {
const logPath = logFilePath();
const logFd = fs.openSync(logPath, "a");
const env = { ...process.env };
delete env.CLAUDECODE; // Allow SDK to spawn Claude Code subprocesses
const child = spawn(process.execPath, [...process.execArgv, process.argv[1], "--daemon"], {
detached: true,
stdio: ["ignore", logFd, logFd],
env,
});
child.unref();
fs.closeSync(logFd);
const childPid = child.pid;
if (!childPid) {
console.error("Failed to spawn daemon.");
process.exit(1);
}
// Wait a moment and verify the daemon is still running
await new Promise((resolve) => setTimeout(resolve, 1000));
if (isRunning(childPid)) {
console.log(` Daemon running in background (pid ${childPid})\n`);
} else {
console.error(" Daemon exited immediately. Check logs:");
console.error(` ${logPath}\n`);
process.exit(1);
}
}convertTables function · typescript · L6-L48 (43 LOC)src/format.ts
function convertTables(text: string): string {
const lines = text.split("\n");
const result: string[] = [];
let i = 0;
while (i < lines.length) {
// Detect table: header row with |, separator row with |---
if (
i + 1 < lines.length &&
/^\|(.+\|)+\s*$/.test(lines[i]) &&
/^\|[\s:]*-+/.test(lines[i + 1])
) {
const headers = lines[i].split("|").map(c => c.trim()).filter(Boolean);
const rows: string[][] = [];
i += 2; // skip header + separator
while (i < lines.length && /^\|(.+\|)+\s*$/.test(lines[i])) {
rows.push(lines[i].split("|").map(c => c.trim()).filter(Boolean));
i++;
}
const bulletLines: string[] = [];
for (const row of rows) {
bulletLines.push(`• ${headers[0]}: ${row[0] || ""}`);
for (let c = 1; c < headers.length; c++) {
bulletLines.push(` ${headers[c]}: ${row[c] || ""}`);
}
bulletLines.push("");
}
// Remove trailing empty line
formatToolDetail function · typescript · L116-L141 (26 LOC)src/format.ts
function formatToolDetail(toolName: string, input: Record<string, unknown>): string {
const e = escapeHtml;
switch (toolName) {
case "Bash":
return `${e(String(input.command || "").slice(0, 200))}`;
case "Edit":
case "Write":
case "Read":
return `${e(String(input.file_path || ""))}`;
case "Glob":
case "Grep":
return `${e(String(input.pattern || ""))}`;
case "Task":
return `${e(String(input.description || ""))}`;
case "WebSearch":
return `${e(String(input.query || "").slice(0, 200))}`;
case "WebFetch":
return `${e(String(input.url || "").slice(0, 200))}`;
case "NotebookEdit":
return `${e(String(input.notebook_path || ""))}`;
case "Skill":
return `${e(String(input.skill || ""))}`;
default:
return "";
}
}detectBashLang function · typescript · L143-L153 (11 LOC)src/format.ts
export function detectBashLang(command: string): string {
const cmd = command.trimStart();
if (/^python[23]?\s/.test(cmd)) return "python";
if (/^node\s/.test(cmd)) return "javascript";
if (/^ruby\s/.test(cmd)) return "ruby";
if (/^go\s/.test(cmd)) return "go";
if (/^cargo\s|^rustc\s/.test(cmd)) return "rust";
if (/^swift\s|^swiftc\s/.test(cmd)) return "swift";
if (/^java\s|^javac\s|^gradle\s|^mvn\s/.test(cmd)) return "java";
return "bash";
}toolLanguage function · typescript · L155-L168 (14 LOC)src/format.ts
function toolLanguage(toolName: string, input: Record<string, unknown>): string {
switch (toolName) {
case "Bash": return detectBashLang(String(input.command || ""));
case "Edit":
case "Write":
case "Read":
case "Glob":
case "Grep": return "bash";
case "WebSearch":
case "WebFetch": return "bash";
case "Task": return "bash";
default: return "bash";
}
}Open data scored by Repobility · https://repobility.com
formatToolDescription function · typescript · L170-L177 (8 LOC)src/format.ts
export function formatToolDescription(toolName: string, input: Record<string, unknown>): string {
const lang = toolLanguage(toolName, input);
const detail = formatToolDetail(toolName, input);
if (detail) {
return `<pre><code class="language-${lang}">${escapeHtml(toolName)}: ${detail}</code></pre>`;
}
return `<pre><code class="language-${lang}">${escapeHtml(toolName)}</code></pre>`;
}mimeFromExt function · typescript · L52-L58 (7 LOC)src/handler.ts
function mimeFromExt(filePath: string): string {
const ext = path.extname(filePath).toLowerCase();
if (ext === ".png") return "image/png";
if (ext === ".gif") return "image/gif";
if (ext === ".webp") return "image/webp";
return "image/jpeg";
}formatPrompt function · typescript · L60-L72 (13 LOC)src/handler.ts
function formatPrompt(prompt: string, imagePaths?: string[]): MessageContent {
if (!imagePaths || imagePaths.length === 0) return prompt;
const blocks: Array<
| { type: "text"; text: string }
| { type: "image"; source: { type: "base64"; media_type: string; data: string } }
> = [];
if (prompt) blocks.push({ type: "text", text: prompt });
for (const p of imagePaths) {
const data = fs.readFileSync(p).toString("base64");
blocks.push({ type: "image", source: { type: "base64", media_type: mimeFromExt(p), data } });
}
return blocks;
}downloadAndSaveImage function · typescript · L81-L89 (9 LOC)src/handler.ts
async function downloadAndSaveImage(config: TelegramConfig, fileId: string): Promise<string> {
const { data, filePath } = await downloadFile(config, fileId);
const ext = path.extname(filePath) || ".jpg";
const tempDir = ensureTempDir();
const filename = `image_${uuidv4().replace(/-/g, "")}${ext}`;
const savePath = path.join(tempDir, filename);
fs.writeFileSync(savePath, data);
return savePath;
}pickBestPhotoId function · typescript · L91-L100 (10 LOC)src/handler.ts
function pickBestPhotoId(photos: Array<{ file_id: string; file_size?: number; width?: number; height?: number }>): string | null {
if (photos.length === 0) return null;
let best = photos[0];
for (const photo of photos) {
const score = (p: typeof photo) =>
p.file_size != null && p.file_size > 0 ? p.file_size : (p.width ?? 0) * (p.height ?? 0);
if (score(photo) > score(best)) best = photo;
}
return best.file_id;
}startTyping function · typescript · L114-L131 (18 LOC)src/handler.ts
function startTyping(config: TelegramConfig, chatId: number): TypingHandle {
let interval: ReturnType<typeof setInterval> | null = null;
const start = () => {
if (interval) return;
silentCatch("typing", "sendChatAction", sendChatAction(config, chatId));
interval = setInterval(() => {
silentCatch("typing", "sendChatAction", sendChatAction(config, chatId));
}, 4000);
};
const stop = () => {
if (interval) { clearInterval(interval); interval = null; }
};
start();
return { stop, pause: stop, resume: start };
}buildCanUseTool function · typescript · L157-L284 (128 LOC)src/handler.ts
function buildCanUseTool(ctx: HandlerContext, chatId: number, messageId: number, sessionId: string, flushRef?: FlushRef, toolMsgRef?: ToolMsgRef): CanUseToolFn {
// Serialize permission dialogs so only one is shown at a time
let permGate: Promise<void> = Promise.resolve();
return async (toolName, input, { decisionReason, toolUseID }) => {
// Helper: reveal tool block and return allow
const allowWithReveal = async () => {
if (toolMsgRef) await toolMsgRef.revealBlock(toolUseID);
return { behavior: "allow" as const, updatedInput: input };
};
// Guard: if session was suppressed (switched away), auto-allow without UI
if (isSessionSuppressed(sessionId)) {
return { behavior: "allow" as const, updatedInput: input };
}
// 1) AskUserQuestion → inline keyboard with options
if (toolName === "AskUserQuestion") {
// Flush accumulated text so the user sees context before the question
if (flushRef) await flushRef.flush();
cohandleMessage function · typescript · L287-L305 (19 LOC)src/handler.ts
export async function handleMessage(msg: Message, ctx: HandlerContext): Promise<void> {
const user = msg.from;
if (!isUserAllowed(user?.id, user?.username, ctx.allowedIds, ctx.allowedNames)) {
const userKey = String(user?.id || user?.username || msg.chat.id);
if (warnedUsers.has(userKey)) {
logger.debug("handler", `Blocked repeat unauthorized message from ${user?.username || user?.id || "unknown"}`);
return;
}
warnedUsers.add(userKey);
logger.warn("handler", `Unauthorized message from ${user?.username || user?.id || "unknown"}`);
await sendMessage(ctx.telegram, msg.chat.id, "Not authorized.", { replyToMessageId: msg.message_id });
return;
}
if (msg.voice || msg.audio) return handleVoiceMessage(msg, ctx);
if (msg.photo && msg.photo.length > 0) return handleImageMessage(msg, ctx, pickBestPhotoId(msg.photo), "photo");
if (msg.document && isImageDocument(msg.document)) return handleImageMessage(msg, ctx, msg.document.file_id, "document")All rows above produced by Repobility · https://repobility.com
handleTextMessage function · typescript · L307-L332 (26 LOC)src/handler.ts
async function handleTextMessage(msg: Message, ctx: HandlerContext): Promise<void> {
const chatId = msg.chat.id;
const messageId = msg.message_id;
const text = (msg.text || "").trim();
if (!text) return;
try {
logger.debug("text", `chat_id=${chatId} message_id=${messageId} text=${text}`);
// Check pending input (e.g. new project name)
const pendingType = consumePendingInput(chatId);
if (pendingType && !text.startsWith("/")) {
if (pendingType === "new_project") {
await handleNewProject(text, chatId, messageId, ctx);
return;
}
}
// Commands always handled immediately (regardless of busy state)
if (await handleCommand(text, chatId, messageId, ctx)) return;
await handlePrompt(text, chatId, messageId, ctx);
} catch (err) {
logger.error("handler", `Error in handleTextMessage: ${errorMessage(err)}`, err);
await sendMessage(ctx.telegram, chatId, `Error: ${escapeHtml(rewriteSdkError(errorMessage(err)))}`, { replyhandleImageMessage function · typescript · L334-L355 (22 LOC)src/handler.ts
async function handleImageMessage(msg: Message, ctx: HandlerContext, fileId: string | null | undefined, tag: string): Promise<void> {
const chatId = msg.chat.id;
const messageId = msg.message_id;
const caption = (msg.caption || "").trim();
const prompt = caption || "User sent an image.";
if (!fileId) {
logger.warn(tag, `No ${tag} data found chat_id=${chatId}`);
await sendMessage(ctx.telegram, chatId, `Error: No ${tag} data found.`, { replyToMessageId: messageId });
return;
}
try {
await sendMessage(ctx.telegram, chatId, "Processing your image...", { replyToMessageId: messageId });
logger.debug(tag, `chat_id=${chatId} message_id=${messageId} caption=${caption}`);
const imagePath = await downloadAndSaveImage(ctx.telegram, fileId);
await handlePrompt(prompt, chatId, messageId, ctx, [imagePath], false, true);
} catch (err) {
logger.error("handler", `Error in handleImageMessage: ${errorMessage(err)}`, err);
await sendMessage(ctx.telegramhandleVoiceMessage function · typescript · L357-L412 (56 LOC)src/handler.ts
async function handleVoiceMessage(msg: Message, ctx: HandlerContext): Promise<void> {
const chatId = msg.chat.id;
const messageId = msg.message_id;
const fileId = msg.voice?.file_id || msg.audio?.file_id;
if (!fileId) {
logger.warn("voice", `No audio data found chat_id=${chatId}`);
await sendMessage(ctx.telegram, chatId, "Error: No audio data found.", { replyToMessageId: messageId });
return;
}
try {
logger.debug("voice", `chat_id=${chatId} message_id=${messageId}`);
if (!isSttReady()) {
logger.warn("whisper", "Speech-to-text not set up");
const notReadyText = isMacOS()
? "Speech-to-text is not set up.\nRun: remotecode setup-stt"
: "Speech-to-text is currently not supported on Linux.";
await sendMessage(ctx.telegram, chatId, notReadyText, { replyToMessageId: messageId });
return;
}
await sendMessage(ctx.telegram, chatId, "Transcribing audio...", { replyToMessageId: messageId });
const { data, filePahandleNewProject function · typescript · L415-L467 (53 LOC)src/handler.ts
async function handleNewProject(
input: string,
chatId: number,
messageId: number,
ctx: HandlerContext,
): Promise<void> {
// Validate input
if (!input || input.includes("..") || path.isAbsolute(input)) {
await sendMessage(ctx.telegram, chatId, "Invalid path.", { replyToMessageId: messageId });
return;
}
const sanitized = input.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\/|\/$/g, "");
if (!sanitized) {
await sendMessage(ctx.telegram, chatId, "Invalid path.", { replyToMessageId: messageId });
return;
}
const fullPath = path.join(os.homedir(), sanitized);
// Check if final directory already exists
if (fs.existsSync(fullPath)) {
await sendMessage(ctx.telegram, chatId, `Already exists: <code>~/${escapeHtml(sanitized)}</code>`, {
replyToMessageId: messageId,
parseMode: "HTML",
});
return;
}
// Create parent directories if needed, then final directory
try {
const parentDir = path.dirname(fullPath);
fshandlePrompt function · typescript · L470-L507 (38 LOC)src/handler.ts
export async function handlePrompt(
prompt: string,
chatId: number,
messageId: number,
ctx: HandlerContext,
imagePaths?: string[],
voiceMode?: boolean,
quiet?: boolean,
): Promise<void> {
const sessionId = getOrCreateSessionId(ctx.sessionsFile);
const sessionCwd = loadSessionCwd(ctx.sessionsFile);
const cwd = sessionCwd || defaultCwd();
if (!fs.existsSync(cwd)) {
await sendMessage(ctx.telegram, chatId, `Working directory not found: ${cwd}\nSwitch session or set a valid project path.`, {
replyToMessageId: messageId,
});
return;
}
const formattedPrompt = formatPrompt(prompt, imagePaths);
// If this session is busy, queue the message
if (isSessionBusy(sessionId)) {
// Pending AskUserQuestion → use text as the answer (don't queue as new prompt)
if (hasPendingAsks()) {
resolveAsksWithText(typeof formattedPrompt === "string" ? formattedPrompt : prompt);
updateReplyTarget(sessionId, messageId);
return;
}
enqueustreamResponse function · typescript · L515-L706 (192 LOC)src/handler.ts
async function streamResponse(
sessionId: string,
prompt: MessageContent,
chatId: number,
messageId: number,
ctx: HandlerContext,
options: { cwd: string; model?: string; typingHandle?: TypingHandle; quiet?: boolean },
): Promise<StreamResult> {
const textParts: string[] = [];
let gotResult = false;
// Mutable reply target: updated when user answers AskUserQuestion via text
const getReplyId = () => getReplyTarget(sessionId) ?? messageId;
// Track the last tool description message so sequential tool_use
// blocks are edited into the same Telegram message instead of spamming.
let toolMsgId: number | null = null;
let toolBlocks: ToolBlock[] = [];
function resetToolMsg(): void {
toolMsgId = null;
toolBlocks = [];
}
function renderToolBlocks(): string {
return toolBlocks
.filter(b => b.visible)
.map(b => b.status ? `${b.desc}\n${b.status}` : b.desc)
.join("\n");
}
// Mutable flush ref: canUseTool calls this to send accumrenderToolBlocks function · typescript · L538-L543 (6 LOC)src/handler.ts
function renderToolBlocks(): string {
return toolBlocks
.filter(b => b.visible)
.map(b => b.status ? `${b.desc}\n${b.status}` : b.desc)
.join("\n");
}sendFinalResponse function · typescript · L709-L737 (29 LOC)src/handler.ts
async function sendFinalResponse(
textParts: string[],
prompt: string,
chatId: number,
messageId: number,
ctx: HandlerContext,
voiceMode?: boolean,
): Promise<void> {
const fullText = stripThinking(textParts.join("\n"));
if (!fullText) return;
if (voiceMode) {
const userHtml = mdToTelegramHtml(prompt);
const botHtml = mdToTelegramHtml(truncateMessage(fullText, 3200));
const formatted = `<blockquote><b><code>You:</code></b>\n${userHtml}\n\n<b><code>Bot:</code></b>\n${botHtml}</blockquote>`;
await sendMessage(ctx.telegram, chatId, formatted, {
replyToMessageId: messageId,
replyMarkup: sessionsReplyKeyboard(ctx.sessionsFile),
parseMode: "HTML",
});
} else {
const formatted = tryMdToHtml(fullText);
await sendMessage(ctx.telegram, chatId, formatted.text, {
replyToMessageId: messageId,
replyMarkup: sessionsReplyKeyboard(ctx.sessionsFile),
parseMode: formatted.parseMode,
});
}
}Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
executePrompt function · typescript · L740-L817 (78 LOC)src/handler.ts
async function executePrompt(
sessionId: string,
prompt: MessageContent,
chatId: number,
messageId: number,
ctx: HandlerContext,
voiceMode?: boolean,
quiet?: boolean,
): Promise<void> {
const cwd = loadSessionCwd(ctx.sessionsFile) || defaultCwd();
const model = loadModel(ctx.sessionsFile);
// Cancel any pending cleanup timeout — we're starting a new query for this session
clearCleanupTimeout(sessionId);
clearPermDenied(sessionId);
markProcessing(sessionId);
activeQueries.add(sessionId);
const typingHandle = startTyping(ctx.telegram, chatId);
try {
const { textParts, gotResult } = await streamResponse(
sessionId, prompt, chatId, messageId, ctx, { cwd, model, typingHandle, quiet },
);
if (!gotResult) return;
// Don't send final response if session was switched away
if (!isSessionSuppressed(sessionId)) {
const replyId = getReplyTarget(sessionId) ?? messageId;
const promptText = typeof prompt === "string" ? prompt :main function · typescript · L12-L118 (107 LOC)src/index.ts
async function main(): Promise<void> {
const args = process.argv.slice(2);
// Internal flag: run as daemon process
if (args.includes("--daemon")) {
await daemonMain();
return;
}
// Verbose flag
if (args.includes("-v") || args.includes("--verbose")) {
process.env.REMOTECODE_VERBOSE = "1";
}
const command = args.find((a) => !a.startsWith("-")) || "";
switch (command) {
case "start":
await cmdStart();
break;
case "stop":
await cmdStop();
break;
case "restart":
await cmdRestart();
break;
case "status":
cmdStatus();
break;
case "logs": {
const levelIdx = args.indexOf("--level");
const tagIdx = args.indexOf("--tag");
const nIdx = args.indexOf("-n");
let lines = 50;
let follow = true;
if (nIdx !== -1 && args[nIdx + 1]) {
const n = parseInt(args[nIdx + 1], 10);
if (!isNaN(n) && n > 0) lines = n;
follow = false; // -n implies statiparseJsonlLines function · typescript · L4-L13 (10 LOC)src/jsonl.ts
export function* parseJsonlLines(content: string, tag: string = "jsonl"): Generator<Record<string, unknown>> {
for (const line of content.split("\n")) {
if (!line.trim()) continue;
try {
yield JSON.parse(line) as Record<string, unknown>;
} catch {
logger.debug(tag, `skipped invalid JSONL line: ${line.slice(0, 80)}`);
}
}
}extractMessageContent function · typescript · L16-L27 (12 LOC)src/jsonl.ts
export function extractMessageContent(content: unknown): string {
if (typeof content === "string") return content;
if (Array.isArray(content)) {
for (const block of content) {
if (block && typeof block === "object" && (block as Record<string, unknown>).type === "text") {
const text = (block as Record<string, unknown>).text;
if (typeof text === "string") return text;
}
}
}
return "";
}cleanUserMessage function · typescript · L30-L36 (7 LOC)src/jsonl.ts
export function cleanUserMessage(text: string): string {
let msg = text;
if (msg.startsWith("User said:\n")) msg = msg.slice("User said:\n".length);
msg = msg.replace(/\n+Reply concisely\.?\s*$/, "");
msg = msg.replace(/\n+Image file path\(s\):.*/s, "");
return msg.trim();
}cleanFirstMessage function · typescript · L39-L46 (8 LOC)src/jsonl.ts
export function cleanFirstMessage(text: string): string | null {
const msg = cleanUserMessage(text);
if (!msg || msg.startsWith("<") || msg.startsWith("#")) return null;
const firstLine = msg.split("\n", 1)[0].trim();
if (!firstLine) return null;
if (firstLine.length > 60) return firstLine.slice(0, 57) + "...";
return firstLine;
}setSessionToolAllow function · typescript · L17-L24 (8 LOC)src/session-state.ts
export function setSessionToolAllow(sessionId: string, toolName: string): void {
let tools = sessionAutoAllowTools.get(sessionId);
if (!tools) {
tools = new Set();
sessionAutoAllowTools.set(sessionId, tools);
}
tools.add(toolName);
}drainNext function · typescript · L99-L105 (7 LOC)src/session-state.ts
export function drainNext(sessionId: string): QueuedMessage | null {
const queue = messageQueue.get(sessionId);
if (!queue || queue.length === 0) return null;
const next = queue.shift()!;
if (queue.length === 0) messageQueue.delete(sessionId);
return next;
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
decodeProjectPath function · typescript · L45-L64 (20 LOC)src/sessions.ts
export function decodeProjectPath(encodedDir: string): string {
// Claude Code encodes: / -> -, _ -> -, leading . stripped (-- means /.)
// Since both / and _ map to -, we resolve ambiguity by checking the filesystem.
// Split on -- first (hidden dir boundary: /. ), then split each part on -
const raw = encodedDir.replace(/^-/, "");
const hiddenParts = raw.split("--");
const segments: string[] = [];
for (let h = 0; h < hiddenParts.length; h++) {
const segs = hiddenParts[h].split("-").filter(Boolean);
if (h > 0 && segs.length > 0) {
// -- means /. so prepend . to the first segment after --
segs[0] = "." + segs[0];
}
segments.push(...segs);
}
return resolvePathSegments("/", segments);
}resolvePathSegments function · typescript · L66-L85 (20 LOC)src/sessions.ts
function resolvePathSegments(base: string, segments: string[]): string {
if (segments.length === 0) return base;
// Try greedily joining segments with _ and check filesystem
for (let take = segments.length; take >= 1; take--) {
const candidate = segments.slice(0, take).join("_");
const full = path.join(base, candidate);
if (fs.existsSync(full)) {
if (take === segments.length) return full;
return resolvePathSegments(full, segments.slice(take));
}
}
// Fallback: treat first segment as a directory (original / encoding)
const first = segments[0];
const rest = segments.slice(1);
const fallback = path.join(base, first);
if (rest.length === 0) return fallback;
return resolvePathSegments(fallback, rest);
}projectDisplayName function · typescript · L87-L92 (6 LOC)src/sessions.ts
function projectDisplayName(encodedDir: string): string {
const decoded = decodeProjectPath(encodedDir);
const home = os.homedir();
if (decoded.replace(/\/$/, "") === home.replace(/\/$/, "")) return "~/";
return decoded.replace(/\/$/, "").split("/").pop() || decoded;
}parseSessionFile function · typescript · L102-L125 (24 LOC)src/sessions.ts
function parseSessionFile(filePath: string): ParsedSession | null {
try {
const content = fs.readFileSync(filePath, "utf-8");
let slug: string | null = null;
let firstMessage: string | null = null;
let lastMessage: string | null = null;
for (const entry of parseJsonlLines(content, "sessions")) {
if (!slug && entry.slug) slug = entry.slug as string;
if (entry.type === "user" && !entry.isMeta) {
const msgObj = entry.message as Record<string, unknown> | undefined;
const msgContent = extractMessageContent(msgObj?.content);
if (msgContent.trim() && !msgContent.startsWith("<")) {
const cleaned = cleanFirstMessage(msgContent.trim());
if (!firstMessage) firstMessage = cleaned;
lastMessage = cleaned;
}
}
}
return { slug, firstMessage, lastMessage };
} catch {
return null;
}
}saveActiveSessionId function · typescript · L132-L137 (6 LOC)src/sessions.ts
export function saveActiveSessionId(sessionsFile: string, sessionId: string | null): void {
let lines = readEnvLines(sessionsFile);
lines = lines.filter((l) => !l.trim().startsWith("REMOTECODE_SESSION_CLAUDE="));
if (sessionId) lines.push(`REMOTECODE_SESSION_CLAUDE=${sessionId}`);
writeEnvLines(sessionsFile, lines);
}getOrCreateSessionId function · typescript · L139-L145 (7 LOC)src/sessions.ts
export function getOrCreateSessionId(sessionsFile: string): string {
const existing = loadActiveSessionId(sessionsFile);
if (existing) return existing;
const newId = uuidv4();
saveActiveSessionId(sessionsFile, newId);
return newId;
}createNewSession function · typescript · L147-L152 (6 LOC)src/sessions.ts
export function createNewSession(sessionsFile: string, cwd?: string): string {
const newId = uuidv4();
saveActiveSessionId(sessionsFile, newId);
saveSessionCwd(sessionsFile, cwd || "");
return newId;
}saveSessionCwd function · typescript · L158-L163 (6 LOC)src/sessions.ts
export function saveSessionCwd(sessionsFile: string, cwd: string): void {
let lines = readEnvLines(sessionsFile);
lines = lines.filter((l) => !l.trim().startsWith("REMOTECODE_SESSION_CLAUDE_CWD="));
if (cwd) lines.push(`REMOTECODE_SESSION_CLAUDE_CWD=${cwd}`);
writeEnvLines(sessionsFile, lines);
}Open data scored by Repobility · https://repobility.com
findSessionFilePath function · typescript · L170-L182 (13 LOC)src/sessions.ts
export function findSessionFilePath(sessionId: string): string | null {
const pDir = projectsDir();
if (!fs.existsSync(pDir)) return null;
try {
for (const dir of fs.readdirSync(pDir)) {
const dirPath = path.join(pDir, dir);
if (!fs.statSync(dirPath).isDirectory()) continue;
const candidate = path.join(dirPath, `${sessionId}.jsonl`);
if (fs.existsSync(candidate)) return candidate;
}
} catch (err) { logger.debug("sessions", `findSessionFilePath scan: ${errorMessage(err)}`); }
return null;
}candidateToSessionInfo function · typescript · L198-L214 (17 LOC)src/sessions.ts
function candidateToSessionInfo(
mtime: number,
filePath: string,
encodedDir: string
): SessionInfo | null {
const parsed = parseSessionFile(filePath);
if (!parsed) return null;
return {
sessionId: path.basename(filePath, ".jsonl"),
slug: parsed.slug,
project: decodeProjectPath(encodedDir),
projectName: projectDisplayName(encodedDir),
lastModified: mtime,
firstMessage: parsed.firstMessage,
lastMessage: parsed.lastMessage,
};
}discoverSessions function · typescript · L217-L240 (24 LOC)src/sessions.ts
export function discoverSessions(limit: number = 10): SessionInfo[] {
const pDir = projectsDir();
if (!fs.existsSync(pDir)) return [];
const all: Array<{ mtime: number; filePath: string; encodedDir: string }> = [];
try {
for (const dir of fs.readdirSync(pDir)) {
const dirPath = path.join(pDir, dir);
if (!fs.statSync(dirPath).isDirectory()) continue;
for (const c of listSessionCandidates(dirPath)) {
all.push({ ...c, encodedDir: dir });
}
}
} catch { return []; }
all.sort((a, b) => b.mtime - a.mtime);
const results: SessionInfo[] = [];
for (const { mtime, filePath, encodedDir } of all.slice(0, limit)) {
const info = candidateToSessionInfo(mtime, filePath, encodedDir);
if (info) results.push(info);
}
return results;
}