Skip to main content
Glama

proxy_session_recover

Rebuild session indexes from records to restore proxy sessions after crash or corruption.

Instructions

Rebuild session indexes from records after crash/corruption.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
session_idNoRecover only this session (default: recover all sessions)

Implementation Reference

  • Tool registration and handler for `proxy_session_recover` in `src/tools/sessions.ts`.
    server.tool(
      "proxy_session_recover",
      "Rebuild session indexes from records after crash/corruption.",
      {
        session_id: z.string().optional().describe("Recover only this session (default: recover all sessions)"),
      },
      async ({ session_id }) => {
        try {
          const result = await proxyManager.recoverSession(session_id);
          return {
            content: [{ type: "text", text: JSON.stringify({ status: "success", ...result }) }],
          };
        } catch (e) {
          return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: toError(e) }) }] };
        }
      },
    );
  • Actual implementation of `recoverSession` logic in `SessionStore` within `src/session-store.ts`.
    async recoverSession(sessionId?: string): Promise<{
      recovered: Array<{ sessionId: string; exchanges: number; droppedTailBytes: number }>;
    }> {
      const ids = sessionId
        ? [sessionId]
        : (await this.listSessions()).map((s) => s.id);
    
      const recovered: Array<{ sessionId: string; exchanges: number; droppedTailBytes: number }> = [];
    
      for (const id of ids) {
        const sessionDir = path.join(this.rootDir, id);
        const recordsPath = path.join(sessionDir, RECORDS_FILENAME);
        const indexPath = path.join(sessionDir, INDEX_FILENAME);
        const manifestPath = path.join(sessionDir, MANIFEST_FILENAME);
    
        let manifest: SessionManifest;
        try {
          manifest = await this.getSession(id);
        } catch {
          continue;
        }
    
        const buffer = await fs.readFile(recordsPath);
        let cursor = 0;
        let seq = 0;
        let validBytes = 0;
        const indexLines: string[] = [];
    
        while (cursor < buffer.length) {
          const nl = buffer.indexOf(0x0a, cursor);
          const end = nl === -1 ? buffer.length : nl;
          const lineBuf = buffer.subarray(cursor, end);
          const lineBytes = (nl === -1 ? end - cursor : (end - cursor + 1));
          if (lineBuf.length === 0) {
            cursor = nl === -1 ? end : end + 1;
            validBytes += lineBytes;
            continue;
          }
    
          try {
            const parsed = JSON.parse(lineBuf.toString("utf8")) as PersistedExchangeRecord;
            seq++;
            const record = {
              ...parsed,
              seq,
            };
            const idx = this.toIndexEntry(record, { recordOffset: validBytes, recordLineBytes: lineBytes });
            indexLines.push(JSON.stringify(idx));
            validBytes += lineBytes;
            cursor = nl === -1 ? end : end + 1;
          } catch {
            break;
          }
        }
    
        if (validBytes < buffer.length) {
          await fs.truncate(recordsPath, validBytes);
        }
    
        await fs.writeFile(indexPath, `${indexLines.join("\n")}${indexLines.length > 0 ? "\n" : ""}`);
    
        manifest.exchangeCount = indexLines.length;
        manifest.lastSequence = indexLines.length;
        manifest.bytesWritten = validBytes + Buffer.byteLength(`${indexLines.join("\n")}${indexLines.length > 0 ? "\n" : ""}`);
        manifest.recoveryState = validBytes < buffer.length ? "recovered" : "clean";
        manifest.lastError = validBytes < buffer.length ? "Recovered from truncated tail." : null;
        manifest.lastFlushAt = Date.now();
        if (manifest.status === "active") {
          manifest.status = "stopped";
          manifest.endedAt = Date.now();
        }
        await this.writeManifest(manifestPath, manifest);
    
        recovered.push({
          sessionId: id,
          exchanges: indexLines.length,
          droppedTailBytes: Math.max(0, buffer.length - validBytes),
        });
      }
    
      return { recovered };
    }
Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yfe404/proxy-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server