@tickernelz/paperclip-pro-adapter-omp-local 2026.925.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/dist/cli/format-event.d.ts +2 -0
- package/dist/cli/format-event.d.ts.map +1 -0
- package/dist/cli/format-event.js +56 -0
- package/dist/cli/format-event.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata.d.ts +9 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +44 -0
- package/dist/metadata.js.map +1 -0
- package/dist/server/config.d.ts +22 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +661 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/execute.cwd.test.d.ts +2 -0
- package/dist/server/execute.cwd.test.d.ts.map +1 -0
- package/dist/server/execute.cwd.test.js +88 -0
- package/dist/server/execute.cwd.test.js.map +1 -0
- package/dist/server/execute.d.ts +8 -0
- package/dist/server/execute.d.ts.map +1 -0
- package/dist/server/execute.js +812 -0
- package/dist/server/execute.js.map +1 -0
- package/dist/server/execute.toggles.test.d.ts +2 -0
- package/dist/server/execute.toggles.test.d.ts.map +1 -0
- package/dist/server/execute.toggles.test.js +147 -0
- package/dist/server/execute.toggles.test.js.map +1 -0
- package/dist/server/failure.d.ts +17 -0
- package/dist/server/failure.d.ts.map +1 -0
- package/dist/server/failure.js +74 -0
- package/dist/server/failure.js.map +1 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +97 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/models.d.ts +19 -0
- package/dist/server/models.d.ts.map +1 -0
- package/dist/server/models.js +208 -0
- package/dist/server/models.js.map +1 -0
- package/dist/server/parse.d.ts +36 -0
- package/dist/server/parse.d.ts.map +1 -0
- package/dist/server/parse.js +378 -0
- package/dist/server/parse.js.map +1 -0
- package/dist/server/parse.test.d.ts +2 -0
- package/dist/server/parse.test.d.ts.map +1 -0
- package/dist/server/parse.test.js +29 -0
- package/dist/server/parse.test.js.map +1 -0
- package/dist/server/profile.d.ts +7 -0
- package/dist/server/profile.d.ts.map +1 -0
- package/dist/server/profile.js +40 -0
- package/dist/server/profile.js.map +1 -0
- package/dist/server/progress.d.ts +16 -0
- package/dist/server/progress.d.ts.map +1 -0
- package/dist/server/progress.js +226 -0
- package/dist/server/progress.js.map +1 -0
- package/dist/server/quota.d.ts +4 -0
- package/dist/server/quota.d.ts.map +1 -0
- package/dist/server/quota.js +129 -0
- package/dist/server/quota.js.map +1 -0
- package/dist/server/redaction-repair.d.ts +5 -0
- package/dist/server/redaction-repair.d.ts.map +1 -0
- package/dist/server/redaction-repair.js +122 -0
- package/dist/server/redaction-repair.js.map +1 -0
- package/dist/server/settings-overlay.d.ts +19 -0
- package/dist/server/settings-overlay.d.ts.map +1 -0
- package/dist/server/settings-overlay.js +60 -0
- package/dist/server/settings-overlay.js.map +1 -0
- package/dist/server/skills.d.ts +5 -0
- package/dist/server/skills.d.ts.map +1 -0
- package/dist/server/skills.js +110 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/test.d.ts +3 -0
- package/dist/server/test.d.ts.map +1 -0
- package/dist/server/test.js +435 -0
- package/dist/server/test.js.map +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +2 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/parse-stdout.d.ts +4 -0
- package/dist/ui/parse-stdout.d.ts.map +1 -0
- package/dist/ui/parse-stdout.js +480 -0
- package/dist/ui/parse-stdout.js.map +1 -0
- package/dist/ui/parse-stdout.test.d.ts +2 -0
- package/dist/ui/parse-stdout.test.d.ts.map +1 -0
- package/dist/ui/parse-stdout.test.js +69 -0
- package/dist/ui/parse-stdout.test.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createOmpOutputAccumulator } from "./parse.js";
|
|
3
|
+
const CAPTURED_TOOL_START = "{\"type\":\"tool_execution_start\",\"toolCallId\":\"call_455341\",\"toolName\":\"bash\",\"args\":{\"command\":\"echo alpha\"},\"intent\":\"Echoing alpha string again\"}";
|
|
4
|
+
const CAPTURED_TOOL_PROGRESS = "{\"type\":\"tool_execution_update\",\"toolCallId\":\"call_455341\",\"toolName\":\"bash\",\"args\":{\"command\":\"echo alpha\"},\"partialResult\":{\"content\":[{\"type\":\"text\",\"text\":\"alpha\\n\\n\\nWall time: 0.07 seconds\"}],\"details\":{\"timeoutSeconds\":300,\"wallTimeMs\":69.76190599999973}}}";
|
|
5
|
+
const CAPTURED_TOOL_END = "{\"type\":\"tool_execution_end\",\"toolCallId\":\"call_455341\",\"toolName\":\"bash\",\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"alpha\\n\\n\\nWall time: 0.07 seconds\"}],\"details\":{\"timeoutSeconds\":300,\"wallTimeMs\":69.76190599999973}},\"isError\":false}";
|
|
6
|
+
function glueLikeLogRedaction(swallowedLine, followingLine) {
|
|
7
|
+
return `${swallowedLine.slice(0, swallowedLine.indexOf('"partialResult"'))}"***REDACTED***"${followingLine}`;
|
|
8
|
+
}
|
|
9
|
+
describe("createOmpOutputAccumulator redaction salvage", () => {
|
|
10
|
+
it("settles a tool call whose end log redaction glued onto the previous line", () => {
|
|
11
|
+
const accumulator = createOmpOutputAccumulator();
|
|
12
|
+
accumulator.push(CAPTURED_TOOL_START);
|
|
13
|
+
accumulator.push(glueLikeLogRedaction(CAPTURED_TOOL_PROGRESS, CAPTURED_TOOL_END));
|
|
14
|
+
const { toolCalls, unknownLines } = accumulator.result();
|
|
15
|
+
expect(toolCalls).toHaveLength(1);
|
|
16
|
+
expect(toolCalls[0]).toMatchObject({ toolCallId: "call_455341", toolName: "bash", isError: false });
|
|
17
|
+
expect(toolCalls[0].result).not.toBeNull();
|
|
18
|
+
expect(unknownLines).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
it("settles a tool call whose end payload is unrecoverable", () => {
|
|
21
|
+
const accumulator = createOmpOutputAccumulator();
|
|
22
|
+
accumulator.push(CAPTURED_TOOL_START);
|
|
23
|
+
accumulator.push("{\"type\":\"tool_execution_end\",\"toolCallId\":\"call_455341\",\"toolName\":\"fabric_exec\",\"result\":{\"content\":");
|
|
24
|
+
const { toolCalls } = accumulator.result();
|
|
25
|
+
expect(toolCalls).toHaveLength(1);
|
|
26
|
+
expect(toolCalls[0].result).not.toBeNull();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=parse.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.test.js","sourceRoot":"","sources":["../../src/server/parse.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,mBAAmB,GAAG,0KAA0K,CAAC;AACvM,MAAM,sBAAsB,GAAG,gTAAgT,CAAC;AAChV,MAAM,iBAAiB,GAAG,kRAAkR,CAAC;AAE7S,SAAS,oBAAoB,CAAC,aAAqB,EAAE,aAAqB;IACxE,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,mBAAmB,aAAa,EAAE,CAAC;AAC/G,CAAC;AAED,QAAQ,CAAC,8CAA8C,EAAE,GAAG,EAAE;IAC5D,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,WAAW,GAAG,0BAA0B,EAAE,CAAC;QAEjD,WAAW,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACtC,WAAW,CAAC,IAAI,CAAC,oBAAoB,CAAC,sBAAsB,EAAE,iBAAiB,CAAC,CAAC,CAAC;QAElF,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC;QACzD,MAAM,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACpG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC5C,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,WAAW,GAAG,0BAA0B,EAAE,CAAC;QAEjD,WAAW,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACtC,WAAW,CAAC,IAAI,CAAC,uHAAuH,CAAC,CAAC;QAE1I,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC;QAC3C,MAAM,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface OmpProfileSelection {
|
|
2
|
+
profile: string | null;
|
|
3
|
+
specified: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function normalizeOmpProfile(value: string | undefined): string | null;
|
|
6
|
+
export declare function resolveOmpProfile(config: Record<string, unknown>, processEnv?: NodeJS.ProcessEnv): OmpProfileSelection;
|
|
7
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/server/profile.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAe5E;AAYD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,GAAE,MAAM,CAAC,UAAwB,GAC1C,mBAAmB,CAerB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
2
|
+
const WINDOWS_RESERVED_BASENAME_RE = /^(?:CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])(?:\..*)?$/i;
|
|
3
|
+
export function normalizeOmpProfile(value) {
|
|
4
|
+
const normalized = value?.trim();
|
|
5
|
+
if (!normalized || normalized === "default")
|
|
6
|
+
return null;
|
|
7
|
+
if (normalized === "." ||
|
|
8
|
+
normalized === ".." ||
|
|
9
|
+
normalized.endsWith(".") ||
|
|
10
|
+
!PROFILE_NAME_RE.test(normalized) ||
|
|
11
|
+
WINDOWS_RESERVED_BASENAME_RE.test(normalized)) {
|
|
12
|
+
throw new Error(`Invalid OMP profile "${value}". Profile names must match ${PROFILE_NAME_RE.source}, cannot be "." or "..", cannot end with ".", and cannot be a Windows reserved device name.`);
|
|
13
|
+
}
|
|
14
|
+
return normalized;
|
|
15
|
+
}
|
|
16
|
+
function envProfile(env) {
|
|
17
|
+
if (typeof env.OMP_PROFILE === "string") {
|
|
18
|
+
return { profile: normalizeOmpProfile(env.OMP_PROFILE), specified: true };
|
|
19
|
+
}
|
|
20
|
+
if (typeof env.PI_PROFILE === "string") {
|
|
21
|
+
return { profile: normalizeOmpProfile(env.PI_PROFILE), specified: true };
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export function resolveOmpProfile(config, processEnv = process.env) {
|
|
26
|
+
if (typeof config.profile === "string" && config.profile.trim()) {
|
|
27
|
+
return { profile: normalizeOmpProfile(config.profile), specified: true };
|
|
28
|
+
}
|
|
29
|
+
const configuredEnv = config.env !== null && typeof config.env === "object" && !Array.isArray(config.env)
|
|
30
|
+
? config.env
|
|
31
|
+
: {};
|
|
32
|
+
const configured = envProfile(configuredEnv);
|
|
33
|
+
if (configured)
|
|
34
|
+
return configured;
|
|
35
|
+
if (processEnv.PAPERCLIP_OMP_PROFILE !== undefined) {
|
|
36
|
+
return { profile: normalizeOmpProfile(processEnv.PAPERCLIP_OMP_PROFILE), specified: true };
|
|
37
|
+
}
|
|
38
|
+
return envProfile(processEnv) ?? { profile: null, specified: false };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/server/profile.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,6BAA6B,CAAC;AACtD,MAAM,4BAA4B,GAAG,mDAAmD,CAAC;AASzF,MAAM,UAAU,mBAAmB,CAAC,KAAyB;IAC3D,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IACjC,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACzD,IACE,UAAU,KAAK,GAAG;QAClB,UAAU,KAAK,IAAI;QACnB,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxB,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC;QACjC,4BAA4B,CAAC,IAAI,CAAC,UAAU,CAAC,EAC7C,CAAC;QACD,MAAM,IAAI,KAAK,CACb,wBAAwB,KAAK,+BAA+B,eAAe,CAAC,MAAM,6FAA6F,CAChL,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,GAAQ;IAC1B,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC5E,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QACvC,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC3E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAA+B,EAC/B,UAAU,GAAsB,OAAO,CAAC,GAAG;IAE3C,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAChE,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,KAAK,IAAI,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;QACvG,CAAC,CAAC,MAAM,CAAC,GAAU;QACnB,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,UAAU,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IAC7C,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,IAAI,UAAU,CAAC,qBAAqB,KAAK,SAAS,EAAE,CAAC;QACnD,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC7F,CAAC;IACD,OAAO,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AdapterExecutionContext } from "@tickernelz/paperclip-pro-adapter-utils";
|
|
2
|
+
type ProgressSink = NonNullable<AdapterExecutionContext["onRuntimeProgress"]>;
|
|
3
|
+
type EventSink = NonNullable<AdapterExecutionContext["onEvent"]>;
|
|
4
|
+
export interface OmpStreamReporter {
|
|
5
|
+
ingest(line: string, parsedEvent?: Record<string, unknown> | null): Promise<void>;
|
|
6
|
+
/** Publish the thinking segment left buffered when the stream ends without a closing event. */
|
|
7
|
+
flush(): Promise<void>;
|
|
8
|
+
/** Tool calls that started and never reported an end. */
|
|
9
|
+
pendingToolCount(): number;
|
|
10
|
+
/** True once OMP emitted assistant output or a tool call. */
|
|
11
|
+
sawProviderWork(): boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Feed OMP stdout lines to Paperclip's live status, durable run events, and recovery evidence. */
|
|
14
|
+
export declare function createOmpProgressReporter(sink: ProgressSink | undefined, events: EventSink | undefined): OmpStreamReporter;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/server/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAGvF,KAAK,YAAY,GAAG,WAAW,CAAC,uBAAuB,CAAC,mBAAmB,CAAC,CAAC,CAAC;AAC9E,KAAK,SAAS,GAAG,WAAW,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC;AA4DjE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,+FAA+F;IAC/F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,yDAAyD;IACzD,gBAAgB,IAAI,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,eAAe,IAAI,OAAO,CAAC;CAC5B;AAED,mGAAmG;AACnG,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,YAAY,GAAG,SAAS,EAC9B,MAAM,EAAE,SAAS,GAAG,SAAS,GAC5B,iBAAiB,CAkKnB"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { parseOmpJsonLine } from "./parse.js";
|
|
2
|
+
const PROGRESS_MIN_INTERVAL_MS = 1000;
|
|
3
|
+
const SNIPPET_CHARS = 200;
|
|
4
|
+
const HINT_CHARS = 120;
|
|
5
|
+
const THINKING_CHARS = 500;
|
|
6
|
+
const THINKING_BUFFER_CHARS = 4000;
|
|
7
|
+
const SNIPPET_BUFFER_CHARS = 1000;
|
|
8
|
+
const MAX_TOOL_EVENTS = 200;
|
|
9
|
+
const MAX_THINKING_EVENTS = 40;
|
|
10
|
+
function text(value) {
|
|
11
|
+
return typeof value === "string" ? value : "";
|
|
12
|
+
}
|
|
13
|
+
function record(value) {
|
|
14
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
15
|
+
? value
|
|
16
|
+
: null;
|
|
17
|
+
}
|
|
18
|
+
function collapse(value) {
|
|
19
|
+
return value.replace(/\s+/g, " ").trim();
|
|
20
|
+
}
|
|
21
|
+
function snippetOf(value) {
|
|
22
|
+
const normalized = collapse(value);
|
|
23
|
+
return normalized.length > SNIPPET_CHARS ? normalized.slice(-SNIPPET_CHARS) : normalized;
|
|
24
|
+
}
|
|
25
|
+
function assistantText(content) {
|
|
26
|
+
if (typeof content === "string")
|
|
27
|
+
return content;
|
|
28
|
+
if (!Array.isArray(content))
|
|
29
|
+
return "";
|
|
30
|
+
let out = "";
|
|
31
|
+
for (const item of content) {
|
|
32
|
+
const block = record(item);
|
|
33
|
+
if (block?.type === "text")
|
|
34
|
+
out += text(block.text);
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function argumentHint(args, intent) {
|
|
39
|
+
const values = record(args) ?? {};
|
|
40
|
+
const display = record(values.display);
|
|
41
|
+
const hint = collapse(text(display?.name))
|
|
42
|
+
|| collapse(text(values.i))
|
|
43
|
+
|| collapse(text(intent))
|
|
44
|
+
|| collapse(text(values.cmd))
|
|
45
|
+
|| collapse(text(values.command))
|
|
46
|
+
|| collapse(text(values.path))
|
|
47
|
+
|| collapse(text(values.file_path))
|
|
48
|
+
|| collapse(text(values.pattern))
|
|
49
|
+
|| collapse(text(values.query));
|
|
50
|
+
return hint.length > HINT_CHARS ? `${hint.slice(0, HINT_CHARS)}…` : hint;
|
|
51
|
+
}
|
|
52
|
+
function seconds(elapsedMs) {
|
|
53
|
+
return `${(elapsedMs / 1000).toFixed(1)}s`;
|
|
54
|
+
}
|
|
55
|
+
/** Feed OMP stdout lines to Paperclip's live status, durable run events, and recovery evidence. */
|
|
56
|
+
export function createOmpProgressReporter(sink, events) {
|
|
57
|
+
let lastEmitMs = 0;
|
|
58
|
+
let currentToolName = null;
|
|
59
|
+
let snippetKind = "none";
|
|
60
|
+
let finalSnippet = null;
|
|
61
|
+
let streamedText = "";
|
|
62
|
+
let streamedThinking = "";
|
|
63
|
+
let thinkingSegment = "";
|
|
64
|
+
let thinkingDropped = false;
|
|
65
|
+
let toolEventCount = 0;
|
|
66
|
+
let thinkingEventCount = 0;
|
|
67
|
+
let eventsBroken = false;
|
|
68
|
+
let providerWorkSeen = false;
|
|
69
|
+
const pendingTools = new Map();
|
|
70
|
+
const snippetValue = () => {
|
|
71
|
+
if (snippetKind === "text")
|
|
72
|
+
return snippetOf(streamedText) || null;
|
|
73
|
+
if (snippetKind === "thinking")
|
|
74
|
+
return `Thinking: ${snippetOf(streamedThinking)}`;
|
|
75
|
+
return finalSnippet;
|
|
76
|
+
};
|
|
77
|
+
const emit = async (message, force) => {
|
|
78
|
+
if (!sink)
|
|
79
|
+
return;
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
if (!force && now - lastEmitMs < PROGRESS_MIN_INTERVAL_MS)
|
|
82
|
+
return;
|
|
83
|
+
lastEmitMs = now;
|
|
84
|
+
try {
|
|
85
|
+
await sink({
|
|
86
|
+
phase: "adapter_startup",
|
|
87
|
+
message,
|
|
88
|
+
currentToolName,
|
|
89
|
+
lastAssistantSnippet: snippetValue(),
|
|
90
|
+
lastEventAt: new Date(now),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
lastEmitMs = now;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const publish = async (eventType, message, failed) => {
|
|
98
|
+
if (!events || eventsBroken)
|
|
99
|
+
return;
|
|
100
|
+
try {
|
|
101
|
+
await events({ eventType, stream: "system", level: failed ? "error" : "info", message });
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
eventsBroken = true;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const publishTool = async (message, failed) => {
|
|
108
|
+
if (toolEventCount > MAX_TOOL_EVENTS)
|
|
109
|
+
return;
|
|
110
|
+
toolEventCount += 1;
|
|
111
|
+
if (toolEventCount > MAX_TOOL_EVENTS) {
|
|
112
|
+
await publish("omp.tool", `Tool event limit reached after ${MAX_TOOL_EVENTS} calls; later calls stay in the run log.`, false);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
await publish("omp.tool", message, failed);
|
|
116
|
+
};
|
|
117
|
+
const flushThinking = async () => {
|
|
118
|
+
const body = collapse(thinkingSegment);
|
|
119
|
+
const dropped = thinkingDropped;
|
|
120
|
+
thinkingSegment = "";
|
|
121
|
+
thinkingDropped = false;
|
|
122
|
+
if (!body)
|
|
123
|
+
return;
|
|
124
|
+
if (thinkingEventCount > MAX_THINKING_EVENTS)
|
|
125
|
+
return;
|
|
126
|
+
thinkingEventCount += 1;
|
|
127
|
+
if (thinkingEventCount > MAX_THINKING_EVENTS) {
|
|
128
|
+
await publish("omp.thinking", `Thinking event limit reached after ${MAX_THINKING_EVENTS} segments; later reasoning stays in the run log.`, false);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const cut = dropped || body.length > THINKING_CHARS;
|
|
132
|
+
await publish("omp.thinking", cut ? `${body.slice(0, THINKING_CHARS)}…` : body, false);
|
|
133
|
+
};
|
|
134
|
+
const ingest = async (line, parsedEvent) => {
|
|
135
|
+
const event = parsedEvent === undefined ? parseOmpJsonLine(line.trim()) : parsedEvent;
|
|
136
|
+
if (!event)
|
|
137
|
+
return;
|
|
138
|
+
switch (text(event.type)) {
|
|
139
|
+
case "tool_execution_start": {
|
|
140
|
+
const toolName = text(event.toolName).trim();
|
|
141
|
+
if (!toolName)
|
|
142
|
+
return;
|
|
143
|
+
const toolCallId = text(event.toolCallId).trim();
|
|
144
|
+
const hint = argumentHint(event.args, event.intent);
|
|
145
|
+
if (toolCallId)
|
|
146
|
+
pendingTools.set(toolCallId, { toolName, hint, startedMs: Date.now() });
|
|
147
|
+
currentToolName = toolName;
|
|
148
|
+
providerWorkSeen = true;
|
|
149
|
+
if (snippetKind === "text") {
|
|
150
|
+
finalSnippet = snippetOf(streamedText) || finalSnippet;
|
|
151
|
+
snippetKind = "final";
|
|
152
|
+
}
|
|
153
|
+
streamedText = "";
|
|
154
|
+
await flushThinking();
|
|
155
|
+
await emit(`Running ${toolName}`, true);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
case "tool_execution_end": {
|
|
159
|
+
const toolCallId = text(event.toolCallId).trim();
|
|
160
|
+
const started = toolCallId ? pendingTools.get(toolCallId) : undefined;
|
|
161
|
+
if (toolCallId)
|
|
162
|
+
pendingTools.delete(toolCallId);
|
|
163
|
+
const toolName = text(event.toolName).trim() || started?.toolName || currentToolName || "tool";
|
|
164
|
+
const failed = event.isError === true;
|
|
165
|
+
const hint = started?.hint ?? argumentHint(event.args, event.intent);
|
|
166
|
+
const duration = started ? ` in ${seconds(Date.now() - started.startedMs)}` : "";
|
|
167
|
+
currentToolName = null;
|
|
168
|
+
await emit(`Finished ${toolName}`, true);
|
|
169
|
+
await publishTool(`${toolName} ${failed ? "failed" : "ok"}${duration}${hint ? ` — ${hint}` : ""}`, failed);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
case "message_update": {
|
|
173
|
+
const update = record(event.assistantMessageEvent);
|
|
174
|
+
if (!update)
|
|
175
|
+
return;
|
|
176
|
+
const delta = text(update.delta);
|
|
177
|
+
if (!delta)
|
|
178
|
+
return;
|
|
179
|
+
providerWorkSeen = true;
|
|
180
|
+
if (text(update.type) === "text_delta") {
|
|
181
|
+
await flushThinking();
|
|
182
|
+
streamedText = (streamedText + delta).slice(-SNIPPET_BUFFER_CHARS);
|
|
183
|
+
snippetKind = "text";
|
|
184
|
+
await emit("Writing response", false);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (text(update.type) === "thinking_delta") {
|
|
188
|
+
const merged = thinkingSegment + delta;
|
|
189
|
+
thinkingSegment = merged.slice(0, THINKING_BUFFER_CHARS);
|
|
190
|
+
if (merged.length > THINKING_BUFFER_CHARS)
|
|
191
|
+
thinkingDropped = true;
|
|
192
|
+
streamedThinking = (streamedThinking + delta).slice(-SNIPPET_BUFFER_CHARS);
|
|
193
|
+
snippetKind = "thinking";
|
|
194
|
+
await emit("Thinking", false);
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
case "message_end":
|
|
199
|
+
case "turn_end": {
|
|
200
|
+
await flushThinking();
|
|
201
|
+
const message = record(event.message);
|
|
202
|
+
if (!message || message.role !== "assistant")
|
|
203
|
+
return;
|
|
204
|
+
const body = assistantText(message.content);
|
|
205
|
+
const snippet = snippetOf(body);
|
|
206
|
+
if (!snippet)
|
|
207
|
+
return;
|
|
208
|
+
streamedText = body.slice(-SNIPPET_BUFFER_CHARS);
|
|
209
|
+
streamedThinking = "";
|
|
210
|
+
finalSnippet = snippet;
|
|
211
|
+
snippetKind = "final";
|
|
212
|
+
await emit("Writing response", true);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
return {
|
|
220
|
+
ingest,
|
|
221
|
+
flush: flushThinking,
|
|
222
|
+
pendingToolCount: () => pendingTools.size,
|
|
223
|
+
sawProviderWork: () => providerWorkSeen,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/server/progress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderQuotaResult } from "@tickernelz/paperclip-pro-adapter-utils";
|
|
2
|
+
/** Project `omp usage --json` onto Paperclip's provider quota contract. */
|
|
3
|
+
export declare function getOmpQuotaWindows(): Promise<ProviderQuotaResult>;
|
|
4
|
+
//# sourceMappingURL=quota.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../src/server/quota.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,yCAAyC,CAAC;AA+EhG,2EAA2E;AAC3E,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAsDvE"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { runChildProcess } from "@tickernelz/paperclip-pro-adapter-utils/server-utils";
|
|
2
|
+
import { detectionEnv, resolveOmpCommand } from "./config.js";
|
|
3
|
+
const QUOTA_PROBE_TIMEOUT_SEC_WITHIN_PROVIDER_DROP_WINDOW = 12;
|
|
4
|
+
function record(value) {
|
|
5
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
6
|
+
? value
|
|
7
|
+
: null;
|
|
8
|
+
}
|
|
9
|
+
function text(value) {
|
|
10
|
+
return typeof value === "string" ? value : "";
|
|
11
|
+
}
|
|
12
|
+
function finite(value) {
|
|
13
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
14
|
+
}
|
|
15
|
+
function envelope(stdout) {
|
|
16
|
+
const trimmed = stdout.trim();
|
|
17
|
+
for (const candidate of [trimmed, ...trimmed.split(/\r?\n/).reverse().map((line) => line.trim())]) {
|
|
18
|
+
if (!candidate.startsWith("{") || !candidate.endsWith("}"))
|
|
19
|
+
continue;
|
|
20
|
+
try {
|
|
21
|
+
const parsed = record(JSON.parse(candidate));
|
|
22
|
+
if (parsed)
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function usedPercent(amount) {
|
|
32
|
+
const usedFraction = finite(amount.usedFraction);
|
|
33
|
+
if (usedFraction !== null)
|
|
34
|
+
return Math.round(usedFraction * 1000) / 10;
|
|
35
|
+
const remainingFraction = finite(amount.remainingFraction);
|
|
36
|
+
if (remainingFraction !== null)
|
|
37
|
+
return Math.round((1 - remainingFraction) * 1000) / 10;
|
|
38
|
+
const used = finite(amount.used);
|
|
39
|
+
const limit = finite(amount.limit);
|
|
40
|
+
if (used !== null && limit !== null && limit > 0)
|
|
41
|
+
return Math.round((used / limit) * 1000) / 10;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function valueLabel(amount) {
|
|
45
|
+
const unit = text(amount.unit);
|
|
46
|
+
const remaining = finite(amount.remaining);
|
|
47
|
+
if (remaining !== null)
|
|
48
|
+
return `${remaining}${unit ? ` ${unit}` : ""} remaining`;
|
|
49
|
+
const used = finite(amount.used);
|
|
50
|
+
const limit = finite(amount.limit);
|
|
51
|
+
if (used !== null && limit !== null)
|
|
52
|
+
return `${used} / ${limit}${unit ? ` ${unit}` : ""}`;
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function toWindow(limit) {
|
|
56
|
+
const window = record(limit.window);
|
|
57
|
+
const amount = record(limit.amount) ?? {};
|
|
58
|
+
const resetsAt = finite(window?.resetsAt);
|
|
59
|
+
const notes = Array.isArray(limit.notes) ? limit.notes.filter((n) => typeof n === "string") : [];
|
|
60
|
+
const status = text(limit.status);
|
|
61
|
+
const detail = [status, ...notes].filter(Boolean).join(" \u00b7 ");
|
|
62
|
+
return {
|
|
63
|
+
label: text(window?.label) || text(limit.label) || text(limit.id) || "window",
|
|
64
|
+
usedPercent: usedPercent(amount),
|
|
65
|
+
resetsAt: resetsAt === null ? null : new Date(resetsAt).toISOString(),
|
|
66
|
+
valueLabel: valueLabel(amount),
|
|
67
|
+
...(detail ? { detail } : {}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function reportWindows(report) {
|
|
71
|
+
const limits = Array.isArray(report.limits) ? report.limits : [];
|
|
72
|
+
return limits
|
|
73
|
+
.map((entry) => record(entry))
|
|
74
|
+
.filter((entry) => entry !== null)
|
|
75
|
+
.map(toWindow);
|
|
76
|
+
}
|
|
77
|
+
/** Project `omp usage --json` onto Paperclip's provider quota contract. */
|
|
78
|
+
export async function getOmpQuotaWindows() {
|
|
79
|
+
const command = resolveOmpCommand({});
|
|
80
|
+
try {
|
|
81
|
+
const result = await runChildProcess(`omp-usage-${Date.now()}-${Math.random().toString(16).slice(2)}`, command, ["usage", "--json"], {
|
|
82
|
+
cwd: process.cwd(),
|
|
83
|
+
env: detectionEnv(),
|
|
84
|
+
timeoutSec: QUOTA_PROBE_TIMEOUT_SEC_WITHIN_PROVIDER_DROP_WINDOW,
|
|
85
|
+
graceSec: 3,
|
|
86
|
+
onLog: async () => { },
|
|
87
|
+
});
|
|
88
|
+
if (result.timedOut) {
|
|
89
|
+
return { provider: "omp", ok: false, error: `\`omp usage --json\` timed out after ${QUOTA_PROBE_TIMEOUT_SEC_WITHIN_PROVIDER_DROP_WINDOW}s.`, windows: [] };
|
|
90
|
+
}
|
|
91
|
+
if ((result.exitCode ?? 1) !== 0) {
|
|
92
|
+
return { provider: "omp", ok: false, error: "`omp usage --json` failed.", windows: [] };
|
|
93
|
+
}
|
|
94
|
+
const payload = envelope(result.stdout);
|
|
95
|
+
const reports = (Array.isArray(payload?.reports) ? payload.reports : [])
|
|
96
|
+
.map((entry) => record(entry))
|
|
97
|
+
.filter((entry) => entry !== null);
|
|
98
|
+
if (reports.length === 0) {
|
|
99
|
+
return {
|
|
100
|
+
provider: "omp",
|
|
101
|
+
source: "omp usage --json",
|
|
102
|
+
ok: false,
|
|
103
|
+
error: "No authenticated OMP account reports provider usage limits.",
|
|
104
|
+
windows: [],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const chosen = reports.find((entry) => reportWindows(entry).length > 0) ?? reports[0];
|
|
108
|
+
if (!chosen) {
|
|
109
|
+
return { provider: "omp", source: "omp usage --json", ok: false, error: "`omp usage --json` returned an unreadable report.", windows: [] };
|
|
110
|
+
}
|
|
111
|
+
const windows = reportWindows(chosen);
|
|
112
|
+
return {
|
|
113
|
+
provider: text(chosen.provider) || "omp",
|
|
114
|
+
source: "omp usage --json",
|
|
115
|
+
ok: windows.length > 0,
|
|
116
|
+
...(windows.length > 0 ? {} : { error: "The provider report carried no usage windows." }),
|
|
117
|
+
windows,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return {
|
|
122
|
+
provider: "omp",
|
|
123
|
+
ok: false,
|
|
124
|
+
error: error instanceof Error ? error.message : String(error),
|
|
125
|
+
windows: [],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=quota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/server/quota.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const REDACTED_LOG_MARKER = "***REDACTED***";
|
|
2
|
+
/** Rebuild a JSON line whose escapes Paperclip's log redaction swallowed, or null when unrecoverable. */
|
|
3
|
+
export declare function repairRedactedJsonLine(line: string): string | null;
|
|
4
|
+
export declare function salvageRedactedEvents(line: string): Record<string, unknown>[];
|
|
5
|
+
//# sourceMappingURL=redaction-repair.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction-repair.d.ts","sourceRoot":"","sources":["../../src/server/redaction-repair.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAqCpD,yGAAyG;AACzG,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2BlE;AA2BD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAqB7E"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export const REDACTED_LOG_MARKER = "***REDACTED***";
|
|
2
|
+
const REPAIR_MAX_STEPS = 16;
|
|
3
|
+
function jsonErrorPosition(line) {
|
|
4
|
+
try {
|
|
5
|
+
JSON.parse(line);
|
|
6
|
+
return -1;
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
const match = /position (\d+)/.exec(error instanceof Error ? error.message : String(error));
|
|
10
|
+
return match ? Number(match[1]) : line.length;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function markerPositions(line) {
|
|
14
|
+
const positions = [];
|
|
15
|
+
let index = line.indexOf(REDACTED_LOG_MARKER);
|
|
16
|
+
while (index >= 0) {
|
|
17
|
+
positions.push(index);
|
|
18
|
+
index = line.indexOf(REDACTED_LOG_MARKER, index + REDACTED_LOG_MARKER.length);
|
|
19
|
+
}
|
|
20
|
+
return positions;
|
|
21
|
+
}
|
|
22
|
+
function repairCandidates(line, at) {
|
|
23
|
+
const end = at + REDACTED_LOG_MARKER.length;
|
|
24
|
+
const head = line.slice(0, end);
|
|
25
|
+
const rest = line.slice(end);
|
|
26
|
+
const candidates = [];
|
|
27
|
+
if (rest.startsWith("\\\""))
|
|
28
|
+
candidates.push(`${head}"${rest.slice(2)}`);
|
|
29
|
+
if (rest.startsWith("\""))
|
|
30
|
+
candidates.push(`${head}\\"${rest.slice(1)}`);
|
|
31
|
+
if (rest.startsWith("}") || rest.startsWith("]") || rest.startsWith(",")) {
|
|
32
|
+
candidates.push(`${head}"${rest}`);
|
|
33
|
+
}
|
|
34
|
+
return candidates;
|
|
35
|
+
}
|
|
36
|
+
/** Rebuild a JSON line whose escapes Paperclip's log redaction swallowed, or null when unrecoverable. */
|
|
37
|
+
export function repairRedactedJsonLine(line) {
|
|
38
|
+
if (!line.includes(REDACTED_LOG_MARKER))
|
|
39
|
+
return null;
|
|
40
|
+
let current = line;
|
|
41
|
+
let position = jsonErrorPosition(current);
|
|
42
|
+
if (position < 0)
|
|
43
|
+
return current;
|
|
44
|
+
for (let step = 0; step < REPAIR_MAX_STEPS; step += 1) {
|
|
45
|
+
const markers = markerPositions(current);
|
|
46
|
+
const preceding = markers.filter((marker) => marker <= position);
|
|
47
|
+
const at = preceding.length > 0 ? preceding[preceding.length - 1] : markers[0];
|
|
48
|
+
if (at === undefined)
|
|
49
|
+
return null;
|
|
50
|
+
let best = null;
|
|
51
|
+
let bestPosition = position;
|
|
52
|
+
for (const candidate of repairCandidates(current, at)) {
|
|
53
|
+
const candidatePosition = jsonErrorPosition(candidate);
|
|
54
|
+
if (candidatePosition < 0)
|
|
55
|
+
return candidate;
|
|
56
|
+
if (candidatePosition > bestPosition) {
|
|
57
|
+
best = candidate;
|
|
58
|
+
bestPosition = candidatePosition;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (best === null)
|
|
62
|
+
return null;
|
|
63
|
+
current = best;
|
|
64
|
+
position = bestPosition;
|
|
65
|
+
}
|
|
66
|
+
return jsonErrorPosition(current) < 0 ? current : null;
|
|
67
|
+
}
|
|
68
|
+
const SALVAGE_EVENT_PREFIX = '{"type":"';
|
|
69
|
+
const SALVAGE_MAX_EVENTS = 8;
|
|
70
|
+
function balancedObjectEnd(line, start) {
|
|
71
|
+
let depth = 0;
|
|
72
|
+
let inString = false;
|
|
73
|
+
let escaped = false;
|
|
74
|
+
for (let index = start; index < line.length; index += 1) {
|
|
75
|
+
const char = line[index];
|
|
76
|
+
if (inString) {
|
|
77
|
+
if (escaped)
|
|
78
|
+
escaped = false;
|
|
79
|
+
else if (char === "\\")
|
|
80
|
+
escaped = true;
|
|
81
|
+
else if (char === '"')
|
|
82
|
+
inString = false;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (char === '"')
|
|
86
|
+
inString = true;
|
|
87
|
+
else if (char === "{")
|
|
88
|
+
depth += 1;
|
|
89
|
+
else if (char === "}") {
|
|
90
|
+
depth -= 1;
|
|
91
|
+
if (depth === 0)
|
|
92
|
+
return index + 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return -1;
|
|
96
|
+
}
|
|
97
|
+
export function salvageRedactedEvents(line) {
|
|
98
|
+
if (!line.includes(REDACTED_LOG_MARKER))
|
|
99
|
+
return [];
|
|
100
|
+
const events = [];
|
|
101
|
+
let cursor = line.indexOf(REDACTED_LOG_MARKER);
|
|
102
|
+
while (cursor >= 0 && events.length < SALVAGE_MAX_EVENTS) {
|
|
103
|
+
const start = line.indexOf(SALVAGE_EVENT_PREFIX, cursor + REDACTED_LOG_MARKER.length);
|
|
104
|
+
if (start < 0)
|
|
105
|
+
break;
|
|
106
|
+
const end = balancedObjectEnd(line, start);
|
|
107
|
+
if (end > start) {
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(line.slice(start, end));
|
|
110
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
111
|
+
events.push(parsed);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
cursor = line.indexOf(REDACTED_LOG_MARKER, start);
|
|
119
|
+
}
|
|
120
|
+
return events;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=redaction-repair.js.map
|