Function bodies 137 total
getVersion function · typescript · L4-L11 (8 LOC)src/banner.ts
function getVersion(): string {
try {
const pkgPath = path.join(__dirname, "..", "package.json");
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version || "0.0.0";
} catch {
return "0.0.0";
}
}stopBannerResize function · typescript · L16-L21 (6 LOC)src/banner.ts
export function stopBannerResize(): void {
if (_bannerCleanup) {
_bannerCleanup();
_bannerCleanup = null;
}
}printBanner function · typescript · L23-L129 (107 LOC)src/banner.ts
export function printBanner(contentLines?: string[]): void {
stopBannerResize();
const version = getVersion();
const tty = process.stdout.isTTY;
const c = tty ? "\x1b[36m" : "";
const r = tty ? "\x1b[0m" : "";
const d = tty ? "\x1b[2m" : "";
const bo = tty ? "\x1b[1m" : "";
function render(cols: number): string[] {
const inner = cols - 2;
const dw = (s: string): number => {
let n = 0;
for (const ch of s) {
const cp = ch.codePointAt(0) || 0;
n += (cp >= 0x1100 && cp <= 0x115F) || (cp >= 0x2E80 && cp <= 0x33BF) ||
(cp >= 0x3400 && cp <= 0x4DBF) || (cp >= 0x4E00 && cp <= 0xA4CF) ||
(cp >= 0xAC00 && cp <= 0xD7FF) || (cp >= 0xF900 && cp <= 0xFAFF) ||
(cp >= 0xFE30 && cp <= 0xFE6F) || (cp >= 0xFF01 && cp <= 0xFF60) ||
(cp >= 0x20000 && cp <= 0x2FA1F) ? 2 : 1;
}
return n;
};
const fit = (s: string, w: number): string => {
const sw = dw(s);
if (sw <= w) return s + " "render function · typescript · L33-L93 (61 LOC)src/banner.ts
function render(cols: number): string[] {
const inner = cols - 2;
const dw = (s: string): number => {
let n = 0;
for (const ch of s) {
const cp = ch.codePointAt(0) || 0;
n += (cp >= 0x1100 && cp <= 0x115F) || (cp >= 0x2E80 && cp <= 0x33BF) ||
(cp >= 0x3400 && cp <= 0x4DBF) || (cp >= 0x4E00 && cp <= 0xA4CF) ||
(cp >= 0xAC00 && cp <= 0xD7FF) || (cp >= 0xF900 && cp <= 0xFAFF) ||
(cp >= 0xFE30 && cp <= 0xFE6F) || (cp >= 0xFF01 && cp <= 0xFF60) ||
(cp >= 0x20000 && cp <= 0x2FA1F) ? 2 : 1;
}
return n;
};
const fit = (s: string, w: number): string => {
const sw = dw(s);
if (sw <= w) return s + " ".repeat(w - sw);
let out = "", ow = 0;
for (const ch of s) {
const cw = dw(ch);
if (ow + cw > w - 1) break;
out += ch; ow += cw;
}
return out + "\u2026" + " ".repeat(Math.max(0, w - ow - 1));
};
const row = (content: string) => c + "\draw function · typescript · L99-L115 (17 LOC)src/banner.ts
function draw(): void {
const cols = Math.max(24, process.stdout.columns || 80);
// Erase previous render, accounting for line wrapping
if (prevWidths.length > 0 && tty) {
let up = 0;
for (const w of prevWidths) {
up += w === 0 ? 1 : Math.ceil(w / cols);
}
process.stdout.write(`\x1b[${up}A\x1b[0J`);
}
const lines = render(cols);
// Store visible widths (ANSI stripped) for next redraw
prevWidths = lines.map((l) => l.replace(/\x1b\[[0-9;]*m/g, "").length);
process.stdout.write(lines.join("\n") + "\n");
}registerPendingInput function · typescript · L44-L49 (6 LOC)src/callbacks.ts
export function registerPendingInput(chatId: number, type: "new_project"): void {
const prev = pendingInputs.get(chatId);
if (prev) clearTimeout(prev.timer);
const timer = setTimeout(() => { pendingInputs.delete(chatId); }, PENDING_INPUT_TIMEOUT_MS);
pendingInputs.set(chatId, { type, timer });
}consumePendingInput function · typescript · L51-L57 (7 LOC)src/callbacks.ts
export function consumePendingInput(chatId: number): PendingInput["type"] | null {
const pending = pendingInputs.get(chatId);
if (!pending) return null;
clearTimeout(pending.timer);
pendingInputs.delete(chatId);
return pending.type;
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
registerPendingAsk function · typescript · L92-L100 (9 LOC)src/callbacks.ts
export function registerPendingAsk(id: string, meta?: AskMeta, msgInfo?: PermMsgInfo): Promise<Record<string, string>> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
pendingAsk.delete(id);
reject(new Error("Timeout waiting for user response"));
}, PENDING_TIMEOUT_MS);
pendingAsk.set(id, { resolve, timer, meta, msgInfo });
});
}registerPendingPerm function · typescript · L102-L110 (9 LOC)src/callbacks.ts
export function registerPendingPerm(id: string, meta: PermMeta, msgInfo?: PermMsgInfo): Promise<"allow" | "deny" | "tool" | "yolo"> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
pendingPerm.delete(id);
reject(new Error("Timeout waiting for permission response"));
}, PENDING_TIMEOUT_MS);
pendingPerm.set(id, { resolve, timer, meta, msgInfo });
});
}denyAllPending function · typescript · L112-L132 (21 LOC)src/callbacks.ts
export function denyAllPending(): void {
for (const [, pending] of pendingPerm) {
clearTimeout(pending.timer);
pending.resolve("deny");
permDenied.add(pending.meta.sessionId);
if (pending.msgInfo) {
silentCatch("callback", "editPermDenied",
editMessageText(pending.msgInfo.telegram, pending.msgInfo.chatId, pending.msgInfo.sentMessageId, "Cancelled"));
}
}
pendingPerm.clear();
for (const [, pending] of pendingAsk) {
clearTimeout(pending.timer);
pending.resolve({ answer: "" });
if (pending.msgInfo) {
silentCatch("callback", "editAskDenied",
editMessageText(pending.msgInfo.telegram, pending.msgInfo.chatId, pending.msgInfo.sentMessageId, "Cancelled"));
}
}
pendingAsk.clear();
}allowAllPending function · typescript · L134-L153 (20 LOC)src/callbacks.ts
export function allowAllPending(): void {
for (const [, pending] of pendingPerm) {
clearTimeout(pending.timer);
pending.resolve("allow");
if (pending.msgInfo) {
silentCatch("callback", "editPermAllowed",
editMessageText(pending.msgInfo.telegram, pending.msgInfo.chatId, pending.msgInfo.sentMessageId, "Allowed"));
}
}
pendingPerm.clear();
for (const [, pending] of pendingAsk) {
clearTimeout(pending.timer);
pending.resolve({ answer: "" });
if (pending.msgInfo) {
silentCatch("callback", "editAskSkipped",
editMessageText(pending.msgInfo.telegram, pending.msgInfo.chatId, pending.msgInfo.sentMessageId, "Skipped"));
}
}
pendingAsk.clear();
}resolveAsksWithText function · typescript · L164-L177 (14 LOC)src/callbacks.ts
export function resolveAsksWithText(text: string): boolean {
if (pendingAsk.size === 0) return false;
for (const [, pending] of pendingAsk) {
clearTimeout(pending.timer);
pending.resolve({ answer: text });
if (pending.msgInfo) {
silentCatch("callback", "editAskTextResolve",
editMessageText(pending.msgInfo.telegram, pending.msgInfo.chatId, pending.msgInfo.sentMessageId,
`Answered: ${escapeHtml(text.length > 200 ? text.slice(0, 200) + "…" : text)}`, { parseMode: "HTML" }));
}
}
pendingAsk.clear();
return true;
}stopOldSession function · typescript · L191-L208 (18 LOC)src/callbacks.ts
export function stopOldSession(sessionsFile: string, newSessionId?: string): void {
const oldId = loadActiveSessionId(sessionsFile);
if (oldId && oldId !== newSessionId) {
if (isSessionBusy(oldId)) {
// Keep work running, just suppress messages and auto-allow permissions
suppressSessionMessages(oldId);
setSessionAutoAllow(oldId);
allowAllPending();
} else {
resetSessionAutoAllow(oldId);
denyAllPending();
}
}
// Unsuppress new session in case it was running in background
if (newSessionId) {
unsuppressSessionMessages(newSessionId);
}
}handleCallbackQuery function · typescript · L240-L271 (32 LOC)src/callbacks.ts
export async function handleCallbackQuery(callback: CallbackQuery, ctx: HandlerContext): Promise<void> {
const user = callback.from;
silentCatch("callback", "answerCallbackQuery", answerCallbackQuery(ctx.telegram, callback.id));
if (!isUserAllowed(user.id, user.username, ctx.allowedIds, ctx.allowedNames)) {
const chatId = callback.message?.chat.id;
const messageId = callback.message?.message_id;
if (chatId !== undefined && messageId !== undefined) {
await sendMessage(ctx.telegram, chatId, "Not authorized.", { replyToMessageId: messageId });
}
return;
}
const data = (callback.data || "").trim();
const chatId = callback.message?.chat.id;
const messageId = callback.message?.message_id;
if (!data || chatId === undefined || messageId === undefined) return;
try {
logger.debug("callback", `chat_id=${chatId} message_id=${messageId} data=${data}`);
if (data.startsWith("proj:")) return handleProjectCallback(data, chatId, messageId, ctx);
handleAskCallback function · typescript · L274-L304 (31 LOC)src/callbacks.ts
async function handleAskCallback(
data: string,
chatId: number,
messageId: number,
ctx: HandlerContext
): Promise<void> {
// Format: "ask:<id>:<optionIndex>:<label>"
const parts = data.split(":");
if (parts.length < 4) { logger.warn("callback", `malformed ask data: ${data}`); return; }
const id = parts[1];
const label = parts.slice(3).join(":");
const pending = pendingAsk.get(id);
if (pending) {
clearTimeout(pending.timer);
pendingAsk.delete(id);
pending.resolve({ answer: label });
}
let displayText: string;
if (label) {
displayText = `Selected: ${label}`;
} else {
const meta = pending?.meta;
if (meta) {
const quoted = `${meta.question}\n${meta.options.map(o => `- ${o}`).join("\n")}`;
displayText = `<blockquote>${escapeHtml(quoted)}</blockquote>\nAnswer skipped`;
} else {
displayText = "Answer skipped";
}
}
silentCatch("callback", "editAskResponse", editMessageText(ctx.telegram, chatId, messageId, displProvenance: Repobility (https://repobility.com) — every score reproducible from /scan/
handlePermCallback function · typescript · L307-L343 (37 LOC)src/callbacks.ts
async function handlePermCallback(
data: string,
chatId: number,
messageId: number,
ctx: HandlerContext
): Promise<void> {
// Formats: perm:<id>:allow | perm:<id>:deny | perm:<id>:tool | perm:<id>:yolo
const parts = data.split(":");
if (parts.length < 3) { logger.warn("callback", `malformed perm data: ${data}`); return; }
const id = parts[1];
const decision = parts[2];
const pending = pendingPerm.get(id);
let label = decision;
if (pending) {
clearTimeout(pending.timer);
pendingPerm.delete(id);
const { sessionId, toolName } = pending.meta;
if (decision === "tool") {
setSessionToolAllow(sessionId, toolName);
pending.resolve("tool");
} else if (decision === "yolo") {
setSessionAutoAllow(sessionId);
pending.resolve("yolo");
} else if (decision === "allow") {
pending.resolve("allow");
} else {
pending.resolve("deny");
}
// Message deletion + status append is handled by handler's canUseTool
handleModelCallback function · typescript · L346-L355 (10 LOC)src/callbacks.ts
async function handleModelCallback(
data: string,
chatId: number,
messageId: number,
ctx: HandlerContext
): Promise<void> {
const model = data.slice("model:".length);
saveModel(ctx.sessionsFile, model);
silentCatch("callback", "editModelResponse", editMessageText(ctx.telegram, chatId, messageId, `Model: ${model}`));
}handleProjectCallback function · typescript · L358-L416 (59 LOC)src/callbacks.ts
async function handleProjectCallback(
data: string,
chatId: number,
messageId: number,
ctx: HandlerContext
): Promise<void> {
const action = data.slice("proj:".length);
if (action === "noop") return;
if (action === "list" || action === "back") {
try {
const projDisplay = buildProjectListDisplay();
await editMessageText(ctx.telegram, chatId, messageId, projDisplay.text, {
parseMode: "HTML",
replyMarkup: { inline_keyboard: projDisplay.buttons },
});
} catch (err) { logger.debug("callback", `editMessageText projects: ${errorMessage(err)}`); }
return;
}
if (action === "add") {
registerPendingInput(chatId, "new_project");
silentCatch("callback", "deleteMessage proj:add", deleteMessage(ctx.telegram, chatId, messageId));
await sendMessage(ctx.telegram, chatId, "Enter project path (e.g. <code>myapp</code> or <code>work/myapp</code>):", {
parseMode: "HTML",
replyMarkup: { force_reply: true, selective: truehandleSessionCallback function · typescript · L419-L474 (56 LOC)src/callbacks.ts
async function handleSessionCallback(
data: string,
chatId: number,
messageId: number,
ctx: HandlerContext
): Promise<void> {
const action = data.slice("sess:".length);
if (action === "noop") return;
if (action === "list") {
const sessions = discoverSessions(5);
const activeId = loadActiveSessionId(ctx.sessionsFile);
const display = buildSessionDisplay(sessions, activeId);
try {
await editMessageText(ctx.telegram, chatId, messageId, display.text, {
parseMode: "HTML",
replyMarkup: { inline_keyboard: display.buttons },
});
} catch (err) { logger.debug("callback", `editMessageText sessList: ${errorMessage(err)}`); }
return;
}
if (action === "close") {
silentCatch("callback", "deleteMessage sess:close", deleteMessage(ctx.telegram, chatId, messageId));
return;
}
if (action === "new") {
stopOldSession(ctx.sessionsFile);
createNewSession(ctx.sessionsFile);
silentCatch("callback", "deleteMessage sehandleSessionDeleteCallback function · typescript · L477-L500 (24 LOC)src/callbacks.ts
async function handleSessionDeleteCallback(
data: string,
chatId: number,
messageId: number,
ctx: HandlerContext
): Promise<void> {
const sessionId = data.slice("sessdel:".length);
if (loadActiveSessionId(ctx.sessionsFile) === sessionId) {
await sendMessage(ctx.telegram, chatId, "Cannot delete the active session.", { replyToMessageId: messageId });
return;
}
const session = findSession(sessionId);
const label = session ? formatSessionLabel(session) : sessionId.slice(0, 8);
if (!deleteSession(sessionId)) {
await sendMessage(ctx.telegram, chatId, "Session file not found.", { replyToMessageId: messageId });
return;
}
silentCatch("callback", "deleteMessage sessDel", deleteMessage(ctx.telegram, chatId, messageId));
await sendMessage(ctx.telegram, chatId, `Deleted: ${label}`, { replyMarkup: sessionsReplyKeyboard(ctx.sessionsFile) });
}MessageChannel.push method · typescript · L48-L55 (8 LOC)src/claude.ts
push(msg: SDKUserMessage): void {
this.queue.push(msg);
if (this.waiter) {
const resolve = this.waiter;
this.waiter = null;
resolve();
}
}MessageChannel.close method · typescript · L57-L64 (8 LOC)src/claude.ts
close(): void {
this.closed = true;
if (this.waiter) {
const resolve = this.waiter;
this.waiter = null;
resolve();
}
}MessageChannel.next method · typescript · L69-L79 (11 LOC)src/claude.ts
async next(): Promise<IteratorResult<SDKUserMessage>> {
while (!self.closed) {
if (self.queue.length > 0) {
return { value: self.queue.shift()!, done: false };
}
await new Promise<void>((resolve) => {
self.waiter = resolve;
});
}
return { value: undefined, done: true } as IteratorReturnResult<undefined>;
},Open data scored by Repobility · https://repobility.com
createUserMessage function · typescript · L89-L96 (8 LOC)src/claude.ts
function createUserMessage(content: MessageContent, sessionId: string): SDKUserMessage {
return {
type: "user",
message: { role: "user", content } as SDKUserMessage["message"],
parent_tool_use_id: null,
session_id: sessionId,
};
}closeSession function · typescript · L115-L123 (9 LOC)src/claude.ts
export function closeSession(sessionId: string): void {
const s = sessions.get(sessionId);
if (s) {
logger.debug("claude", `closing session ${sessionId.slice(0, 8)}`);
s.channel.close();
s.q.close();
sessions.delete(sessionId);
}
}interruptSession function · typescript · L125-L132 (8 LOC)src/claude.ts
export function interruptSession(sessionId: string): void {
const s = sessions.get(sessionId);
if (s) {
logger.debug("claude", `interrupting session ${sessionId.slice(0, 8)}`);
s.interrupted = true;
s.q.interrupt().catch(() => {});
}
}wasSessionInterrupted function · typescript · L134-L140 (7 LOC)src/claude.ts
export function wasSessionInterrupted(sessionId: string): boolean {
const s = sessions.get(sessionId);
if (!s) return false;
const val = s.interrupted;
s.interrupted = false;
return val;
}markSessionStale function · typescript · L146-L152 (7 LOC)src/claude.ts
export function markSessionStale(sessionId: string): void {
const s = sessions.get(sessionId);
if (s) {
logger.debug("claude", `marking session ${sessionId.slice(0, 8)} as stale (external changes detected)`);
s.stale = true;
}
}initNewSession function · typescript · L156-L209 (54 LOC)src/claude.ts
function initNewSession(
prompt: MessageContent,
sessionId: string,
options: QueryOptions,
resume: boolean,
): SessionState {
logger.debug(
"claude",
`new session: ${sessionId.slice(0, 8)} resume=${resume} model=${options.model || "default"}`,
);
const canUseToolRef: { current: CanUseToolFn | undefined } = {
current: options.canUseTool,
};
const wrappedCanUseTool: CanUseToolFn = (toolName, input, opts) => {
if (!canUseToolRef.current) {
return Promise.resolve({ behavior: "deny" as const, message: "No handler" });
}
return canUseToolRef.current(toolName, input, opts);
};
const channel = new MessageChannel();
channel.push(createUserMessage(prompt, sessionId));
const q = query({
prompt: channel,
options: {
...(resume ? { resume: sessionId } : { sessionId }),
cwd: options.cwd,
model: options.model,
permissionMode: options.yolo ? "bypassPermissions" : undefined,
allowDangerouslySkipPermissions: querySession function · typescript · L213-L274 (62 LOC)src/claude.ts
export async function* querySession(
prompt: MessageContent,
options: QueryOptions,
): AsyncGenerator<SDKMessage> {
const sessionId = options.sessionId!;
let session = sessions.get(sessionId);
// Stale session: external changes detected, recreate to pick up new JSONL context
if (session?.stale) {
logger.debug("claude", `recreating stale session ${sessionId.slice(0, 8)}`);
session.channel.close();
session.q.close();
sessions.delete(sessionId);
session = undefined;
}
if (session) {
// Wait for session's turn lock
await session.turnLock;
session.turnLock = new Promise((r) => { session!.releaseTurn = r; });
logger.debug("claude", `reusing session ${sessionId.slice(0, 8)}`);
// Update canUseTool reference for new message's context
session.canUseToolRef.current = options.canUseTool;
// Update model if needed
if (options.model) {
try { await session.q.setModel(options.model); } catch (err) { logger.debug("claude", readUntilResult function · typescript · L279-L307 (29 LOC)src/claude.ts
async function* readUntilResult(sessionId: string): AsyncGenerator<SDKMessage> {
const s = sessions.get(sessionId);
if (!s) return;
while (true) {
let result: IteratorResult<SDKMessage, void>;
try {
result = await s.q.next();
} catch (err) {
// Session may have been closed externally
if (!sessions.has(sessionId)) return;
logger.error("claude", `session ${sessionId.slice(0, 8)} read error: ${err}`);
sessions.delete(sessionId);
throw err;
}
if (result.done) {
logger.debug("claude", `session ${sessionId.slice(0, 8)} iterator ended`);
sessions.delete(sessionId);
return;
}
yield result.value;
if (result.value.type === "result") {
return;
}
}
}About: code-quality intelligence by Repobility · https://repobility.com
cmdStart function · typescript · L13-L37 (25 LOC)src/cli.ts
export async function cmdStart(opts?: { followLogs?: boolean }): Promise<void> {
const { running, pid } = isDaemonRunning();
if (running) {
printBanner(["Already running (pid " + pid + ").", "Use 'remotecode restart' to restart."]);
process.exit(1);
}
// runSetupIfNeeded prints its own banner on first run
const needsSetup = !fs.existsSync(path.join(globalConfigDir(), "config"));
await runSetupIfNeeded();
stopBannerResize();
const configPath = loadConfig();
const config = getConfig();
if (config.yolo && isPrivileged()) {
console.error("\x1b[31mError: YOLO mode cannot run with root/sudo privileges (Claude Code restriction).\x1b[0m");
console.error("\x1b[31mEither switch to a non-root user or set REMOTECODE_YOLO=false in config.\x1b[0m");
process.exit(1);
}
if (!needsSetup && configPath) printBanner(["Config: " + configPath]);
killOrphanDaemons();
await spawnDaemon();
cmdStatus();
if (opts?.followLogs !== false) {
cmdLogs({ follow: tcmdStop function · typescript · L39-L68 (30 LOC)src/cli.ts
export async function cmdStop(): Promise<void> {
const { running, pid } = isDaemonRunning();
if (!running || pid === null) {
// PID file missing -- try to kill orphans
killOrphanDaemons();
return;
}
// Collect ALL descendant PIDs before killing (SDK may spawn claude in separate process groups)
const descendants = collectDescendants(pid);
// Kill entire process group (daemon + direct children in same group)
try { process.kill(-pid, "SIGTERM"); } catch { process.kill(pid, "SIGTERM"); }
// Wait for daemon to exit
for (let i = 0; i < 30; i++) {
await new Promise((resolve) => setTimeout(resolve, 100));
if (!isStillRunning(pid)) break;
}
if (isStillRunning(pid)) {
try { process.kill(-pid, "SIGKILL"); } catch { try { process.kill(pid, "SIGKILL"); } catch { /* already exited */ } }
}
// Kill surviving descendants (e.g. SDK-spawned claude processes in their own process groups)
for (const desc of descendants) {
if (desc === process.pid) ccollectDescendants function · typescript · L71-L94 (24 LOC)src/cli.ts
function collectDescendants(rootPid: number): number[] {
const result: number[] = [];
try {
const out = execSync("ps -eo pid,ppid", { stdio: "pipe" }).toString();
const children = new Map<number, number[]>();
for (const line of out.trim().split("\n").slice(1)) {
const parts = line.trim().split(/\s+/);
const pid = parseInt(parts[0], 10);
const ppid = parseInt(parts[1], 10);
if (isNaN(pid) || isNaN(ppid)) continue;
if (!children.has(ppid)) children.set(ppid, []);
children.get(ppid)!.push(pid);
}
const queue = [rootPid];
while (queue.length > 0) {
const p = queue.shift()!;
for (const child of children.get(p) || []) {
result.push(child);
queue.push(child);
}
}
} catch { /* ps failed */ }
return result;
}isStillRunning function · typescript · L96-L103 (8 LOC)src/cli.ts
function isStillRunning(pid: number): boolean {
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}cmdRestart function · typescript · L105-L111 (7 LOC)src/cli.ts
export async function cmdRestart(): Promise<void> {
const { running } = isDaemonRunning();
if (running) {
await cmdStop();
}
await cmdStart({ followLogs: false });
}formatUptime function · typescript · L113-L123 (11 LOC)src/cli.ts
function formatUptime(ms: number): string {
const seconds = Math.floor(ms / 1000);
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const parts: string[] = [];
if (days > 0) parts.push(`${days}d`);
if (hours > 0) parts.push(`${hours}h`);
parts.push(`${minutes}m`);
return parts.join(" ");
}cmdStatus function · typescript · L125-L195 (71 LOC)src/cli.ts
export function cmdStatus(): void {
const { running, pid } = isDaemonRunning();
if (!running || pid === null) {
printBanner([
"Status: not running",
"STT: " + getSttSummary(),
...getSttDetailLines(),
"--- Commands",
"start Start the daemon",
"stop Stop the daemon",
"restart Restart the daemon",
"status Show status",
"logs Follow logs (default: -f)",
" -n N Show last N lines (static)",
" --level LEVEL Filter by DEBUG|INFO|WARN|ERROR",
" --tag TAG Filter by component tag",
"config Edit configuration",
...(isMacOS() ? ["setup-stt Setup STT (speech-to-text)"] : []),
]);
return;
}
let uptime = "unknown";
try {
const pidStat = fs.statSync(pidFilePath());
uptime = formatUptime(Date.now() - pidStat.mtimeMs);
} catch { /* pid file may not exist */ }
const sessionscolorizeLine function · typescript · L200-L215 (16 LOC)src/cli.ts
function colorizeLine(line: string): string {
if (!process.stdout.isTTY) return line;
const m = line.match(LOG_LINE_RE);
if (!m) return line;
const [, ts, level, tag, msg] = m;
const dim = "\x1b[2m";
const r = "\x1b[0m";
const levelColors: Record<string, string> = {
DEBUG: "\x1b[2m", // dim
INFO: "\x1b[36m", // cyan
WARN: "\x1b[33m", // yellow
ERROR: "\x1b[31m", // red
};
const lc = levelColors[level] || "";
return `${dim}${ts}${r} ${lc}[${level}]${r}${dim}${tag}${r} ${msg}`;
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
matchesFilter function · typescript · L224-L231 (8 LOC)src/cli.ts
function matchesFilter(line: string, opts: LogsOptions): boolean {
if (!opts.level && !opts.tag) return true;
const m = line.match(LOG_LINE_RE);
if (!m) return !opts.level && !opts.tag; // non-structured lines: show only if no filters
if (opts.level && m[2].toUpperCase() !== opts.level) return false;
if (opts.tag && m[3] !== `[${opts.tag}]`) return false;
return true;
}cmdLogs function · typescript · L233-L277 (45 LOC)src/cli.ts
export function cmdLogs(opts: LogsOptions): void {
const logPath = logFilePath();
if (!fs.existsSync(logPath)) {
console.log("No log file found.");
return;
}
if (!opts.follow) {
// Static: read last N matching lines
const allLines = fs.readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
const filtered = allLines.filter((l) => matchesFilter(l, opts));
const tail = filtered.slice(-opts.lines);
for (const line of tail) console.log(colorizeLine(line));
return;
}
// Follow mode: dump last N matching lines, then tail
const allLines = fs.readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
const filtered = allLines.filter((l) => matchesFilter(l, opts));
const initial = filtered.slice(-opts.lines);
for (const line of initial) console.log(colorizeLine(line));
// Watch for new data
let offset = fs.statSync(logPath).size;
let lineBuf = "";
const watcher = fs.watch(logPath, () => {
let size: number;
try { size =cmdConfig function · typescript · L279-L290 (12 LOC)src/cli.ts
export async function cmdConfig(): Promise<void> {
await runConfigEditor();
const { running } = isDaemonRunning();
if (running) {
await cmdStop();
}
loadConfig();
getConfig();
await spawnDaemon();
cmdStatus();
cmdLogs({ follow: true, lines: 10, level: null, tag: null });
}handleCommand function · typescript · L30-L229 (200 LOC)src/commands.ts
export async function handleCommand(
text: string,
chatId: number,
messageId: number,
ctx: HandlerContext
): Promise<boolean> {
if (!text.startsWith("/")) return false;
const [rawCommand] = text.split(/\s+/);
const command = rawCommand.split("@")[0].toLowerCase();
if (command === "/start" || command === "/help") {
logger.debug("command", `chat_id=${chatId} command=${command}`);
const welcome = [
"<b>RemoteCode</b> - Claude Code via Telegram",
"",
"Send any message to chat with Claude.",
"You can also send images and voice messages.",
"",
"<b>Commands:</b>",
"/sessions - Browse and switch sessions",
"/projects - Browse sessions by project",
"/new - Start a new session",
"/history - Show conversation history",
"/model - Switch Claude model",
"/cancel - Cancel the current task",
"/sync - Toggle auto-sync notifications",
].join("\n");
// Send reply keyboard first, then inline keybensureConfigDir function · typescript · L17-L22 (6 LOC)src/config.ts
export function ensureConfigDir(): void {
const dir = globalConfigDir();
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}readKvFile function · typescript · L54-L67 (14 LOC)src/config.ts
export function readKvFile(filePath: string): Record<string, string> {
if (!fs.existsSync(filePath)) return {};
const data: Record<string, string> = {};
const content = fs.readFileSync(filePath, "utf-8");
for (const line of content.split("\n")) {
const stripped = line.trim();
if (!stripped || stripped.startsWith("#") || !stripped.includes("=")) continue;
const idx = stripped.indexOf("=");
const key = stripped.slice(0, idx).trim();
const value = stripped.slice(idx + 1).trim();
data[key] = value;
}
return data;
}loadConfig function · typescript · L79-L90 (12 LOC)src/config.ts
export function loadConfig(): string | null {
const gp = globalConfigPath();
const config = readKvFile(gp);
for (const [key, value] of Object.entries(config)) {
if (process.env[key] === undefined) {
process.env[key] = value;
}
}
if (fs.existsSync(gp)) return gp;
return null;
}getConfig function · typescript · L107-L120 (14 LOC)src/config.ts
export function getConfig(): Config {
const botToken = process.env.TELEGRAM_BOT_TOKEN;
if (!botToken) {
throw new Error("Missing required env var: TELEGRAM_BOT_TOKEN");
}
const allowedUsers = parseAllowedUsers(process.env.REMOTECODE_ALLOWED_USERS || "");
if (allowedUsers.ids.size === 0 && allowedUsers.usernames.size === 0) {
throw new Error("REMOTECODE_ALLOWED_USERS is empty or invalid. Set at least one user ID or username.");
}
const yolo = parseTruthyEnv(process.env.REMOTECODE_YOLO);
const verbose = parseTruthyEnv(process.env.REMOTECODE_VERBOSE);
return { botToken, allowedUsers, yolo, verbose };
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
isUserAllowed function · typescript · L11-L20 (10 LOC)src/context.ts
export function isUserAllowed(
userId?: number,
username?: string,
allowedIds?: Set<number>,
allowedNames?: Set<string>
): boolean {
if (userId !== undefined && allowedIds?.has(userId)) return true;
if (username && allowedNames?.has(username.toLowerCase())) return true;
return false;
}readPid function · typescript · L22-L30 (9 LOC)src/daemon.ts
export function readPid(): number | null {
try {
const content = fs.readFileSync(pidFilePath(), "utf-8").trim();
const pid = parseInt(content, 10);
return isNaN(pid) ? null : pid;
} catch {
return null;
}
}isRunning function · typescript · L32-L39 (8 LOC)src/daemon.ts
export function isRunning(pid: number): boolean {
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}page 1 / 3next ›