← back to kcisoul__remotecode

Function bodies 137 total

All specs Real LLM only Function bodies
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, displ
Provenance: 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: true
handleSessionCallback 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 se
handleSessionDeleteCallback 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: t
cmdStop 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) c
collectDescendants 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 sessions
colorizeLine 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 keyb
ensureConfigDir 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 ›