cloving 1.0.1 → 1.2.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/README.md +251 -20
- package/assets/watch-fonts/fira-code-OFL.md +93 -0
- package/assets/watch-fonts/fira-code-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/fira-code-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-OFL.md +93 -0
- package/assets/watch-fonts/heebo-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-700-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-800-normal.woff2 +0 -0
- package/assets/watch-fonts/manifest.json +112 -0
- package/assets/watch-fonts/space-grotesk-OFL.md +93 -0
- package/assets/watch-fonts/space-grotesk-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-600-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-700-normal.woff2 +0 -0
- package/dist/src/authority/commands.d.ts +1 -0
- package/dist/src/authority/commands.js +33 -4
- package/dist/src/authority/dispatch-decision.d.ts +1 -0
- package/dist/src/authority/dispatch-decision.js +7 -4
- package/dist/src/authority/events.js +2 -2
- package/dist/src/authority/handoff-chain.d.ts +24 -0
- package/dist/src/authority/handoff-chain.js +46 -0
- package/dist/src/authority/reconciliation.d.ts +3 -0
- package/dist/src/authority/reconciliation.js +21 -5
- package/dist/src/authority/transitions.d.ts +1 -1
- package/dist/src/authority/transitions.js +5 -0
- package/dist/src/broker/actor/authority-queries.d.ts +4 -0
- package/dist/src/broker/actor/authority-queries.js +51 -2
- package/dist/src/broker/actor/authority-session.d.ts +2 -0
- package/dist/src/broker/actor/authority-session.js +3 -4
- package/dist/src/broker/actor/delivery-intents.d.ts +1 -0
- package/dist/src/broker/actor/effect-delivery.js +9 -0
- package/dist/src/broker/authority-admission.js +13 -5
- package/dist/src/chats/analysis-batches.d.ts +42 -0
- package/dist/src/chats/analysis-batches.js +97 -0
- package/dist/src/chats/analysis-checkpoint.d.ts +29 -0
- package/dist/src/chats/analysis-checkpoint.js +31 -0
- package/dist/src/chats/analysis-contract.d.ts +80 -0
- package/dist/src/chats/analysis-contract.js +16 -0
- package/dist/src/chats/analysis-data.d.ts +3 -0
- package/dist/src/chats/analysis-data.js +85 -0
- package/dist/src/chats/analysis-input.d.ts +18 -0
- package/dist/src/chats/analysis-input.js +97 -0
- package/dist/src/chats/analysis-links.d.ts +2 -0
- package/dist/src/chats/analysis-links.js +25 -0
- package/dist/src/chats/analysis-merge.d.ts +16 -0
- package/dist/src/chats/analysis-merge.js +134 -0
- package/dist/src/chats/analysis-prompt.d.ts +18 -0
- package/dist/src/chats/analysis-prompt.js +42 -0
- package/dist/src/chats/analysis-proposal.d.ts +4 -0
- package/dist/src/chats/analysis-proposal.js +41 -0
- package/dist/src/chats/analysis-provenance.d.ts +4 -0
- package/dist/src/chats/analysis-provenance.js +99 -0
- package/dist/src/chats/analysis-shape.d.ts +4 -0
- package/dist/src/chats/analysis-shape.js +235 -0
- package/dist/src/chats/analysis.d.ts +3 -0
- package/dist/src/chats/analysis.js +42 -0
- package/dist/src/chats/corpus-input.d.ts +5 -0
- package/dist/src/chats/corpus-input.js +288 -0
- package/dist/src/chats/destination.js +3 -1
- package/dist/src/chats/import.d.ts +8 -6
- package/dist/src/chats/import.js +195 -23
- package/dist/src/chats/input-file.d.ts +3 -0
- package/dist/src/chats/input-file.js +32 -0
- package/dist/src/chats/normalize.d.ts +11 -2
- package/dist/src/chats/normalize.js +18 -2
- package/dist/src/chats/previous-import.d.ts +5 -0
- package/dist/src/chats/previous-import.js +34 -0
- package/dist/src/chats/reader.d.ts +7 -1
- package/dist/src/chats/reader.js +46 -15
- package/dist/src/chats/redact-import.d.ts +14 -0
- package/dist/src/chats/redact-import.js +141 -0
- package/dist/src/chats/redaction.d.ts +41 -0
- package/dist/src/chats/redaction.js +161 -0
- package/dist/src/chats/reference-mapping.d.ts +16 -0
- package/dist/src/chats/reference-mapping.js +153 -0
- package/dist/src/chats/revision-input.d.ts +3 -0
- package/dist/src/chats/revision-input.js +180 -0
- package/dist/src/chats/revision.d.ts +48 -0
- package/dist/src/chats/revision.js +94 -0
- package/dist/src/chats/selection.d.ts +25 -0
- package/dist/src/chats/selection.js +109 -0
- package/dist/src/chats/source.d.ts +23 -0
- package/dist/src/chats/source.js +135 -0
- package/dist/src/chats/structure.d.ts +57 -0
- package/dist/src/chats/structure.js +216 -0
- package/dist/src/chats/workflow-generation-projection.d.ts +8 -0
- package/dist/src/chats/workflow-generation-projection.js +75 -0
- package/dist/src/chats/workflow-generation-source.d.ts +25 -0
- package/dist/src/chats/workflow-generation-source.js +124 -0
- package/dist/src/chats/workflow-generation-text.d.ts +23 -0
- package/dist/src/chats/workflow-generation-text.js +74 -0
- package/dist/src/chats/workflow-generation.d.ts +69 -0
- package/dist/src/chats/workflow-generation.js +142 -0
- package/dist/src/cli/arguments.d.ts +8 -0
- package/dist/src/cli/arguments.js +94 -13
- package/dist/src/cli/client.d.ts +1 -0
- package/dist/src/cli/client.js +38 -1
- package/dist/src/cli/commands.d.ts +3 -0
- package/dist/src/cli/commands.js +29 -11
- package/dist/src/cli/main.d.ts +2 -1
- package/dist/src/cli/main.js +61 -14
- package/dist/src/cli/render.d.ts +3 -0
- package/dist/src/cli/render.js +122 -7
- package/dist/src/cli/usage-render.d.ts +3 -0
- package/dist/src/cli/usage-render.js +28 -0
- package/dist/src/cli/usage.d.ts +3 -2
- package/dist/src/cli/usage.js +42 -7
- package/dist/src/cli/watch-actions.d.ts +10 -0
- package/dist/src/cli/watch-actions.js +58 -0
- package/dist/src/cli/watch-board-view.d.ts +44 -1
- package/dist/src/cli/watch-board-view.js +137 -18
- package/dist/src/cli/watch-board.d.ts +7 -1
- package/dist/src/cli/watch-board.js +48 -11
- package/dist/src/cli/watch-card-client.d.ts +1 -0
- package/dist/src/cli/watch-card-client.js +44 -0
- package/dist/src/cli/watch-card-forms.d.ts +2 -0
- package/dist/src/cli/watch-card-forms.js +33 -0
- package/dist/src/cli/watch-card-reply.d.ts +5 -0
- package/dist/src/cli/watch-card-reply.js +34 -0
- package/dist/src/cli/watch-card-view.d.ts +40 -0
- package/dist/src/cli/watch-card-view.js +86 -0
- package/dist/src/cli/watch-client.d.ts +1 -0
- package/dist/src/cli/watch-client.js +122 -0
- package/dist/src/cli/watch-model.d.ts +10 -4
- package/dist/src/cli/watch-model.js +61 -67
- package/dist/src/cli/watch-page.d.ts +1 -1
- package/dist/src/cli/watch-page.js +89 -86
- package/dist/src/cli/watch-phases.js +3 -3
- package/dist/src/cli/watch-pr-board.d.ts +3 -0
- package/dist/src/cli/watch-pr-board.js +26 -0
- package/dist/src/cli/watch-pr-command.d.ts +18 -0
- package/dist/src/cli/watch-pr-command.js +74 -0
- package/dist/src/cli/watch-pr-identity.d.ts +32 -0
- package/dist/src/cli/watch-pr-identity.js +87 -0
- package/dist/src/cli/watch-pr-observer.d.ts +20 -0
- package/dist/src/cli/watch-pr-observer.js +150 -0
- package/dist/src/cli/watch-pr-settings.d.ts +8 -0
- package/dist/src/cli/watch-pr-settings.js +47 -0
- package/dist/src/cli/watch-pull-request.d.ts +10 -0
- package/dist/src/cli/watch-pull-request.js +130 -0
- package/dist/src/cli/watch-reads.d.ts +7 -1
- package/dist/src/cli/watch-reads.js +44 -18
- package/dist/src/cli/watch-render.d.ts +29 -2
- package/dist/src/cli/watch-render.js +11 -3
- package/dist/src/cli/watch-run-input.d.ts +14 -0
- package/dist/src/cli/watch-run-input.js +49 -0
- package/dist/src/cli/watch-server.d.ts +3 -0
- package/dist/src/cli/watch-server.js +77 -8
- package/dist/src/cli/watch-start-view.d.ts +18 -0
- package/dist/src/cli/watch-start-view.js +77 -0
- package/dist/src/cli/watch-turn.d.ts +1 -0
- package/dist/src/cli/watch-turn.js +8 -3
- package/dist/src/cli/watch-usage.d.ts +6 -0
- package/dist/src/cli/watch-usage.js +65 -0
- package/dist/src/cli/watch-view.d.ts +22 -0
- package/dist/src/cli/watch-view.js +111 -19
- package/dist/src/cli.js +48 -6
- package/dist/src/daemon/analysis-launch.d.ts +5 -0
- package/dist/src/daemon/analysis-launch.js +81 -0
- package/dist/src/daemon/analysis-token.d.ts +6 -0
- package/dist/src/daemon/analysis-token.js +88 -0
- package/dist/src/daemon/analysis-worker.d.ts +20 -0
- package/dist/src/daemon/analysis-worker.js +271 -0
- package/dist/src/daemon/chats-batch-binding.d.ts +23 -0
- package/dist/src/daemon/chats-batch-binding.js +99 -0
- package/dist/src/daemon/chats-capture.d.ts +18 -0
- package/dist/src/daemon/chats-capture.js +194 -0
- package/dist/src/daemon/chats-checkpoints.d.ts +41 -0
- package/dist/src/daemon/chats-checkpoints.js +186 -0
- package/dist/src/daemon/chats-files.d.ts +42 -0
- package/dist/src/daemon/chats-files.js +252 -0
- package/dist/src/daemon/chats-follow-up.d.ts +38 -0
- package/dist/src/daemon/chats-follow-up.js +180 -0
- package/dist/src/daemon/chats-question.d.ts +15 -0
- package/dist/src/daemon/chats-question.js +93 -0
- package/dist/src/daemon/chats-worker.d.ts +13 -0
- package/dist/src/daemon/chats-worker.js +201 -0
- package/dist/src/daemon/claude-turn.d.ts +11 -0
- package/dist/src/daemon/claude-turn.js +96 -15
- package/dist/src/daemon/claude-worker.d.ts +2 -1
- package/dist/src/daemon/claude-worker.js +92 -45
- package/dist/src/daemon/codex-stream.js +16 -0
- package/dist/src/daemon/daemon.d.ts +8 -1
- package/dist/src/daemon/daemon.js +51 -14
- package/dist/src/daemon/delivery-router.js +6 -0
- package/dist/src/daemon/github-calls.d.ts +4 -0
- package/dist/src/daemon/github-calls.js +16 -7
- package/dist/src/daemon/github-ci.d.ts +6 -0
- package/dist/src/daemon/github-ci.js +170 -0
- package/dist/src/daemon/github-merge-call.d.ts +14 -0
- package/dist/src/daemon/github-merge-call.js +73 -0
- package/dist/src/daemon/github-merge.d.ts +3 -5
- package/dist/src/daemon/github-merge.js +52 -95
- package/dist/src/daemon/github-methods.d.ts +28 -0
- package/dist/src/daemon/github-methods.js +84 -16
- package/dist/src/daemon/github-operation.d.ts +2 -0
- package/dist/src/daemon/github-retry.d.ts +6 -0
- package/dist/src/daemon/github-retry.js +49 -0
- package/dist/src/daemon/github-ticket-status.d.ts +3 -0
- package/dist/src/daemon/github-ticket-status.js +294 -0
- package/dist/src/daemon/github-worker.js +2 -1
- package/dist/src/daemon/handoff-worker.d.ts +10 -0
- package/dist/src/daemon/handoff-worker.js +232 -0
- package/dist/src/daemon/issue-queue.d.ts +2 -0
- package/dist/src/daemon/issue-queue.js +186 -43
- package/dist/src/daemon/notify-journal.d.ts +18 -0
- package/dist/src/daemon/notify-journal.js +108 -0
- package/dist/src/daemon/notify-message.js +2 -1
- package/dist/src/daemon/notify.js +176 -37
- package/dist/src/daemon/operation-journal.d.ts +1 -0
- package/dist/src/daemon/operation-journal.js +4 -2
- package/dist/src/daemon/retrying-turn.d.ts +12 -0
- package/dist/src/daemon/retrying-turn.js +231 -0
- package/dist/src/daemon/ticket-dependency-waits.d.ts +18 -0
- package/dist/src/daemon/ticket-dependency-waits.js +60 -0
- package/dist/src/daemon/ticket-registration.d.ts +8 -4
- package/dist/src/daemon/ticket-registration.js +76 -22
- package/dist/src/daemon/ticket-source.d.ts +4 -1
- package/dist/src/daemon/ticket-source.js +8 -2
- package/dist/src/decision-visibility.d.ts +2 -0
- package/dist/src/decision-visibility.js +10 -0
- package/dist/src/execution-pin.d.ts +4 -0
- package/dist/src/execution-pin.js +23 -2
- package/dist/src/host/start-delivery.d.ts +2 -1
- package/dist/src/host/start-delivery.js +1 -1
- package/dist/src/host/step-bridge-host.d.ts +1 -0
- package/dist/src/host/step-bridge-host.js +16 -0
- package/dist/src/host/world-reconciliation.d.ts +3 -0
- package/dist/src/host/world-reconciliation.js +9 -0
- package/dist/src/host/world-runs.js +14 -1
- package/dist/src/issue-identity.d.ts +6 -0
- package/dist/src/issue-identity.js +23 -0
- package/dist/src/service/queue-runs.d.ts +3 -0
- package/dist/src/service/queue-runs.js +39 -0
- package/dist/src/service/registry.d.ts +8 -2
- package/dist/src/service/registry.js +20 -8
- package/dist/src/service/replies.d.ts +2 -0
- package/dist/src/service/run-handlers.js +54 -30
- package/dist/src/service/service.d.ts +3 -0
- package/dist/src/service/service.js +3 -1
- package/dist/src/service/submission-handler.d.ts +1 -0
- package/dist/src/service/submission-handler.js +92 -13
- package/dist/src/service/worker-totals.d.ts +3 -0
- package/dist/src/service/worker-totals.js +52 -0
- package/dist/src/step-bridge.d.ts +9 -0
- package/dist/src/steps/handoff.d.ts +2 -0
- package/dist/src/steps/handoff.js +13 -0
- package/dist/src/ticket-dependencies.d.ts +1 -0
- package/dist/src/ticket-dependencies.js +15 -0
- package/dist/src/ticket-status.d.ts +8 -0
- package/dist/src/ticket-status.js +48 -0
- package/dist/src/token-prices.d.ts +21 -0
- package/dist/src/token-prices.js +108 -0
- package/dist/src/worker-usage.d.ts +31 -0
- package/dist/src/worker-usage.js +44 -0
- package/dist/src/workflow-admission.d.ts +1 -0
- package/dist/src/workflow-candidate.d.ts +29 -0
- package/dist/src/workflow-candidate.js +60 -0
- package/dist/src/workflow-contracts.d.ts +4 -0
- package/dist/src/workflow-contracts.js +32 -5
- package/dist/src/workflow-declaration.d.ts +12 -1
- package/dist/src/workflow-declaration.js +71 -13
- package/dist/src/workflow-discovery.d.ts +18 -0
- package/dist/src/workflow-discovery.js +33 -0
- package/dist/src/workflow-refusals.d.ts +6 -0
- package/dist/src/workflow-refusals.js +39 -0
- package/dist/src/workflows.d.ts +3 -1
- package/dist/src/workflows.js +2 -0
- package/dist/tools/admit-workflow.d.ts +13 -0
- package/dist/tools/admit-workflow.js +94 -0
- package/dist/tools/self-host-processes.js +7 -2
- package/dist/tools/self-host-project.d.ts +17 -0
- package/dist/tools/self-host-project.js +177 -0
- package/dist/tools/self-host-workflows.d.ts +1 -1
- package/dist/tools/self-host-workflows.js +2 -2
- package/dist/tools/self-host.js +30 -84
- package/dist/tools/self-update-host.d.ts +7 -0
- package/dist/tools/self-update-host.js +48 -1
- package/dist/tools/self-update.d.ts +1 -0
- package/dist/tools/self-update.js +85 -13
- package/dist/tools/workflow-bundle-dependencies.d.ts +5 -0
- package/dist/tools/workflow-bundle-dependencies.js +93 -0
- package/dist/tools/workflow-bundles.d.ts +20 -0
- package/dist/tools/workflow-bundles.js +173 -0
- package/dist/tools/workflow-candidate-counts.d.ts +8 -0
- package/dist/tools/workflow-candidate-counts.js +251 -0
- package/dist/tools/workflow-candidate-program.d.ts +11 -0
- package/dist/tools/workflow-candidate-program.js +78 -0
- package/dist/tools/workflow-candidate-source.d.ts +6 -0
- package/dist/tools/workflow-candidate-source.js +107 -0
- package/dist/tools/workflow-candidate-syntax.d.ts +15 -0
- package/dist/tools/workflow-candidate-syntax.js +219 -0
- package/dist/tools/workflow-folder-capture.d.ts +17 -0
- package/dist/tools/workflow-folder-capture.js +216 -0
- package/dist/tools/workflow-folders.d.ts +33 -0
- package/dist/tools/workflow-folders.js +150 -0
- package/dist/tools/workflow-source-effects.d.ts +10 -0
- package/dist/tools/workflow-source-effects.js +213 -0
- package/dist/workflows/analyze-chats.d.ts +15 -0
- package/dist/workflows/analyze-chats.declaration.json +22 -0
- package/dist/workflows/analyze-chats.js +250 -0
- package/dist/workflows/develop-prd.js +71 -3
- package/dist/workflows/develop-ticket.d.ts +1 -7
- package/dist/workflows/develop-ticket.js +47 -141
- package/dist/workflows/issue-labels.d.ts +3 -1
- package/dist/workflows/issue-labels.js +34 -0
- package/dist/workflows/ticket-prompts.d.ts +35 -0
- package/dist/workflows/ticket-prompts.js +143 -0
- package/package.json +25 -5
package/dist/src/chats/import.js
CHANGED
|
@@ -1,23 +1,97 @@
|
|
|
1
|
+
import { readPreviousImport } from "./previous-import.js";
|
|
2
|
+
import { pairEvidence, compareEvidence, } from "./revision.js";
|
|
3
|
+
import { structureImport } from "./structure.js";
|
|
1
4
|
import { randomUUID } from "node:crypto";
|
|
2
5
|
import { rm } from "node:fs/promises";
|
|
3
6
|
import { join, resolve } from "node:path";
|
|
4
7
|
import { createPrivateFile, importDirectory, validateDestination, } from "./destination.js";
|
|
5
8
|
import { readTranscript } from "./reader.js";
|
|
6
|
-
|
|
9
|
+
import { selectChatSources, } from "./selection.js";
|
|
10
|
+
const corpusLimit = 52_428_800;
|
|
11
|
+
function boundedCorpusLine(record, previousBytes) {
|
|
12
|
+
const line = `${JSON.stringify(record)}\n`;
|
|
13
|
+
const bytes = previousBytes + Buffer.byteLength(line);
|
|
14
|
+
if (bytes > corpusLimit)
|
|
15
|
+
throw new Error("corpus-too-large");
|
|
16
|
+
return { line, bytes };
|
|
17
|
+
}
|
|
18
|
+
async function writeImport(directory, data) {
|
|
19
|
+
const { request, previous } = data;
|
|
20
|
+
const selection = await selectChatSources(request);
|
|
21
|
+
const keys = selection.entries.map((selected) => selectionKey(selected, selection.roots));
|
|
22
|
+
const pairs = pairEvidence({ previous, current: keys });
|
|
23
|
+
const evidence = [];
|
|
7
24
|
const files = [];
|
|
25
|
+
const records = [];
|
|
26
|
+
let corpusBytes = 0;
|
|
8
27
|
const corpus = await createPrivateFile(join(directory, "corpus.jsonl"));
|
|
9
28
|
try {
|
|
10
|
-
for (const selected of
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
for (const [index, selected] of selection.entries.entries()) {
|
|
30
|
+
const matched = pairs[index];
|
|
31
|
+
const before = matched === null || matched === undefined
|
|
32
|
+
? undefined
|
|
33
|
+
: previous[matched];
|
|
34
|
+
const result = await readSelected(selected, before?.byteSize);
|
|
35
|
+
evidence.push({
|
|
36
|
+
...selectionKey(selected, selection.roots),
|
|
37
|
+
...result.coverage,
|
|
38
|
+
...(result.prefixDigest === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: { prefixDigest: result.prefixDigest }),
|
|
41
|
+
});
|
|
42
|
+
files.push({
|
|
43
|
+
...result.coverage,
|
|
44
|
+
kind: selected.kind,
|
|
45
|
+
selection: selected.selection,
|
|
46
|
+
});
|
|
47
|
+
for (const record of result.records) {
|
|
48
|
+
const next = boundedCorpusLine(record, corpusBytes);
|
|
49
|
+
await corpus.writeFile(next.line);
|
|
50
|
+
corpusBytes = next.bytes;
|
|
51
|
+
records.push(record);
|
|
52
|
+
}
|
|
15
53
|
}
|
|
16
54
|
}
|
|
17
55
|
finally {
|
|
18
56
|
await corpus.close();
|
|
19
57
|
}
|
|
20
|
-
return files;
|
|
58
|
+
return { roots: selection.roots, files, records, evidence };
|
|
59
|
+
}
|
|
60
|
+
function selectionKey(selected, roots) {
|
|
61
|
+
return {
|
|
62
|
+
path: selected.path,
|
|
63
|
+
root: selected.selection.kind === "root"
|
|
64
|
+
? (roots[selected.selection.index] ?? null)
|
|
65
|
+
: null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async function readSelected(selected, prefixBytes) {
|
|
69
|
+
if (selected.outcome !== undefined)
|
|
70
|
+
return {
|
|
71
|
+
coverage: {
|
|
72
|
+
path: selected.path,
|
|
73
|
+
...selected.outcome,
|
|
74
|
+
digest: null,
|
|
75
|
+
recordCount: 0,
|
|
76
|
+
omissions: { metadataRecords: 0, thinkingBlocks: 0 },
|
|
77
|
+
},
|
|
78
|
+
records: [],
|
|
79
|
+
};
|
|
80
|
+
return readTranscript(selected.path, {
|
|
81
|
+
...(selected.inspection === undefined
|
|
82
|
+
? {}
|
|
83
|
+
: { inspection: selected.inspection }),
|
|
84
|
+
...(prefixBytes === undefined ? {} : { prefixBytes }),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async function writeMetadata(path, data) {
|
|
88
|
+
const handle = await createPrivateFile(path);
|
|
89
|
+
try {
|
|
90
|
+
await handle.writeFile(`${JSON.stringify({ schemaVersion: 1, ...data }, null, 2)}\n`);
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
await handle.close();
|
|
94
|
+
}
|
|
21
95
|
}
|
|
22
96
|
async function writeCoverage(directory, coverage) {
|
|
23
97
|
const manifest = join(directory, "coverage.json");
|
|
@@ -41,31 +115,129 @@ async function writeCoverage(directory, coverage) {
|
|
|
41
115
|
}
|
|
42
116
|
}
|
|
43
117
|
export async function importChats(request) {
|
|
44
|
-
const empty =
|
|
118
|
+
const empty = emptyImport(request);
|
|
119
|
+
if (process.platform !== "darwin")
|
|
120
|
+
return { ...empty, code: "platform-unsupported" };
|
|
45
121
|
if (request.runtimeDirectory === undefined)
|
|
46
122
|
return { ...empty, code: "runtime-directory-unknown" };
|
|
47
123
|
try {
|
|
124
|
+
const runtime = resolve(request.workingDirectory, request.runtimeDirectory);
|
|
125
|
+
const previous = request.previousImportId === undefined
|
|
126
|
+
? undefined
|
|
127
|
+
: await readPreviousImport(runtime, request.previousImportId);
|
|
48
128
|
const importId = randomUUID();
|
|
49
|
-
const directory = await importDirectory(
|
|
50
|
-
|
|
51
|
-
const included = files.filter((file) => file.status === "included").length;
|
|
52
|
-
const records = files.reduce((count, file) => count + file.recordCount, 0);
|
|
53
|
-
const refused = files.length - included;
|
|
54
|
-
await writeCoverage(directory, { schemaVersion: 1, importId, files });
|
|
55
|
-
return {
|
|
56
|
-
code: refused === 0 ? "import-complete" : "import-partial",
|
|
57
|
-
importId,
|
|
58
|
-
included,
|
|
59
|
-
refused,
|
|
60
|
-
records,
|
|
61
|
-
};
|
|
129
|
+
const directory = await importDirectory(runtime, importId);
|
|
130
|
+
return await completeImport(directory, { request, previous, importId });
|
|
62
131
|
}
|
|
63
132
|
catch (error) {
|
|
64
133
|
return {
|
|
65
134
|
...empty,
|
|
66
|
-
code: error instanceof Error &&
|
|
67
|
-
|
|
135
|
+
code: error instanceof Error &&
|
|
136
|
+
(error.message === "destination-refused" ||
|
|
137
|
+
error.message === "previous-import-refused")
|
|
138
|
+
? error.message
|
|
68
139
|
: "output-failed",
|
|
69
140
|
};
|
|
70
141
|
}
|
|
71
142
|
}
|
|
143
|
+
function importTotals(files) {
|
|
144
|
+
return {
|
|
145
|
+
included: files.filter((file) => file.status === "included").length,
|
|
146
|
+
refused: files.filter((file) => ["unreadable", "unsupported", "too-large"].includes(file.status)).length,
|
|
147
|
+
excluded: files.filter((file) => file.status === "excluded").length,
|
|
148
|
+
pending: files.filter((file) => file.status === "pending").length,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function coverageManifest(data) {
|
|
152
|
+
const { importId, roots, files, exclusions } = data;
|
|
153
|
+
if (roots.length > 0)
|
|
154
|
+
return {
|
|
155
|
+
schemaVersion: 2,
|
|
156
|
+
importId,
|
|
157
|
+
roots,
|
|
158
|
+
exclusions,
|
|
159
|
+
limits: {
|
|
160
|
+
depth: 4,
|
|
161
|
+
files: 100,
|
|
162
|
+
sourceBytes: 10_485_760,
|
|
163
|
+
corpusBytes: corpusLimit,
|
|
164
|
+
},
|
|
165
|
+
entries: files,
|
|
166
|
+
};
|
|
167
|
+
return {
|
|
168
|
+
schemaVersion: 1,
|
|
169
|
+
importId,
|
|
170
|
+
files: files.map((entry) => ({
|
|
171
|
+
path: entry.path,
|
|
172
|
+
status: entry.status,
|
|
173
|
+
reason: entry.reason,
|
|
174
|
+
digest: entry.digest,
|
|
175
|
+
...(entry.byteSize === undefined ? {} : { byteSize: entry.byteSize }),
|
|
176
|
+
recordCount: entry.recordCount,
|
|
177
|
+
omissions: entry.omissions,
|
|
178
|
+
})),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function emptyImport(request) {
|
|
182
|
+
return {
|
|
183
|
+
importId: null,
|
|
184
|
+
included: 0,
|
|
185
|
+
refused: 0,
|
|
186
|
+
...((request.roots?.length ?? 0) === 0 ? {} : { excluded: 0, pending: 0 }),
|
|
187
|
+
...structureImport([]).counts,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async function completeImport(directory, data) {
|
|
191
|
+
const { request, previous, importId } = data;
|
|
192
|
+
const { roots, files, records, evidence } = await writeImport(directory, {
|
|
193
|
+
request,
|
|
194
|
+
previous: previous?.entries ?? [],
|
|
195
|
+
});
|
|
196
|
+
const structure = structureImport(records);
|
|
197
|
+
await writeMetadata(join(directory, "structure.json"), {
|
|
198
|
+
importId,
|
|
199
|
+
...structure,
|
|
200
|
+
});
|
|
201
|
+
const partial = await writeRevision(directory, {
|
|
202
|
+
previous,
|
|
203
|
+
evidence,
|
|
204
|
+
importId,
|
|
205
|
+
});
|
|
206
|
+
const totals = importTotals(files);
|
|
207
|
+
const { refused, pending } = totals;
|
|
208
|
+
await writeCoverage(directory, coverageManifest({
|
|
209
|
+
importId,
|
|
210
|
+
roots,
|
|
211
|
+
files,
|
|
212
|
+
exclusions: request.excludeProjects ?? [],
|
|
213
|
+
}));
|
|
214
|
+
return {
|
|
215
|
+
code: refused + pending + partial === 0 ? "import-complete" : "import-partial",
|
|
216
|
+
importId,
|
|
217
|
+
included: totals.included,
|
|
218
|
+
refused,
|
|
219
|
+
...(roots.length === 0 ? {} : { excluded: totals.excluded, pending }),
|
|
220
|
+
...structure.counts,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async function writeRevision(directory, data) {
|
|
224
|
+
const { previous, evidence, importId } = data;
|
|
225
|
+
if (previous === undefined)
|
|
226
|
+
return 0;
|
|
227
|
+
const comparison = compareEvidence({
|
|
228
|
+
previous: previous.entries,
|
|
229
|
+
current: evidence,
|
|
230
|
+
});
|
|
231
|
+
try {
|
|
232
|
+
await writeMetadata(join(directory, "revision.json"), {
|
|
233
|
+
importId,
|
|
234
|
+
previousImportId: previous.importId,
|
|
235
|
+
evidenceRevision: previous.evidenceRevision,
|
|
236
|
+
...comparison,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
throw new Error("output-failed");
|
|
241
|
+
}
|
|
242
|
+
return comparison.counts.missing + comparison.counts.truncated;
|
|
243
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { lstat, open } from "node:fs/promises";
|
|
3
|
+
export async function openInput(path) {
|
|
4
|
+
if (process.platform !== "darwin")
|
|
5
|
+
throw new Error("platform-unsupported");
|
|
6
|
+
const before = await lstat(path);
|
|
7
|
+
if (!before.isFile() || before.nlink !== 1)
|
|
8
|
+
throw new Error("invalid-import");
|
|
9
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);
|
|
10
|
+
try {
|
|
11
|
+
const opened = await handle.stat();
|
|
12
|
+
if (!opened.isFile() ||
|
|
13
|
+
opened.nlink !== 1 ||
|
|
14
|
+
opened.dev !== before.dev ||
|
|
15
|
+
opened.ino !== before.ino)
|
|
16
|
+
throw new Error("invalid-import");
|
|
17
|
+
return handle;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
await handle.close();
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function readInput(path) {
|
|
25
|
+
const handle = await openInput(path);
|
|
26
|
+
try {
|
|
27
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(await handle.readFile());
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
await handle.close();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -2,8 +2,7 @@ export interface Omissions {
|
|
|
2
2
|
metadataRecords: number;
|
|
3
3
|
thinkingBlocks: number;
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
readonly schemaVersion: 1;
|
|
5
|
+
interface MessageFields {
|
|
7
6
|
readonly sourceId: string;
|
|
8
7
|
readonly role: "user" | "assistant";
|
|
9
8
|
readonly timestamp: string | null;
|
|
@@ -11,10 +10,20 @@ export interface MessageRecord {
|
|
|
11
10
|
readonly text: string;
|
|
12
11
|
readonly blocks: readonly Record<string, unknown>[];
|
|
13
12
|
}
|
|
13
|
+
export type MessageRecord = MessageFields & ({
|
|
14
|
+
readonly schemaVersion: 1;
|
|
15
|
+
} | {
|
|
16
|
+
readonly schemaVersion: 2;
|
|
17
|
+
readonly uuid: string | null;
|
|
18
|
+
readonly isSidechain: boolean | null;
|
|
19
|
+
readonly parentUuid?: string | null;
|
|
20
|
+
});
|
|
14
21
|
export interface NormalizedTranscript {
|
|
15
22
|
readonly status: "included" | "unsupported";
|
|
16
23
|
readonly reason: string;
|
|
17
24
|
readonly records: readonly MessageRecord[];
|
|
18
25
|
readonly omissions: Omissions;
|
|
19
26
|
}
|
|
27
|
+
export declare function parsePreservedJson(text: string): unknown;
|
|
20
28
|
export declare function normalizeTranscript(text: string, digest: string): NormalizedTranscript;
|
|
29
|
+
export {};
|
|
@@ -58,6 +58,9 @@ function preserveJson(...argumentsList) {
|
|
|
58
58
|
throw new Error("unsupported-record");
|
|
59
59
|
return JSON.rawJSON(context["source"]);
|
|
60
60
|
}
|
|
61
|
+
export function parsePreservedJson(text) {
|
|
62
|
+
return JSON.parse(text, preserveJson);
|
|
63
|
+
}
|
|
61
64
|
function blocks(content, omissions) {
|
|
62
65
|
if (typeof content === "string")
|
|
63
66
|
return [{ position: 0, type: "text", text: content }];
|
|
@@ -73,6 +76,18 @@ function blocks(content, omissions) {
|
|
|
73
76
|
return [{ ...supportedBlock(block), position }];
|
|
74
77
|
});
|
|
75
78
|
}
|
|
79
|
+
function identity(value) {
|
|
80
|
+
const uuid = value["uuid"];
|
|
81
|
+
const parent = value["parentUuid"];
|
|
82
|
+
const sidechain = value["isSidechain"];
|
|
83
|
+
return {
|
|
84
|
+
uuid: typeof uuid === "string" && uuid !== "" ? uuid : null,
|
|
85
|
+
isSidechain: typeof sidechain === "boolean" ? sidechain : null,
|
|
86
|
+
...(parent === null || (typeof parent === "string" && parent !== "")
|
|
87
|
+
? { parentUuid: parent }
|
|
88
|
+
: {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
76
91
|
function message(value, context) {
|
|
77
92
|
if (!object(value) || typeof value["type"] !== "string")
|
|
78
93
|
throw new Error("unsupported-record");
|
|
@@ -89,7 +104,8 @@ function message(value, context) {
|
|
|
89
104
|
throw new Error("unsupported-message");
|
|
90
105
|
const normalized = blocks(envelope["content"], context.omissions);
|
|
91
106
|
return {
|
|
92
|
-
schemaVersion:
|
|
107
|
+
schemaVersion: 2,
|
|
108
|
+
...identity(value),
|
|
93
109
|
sourceId: context.sourceId,
|
|
94
110
|
role: value["type"],
|
|
95
111
|
timestamp: optionalText(value["timestamp"]),
|
|
@@ -112,7 +128,7 @@ export function normalizeTranscript(text, digest) {
|
|
|
112
128
|
lines.pop();
|
|
113
129
|
try {
|
|
114
130
|
for (const line of lines) {
|
|
115
|
-
const parsed =
|
|
131
|
+
const parsed = parsePreservedJson(line);
|
|
116
132
|
const record = message(parsed, {
|
|
117
133
|
sourceId: `${digest}:${String(offset)}`,
|
|
118
134
|
omissions,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { lstat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { validateDestination } from "./destination.js";
|
|
4
|
+
import { readInput } from "./input-file.js";
|
|
5
|
+
import { parseEvidenceCoverage, nextEvidenceRevision, } from "./revision-input.js";
|
|
6
|
+
async function optionalRevision(path) {
|
|
7
|
+
try {
|
|
8
|
+
await lstat(path);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
12
|
+
return;
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
return readInput(path);
|
|
16
|
+
}
|
|
17
|
+
export async function readPreviousImport(runtime, importId) {
|
|
18
|
+
if (process.platform !== "darwin")
|
|
19
|
+
throw new Error("platform-unsupported");
|
|
20
|
+
try {
|
|
21
|
+
if (!/^[a-f\d]{8}-[a-f\d]{4}-4[a-f\d]{3}-[89ab][a-f\d]{3}-[a-f\d]{12}$/.test(importId))
|
|
22
|
+
throw new Error("invalid-import");
|
|
23
|
+
const directory = join(runtime, "chats", importId);
|
|
24
|
+
await validateDestination(directory);
|
|
25
|
+
if (!(await lstat(directory)).isDirectory())
|
|
26
|
+
throw new Error("invalid-import");
|
|
27
|
+
const entries = parseEvidenceCoverage(await readInput(join(directory, "coverage.json")), importId);
|
|
28
|
+
const evidenceRevision = nextEvidenceRevision(await optionalRevision(join(directory, "revision.json")), importId);
|
|
29
|
+
return { importId, entries, evidenceRevision };
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new Error("previous-import-refused");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ScanSource } from "./source.ts";
|
|
1
2
|
import { type MessageRecord, type Omissions } from "./normalize.ts";
|
|
2
3
|
export declare const transcriptLimit = 10485760;
|
|
3
4
|
export interface SelectedTranscript {
|
|
@@ -5,11 +6,16 @@ export interface SelectedTranscript {
|
|
|
5
6
|
readonly status: "included" | "unreadable" | "unsupported" | "too-large";
|
|
6
7
|
readonly reason: string;
|
|
7
8
|
readonly digest: string | null;
|
|
9
|
+
readonly byteSize?: number;
|
|
8
10
|
readonly recordCount: number;
|
|
9
11
|
readonly omissions: Omissions;
|
|
10
12
|
}
|
|
11
13
|
export interface ReadTranscript {
|
|
12
14
|
readonly coverage: SelectedTranscript;
|
|
13
15
|
readonly records: readonly MessageRecord[];
|
|
16
|
+
readonly prefixDigest?: string;
|
|
14
17
|
}
|
|
15
|
-
export declare function readTranscript(path: string
|
|
18
|
+
export declare function readTranscript(path: string, options?: {
|
|
19
|
+
readonly inspection?: ScanSource;
|
|
20
|
+
readonly prefixBytes?: number;
|
|
21
|
+
}): Promise<ReadTranscript>;
|
package/dist/src/chats/reader.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import { lstat, open } from "node:fs/promises";
|
|
4
|
+
import { sourceFailure, verifyScanSource, verifyOpenSource, } from "./source.js";
|
|
4
5
|
import { normalizeTranscript, } from "./normalize.js";
|
|
5
6
|
export const transcriptLimit = 10_485_760;
|
|
6
7
|
function refused(path, reason) {
|
|
@@ -37,7 +38,8 @@ async function boundedBytes(handle) {
|
|
|
37
38
|
return "file-too-large";
|
|
38
39
|
return Buffer.concat(chunks, total);
|
|
39
40
|
}
|
|
40
|
-
function normalizeBytes(path,
|
|
41
|
+
function normalizeBytes(path, options) {
|
|
42
|
+
const { bytes } = options;
|
|
41
43
|
const digest = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
42
44
|
let text;
|
|
43
45
|
try {
|
|
@@ -54,34 +56,63 @@ function normalizeBytes(path, bytes) {
|
|
|
54
56
|
status: result.status,
|
|
55
57
|
reason: result.reason,
|
|
56
58
|
digest,
|
|
59
|
+
...(result.status === "included" ? { byteSize: bytes.length } : {}),
|
|
57
60
|
recordCount: result.records.length,
|
|
58
61
|
omissions: result.omissions,
|
|
59
62
|
},
|
|
60
63
|
records: result.records,
|
|
64
|
+
...(options.prefixBytes !== undefined &&
|
|
65
|
+
options.prefixBytes >= 0 &&
|
|
66
|
+
options.prefixBytes < bytes.length
|
|
67
|
+
? {
|
|
68
|
+
prefixDigest: `sha256:${createHash("sha256").update(bytes.subarray(0, options.prefixBytes)).digest("hex")}`,
|
|
69
|
+
}
|
|
70
|
+
: {}),
|
|
61
71
|
};
|
|
62
72
|
}
|
|
63
|
-
|
|
73
|
+
async function readSourceBytes(path, inspection) {
|
|
74
|
+
const handle = await open(path, constants.O_RDONLY |
|
|
75
|
+
constants.O_NONBLOCK |
|
|
76
|
+
(inspection === undefined ? constants.O_NOFOLLOW : 0x20000000));
|
|
64
77
|
try {
|
|
78
|
+
if (inspection !== undefined) {
|
|
79
|
+
verifyOpenSource(inspection, await handle.stat());
|
|
80
|
+
await verifyScanSource(inspection);
|
|
81
|
+
}
|
|
82
|
+
return await boundedBytes(handle);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
await handle.close();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function readTranscript(path, options = {}) {
|
|
89
|
+
const { inspection } = options;
|
|
90
|
+
try {
|
|
91
|
+
if (inspection !== undefined)
|
|
92
|
+
await verifyScanSource(inspection);
|
|
65
93
|
const stats = await lstat(path);
|
|
66
94
|
if (stats.isSymbolicLink())
|
|
67
95
|
return refused(path, "symbolic-link");
|
|
68
96
|
if (!stats.isFile())
|
|
69
97
|
return refused(path, "nonregular-file");
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
bytes = await boundedBytes(handle);
|
|
74
|
-
}
|
|
75
|
-
finally {
|
|
76
|
-
await handle.close();
|
|
77
|
-
}
|
|
78
|
-
return typeof bytes === "string"
|
|
98
|
+
const bytes = await readSourceBytes(path, inspection);
|
|
99
|
+
const result = typeof bytes === "string"
|
|
79
100
|
? refused(path, bytes)
|
|
80
|
-
: normalizeBytes(path, bytes);
|
|
101
|
+
: normalizeBytes(path, { bytes, ...options });
|
|
102
|
+
return scannedResult(result, inspection);
|
|
81
103
|
}
|
|
82
104
|
catch (error) {
|
|
83
|
-
|
|
84
|
-
return refused(path, "symbolic-link");
|
|
85
|
-
return refused(path, "source-unreadable");
|
|
105
|
+
return readFailure(path, { error, inspection });
|
|
86
106
|
}
|
|
87
107
|
}
|
|
108
|
+
function scannedResult(result, inspection) {
|
|
109
|
+
if (inspection === undefined || result.coverage.status !== "too-large")
|
|
110
|
+
return result;
|
|
111
|
+
return { ...result, coverage: { ...result.coverage, status: "unsupported" } };
|
|
112
|
+
}
|
|
113
|
+
function readFailure(path, data) {
|
|
114
|
+
const { error, inspection } = data;
|
|
115
|
+
if (error instanceof Error && "code" in error && error.code === "ELOOP")
|
|
116
|
+
return refused(path, "symbolic-link");
|
|
117
|
+
return refused(path, inspection === undefined ? "source-unreadable" : sourceFailure(error));
|
|
118
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RedactionSettings } from "./redaction.ts";
|
|
2
|
+
export interface RedactImportRequest {
|
|
3
|
+
readonly importId: string;
|
|
4
|
+
readonly workingDirectory: string;
|
|
5
|
+
readonly runtimeDirectory: string | undefined;
|
|
6
|
+
readonly settings: RedactionSettings;
|
|
7
|
+
}
|
|
8
|
+
export declare function redactImport(request: RedactImportRequest): Promise<{
|
|
9
|
+
code: string;
|
|
10
|
+
importId: string;
|
|
11
|
+
recordCount: number;
|
|
12
|
+
counts: import("./redaction.ts").RedactionCounts;
|
|
13
|
+
totalCount: number;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { openInput, readInput } from "./input-file.js";
|
|
2
|
+
import { lstat, rm } from "node:fs/promises";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { createPrivateFile, validateDestination } from "./destination.js";
|
|
5
|
+
import { parseCorpusRecord, validateCoverage } from "./corpus-input.js";
|
|
6
|
+
import { emptyCounts, redactRecord, categories, } from "./redaction.js";
|
|
7
|
+
async function absent(path) {
|
|
8
|
+
try {
|
|
9
|
+
await lstat(path);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
13
|
+
return;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
throw new Error("outputs-exist");
|
|
17
|
+
}
|
|
18
|
+
async function* corpusLines(handle) {
|
|
19
|
+
const buffer = Buffer.alloc(65_536);
|
|
20
|
+
const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
21
|
+
let pending = "";
|
|
22
|
+
let bytesRead = -1;
|
|
23
|
+
while (bytesRead !== 0) {
|
|
24
|
+
({ bytesRead } = await handle.read({ buffer, position: null }));
|
|
25
|
+
pending += decoder.decode(buffer.subarray(0, bytesRead), {
|
|
26
|
+
stream: bytesRead !== 0,
|
|
27
|
+
});
|
|
28
|
+
let start = 0;
|
|
29
|
+
let end = pending.indexOf("\n");
|
|
30
|
+
while (end !== -1) {
|
|
31
|
+
yield pending.slice(start, end);
|
|
32
|
+
start = end + 1;
|
|
33
|
+
end = pending.indexOf("\n", start);
|
|
34
|
+
}
|
|
35
|
+
pending = pending.slice(start);
|
|
36
|
+
}
|
|
37
|
+
if (pending !== "")
|
|
38
|
+
yield pending;
|
|
39
|
+
}
|
|
40
|
+
async function redactCorpus(handles, settings) {
|
|
41
|
+
const counts = emptyCounts();
|
|
42
|
+
let recordCount = 0;
|
|
43
|
+
for await (const line of corpusLines(handles.input)) {
|
|
44
|
+
const result = redactRecord(parseCorpusRecord(line), settings);
|
|
45
|
+
for (const category of categories)
|
|
46
|
+
counts[category] += result.counts[category];
|
|
47
|
+
await handles.output.writeFile(JSON.stringify(result.record) + "\n");
|
|
48
|
+
recordCount++;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
counts,
|
|
52
|
+
recordCount,
|
|
53
|
+
totalCount: Object.values(counts).reduce((sum, count) => sum + count, 0),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function writeRedactedCorpus(directory, settings) {
|
|
57
|
+
const path = join(directory, "redacted.jsonl");
|
|
58
|
+
let created = false;
|
|
59
|
+
try {
|
|
60
|
+
const input = await openInput(join(directory, "corpus.jsonl"));
|
|
61
|
+
try {
|
|
62
|
+
const output = await createPrivateFile(path);
|
|
63
|
+
created = true;
|
|
64
|
+
try {
|
|
65
|
+
return await redactCorpus({ input, output }, settings);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await output.close();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await input.close();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (created) {
|
|
77
|
+
await validateDestination(directory);
|
|
78
|
+
await rm(path, { force: true });
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function writeManifest(directory, manifest) {
|
|
84
|
+
const path = join(directory, "redaction.json");
|
|
85
|
+
let created = false;
|
|
86
|
+
try {
|
|
87
|
+
const handle = await createPrivateFile(path);
|
|
88
|
+
created = true;
|
|
89
|
+
try {
|
|
90
|
+
await handle.writeFile(JSON.stringify(manifest) + "\n");
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
await handle.close();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (created) {
|
|
98
|
+
await validateDestination(directory);
|
|
99
|
+
await rm(path, { force: true });
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function redactImport(request) {
|
|
105
|
+
const empty = {
|
|
106
|
+
importId: request.importId,
|
|
107
|
+
recordCount: 0,
|
|
108
|
+
counts: emptyCounts(),
|
|
109
|
+
totalCount: 0,
|
|
110
|
+
};
|
|
111
|
+
if (process.platform !== "darwin")
|
|
112
|
+
return { ...empty, code: "platform-unsupported" };
|
|
113
|
+
if (request.runtimeDirectory === undefined)
|
|
114
|
+
return { ...empty, code: "runtime-directory-unknown" };
|
|
115
|
+
try {
|
|
116
|
+
if (!/^[a-f\d]{8}-[a-f\d]{4}-4[a-f\d]{3}-[89ab][a-f\d]{3}-[a-f\d]{12}$/.test(request.importId))
|
|
117
|
+
throw new Error("invalid-import");
|
|
118
|
+
const directory = resolve(request.workingDirectory, request.runtimeDirectory, "chats", request.importId);
|
|
119
|
+
await validateDestination(directory);
|
|
120
|
+
if (!(await lstat(directory)).isDirectory())
|
|
121
|
+
throw new Error("invalid-import");
|
|
122
|
+
await absent(join(directory, "redacted.jsonl"));
|
|
123
|
+
await absent(join(directory, "redaction.json"));
|
|
124
|
+
validateCoverage(await readInput(join(directory, "coverage.json")), request.importId);
|
|
125
|
+
const summary = await writeRedactedCorpus(directory, request.settings);
|
|
126
|
+
await writeManifest(directory, {
|
|
127
|
+
schemaVersion: 1,
|
|
128
|
+
importId: request.importId,
|
|
129
|
+
ruleVersion: 1,
|
|
130
|
+
...summary,
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
code: "redaction-complete",
|
|
134
|
+
importId: request.importId,
|
|
135
|
+
...summary,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return { ...empty, code: "redaction-failed" };
|
|
140
|
+
}
|
|
141
|
+
}
|