mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 11:20:37 +09:00
Merge remote-tracking branch 'public-gh/master' into paperclip-company-import-export
* public-gh/master: fix: address greptile follow-up feedback docs: clarify quickstart npx usage Add guarded dev restart handling Fix PAP-576 settings toggles and transcript default Add username log censor setting fix: use standard toggle component for permission controls # Conflicts: # server/src/routes/agents.ts # ui/src/pages/AgentDetail.tsx
This commit is contained in:
commit
5140d7b0c4
44 changed files with 11673 additions and 208 deletions
30
ui/src/adapters/transcript.test.ts
Normal file
30
ui/src/adapters/transcript.test.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { buildTranscript, type RunLogChunk } from "./transcript";
|
||||
|
||||
describe("buildTranscript", () => {
|
||||
const ts = "2026-03-20T13:00:00.000Z";
|
||||
const chunks: RunLogChunk[] = [
|
||||
{ ts, stream: "stdout", chunk: "opened /Users/dotta/project\n" },
|
||||
{ ts, stream: "stderr", chunk: "stderr /Users/dotta/project" },
|
||||
];
|
||||
|
||||
it("defaults username censoring to off when options are omitted", () => {
|
||||
const entries = buildTranscript(chunks, (line, entryTs) => [{ kind: "stdout", ts: entryTs, text: line }]);
|
||||
|
||||
expect(entries).toEqual([
|
||||
{ kind: "stdout", ts, text: "opened /Users/dotta/project" },
|
||||
{ kind: "stderr", ts, text: "stderr /Users/dotta/project" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("still redacts usernames when explicitly enabled", () => {
|
||||
const entries = buildTranscript(chunks, (line, entryTs) => [{ kind: "stdout", ts: entryTs, text: line }], {
|
||||
censorUsernameInLogs: true,
|
||||
});
|
||||
|
||||
expect(entries).toEqual([
|
||||
{ kind: "stdout", ts, text: "opened /Users/d****/project" },
|
||||
{ kind: "stderr", ts, text: "stderr /Users/d****/project" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -2,6 +2,7 @@ import { redactHomePathUserSegments, redactTranscriptEntryPaths } from "@papercl
|
|||
import type { TranscriptEntry, StdoutLineParser } from "./types";
|
||||
|
||||
export type RunLogChunk = { ts: string; stream: "stdout" | "stderr" | "system"; chunk: string };
|
||||
type TranscriptBuildOptions = { censorUsernameInLogs?: boolean };
|
||||
|
||||
export function appendTranscriptEntry(entries: TranscriptEntry[], entry: TranscriptEntry) {
|
||||
if ((entry.kind === "thinking" || entry.kind === "assistant") && entry.delta) {
|
||||
|
|
@ -21,17 +22,22 @@ export function appendTranscriptEntries(entries: TranscriptEntry[], incoming: Tr
|
|||
}
|
||||
}
|
||||
|
||||
export function buildTranscript(chunks: RunLogChunk[], parser: StdoutLineParser): TranscriptEntry[] {
|
||||
export function buildTranscript(
|
||||
chunks: RunLogChunk[],
|
||||
parser: StdoutLineParser,
|
||||
opts?: TranscriptBuildOptions,
|
||||
): TranscriptEntry[] {
|
||||
const entries: TranscriptEntry[] = [];
|
||||
let stdoutBuffer = "";
|
||||
const redactionOptions = { enabled: opts?.censorUsernameInLogs ?? false };
|
||||
|
||||
for (const chunk of chunks) {
|
||||
if (chunk.stream === "stderr") {
|
||||
entries.push({ kind: "stderr", ts: chunk.ts, text: redactHomePathUserSegments(chunk.chunk) });
|
||||
entries.push({ kind: "stderr", ts: chunk.ts, text: redactHomePathUserSegments(chunk.chunk, redactionOptions) });
|
||||
continue;
|
||||
}
|
||||
if (chunk.stream === "system") {
|
||||
entries.push({ kind: "system", ts: chunk.ts, text: redactHomePathUserSegments(chunk.chunk) });
|
||||
entries.push({ kind: "system", ts: chunk.ts, text: redactHomePathUserSegments(chunk.chunk, redactionOptions) });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -41,14 +47,14 @@ export function buildTranscript(chunks: RunLogChunk[], parser: StdoutLineParser)
|
|||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
appendTranscriptEntries(entries, parser(trimmed, chunk.ts).map(redactTranscriptEntryPaths));
|
||||
appendTranscriptEntries(entries, parser(trimmed, chunk.ts).map((entry) => redactTranscriptEntryPaths(entry, redactionOptions)));
|
||||
}
|
||||
}
|
||||
|
||||
const trailing = stdoutBuffer.trim();
|
||||
if (trailing) {
|
||||
const ts = chunks.length > 0 ? chunks[chunks.length - 1]!.ts : new Date().toISOString();
|
||||
appendTranscriptEntries(entries, parser(trailing, ts).map(redactTranscriptEntryPaths));
|
||||
appendTranscriptEntries(entries, parser(trailing, ts).map((entry) => redactTranscriptEntryPaths(entry, redactionOptions)));
|
||||
}
|
||||
|
||||
return entries;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue