sealkeep 0.8.1 → 0.11.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/ARCHITECTURE.md +163 -14
- package/CHANGELOG.md +244 -1
- package/CONTROL_PLANE.md +2 -2
- package/LICENSE +1 -1
- package/README.md +108 -22
- package/THIRD_PARTY.md +2 -2
- package/THREAT_MODEL.md +23 -4
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/chunk-access.d.ts +26 -4
- package/dist/packages/sealkeep-crypto/src/chunk-access.js +219 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.d.ts +11 -1
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.js +58 -21
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.d.ts +2 -2
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.d.ts +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.js +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.js +4 -4
- package/dist/packages/sealkeep-crypto/src/squeeze.d.ts +6 -0
- package/dist/packages/sealkeep-crypto/src/squeeze.js +39 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.d.ts +36 -3
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.js +196 -37
- package/dist/site/index.html +1808 -1904
- package/dist/site/llms.txt +67 -0
- package/dist/site/trust/architecture-data-flow.html +53 -0
- package/dist/site/trust/audit-roadmap.html +37 -0
- package/dist/site/trust/deployment-responsibility.html +11 -0
- package/dist/site/trust/dpa-sample.html +30 -0
- package/dist/site/trust/release-provenance.html +21 -0
- package/dist/site/trust/subprocessors.html +15 -0
- package/dist/site/trust/threat-model.html +34 -0
- package/dist/site/trust/toms.html +41 -0
- package/dist/site/trust-document.css +32 -0
- package/dist/site/trust.html +73 -0
- package/dist/site/visual/assets/index-8Nxnkc7o.js +207 -0
- package/dist/site/visual/assets/index-oxLy1bpy.css +1 -0
- package/dist/site/visual/index.html +18 -0
- package/dist/site.zip +0 -0
- package/dist/src/activity.d.ts +9 -0
- package/dist/src/activity.js +90 -1
- package/dist/src/adapters.d.ts +175 -5
- package/dist/src/adapters.js +961 -73
- package/dist/src/agent-context.d.ts +135 -0
- package/dist/src/agent-context.js +1059 -0
- package/dist/src/archive-copies.d.ts +47 -0
- package/dist/src/archive-copies.js +179 -0
- package/dist/src/audit.d.ts +1 -1
- package/dist/src/audit.js +29 -4
- package/dist/src/autopilot.d.ts +52 -7
- package/dist/src/autopilot.js +143 -25
- package/dist/src/background-bandwidth.d.ts +46 -0
- package/dist/src/background-bandwidth.js +301 -0
- package/dist/src/background-cpu.d.ts +82 -0
- package/dist/src/background-cpu.js +212 -0
- package/dist/src/background-worker-error.d.ts +12 -0
- package/dist/src/background-worker-error.js +18 -0
- package/dist/src/branding.d.ts +24 -1
- package/dist/src/branding.js +31 -1
- package/dist/src/bridge.d.ts +233 -0
- package/dist/src/bridge.js +604 -0
- package/dist/src/byte-stream.d.ts +91 -0
- package/dist/src/byte-stream.js +385 -0
- package/dist/src/chunk-store.d.ts +41 -8
- package/dist/src/chunk-store.js +161 -65
- package/dist/src/cli.js +1746 -166
- package/dist/src/cloud.d.ts +841 -31
- package/dist/src/cloud.js +3196 -277
- package/dist/src/context-background.d.ts +37 -0
- package/dist/src/context-background.js +309 -0
- package/dist/src/context-drain-child.d.ts +1 -0
- package/dist/src/context-drain-child.js +98 -0
- package/dist/src/context-reader.d.ts +118 -0
- package/dist/src/context-reader.js +447 -0
- package/dist/src/control-plane/auth.d.ts +32 -4
- package/dist/src/control-plane/auth.js +85 -24
- package/dist/src/control-plane/server.js +19 -6
- package/dist/src/control-plane.d.ts +17 -1
- package/dist/src/control-plane.js +32 -6
- package/dist/src/crypto.d.ts +1 -1
- package/dist/src/crypto.js +5 -5
- package/dist/src/daemon-lease.d.ts +70 -0
- package/dist/src/daemon-lease.js +420 -0
- package/dist/src/daemon.d.ts +94 -1
- package/dist/src/daemon.js +1082 -105
- package/dist/src/darwin-service-policy.d.ts +41 -0
- package/dist/src/darwin-service-policy.js +60 -0
- package/dist/src/dashboard-cli.js +15 -15
- package/dist/src/device-authorization.d.ts +37 -0
- package/dist/src/device-authorization.js +199 -0
- package/dist/src/device-enrollment.d.ts +91 -0
- package/dist/src/device-enrollment.js +349 -0
- package/dist/src/disk.d.ts +17 -12
- package/dist/src/disk.js +43 -17
- package/dist/src/doctor.d.ts +35 -1
- package/dist/src/doctor.js +316 -41
- package/dist/src/durable-ticket-lock.d.ts +24 -0
- package/dist/src/durable-ticket-lock.js +232 -0
- package/dist/src/enroll.d.ts +1 -1
- package/dist/src/enroll.js +13 -7
- package/dist/src/env.d.ts +10 -1
- package/dist/src/env.js +11 -3
- package/dist/src/errors.d.ts +8 -8
- package/dist/src/errors.js +6 -6
- package/dist/src/flush.d.ts +12 -0
- package/dist/src/flush.js +37 -0
- package/dist/src/heartbeat.d.ts +86 -12
- package/dist/src/heartbeat.js +415 -29
- package/dist/src/index-background-watchdog.d.ts +1 -0
- package/dist/src/index-background-watchdog.js +94 -0
- package/dist/src/index-background-work.d.ts +21 -0
- package/dist/src/index-background-work.js +25 -0
- package/dist/src/index-background.d.ts +64 -0
- package/dist/src/index-background.js +394 -0
- package/dist/src/index-build-child.d.ts +1 -0
- package/dist/src/index-build-child.js +109 -0
- package/dist/src/index-manifest.d.ts +52 -0
- package/dist/src/index-manifest.js +444 -0
- package/dist/src/index-publication-proof.d.ts +84 -0
- package/dist/src/index-publication-proof.js +380 -0
- package/dist/src/index-publication-state.d.ts +149 -0
- package/dist/src/index-publication-state.js +696 -0
- package/dist/src/index-publication-verifier.d.ts +89 -0
- package/dist/src/index-publication-verifier.js +341 -0
- package/dist/src/index-publish.d.ts +62 -0
- package/dist/src/index-publish.js +540 -0
- package/dist/src/index-scratch-cleanup.d.ts +19 -0
- package/dist/src/index-scratch-cleanup.js +166 -0
- package/dist/src/index-segment-types.d.ts +132 -0
- package/dist/src/index-segment-types.js +21 -0
- package/dist/src/index-segments.d.ts +9 -0
- package/dist/src/index-segments.js +516 -0
- package/dist/src/index-store.d.ts +123 -0
- package/dist/src/index-store.js +495 -0
- package/dist/src/index-sync.d.ts +91 -15
- package/dist/src/index-sync.js +286 -53
- package/dist/src/index-upgrade-publication.d.ts +30 -0
- package/dist/src/index-upgrade-publication.js +179 -0
- package/dist/src/integration-manager.d.ts +32 -0
- package/dist/src/integration-manager.js +394 -0
- package/dist/src/leakscan.js +1 -1
- package/dist/src/local-api.d.ts +56 -15
- package/dist/src/local-api.js +4979 -541
- package/dist/src/machine-settings.d.ts +51 -0
- package/dist/src/machine-settings.js +166 -0
- package/dist/src/managed-chunks.d.ts +5 -2
- package/dist/src/managed-chunks.js +14 -14
- package/dist/src/mcp-install.d.ts +11 -9
- package/dist/src/mcp-install.js +73 -28
- package/dist/src/mcp-workspace.d.ts +18 -0
- package/dist/src/mcp-workspace.js +50 -0
- package/dist/src/mcp.js +294 -25
- package/dist/src/migrate.d.ts +1 -0
- package/dist/src/migrate.js +33 -5
- package/dist/src/notify.d.ts +1 -1
- package/dist/src/notify.js +5 -5
- package/dist/src/offload.d.ts +201 -14
- package/dist/src/offload.js +1848 -140
- package/dist/src/onboarding.d.ts +8 -1
- package/dist/src/onboarding.js +4 -4
- package/dist/src/packages.d.ts +2 -2
- package/dist/src/packages.js +10 -2
- package/dist/src/passkey.d.ts +0 -1
- package/dist/src/passkey.js +2 -7
- package/dist/src/password-lock.d.ts +2 -2
- package/dist/src/password-lock.js +6 -6
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/presence.d.ts +86 -0
- package/dist/src/presence.js +240 -0
- package/dist/src/progress-deadline.d.ts +21 -0
- package/dist/src/progress-deadline.js +91 -0
- package/dist/src/project-repair.d.ts +55 -0
- package/dist/src/project-repair.js +131 -0
- package/dist/src/providers/gcs.d.ts +28 -7
- package/dist/src/providers/gcs.js +35 -24
- package/dist/src/providers/gdrive.d.ts +71 -8
- package/dist/src/providers/gdrive.js +223 -50
- package/dist/src/providers/index.d.ts +11 -3
- package/dist/src/providers/index.js +30 -10
- package/dist/src/providers/s3.d.ts +30 -8
- package/dist/src/providers/s3.js +41 -30
- package/dist/src/providers/safe-storage-fetch.d.ts +12 -0
- package/dist/src/providers/safe-storage-fetch.js +72 -0
- package/dist/src/queue.d.ts +136 -19
- package/dist/src/queue.js +862 -96
- package/dist/src/reclaim-transaction.d.ts +156 -0
- package/dist/src/reclaim-transaction.js +1027 -0
- package/dist/src/recovery-codes.d.ts +32 -0
- package/dist/src/recovery-codes.js +338 -0
- package/dist/src/recovery.js +12 -9
- package/dist/src/rehydrate.d.ts +25 -22
- package/dist/src/rehydrate.js +319 -23
- package/dist/src/restore.d.ts +57 -4
- package/dist/src/restore.js +272 -36
- package/dist/src/resume-stub.d.ts +92 -0
- package/dist/src/resume-stub.js +417 -0
- package/dist/src/retention.d.ts +98 -7
- package/dist/src/retention.js +1066 -62
- package/dist/src/rotate.js +3 -3
- package/dist/src/search.d.ts +566 -8
- package/dist/src/search.js +5897 -290
- package/dist/src/secrets.d.ts +51 -7
- package/dist/src/secrets.js +316 -24
- package/dist/src/service.d.ts +49 -11
- package/dist/src/service.js +776 -35
- package/dist/src/share.js +3 -3
- package/dist/src/shared-spaces.d.ts +98 -0
- package/dist/src/shared-spaces.js +214 -0
- package/dist/src/source-reader.d.ts +73 -0
- package/dist/src/source-reader.js +715 -0
- package/dist/src/spool.d.ts +1 -1
- package/dist/src/spool.js +1 -1
- package/dist/src/start-tui.js +2 -1
- package/dist/src/start.js +2 -2
- package/dist/src/storage-endpoint.d.ts +21 -0
- package/dist/src/storage-endpoint.js +122 -0
- package/dist/src/storage-setup.js +12 -12
- package/dist/src/storage-targets.d.ts +109 -6
- package/dist/src/storage-targets.js +975 -67
- package/dist/src/stream-to-cloud.d.ts +5 -1
- package/dist/src/stream-to-cloud.js +34 -14
- package/dist/src/sync-rules.d.ts +31 -6
- package/dist/src/sync-rules.js +153 -14
- package/dist/src/team-backfill-scheduling.d.ts +8 -0
- package/dist/src/team-backfill-scheduling.js +33 -0
- package/dist/src/team-backfill.d.ts +116 -0
- package/dist/src/team-backfill.js +1429 -0
- package/dist/src/team-index-cache.d.ts +16 -0
- package/dist/src/team-index-cache.js +152 -0
- package/dist/src/team-offboarding.d.ts +38 -0
- package/dist/src/team-offboarding.js +1043 -0
- package/dist/src/team-presence.d.ts +127 -0
- package/dist/src/team-presence.js +904 -0
- package/dist/src/team-publication-policy.d.ts +20 -0
- package/dist/src/team-publication-policy.js +140 -0
- package/dist/src/team-realtime.d.ts +68 -0
- package/dist/src/team-realtime.js +816 -0
- package/dist/src/team-source-facts-cache.d.ts +23 -0
- package/dist/src/team-source-facts-cache.js +255 -0
- package/dist/src/trash.d.ts +1 -1
- package/dist/src/trash.js +2 -2
- package/dist/src/tui.js +11 -12
- package/dist/src/types.d.ts +173 -7
- package/dist/src/types.js +20 -0
- package/dist/src/ui-server.d.ts +163 -35
- package/dist/src/ui-server.js +712 -72
- package/dist/src/ui.d.ts +1 -2
- package/dist/src/ui.js +1 -2
- package/dist/src/upload.d.ts +27 -0
- package/dist/src/upload.js +383 -43
- package/dist/src/vault.d.ts +235 -29
- package/dist/src/vault.js +1783 -190
- package/dist/src/watcher.d.ts +7 -1
- package/dist/src/watcher.js +198 -55
- package/dist/src/worker.d.ts +27 -3
- package/dist/src/worker.js +274 -55
- package/package.json +33 -12
- package/scripts/native-reboot-rehearsal.mjs +90 -0
- package/web/app.js +6032 -343
- package/web/bootstrap.js +17 -0
- package/web/index.html +255 -57
- package/web/rail.js +317 -40
- package/web/retention.html +2 -2
- package/web/rules-view.js +188 -16
- package/web/sessions-view.js +485 -62
- package/web/sessions.html +2 -2
- package/web/setup-api.js +152 -29
- package/web/setup-logic.js +68 -9
- package/web/setup.html +113 -44
- package/web/setup.js +604 -71
- package/web/style.css +513 -98
- package/dist/packages/vaultline-crypto/src/chunk-access.js +0 -93
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.js +0 -0
package/dist/src/watcher.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentId } from "./adapters.js";
|
|
1
|
+
import { detectAgents, findTranscripts, type AgentId } from "./adapters.js";
|
|
2
2
|
import { ArchiveQueue, type ArchiveJob } from "./queue.js";
|
|
3
3
|
export type WatcherEvent = {
|
|
4
4
|
agent: AgentId;
|
|
@@ -10,10 +10,16 @@ export type WatcherEvent = {
|
|
|
10
10
|
export type WatcherOptions = {
|
|
11
11
|
home?: string;
|
|
12
12
|
agents?: AgentId[];
|
|
13
|
+
/** Overrides saved path exclusions, principally for isolated callers/tests. */
|
|
14
|
+
exclusions?: string[];
|
|
13
15
|
/** Wait for a file to stop changing before queuing it. */
|
|
14
16
|
debounceMs?: number;
|
|
15
17
|
queue?: ArchiveQueue;
|
|
16
18
|
onEvent?: (event: WatcherEvent) => void;
|
|
19
|
+
/** Dependency seam for deterministic close/reconciliation tests. */
|
|
20
|
+
detectAgents?: typeof detectAgents;
|
|
21
|
+
/** Dependency seam for deterministic scan lifecycle tests. */
|
|
22
|
+
findTranscripts?: typeof findTranscripts;
|
|
17
23
|
};
|
|
18
24
|
export type TranscriptWatcher = {
|
|
19
25
|
roots: string[];
|
package/dist/src/watcher.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { watch } from "node:fs";
|
|
2
2
|
import { stat } from "node:fs/promises";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
4
|
-
import { detectAgents } from "./adapters.js";
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
4
|
+
import { detectAgents, findTranscripts } from "./adapters.js";
|
|
5
5
|
import { ArchiveQueue } from "./queue.js";
|
|
6
|
+
import { localSettingsPath, readLocalSettings, transcriptPathIsExcluded } from "./machine-settings.js";
|
|
6
7
|
import { applySyncRules, resolveSyncRules } from "./sync-rules.js";
|
|
7
8
|
/**
|
|
8
9
|
* Watches adapter transcript roots and records archive intents.
|
|
@@ -14,20 +15,120 @@ import { applySyncRules, resolveSyncRules } from "./sync-rules.js";
|
|
|
14
15
|
export async function startTranscriptWatcher(dataDir, options = {}) {
|
|
15
16
|
const queue = options.queue ?? new ArchiveQueue(dataDir);
|
|
16
17
|
const debounceMs = options.debounceMs ?? 2000;
|
|
17
|
-
const
|
|
18
|
-
|
|
18
|
+
const settings = await readLocalSettings(dataDir);
|
|
19
|
+
let selectedAgents = new Set(options.agents ?? settings.watchedAgents);
|
|
20
|
+
const watchers = new Map();
|
|
19
21
|
const timers = new Map();
|
|
22
|
+
// Returned by reference and mutated during reconciliation, so daemon status
|
|
23
|
+
// reflects a Settings change without replacing the TranscriptWatcher object.
|
|
20
24
|
const roots = [];
|
|
21
25
|
let closed = false;
|
|
26
|
+
let settingsWatcher;
|
|
27
|
+
let settingsTimer;
|
|
28
|
+
let reconciliation = Promise.resolve();
|
|
29
|
+
let closeInFlight;
|
|
22
30
|
// Rules are resolved once at startup and refreshed only on scan — the daemon's
|
|
23
31
|
// periodic reconciliation point — rather than per event, so a burst of writes
|
|
24
32
|
// cannot turn into a burst of account lookups.
|
|
25
33
|
let rules = (await resolveSyncRules(dataDir)).rules;
|
|
34
|
+
let exclusions = options.exclusions ?? settings.exclusions;
|
|
35
|
+
const excluded = (path) => transcriptPathIsExcluded(path, exclusions, options.home);
|
|
36
|
+
function schedule(agent, path) {
|
|
37
|
+
const existing = timers.get(path);
|
|
38
|
+
if (existing)
|
|
39
|
+
clearTimeout(existing.timer);
|
|
40
|
+
const timer = setTimeout(() => { timers.delete(path); void admitAndEnqueue(agent, path); }, debounceMs);
|
|
41
|
+
timer.unref?.();
|
|
42
|
+
timers.set(path, { agent, timer });
|
|
43
|
+
}
|
|
44
|
+
function attach(agent, root) {
|
|
45
|
+
if (closed)
|
|
46
|
+
return;
|
|
47
|
+
try {
|
|
48
|
+
const watcher = watch(root, { recursive: true }, (_event, filename) => {
|
|
49
|
+
if (!filename || !selectedAgents.has(agent))
|
|
50
|
+
return;
|
|
51
|
+
const path = resolve(join(root, filename.toString()));
|
|
52
|
+
if (!path.endsWith(".jsonl") || excluded(path))
|
|
53
|
+
return;
|
|
54
|
+
schedule(agent, path);
|
|
55
|
+
});
|
|
56
|
+
watcher.on("error", (error) => options.onEvent?.({ agent, path: root, error: error.message }));
|
|
57
|
+
watchers.set(root, { agent, watcher });
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
options.onEvent?.({ agent, path: root, error: error instanceof Error ? error.message : "watch failed" });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Settings are live daemon policy, not merely restart configuration. Rebuild
|
|
65
|
+
* the small set of native roots at the same cadence as the safety scan: stop
|
|
66
|
+
* observing disabled agents, attach newly enabled/created stores, and cancel
|
|
67
|
+
* debounce work that was admitted under the previous selection.
|
|
68
|
+
*/
|
|
69
|
+
async function reconcileRoots(refreshSettings) {
|
|
70
|
+
if (closed)
|
|
71
|
+
return;
|
|
72
|
+
if (refreshSettings) {
|
|
73
|
+
const latest = await readLocalSettings(dataDir);
|
|
74
|
+
if (options.agents === undefined)
|
|
75
|
+
selectedAgents = new Set(latest.watchedAgents);
|
|
76
|
+
if (options.exclusions === undefined)
|
|
77
|
+
exclusions = latest.exclusions;
|
|
78
|
+
}
|
|
79
|
+
const installs = await (options.detectAgents ?? detectAgents)(options.home);
|
|
80
|
+
const desired = new Map();
|
|
81
|
+
for (const install of installs) {
|
|
82
|
+
if (!selectedAgents.has(install.agent))
|
|
83
|
+
continue;
|
|
84
|
+
for (const root of install.transcriptRoots) {
|
|
85
|
+
const entry = await stat(root).catch(() => null);
|
|
86
|
+
if (entry?.isDirectory())
|
|
87
|
+
desired.set(root, install.agent);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// close() may have run while settings, agent detection, or root stats were
|
|
91
|
+
// awaiting I/O. Mutating the native watcher map after that point would
|
|
92
|
+
// resurrect handles that no caller can close.
|
|
93
|
+
if (closed)
|
|
94
|
+
return;
|
|
95
|
+
for (const [root, current] of watchers) {
|
|
96
|
+
if (desired.get(root) === current.agent)
|
|
97
|
+
continue;
|
|
98
|
+
current.watcher.close();
|
|
99
|
+
watchers.delete(root);
|
|
100
|
+
}
|
|
101
|
+
for (const [root, agent] of desired)
|
|
102
|
+
if (!watchers.has(root))
|
|
103
|
+
attach(agent, root);
|
|
104
|
+
for (const [path, pending] of timers) {
|
|
105
|
+
if (selectedAgents.has(pending.agent))
|
|
106
|
+
continue;
|
|
107
|
+
clearTimeout(pending.timer);
|
|
108
|
+
timers.delete(path);
|
|
109
|
+
}
|
|
110
|
+
roots.splice(0, roots.length, ...[...watchers.keys()].sort());
|
|
111
|
+
}
|
|
112
|
+
function requestReconcile(refreshSettings) {
|
|
113
|
+
if (closed)
|
|
114
|
+
return Promise.resolve();
|
|
115
|
+
// A settings rename and a periodic scan can arrive together. Serialize
|
|
116
|
+
// their close/attach mutations so neither resurrects a root the other just
|
|
117
|
+
// disabled or loses a newly enabled one.
|
|
118
|
+
const run = reconciliation.then(() => reconcileRoots(refreshSettings));
|
|
119
|
+
reconciliation = run.catch(() => undefined);
|
|
120
|
+
return run;
|
|
121
|
+
}
|
|
26
122
|
async function discover() {
|
|
27
|
-
const { findTranscripts } = await import("./adapters.js");
|
|
28
123
|
const found = [];
|
|
29
|
-
for (const
|
|
30
|
-
|
|
124
|
+
for (const agent of selectedAgents) {
|
|
125
|
+
if (closed)
|
|
126
|
+
break;
|
|
127
|
+
found.push(...await (options.findTranscripts ?? findTranscripts)(agent, options.home, undefined, {
|
|
128
|
+
exclude: excluded,
|
|
129
|
+
includeProjects: false,
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
31
132
|
return found;
|
|
32
133
|
}
|
|
33
134
|
/**
|
|
@@ -45,7 +146,7 @@ export async function startTranscriptWatcher(dataDir, options = {}) {
|
|
|
45
146
|
return;
|
|
46
147
|
try {
|
|
47
148
|
const info = await stat(path).catch(() => null);
|
|
48
|
-
if (!info?.isFile())
|
|
149
|
+
if (closed || !info?.isFile())
|
|
49
150
|
return;
|
|
50
151
|
const { job, deduped } = await queue.enqueue({ sourcePath: path, agent, event: "watcher" });
|
|
51
152
|
options.onEvent?.({ agent, path, job, deduped });
|
|
@@ -58,64 +159,106 @@ export async function startTranscriptWatcher(dataDir, options = {}) {
|
|
|
58
159
|
// live events, enqueueNow — so a session the sync rules exclude is never queued
|
|
59
160
|
// no matter which of those triggered it.
|
|
60
161
|
async function admitAndEnqueue(agent, path) {
|
|
61
|
-
if (closed || !(await admitted(path)))
|
|
162
|
+
if (closed || !selectedAgents.has(agent) || excluded(path) || !(await admitted(path)))
|
|
62
163
|
return;
|
|
63
164
|
await enqueue(agent, path);
|
|
64
165
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
166
|
+
async function scanCurrent() {
|
|
167
|
+
if (closed)
|
|
168
|
+
return 0;
|
|
169
|
+
// Reconcile identity/policy first: a settings-triggered scan must detach a
|
|
170
|
+
// disabled store before it walks anything, and must attach an enabled store
|
|
171
|
+
// even if the account rules lookup is temporarily slow.
|
|
172
|
+
await requestReconcile(true);
|
|
173
|
+
if (closed)
|
|
174
|
+
return 0;
|
|
175
|
+
rules = (await resolveSyncRules(dataDir)).rules;
|
|
176
|
+
if (closed)
|
|
177
|
+
return 0;
|
|
178
|
+
const found = await discover();
|
|
179
|
+
if (closed)
|
|
180
|
+
return 0;
|
|
181
|
+
const included = new Set(applySyncRules(found, rules).included.map((item) => item.path));
|
|
182
|
+
const candidates = found.filter((candidate) => included.has(candidate.path));
|
|
183
|
+
const outcomes = await queue.enqueueMany(candidates.map((candidate) => ({
|
|
184
|
+
sourcePath: candidate.path, agent: candidate.agent, event: "watcher"
|
|
185
|
+
})));
|
|
186
|
+
for (const outcome of outcomes) {
|
|
187
|
+
const agent = outcome.request.agent;
|
|
188
|
+
if (outcome.ok) {
|
|
189
|
+
options.onEvent?.({ agent, path: outcome.request.sourcePath, job: outcome.job, deduped: outcome.deduped });
|
|
89
190
|
}
|
|
90
|
-
|
|
91
|
-
options.onEvent?.({ agent
|
|
191
|
+
else {
|
|
192
|
+
options.onEvent?.({ agent, path: outcome.request.sourcePath, error: outcome.error instanceof Error ? outcome.error.message : "enqueue failed" });
|
|
92
193
|
}
|
|
93
194
|
}
|
|
195
|
+
return found.length;
|
|
196
|
+
}
|
|
197
|
+
await requestReconcile(false);
|
|
198
|
+
// Settings are written by atomic rename, so watch the containing directory
|
|
199
|
+
// rather than the old inode. This lane is independent of the archive tick:
|
|
200
|
+
// disabling an agent closes its native watcher promptly even on a five-minute
|
|
201
|
+
// daemon schedule, and enabling one attaches as soon as its store exists.
|
|
202
|
+
if (options.agents === undefined || options.exclusions === undefined) {
|
|
203
|
+
const wanted = basename(localSettingsPath(dataDir));
|
|
204
|
+
try {
|
|
205
|
+
settingsWatcher = watch(dataDir, (_event, filename) => {
|
|
206
|
+
if (closed || (filename && basename(filename.toString()) !== wanted))
|
|
207
|
+
return;
|
|
208
|
+
if (settingsTimer)
|
|
209
|
+
clearTimeout(settingsTimer);
|
|
210
|
+
settingsTimer = setTimeout(() => {
|
|
211
|
+
settingsTimer = undefined;
|
|
212
|
+
// Enabling a store should also pick up transcripts already present;
|
|
213
|
+
// otherwise the new root attaches promptly but its backlog still
|
|
214
|
+
// waits for the daemon's long safety-scan interval.
|
|
215
|
+
void scanCurrent().catch((error) => options.onEvent?.({
|
|
216
|
+
agent: "codex",
|
|
217
|
+
path: localSettingsPath(dataDir),
|
|
218
|
+
error: error instanceof Error ? error.message : "settings reconciliation failed",
|
|
219
|
+
}));
|
|
220
|
+
}, 25);
|
|
221
|
+
settingsTimer.unref?.();
|
|
222
|
+
});
|
|
223
|
+
settingsWatcher.on("error", (error) => options.onEvent?.({ agent: "codex", path: dataDir, error: error.message }));
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
options.onEvent?.({ agent: "codex", path: dataDir, error: error instanceof Error ? error.message : "settings watch failed" });
|
|
227
|
+
}
|
|
94
228
|
}
|
|
95
229
|
return {
|
|
96
|
-
roots
|
|
230
|
+
roots,
|
|
97
231
|
enqueueNow: (agent, path) => admitAndEnqueue(agent, resolve(path)),
|
|
98
|
-
|
|
99
|
-
// The reconciliation point: pick up any change to the rules themselves,
|
|
100
|
-
// then decide admission once for the whole batch rather than once per
|
|
101
|
-
// candidate the way a live event has to.
|
|
102
|
-
rules = (await resolveSyncRules(dataDir)).rules;
|
|
103
|
-
const found = await discover();
|
|
104
|
-
const included = new Set(applySyncRules(found, rules).included.map((item) => item.path));
|
|
105
|
-
for (const candidate of found) {
|
|
106
|
-
if (included.has(candidate.path))
|
|
107
|
-
await enqueue(candidate.agent, candidate.path);
|
|
108
|
-
}
|
|
109
|
-
return found.length;
|
|
110
|
-
},
|
|
232
|
+
scan: scanCurrent,
|
|
111
233
|
pending: () => timers.size,
|
|
112
|
-
|
|
234
|
+
close() {
|
|
235
|
+
if (closeInFlight)
|
|
236
|
+
return closeInFlight;
|
|
113
237
|
closed = true;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
238
|
+
closeInFlight = (async () => {
|
|
239
|
+
const closeObservedHandles = () => {
|
|
240
|
+
if (settingsTimer)
|
|
241
|
+
clearTimeout(settingsTimer);
|
|
242
|
+
settingsTimer = undefined;
|
|
243
|
+
settingsWatcher?.close();
|
|
244
|
+
settingsWatcher = undefined;
|
|
245
|
+
for (const { timer } of timers.values())
|
|
246
|
+
clearTimeout(timer);
|
|
247
|
+
timers.clear();
|
|
248
|
+
for (const { watcher } of watchers.values())
|
|
249
|
+
watcher.close();
|
|
250
|
+
watchers.clear();
|
|
251
|
+
roots.splice(0, roots.length);
|
|
252
|
+
};
|
|
253
|
+
closeObservedHandles();
|
|
254
|
+
// A reconciliation which passed its first closed check before this
|
|
255
|
+
// call owns no handles after its final check, but join it before
|
|
256
|
+
// returning so close is a complete lifecycle boundary. The second
|
|
257
|
+
// sweep also protects older/injected reconciliation implementations.
|
|
258
|
+
await reconciliation;
|
|
259
|
+
closeObservedHandles();
|
|
260
|
+
})();
|
|
261
|
+
return closeInFlight;
|
|
119
262
|
}
|
|
120
263
|
};
|
|
121
264
|
}
|
package/dist/src/worker.d.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import { ArchiveQueue, type ArchiveJob } from "./queue.js";
|
|
2
|
+
import { archiveFile } from "./vault.js";
|
|
2
3
|
import { Reservations } from "./disk.js";
|
|
3
4
|
/** Parallel seals are capped whatever the config asks: sealing is disk- and CPU-bound, and a machine short of space is rarely long on either. */
|
|
4
5
|
export declare const MAX_SEAL_CONCURRENCY = 4;
|
|
6
|
+
/** Long enough for a slow local chunk, short enough to expire before the
|
|
7
|
+
* ordinary five-minute queue lease can be mistaken for useful work. */
|
|
8
|
+
export declare const DEFAULT_SEAL_IDLE_MS: number;
|
|
9
|
+
export type ActiveSeal = {
|
|
10
|
+
project: string;
|
|
11
|
+
projectKey: string | null;
|
|
12
|
+
sourcePath: string;
|
|
13
|
+
};
|
|
5
14
|
export type DrainOptions = {
|
|
6
15
|
queue?: ArchiveQueue;
|
|
7
16
|
owner?: string;
|
|
8
17
|
max?: number;
|
|
18
|
+
/** Off to seal without the end-of-drain active-project announcement (index sync + finished lines). */
|
|
19
|
+
announce?: boolean;
|
|
20
|
+
/** Daemon handoff: report active seals without starting another index writer. */
|
|
21
|
+
onActiveSealed?: (seals: readonly ActiveSeal[]) => void;
|
|
9
22
|
/** Off only for tests that want to archive without a real filesystem behind them. */
|
|
10
23
|
requireSpace?: boolean;
|
|
11
24
|
/**
|
|
@@ -16,9 +29,8 @@ export type DrainOptions = {
|
|
|
16
29
|
*/
|
|
17
30
|
reserveBytes?: number;
|
|
18
31
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* preserves fields it does not know), and to 1 otherwise. Clamped to
|
|
32
|
+
* Internal/test seam for how many seals may run at once. Production callers
|
|
33
|
+
* omit it and use the safe default of one. Clamped to
|
|
22
34
|
* 1..MAX_SEAL_CONCURRENCY.
|
|
23
35
|
*/
|
|
24
36
|
concurrency?: number;
|
|
@@ -35,6 +47,18 @@ export type DrainOptions = {
|
|
|
35
47
|
* and off by default in archiveFile itself, so nothing else changes.
|
|
36
48
|
*/
|
|
37
49
|
delta?: boolean;
|
|
50
|
+
/** Cancels the owned seal and leaves its queue row retryable. */
|
|
51
|
+
signal?: AbortSignal;
|
|
52
|
+
/** Maximum silence between source/provider byte progress events. */
|
|
53
|
+
sealIdleMs?: number;
|
|
54
|
+
/** Background-only duty cycle. Manual/foreground callers omit it. */
|
|
55
|
+
backgroundCpuTarget?: number;
|
|
56
|
+
/** Deterministic CPU scheduling seams. */
|
|
57
|
+
cpuUsage?: typeof process.cpuUsage;
|
|
58
|
+
now?: () => number;
|
|
59
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
60
|
+
/** Deterministic liveness seam. Production always uses archiveFile. */
|
|
61
|
+
archive?: typeof archiveFile;
|
|
38
62
|
};
|
|
39
63
|
/**
|
|
40
64
|
* Encrypts queued transcripts. The recovery phrase lives only in this process:
|