[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/thaolaptrinh/commandcode-api-proxy/main/src/stream.ts [Back]  [Original]

import type { CCEvent } from "@/translate/types.js";

/**
 * Result from parsing a single line of CC NDJSON data.
 */
export interface ParsedChunk {
  type: "event" | "done" | "ping" | "unknown";
  event?: CCEvent;
}

/**
 * Parse a single NDJSON line from the CC stream.
 */
export function parseCCLine(line: string): ParsedChunk {
  const trimmed = line.trim();

  if (!trimmed || trimmed === "") return { type: "ping" };

  // The CC API sends JSON lines prefixed with "data: "
  // Handle both with and without prefix
  const dataStr = trimmed.startsWith("data: ") ? trimmed.slice(6) : trimmed;

  if (dataStr === "[DONE]") return { type: "done" };

  try {
    const parsed = JSON.parse(dataStr);

    // CC API format 1 (flat): { type: "text-delta", id: "txt-0", text: "4" }
    // CC API format 2 (nested): { type: "text-delta", data: { text: "Hello" } }
    // Merge all non-type fields into data.
    if (parsed && typeof parsed === "object" && parsed.type) {
      const { type, id: _id, ...rest } = parsed;
      const data = parsed.data && typeof parsed.data === "object" ? parsed.data : rest;
      return {
        type: "event",
        event: { type, data },
      };
    }

    return { type: "unknown" };
  } catch {
    // If we can't parse as JSON, it might be a ping or keepalive
    return { type: "ping" };
  }
}

/**
 * Format an object as an SSE message string.
 */
export function formatSSE(data: object): string {
  return `data: ${JSON.stringify(data)}\n\n`;
}

/**
 * Format the [DONE] signal for non-streaming mode.
 */
export function formatSSEDone(): string {
  return "data: [DONE]\n\n";
}

export function formatAnthropicSSE(eventType: string, data: unknown): string {
  if (eventType === "message_stop") {
    return "event: message_stop\ndata: {}\n\n";
  }
  return `event: ${eventType}\ndata: ${JSON.stringify(data)}\n\n`;
}

Web Proxy Viewer  |  New URL  |  Original Page