@sema-agent/server 7.50.0 → 7.52.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 +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +83 -20
- package/dist/adoption/plan.d.ts +4 -4
- package/dist/adoption/plan.js +11 -0
- package/dist/approval-card.d.ts +26 -2
- package/dist/approval-card.js +9 -0
- package/dist/approval-hmac.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +2 -2
- package/dist/approval-reconciler.js +1 -1
- package/dist/bench/l8/artifact.d.ts +1 -1
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/live-deps.d.ts +1 -1
- package/dist/bench/s1/oracle.d.ts +1 -1
- package/dist/bench/s1/row.d.ts +1 -1
- package/dist/boot/budget-tracing.d.ts +1 -1
- package/dist/boot/budget-tracing.js +2 -5
- package/dist/boot/coordinators.js +3 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +1 -1
- package/dist/boot/device-lane.d.ts +79 -0
- package/dist/boot/device-lane.js +63 -0
- package/dist/boot/engine-lease.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +48 -2
- package/dist/boot/execution-env.js +57 -5
- package/dist/boot/governance-seams.d.ts +2 -2
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +6 -6
- package/dist/boot/org-memory.d.ts +1 -1
- package/dist/boot/parked-revive-gate.d.ts +3 -3
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/reapers.js +17 -4
- package/dist/boot/resolve-spec.d.ts +15 -0
- package/dist/boot/resolve-spec.js +40 -15
- package/dist/boot/retention-lane.d.ts +2 -2
- package/dist/boot/runner-deps.d.ts +17 -7
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/runtime-caps.d.ts +23 -0
- package/dist/boot/runtime-caps.js +24 -2
- package/dist/boot/session-faces.d.ts +6 -1
- package/dist/boot/session-faces.js +17 -3
- package/dist/boot/shutdown.d.ts +8 -0
- package/dist/boot/shutdown.js +3 -1
- package/dist/boot/stores.js +40 -1
- package/dist/boot/task-list-lane.d.ts +2 -2
- package/dist/brain.js +51 -31
- package/dist/budget.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/collab-workflows.d.ts +1 -1
- package/dist/capabilities/hands-lane.d.ts +3 -3
- package/dist/capabilities/scenarios.d.ts +3 -3
- package/dist/capabilities/skills.d.ts +1 -1
- package/dist/config-center/hot-keys-registry.js +5 -0
- package/dist/config-center/http-client.d.ts +2 -2
- package/dist/config-center/read-face.d.ts +1 -1
- package/dist/config-center/restart-signal.d.ts +1 -1
- package/dist/config-center/types.d.ts +10 -3
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +18 -0
- package/dist/config-types.d.ts +90 -18
- package/dist/config.js +91 -19
- package/dist/deployment-governance.d.ts +1 -1
- package/dist/device-enrollment.d.ts +125 -0
- package/dist/device-enrollment.js +156 -0
- package/dist/device-store.d.ts +385 -0
- package/dist/device-store.js +407 -0
- package/dist/device-ws-hub.d.ts +182 -0
- package/dist/device-ws-hub.js +1012 -0
- package/dist/device-ws-protocol.d.ts +429 -0
- package/dist/device-ws-protocol.js +464 -0
- package/dist/env-facts.d.ts +5 -2
- package/dist/env-facts.js +1 -0
- package/dist/execution-lane-caps.d.ts +152 -0
- package/dist/execution-lane-caps.js +166 -0
- package/dist/fleet/fleet-bus.d.ts +1 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/fleet-terminal-window.d.ts +1 -1
- package/dist/http/active-run-conflict.d.ts +2 -2
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/route-ctx.d.ts +24 -4
- package/dist/http/routes/admin-memory-optout.d.ts +48 -0
- package/dist/http/routes/admin-memory-optout.js +75 -0
- package/dist/http/routes/agents-roster.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +21 -21
- package/dist/http/routes/capabilities.js +27 -11
- package/dist/http/routes/diagnostics.d.ts +1 -1
- package/dist/http/routes/leader.d.ts +1 -1
- package/dist/http/routes/memory-bundle.d.ts +1 -1
- package/dist/http/routes/memory-bundle.js +3 -1
- package/dist/http/routes/memory-compliance.d.ts +4 -4
- package/dist/http/routes/memory-origin.d.ts +4 -4
- package/dist/http/routes/memory-origin.js +3 -1
- package/dist/http/routes/memory-policy.js +6 -2
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/rules.d.ts +5 -2
- package/dist/http/routes/rules.js +84 -5
- package/dist/http/routes/run-memory-capture.d.ts +34 -0
- package/dist/http/routes/run-memory-capture.js +97 -0
- package/dist/http/routes/runs.d.ts +1 -0
- package/dist/http/routes/runs.js +212 -6
- package/dist/http/routes/session-sync.d.ts +4 -6
- package/dist/http/routes/session-sync.js +60 -47
- package/dist/http/routes/sessions.js +61 -188
- package/dist/http/routes/shared-memory.d.ts +1 -1
- package/dist/http/routes/tasks.js +1 -1
- package/dist/http/routes/workflows.js +19 -0
- package/dist/http/server.d.ts +49 -13
- package/dist/http/server.js +109 -13
- package/dist/http/verify-rounds.d.ts +1 -1
- package/dist/http/wire-types.d.ts +41 -8
- package/dist/leader/endpoint.d.ts +1 -1
- package/dist/leader/leader.d.ts +1 -1
- package/dist/leader/repair-oracle.d.ts +2 -2
- package/dist/leader/repair-oracle.js +1 -1
- package/dist/leader/wire.js +4 -2
- package/dist/main.js +23 -9
- package/dist/memory-bundle-engine.d.ts +1 -1
- package/dist/memory-operator-faces.d.ts +5 -5
- package/dist/memory-origin-fence.d.ts +50 -0
- package/dist/memory-origin-fence.js +15 -0
- package/dist/memory-posture.d.ts +13 -1
- package/dist/memory-posture.js +1 -0
- package/dist/memory-scope.d.ts +6 -1
- package/dist/memory-scope.js +3 -2
- package/dist/model-select.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +17 -5
- package/dist/observability/fail-open.js +17 -5
- package/dist/orchestration/hardened-vm-runner.d.ts +9 -2
- package/dist/orchestration/hardened-vm-runner.js +11 -1
- package/dist/orchestration/hardened-vm-worker-runner.js +2 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +2 -2
- package/dist/plugins/background-shell-support.d.ts +2 -2
- package/dist/plugins/blob-backend.d.ts +7 -2
- package/dist/plugins/blob-backend.js +22 -18
- package/dist/plugins/caching-session-store.d.ts +7 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +2 -2
- package/dist/plugins/device-store-sql.d.ts +130 -0
- package/dist/plugins/device-store-sql.js +574 -0
- package/dist/plugins/file-history-store-sql.d.ts +170 -0
- package/dist/plugins/file-history-store-sql.js +827 -0
- package/dist/plugins/file-run-store.d.ts +44 -5
- package/dist/plugins/file-run-store.js +85 -13
- package/dist/plugins/fork-routing-session-store.d.ts +1 -1
- package/dist/plugins/fork-routing-session-store.js +2 -2
- package/dist/plugins/leader-run-store-sql.d.ts +1 -1
- package/dist/plugins/local-session-store.d.ts +9 -1
- package/dist/plugins/memory-embedder-fingerprint.d.ts +1 -1
- package/dist/plugins/memory-optout-grant-store-sql.d.ts +139 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +157 -0
- package/dist/plugins/memory-origin-law.d.ts +2 -2
- package/dist/plugins/memory-run-store.d.ts +4 -3
- package/dist/plugins/memory-run-store.js +2 -1
- package/dist/plugins/permission-rule-store-file.d.ts +16 -6
- package/dist/plugins/permission-rule-store-file.js +77 -13
- package/dist/plugins/permission-rule-store-sql.d.ts +156 -8
- package/dist/plugins/permission-rule-store-sql.js +123 -21
- package/dist/plugins/pg-pool.js +59 -0
- package/dist/plugins/pg-safe-json.d.ts +1 -1
- package/dist/plugins/remote-env-device.d.ts +271 -0
- package/dist/plugins/remote-env-device.js +727 -0
- package/dist/plugins/remote-env-k8s.d.ts +1 -1
- package/dist/plugins/remote-scratchpad.js +1 -1
- package/dist/plugins/retention-lane-store-sql.d.ts +3 -3
- package/dist/plugins/retention-store-sql.d.ts +7 -7
- package/dist/plugins/retention-store-sql.js +6 -0
- package/dist/plugins/run-store-sql.d.ts +9 -3
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/send-user-file.d.ts +1 -1
- package/dist/plugins/session-placement.d.ts +1 -1
- package/dist/plugins/sql-errors.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +68 -29
- package/dist/plugins/store-backend.js +78 -15
- package/dist/plugins/store-contracts.d.ts +5 -3
- package/dist/plugins/task-list-store-sql.d.ts +1 -1
- package/dist/plugins/tidb-pool.js +59 -0
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/prompts-domain-validate.js +1 -1
- package/dist/resource-suspend.d.ts +1 -1
- package/dist/resource-window.d.ts +3 -3
- package/dist/rules-consent.d.ts +231 -9
- package/dist/rules-consent.js +411 -13
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +15 -1
- package/dist/runs.js +19 -0
- package/dist/runtime-caps-resolver.d.ts +36 -1
- package/dist/runtime-caps-resolver.js +25 -0
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/security.d.ts +5 -5
- package/dist/security.js +1 -1
- package/dist/session-sync.d.ts +52 -37
- package/dist/session-sync.js +19 -16
- package/dist/shared-memory-scope-authorizer.d.ts +1 -1
- package/dist/sighup-idle.d.ts +1 -1
- package/dist/spec-fields.d.ts +22 -1
- package/dist/spec-fields.js +11 -0
- package/dist/store-live-probe.d.ts +2 -2
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-cwd.d.ts +25 -0
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-settings.d.ts +3 -3
- package/dist/task-settings.js +3 -1
- package/dist/task-workflow.d.ts +1 -1
- package/dist/text-bidi.d.ts +2 -2
- package/dist/tool-approval.d.ts +191 -9
- package/dist/tool-approval.js +248 -29
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/engine-notice-wire.d.ts +6 -4
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/ledger-events.d.ts +1 -1
- package/dist/trace/ledger-sink.d.ts +2 -2
- package/dist/trace/project.d.ts +3 -2
- package/dist/trace/project.js +1 -0
- package/dist/turn-activity.d.ts +1 -1
- package/dist/wall-clock-jump-guard.d.ts +3 -3
- package/package.json +2 -2
- package/dist/http/tar.d.ts +0 -17
- package/dist/http/tar.js +0 -53
- package/dist/http/workspace-content.d.ts +0 -11
- package/dist/http/workspace-content.js +0 -20
- package/dist/plugins/file-snapshot-store-sql.d.ts +0 -170
- package/dist/plugins/file-snapshot-store-sql.js +0 -213
package/dist/http/tar.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 极小 ustar 写器(workspace 浏览面 #3 的 archive 端点用,[1882]/[1894])。零依赖(仓内 no-SDK 姿势,
|
|
3
|
-
* 同 s3-presign/OTLP 先例)。只写 regular file 条目:512B ustar 头(prefix 155/name 100 拆分)+
|
|
4
|
-
* 内容 512 对齐 + 两块零尾。**不做** PAX/GNU 长名扩展——快照 relPath 超过 prefix+name 可承载
|
|
5
|
-
* (>255 或不可在 "/" 处拆)属病态路径,调用方按 fail-loud 处理(archive 面 422 点名)。
|
|
6
|
-
*/
|
|
7
|
-
/** 把 relPath 拆成 ustar 的 (prefix ≤155, name ≤100);不可拆 ⇒ null(调用方 fail-loud)。 */
|
|
8
|
-
export declare function splitTarPath(relPath: string): {
|
|
9
|
-
prefix: string;
|
|
10
|
-
name: string;
|
|
11
|
-
} | null;
|
|
12
|
-
export declare function tarHeader(relPath: string, sizeBytes: number, mtimeSec: number): Buffer;
|
|
13
|
-
/** 内容块的 512 对齐尾垫(0 长内容=0 垫)。 */
|
|
14
|
-
export declare function tarPadding(sizeBytes: number): Buffer;
|
|
15
|
-
/** 归档终止:两块 512 零。 */
|
|
16
|
-
export declare const TAR_END: Buffer<ArrayBuffer>;
|
|
17
|
-
//# sourceMappingURL=tar.d.ts.map
|
package/dist/http/tar.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
const BLOCK = 512;
|
|
2
|
-
function octal(n, width) {
|
|
3
|
-
const s = n.toString(8).padStart(width - 1, "0");
|
|
4
|
-
return Buffer.from(s + "\0", "ascii");
|
|
5
|
-
}
|
|
6
|
-
export function splitTarPath(relPath) {
|
|
7
|
-
const bytes = Buffer.byteLength(relPath, "utf8");
|
|
8
|
-
if (bytes <= 100)
|
|
9
|
-
return { prefix: "", name: relPath };
|
|
10
|
-
if (bytes > 255)
|
|
11
|
-
return null;
|
|
12
|
-
for (let i = relPath.length - 1; i > 0; i--) {
|
|
13
|
-
if (relPath[i] !== "/")
|
|
14
|
-
continue;
|
|
15
|
-
const prefix = relPath.slice(0, i);
|
|
16
|
-
const name = relPath.slice(i + 1);
|
|
17
|
-
if (Buffer.byteLength(name, "utf8") <= 100 && Buffer.byteLength(prefix, "utf8") <= 155 && name.length > 0)
|
|
18
|
-
return { prefix, name };
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
const USTAR_MAX_SIZE = 8 * 1024 * 1024 * 1024 - 1;
|
|
23
|
-
export function tarHeader(relPath, sizeBytes, mtimeSec) {
|
|
24
|
-
if (!Number.isFinite(sizeBytes) || sizeBytes < 0 || sizeBytes > USTAR_MAX_SIZE) {
|
|
25
|
-
throw new Error(`tar: size ${sizeBytes} not representable in a ustar header (12-octal-byte field, max 8GiB-1): ${relPath}`);
|
|
26
|
-
}
|
|
27
|
-
const split = splitTarPath(relPath);
|
|
28
|
-
if (split === null)
|
|
29
|
-
throw new Error(`tar: path not representable in ustar (must be ≤255 bytes and splittable at "/"): ${relPath}`);
|
|
30
|
-
const h = Buffer.alloc(BLOCK, 0);
|
|
31
|
-
h.write(split.name, 0, 100, "utf8");
|
|
32
|
-
octal(0o644, 8).copy(h, 100);
|
|
33
|
-
octal(0, 8).copy(h, 108);
|
|
34
|
-
octal(0, 8).copy(h, 116);
|
|
35
|
-
octal(sizeBytes, 12).copy(h, 124);
|
|
36
|
-
octal(Math.max(0, Math.floor(mtimeSec)), 12).copy(h, 136);
|
|
37
|
-
h.fill(0x20, 148, 156);
|
|
38
|
-
h.write("0", 156, 1, "ascii");
|
|
39
|
-
h.write("ustar\0", 257, 6, "ascii");
|
|
40
|
-
h.write("00", 263, 2, "ascii");
|
|
41
|
-
h.write(split.prefix, 345, 155, "utf8");
|
|
42
|
-
let sum = 0;
|
|
43
|
-
for (const b of h)
|
|
44
|
-
sum += b;
|
|
45
|
-
Buffer.from(sum.toString(8).padStart(6, "0") + "\0 ", "ascii").copy(h, 148);
|
|
46
|
-
return h;
|
|
47
|
-
}
|
|
48
|
-
export function tarPadding(sizeBytes) {
|
|
49
|
-
const rem = sizeBytes % BLOCK;
|
|
50
|
-
return Buffer.alloc(rem === 0 ? 0 : BLOCK - rem, 0);
|
|
51
|
-
}
|
|
52
|
-
export const TAR_END = Buffer.alloc(BLOCK * 2, 0);
|
|
53
|
-
//# sourceMappingURL=tar.js.map
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* design/158 A9 seam —— workspace 文件面的内容类型/二进制判别,从 `http/server.ts` 整段搬出的原文。
|
|
3
|
-
* routes/sessions.ts 的 workspace 腿要用它,所以住在 http/ 叶子里(routes/* 不可值 import server.ts)。
|
|
4
|
-
*/
|
|
5
|
-
/** workspace file 面的 content-type(#3,[1894]③):保守扩展名表——只收「浏览器渲染无歧义且
|
|
6
|
-
* 无主动内容」的类型,其余一律 octet-stream(nosniff 恒带;html/svg 等可执行内容**故意**不进表,
|
|
7
|
-
* agent 产物=不可信,按下载处理)。 */
|
|
8
|
-
export declare function workspaceContentType(relPath: string): string;
|
|
9
|
-
/** 二进制判别([1894]③):首 8KiB 含 NUL 即二进制(git 同判据——快、稳、无误伤 UTF-8)。 */
|
|
10
|
-
export declare function looksBinary(bytes: Uint8Array): boolean;
|
|
11
|
-
//# sourceMappingURL=workspace-content.d.ts.map
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export function workspaceContentType(relPath) {
|
|
2
|
-
const base = relPath.slice(relPath.lastIndexOf("/") + 1);
|
|
3
|
-
const dot = base.lastIndexOf(".");
|
|
4
|
-
const ext = dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
|
|
5
|
-
const TABLE = {
|
|
6
|
-
txt: "text/plain; charset=utf-8", md: "text/plain; charset=utf-8", log: "text/plain; charset=utf-8",
|
|
7
|
-
json: "application/json; charset=utf-8", csv: "text/csv; charset=utf-8",
|
|
8
|
-
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp",
|
|
9
|
-
pdf: "application/pdf",
|
|
10
|
-
};
|
|
11
|
-
return TABLE[ext] ?? "application/octet-stream";
|
|
12
|
-
}
|
|
13
|
-
export function looksBinary(bytes) {
|
|
14
|
-
const n = Math.min(bytes.byteLength, 8192);
|
|
15
|
-
for (let i = 0; i < n; i++)
|
|
16
|
-
if (bytes[i] === 0)
|
|
17
|
-
return true;
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=workspace-content.js.map
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable `FileSnapshotStore` (E19 rewind-files) — SINGLE-FILE DUAL-DIALECT (design/158 A12 定型半场).
|
|
3
|
-
* ONE implementation, TWO dialects; the historical `TiDBFileSnapshotStore` / `PgFileSnapshotStore` class
|
|
4
|
-
* names survive as thin ctor subclasses so every consumer (store-backend.ts, the migration script, the
|
|
5
|
-
* contract/integration/minio-equivalence suites) is untouched.
|
|
6
|
-
*
|
|
7
|
-
* Cross-replica working-tree snapshots keyed by `(scope=sessionId, key=SessionTreeEntry.id)`, the SAME
|
|
8
|
-
* entryId anchor as E18 resume-at. Used for (a) rewind (restore a PAST turn's tree in the same env) and
|
|
9
|
-
* (b) the seamless local↔cloud work-artifact transfer (2c: a snapshot synced to the cloud, restored in the
|
|
10
|
-
* other environment — conversation + files in lockstep).
|
|
11
|
-
*
|
|
12
|
-
* The env-operating + security-critical half (the tree walk + the symlink-neutralizing restore
|
|
13
|
-
* convergence) is core's `captureManifest`/`applyManifest` — REUSED verbatim (re-implementing risks
|
|
14
|
-
* re-introducing the symlink-escape / fail-closed bugs core's dual-review fixed). This store provides ONLY
|
|
15
|
-
* the durable persistence: content-addressed blobs (sha256 → bytes, deduped across all snapshots, via the
|
|
16
|
-
* shared {@link BlobBackend} seam — SQL by default, MinIO when configured) + a per-`(scope,key)` manifest
|
|
17
|
-
* (relPath → blobHash) that stays in SQL either way (`snapshot_manifest`).
|
|
18
|
-
*
|
|
19
|
-
* ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
|
|
20
|
-
* - `?` placeholders vs `$n` on every `snapshot_manifest` statement (exists-check, insert, selects, the
|
|
21
|
-
* scoped/keyed deletes) — every statement stays written out at the call site via `q()`, never a single
|
|
22
|
-
* text with a numbering loop.
|
|
23
|
-
* - result unwrapping (`rows`/`affected`) and transaction control (mysql2 native begin/commit/rollback vs
|
|
24
|
-
* PG statement form) go through the shared `SqlDriver`/`SqlTxConn` seam (sql-driver.ts) — same
|
|
25
|
-
* normalization the other A12 collapses use; NOT a store-specific concern.
|
|
26
|
-
* - the default {@link BlobBackend} (`SqlBlobBackend`) and `blobSizes` (`sqlBlobSizes`) take the RAW pool
|
|
27
|
-
* + a `"tidb"|"pg"` dialect string directly (blob-backend.ts predates/sits outside this seam — it is a
|
|
28
|
-
* sibling module, not part of this collapse), so this store keeps BOTH the `SqlDriver` (for its own
|
|
29
|
-
* `snapshot_manifest` statements) and the raw native pool (to hand to blob-backend.ts unchanged).
|
|
30
|
-
* - in-flight guard key separator: the TiDB twin used `\x00`, the PG twin used a plain space — NOT a
|
|
31
|
-
* dialect requirement (this key never leaves the process or touches SQL), just accidental drift.
|
|
32
|
-
* Harmonized to `\x00` (can't collide with a normal scope/key string) — zero observable behavior change
|
|
33
|
-
* either way, since the key only dedups concurrent calls on THIS replica within THIS process.
|
|
34
|
-
* - `gcOrphanBlobs`'s orphan-SET query text is IDENTICAL for both dialects (no placeholders) — kept as one
|
|
35
|
-
* shared statement, not a `q()` pair.
|
|
36
|
-
*/
|
|
37
|
-
import { type FileSnapshotBounds, type FileSnapshotStore, type FileSnapshotResult, type ExecutionEnv } from "@sema-agent/core";
|
|
38
|
-
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
39
|
-
import type { Pool as PgPool } from "pg";
|
|
40
|
-
import { type BlobBackend } from "./blob-backend.js";
|
|
41
|
-
import { type SqlDriver } from "./sql-driver.js";
|
|
42
|
-
/** Dual-dialect durable FileSnapshotStore. See the file header for the dialect-delta ledger. */
|
|
43
|
-
export declare class SqlFileSnapshotStore implements FileSnapshotStore {
|
|
44
|
-
protected readonly db: SqlDriver;
|
|
45
|
-
/** The RAW native pool — handed to blob-backend.ts (SqlBlobBackend / sqlBlobSizes), which sits OUTSIDE
|
|
46
|
-
* the SqlDriver seam and dialect-switches on its own `"tidb"|"pg"` string. Not used for this class's OWN
|
|
47
|
-
* `snapshot_manifest` statements (those go through `db`). */
|
|
48
|
-
protected readonly pool: MySqlPool | PgPool;
|
|
49
|
-
protected readonly bounds: FileSnapshotBounds;
|
|
50
|
-
/** In-flight create-once guard (per process) — mirrors InMemoryFileSnapshotStore: a concurrent 2nd
|
|
51
|
-
* snapshot/import of the same (scope,key) on THIS replica is a no-op, not a re-capture. CROSS-REPLICA dup
|
|
52
|
-
* is HARMLESS here: the key is an immutable SessionTreeEntry.id (one settled turn → one fixed tree), so
|
|
53
|
-
* two replicas capture the SAME relPath→hash rows → restore's Map dedups them identically. (A
|
|
54
|
-
* snapshot_header(scope,snap_key) claim row would serialize one winner + drop dup rows — a follow-on
|
|
55
|
-
* hardening for if same-session-multi-replica ever happens; v1 is single-replica affinity so it doesn't.) */
|
|
56
|
-
private readonly inFlight;
|
|
57
|
-
/** The content-addressed BYTE store (clay 2026-06-26 object-store offload). DEFAULT = the SqlBlobBackend
|
|
58
|
-
* over THIS pool — so a MINIO-unset deployment is byte-identical to before this refactor (the
|
|
59
|
-
* `snapshot_blob` table). When MinIO is configured the StoreBackend passes a MinioBlobBackend, moving ONLY
|
|
60
|
-
* the heavy bytes off-DB; the MANIFEST (snapshot_manifest) + the orphan-reference computation STAY in SQL. */
|
|
61
|
-
private readonly blobs;
|
|
62
|
-
/** [805]③a/[806]④: core 1.287's captureManifest counts enumerate→read-window vanished files it TOLERATED
|
|
63
|
-
* (E19 fix — skipped, not failed). The count rides ONLY the captureManifest return value; without this
|
|
64
|
-
* sink it is silently dropped. Optional observer set by main.ts (metrics face) — never throws into the
|
|
65
|
-
* seam. codex F8: the type admits an ASYNC sink (`void | Promise<void>`) — the call site swallows a
|
|
66
|
-
* rejected promise too (a bare try/catch only caught sync throws, so an async observer's rejection
|
|
67
|
-
* surfaced as an unhandledRejection and could pierce the never-throw posture process-wide). */
|
|
68
|
-
onSkippedVanished: ((count: number) => void | Promise<void>) | undefined;
|
|
69
|
-
constructor(db: SqlDriver,
|
|
70
|
-
/** The RAW native pool — handed to blob-backend.ts (SqlBlobBackend / sqlBlobSizes), which sits OUTSIDE
|
|
71
|
-
* the SqlDriver seam and dialect-switches on its own `"tidb"|"pg"` string. Not used for this class's OWN
|
|
72
|
-
* `snapshot_manifest` statements (those go through `db`). */
|
|
73
|
-
pool: MySqlPool | PgPool, blobBackend?: BlobBackend, bounds?: FileSnapshotBounds);
|
|
74
|
-
/** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
|
|
75
|
-
private q;
|
|
76
|
-
/** THE ONE content-addressed blob write — the SINGLE source `snapshot()`, `importManifest()`, and the
|
|
77
|
-
* `putBlob` seam all funnel through (no drift across the three writers). Routes the bytes through the
|
|
78
|
-
* BlobBackend (SQL default or MinIO); the SqlBlobBackend's upsert REFRESHES `created_at` even on a dedup
|
|
79
|
-
* hit so a re-used/just-written blob is "touched=now" and GC-grace-protected until a manifest row commits
|
|
80
|
-
* (closes the delete-in-use race). */
|
|
81
|
-
private putBlobRow;
|
|
82
|
-
/** 2c session-sync PUSH side — upload ONE content-addressed blob to the cloud's snapshot store AHEAD of a
|
|
83
|
-
* bundle import (§5 streamed blob negotiation: the local peer PUTs only the blobs the cloud is missing,
|
|
84
|
-
* then commits the bundle via importSession, whose getBlob reads the cloud's OWN store). A SERVICE method
|
|
85
|
-
* (NOT on core's FileSnapshotStore interface — core was asked, non-blocking; the route feature-detects
|
|
86
|
-
* `typeof fs.putBlob`). Reuses {@link putBlobRow} VERBATIM (the SAME created_at-refreshing upsert
|
|
87
|
-
* importManifest uses), so a PUT and a concurrent reap can't race: the just-PUT blob's created_at=now
|
|
88
|
-
* keeps it inside the GC grace window until the import references it from a manifest. Idempotent
|
|
89
|
-
* (content-addressed). The route verifies sha256(bytes)===hash BEFORE this. core 1.142.0 made this a
|
|
90
|
-
* fail-closed never-throw seam (Promise<FileSnapshotResult>): a BlobBackend write error (e.g. a
|
|
91
|
-
* MinioBlobBackend non-2xx PUT) is caught + encoded as `restore_failed`, NEVER thrown out of the seam. */
|
|
92
|
-
putBlob(hash: string, bytes: Uint8Array): Promise<FileSnapshotResult>;
|
|
93
|
-
/** CREATE-ONCE: an entryId's tree is immutable, so a 2nd snapshot for an existing key is a no-op {ok:true}. */
|
|
94
|
-
snapshot(scope: string, key: string, env: ExecutionEnv, root: string, signal?: AbortSignal): Promise<FileSnapshotResult>;
|
|
95
|
-
/** 2c session-sync IMPORT side — the inverse of `exportManifest`/`getBlob`: persist a snapshot's content
|
|
96
|
-
* (manifest + content-addressed blobs) that was EXPORTED from ANOTHER backend (the local→cloud transfer: a src
|
|
97
|
-
* snapshot's manifest+blobs land here as a NEW `(scope,key)`). Unlike `snapshot()` this does NOT walk an env — the
|
|
98
|
-
* manifest is given; bytes come from `srcGetBlob(blobHash)`. Mirrors core's InMemory/File `importManifest` fail-closed
|
|
99
|
-
* matrix verbatim (core 1.141.0). Reuses snapshot()'s EXACT putBlob (created_at-refreshing upsert, GC-grace-safe)
|
|
100
|
-
* + manifest-tx (re-check under txn) patterns.
|
|
101
|
-
*
|
|
102
|
-
* NEVER-throws + FAIL-CLOSED (the matrix core's dual-review fixed, point-named to the durable twins):
|
|
103
|
-
* - CREATE-ONCE: an entryId's tree is immutable → an existing OR in-flight `(scope,key)` is a no-op `{ok:true}`.
|
|
104
|
-
* - `srcGetBlob` is a CROSS-BACKEND fetch (remote/IPC) that CAN REJECT (timeout/ECONNRESET/query error) — caught and
|
|
105
|
-
* encoded as `read_failed`, NEVER thrown out of the seam.
|
|
106
|
-
* - a MISSING blob (`bytes===undefined`) → `read_failed`.
|
|
107
|
-
* - content-address INTEGRITY: `sha256(bytes) !== hash` → `read_failed` (a 2c transfer can cross net/IPC; a corrupt
|
|
108
|
-
* blob stored under a mismatched hash would let a later restore silently write WRONG bytes — verify, don't trust).
|
|
109
|
-
* - ANY blob failure → EARLY-RETURN `{ok:false}` and the manifest tx is NEVER run, so `has()` stays false (no
|
|
110
|
-
* half-snapshot). The DISTINCT blobs already staged before the failing one are orphans (no manifest references
|
|
111
|
-
* them) — their snapshot_blob INDEX rows are reaped (and, under MinIO, the objects deleted) by the async grace-
|
|
112
|
-
* window sweepOrphanBlobs reaper; they never make this `(scope,key)` visible. */
|
|
113
|
-
importManifest(scope: string, key: string, manifest: Map<string, string>, srcGetBlob: (hash: string) => Promise<Uint8Array | undefined>): Promise<FileSnapshotResult>;
|
|
114
|
-
/** Converge `root` back to the snapshot (applyManifest: PHASE-1 remove symlinks/strays, PHASE-2 write captured). */
|
|
115
|
-
restore(scope: string, key: string, env: ExecutionEnv, root: string, signal?: AbortSignal): Promise<FileSnapshotResult>;
|
|
116
|
-
has(scope: string, key: string): Promise<boolean>;
|
|
117
|
-
/** 2c session-sync: enumerate every snapshot key (entryId) in a scope — the SAME enumeration reap()
|
|
118
|
-
* does (`SELECT DISTINCT snap_key`), exposed so 2c can export a session's full set of file snapshots. */
|
|
119
|
-
listKeys(scope: string): Promise<string[]>;
|
|
120
|
-
/** 2c session-sync: the manifest (relPath → blobHash) for one snapshot — to EXPORT its content to another
|
|
121
|
-
* backend byte-for-byte (paired with getBlob for content-addressed transfer). Reads the STORED manifest rows (never
|
|
122
|
-
* re-hashes). 0 rows = no snapshot (a real snapshot always has ≥1 manifest row) → null (parity with InMemory). */
|
|
123
|
-
exportManifest(scope: string, key: string): Promise<Map<string, string> | null>;
|
|
124
|
-
/** 2c session-sync: the content-addressed bytes for `hash` (undefined if absent) — reads via the BlobBackend
|
|
125
|
-
* (SQL default or MinIO, where getBlob also verifies content-address integrity). */
|
|
126
|
-
getBlob(hash: string): Promise<Uint8Array | undefined>;
|
|
127
|
-
/** The subset of `hashes` already present in the blob store — reads the SQL INDEX (one IN(...) for BOTH backends),
|
|
128
|
-
* so the §10 import blob-presence pre-check is a cheap index probe, NOT N full-object getBlob GETs (finding 12). */
|
|
129
|
-
hasBlobs(hashes: string[]): Promise<Set<string>>;
|
|
130
|
-
/** workspace 浏览面(#3,[1894]②):per-blob 字节数——读 SQL INDEX 的 byte_len(单 IN 查询;
|
|
131
|
-
* MinIO 后端的索引行同带 byte_len,两后端同源)。tree 的 size 投影 + file 面「超限先拒后拉」用。 */
|
|
132
|
-
blobSizes(hashes: string[]): Promise<Map<string, number>>;
|
|
133
|
-
/** GC: drop every snapshot in `scope` whose key is NOT in keepKeys. Drops ONLY the SQL manifest rows synchronously —
|
|
134
|
-
* the now-unreferenced blobs become ORPHANS (their snapshot_blob INDEX rows survive) and are collected LATER by the
|
|
135
|
-
* async sweepOrphanBlobs reaper (NEVER a synchronous external-store delete that could block/fail this reap on a MinIO
|
|
136
|
-
* blip — clay 2026-06-26 async-purge design). */
|
|
137
|
-
reap(scope: string, keepKeys: string[]): Promise<number>;
|
|
138
|
-
/** E21 purge — drop ALL snapshots for a session (every key). Drops ONLY the SQL manifest rows synchronously → the
|
|
139
|
-
* session is immediately gone + its blobs become orphans; the actual MinIO/SQL BYTE deletion is the async
|
|
140
|
-
* sweepOrphanBlobs reaper's job (bounded-window EVENTUAL CONSISTENCY satisfies the E21 privacy/compliance "really
|
|
141
|
-
* delete the bytes" requirement WITHOUT a fragile synchronous object-store delete on the purge path). Scoped by sessionId. */
|
|
142
|
-
deleteBySession(scope: string): Promise<number>;
|
|
143
|
-
/** Content-addressed blobs are shared across snapshots → GC only those NO manifest references. The orphan-hash SET is
|
|
144
|
-
* computed from the `snapshot_blob` INDEX in SQL (reference tracking stays in the DB — works for BOTH the SQL backend
|
|
145
|
-
* AND MinIO, whose index rows are kept bytes-NULL); the BlobBackend's deleteBlobs applies the grace window (a just-
|
|
146
|
-
* written blob a concurrent in-flight snapshot needs is never delete-in-use) and, for MinIO, deletes the objects.
|
|
147
|
-
* Returns the count ACTUALLY deleted (the grace-passed subset deleteBlobs reports — NOT the pre-grace orphan count,
|
|
148
|
-
* finding 13). Called ONLY from the async sweepOrphanBlobs reaper (never the synchronous purge path).
|
|
149
|
-
*
|
|
150
|
-
* The orphan-SET query text is IDENTICAL for both dialects (no placeholders) — a single shared statement, not a `q()`
|
|
151
|
-
* pair. A query error degrades to "zero orphans this sweep" (both twins already treated it that way — the reaper
|
|
152
|
-
* retries next tick, so under-collecting once is harmless; never surfaced as a hard failure). */
|
|
153
|
-
private gcOrphanBlobs;
|
|
154
|
-
/** 2c session-sync DoS bound + the ASYNC byte-GC for reap/deleteBySession orphans — GLOBALLY GC grace-passed
|
|
155
|
-
* orphan blobs (standalone PUTs never imported AND the blobs a purge/reap left orphaned). The driver is the main.ts
|
|
156
|
-
* reaper (scheduled, error-tolerant: a per-object MinIO delete failure is skipped + retried next sweep → bounded-
|
|
157
|
-
* window eventual consistency). Reuses {@link gcOrphanBlobs} VERBATIM; returns the count actually deleted this sweep. */
|
|
158
|
-
sweepOrphanBlobs(): Promise<number>;
|
|
159
|
-
/** Run statements atomically; rolls back on failure. */
|
|
160
|
-
private tx;
|
|
161
|
-
}
|
|
162
|
-
/** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
|
|
163
|
-
export declare class TiDBFileSnapshotStore extends SqlFileSnapshotStore {
|
|
164
|
-
constructor(pool: MySqlPool, blobBackend?: BlobBackend, bounds?: FileSnapshotBounds);
|
|
165
|
-
}
|
|
166
|
-
/** PostgreSQL binding — historical class name + ctor shape preserved. */
|
|
167
|
-
export declare class PgFileSnapshotStore extends SqlFileSnapshotStore {
|
|
168
|
-
constructor(pool: PgPool, blobBackend?: BlobBackend, bounds?: FileSnapshotBounds);
|
|
169
|
-
}
|
|
170
|
-
//# sourceMappingURL=file-snapshot-store-sql.d.ts.map
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { captureManifest, applyManifest, DEFAULT_SNAPSHOT_BOUNDS } from "@sema-agent/core";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { SqlBlobBackend, sqlBlobSizes } from "./blob-backend.js";
|
|
4
|
-
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
5
|
-
export class SqlFileSnapshotStore {
|
|
6
|
-
db;
|
|
7
|
-
pool;
|
|
8
|
-
bounds;
|
|
9
|
-
inFlight = new Set();
|
|
10
|
-
blobs;
|
|
11
|
-
onSkippedVanished = undefined;
|
|
12
|
-
constructor(db, pool, blobBackend, bounds = DEFAULT_SNAPSHOT_BOUNDS) {
|
|
13
|
-
this.db = db;
|
|
14
|
-
this.pool = pool;
|
|
15
|
-
this.bounds = bounds;
|
|
16
|
-
this.blobs = blobBackend ?? new SqlBlobBackend(this.db.dialect, this.pool);
|
|
17
|
-
}
|
|
18
|
-
q(tidb, pg) {
|
|
19
|
-
return this.db.dialect === "tidb" ? tidb : pg;
|
|
20
|
-
}
|
|
21
|
-
async putBlobRow(hash, bytes) {
|
|
22
|
-
await this.blobs.putBlob(hash, bytes);
|
|
23
|
-
}
|
|
24
|
-
async putBlob(hash, bytes) {
|
|
25
|
-
if (createHash("sha256").update(bytes).digest("hex") !== hash) {
|
|
26
|
-
return { ok: false, error: { code: "read_failed", message: `putBlob ${hash} fails content-address integrity — nothing stored` } };
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
await this.putBlobRow(hash, bytes);
|
|
30
|
-
return { ok: true };
|
|
31
|
-
}
|
|
32
|
-
catch (err) {
|
|
33
|
-
return { ok: false, error: { code: "restore_failed", message: `putBlob ${hash} write/upload failed: ${err.message}` } };
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async snapshot(scope, key, env, root, signal) {
|
|
37
|
-
const flightKey = `${scope}\x00${key}`;
|
|
38
|
-
if (this.inFlight.has(flightKey))
|
|
39
|
-
return { ok: true };
|
|
40
|
-
this.inFlight.add(flightKey);
|
|
41
|
-
try {
|
|
42
|
-
if (await this.has(scope, key))
|
|
43
|
-
return { ok: true };
|
|
44
|
-
let cap;
|
|
45
|
-
try {
|
|
46
|
-
cap = await captureManifest(env, root, this.bounds, signal, async (hash, bytes) => {
|
|
47
|
-
await this.putBlobRow(hash, bytes);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
return { ok: false, error: { code: "restore_failed", message: `snapshot blob write failed: ${err.message}` } };
|
|
52
|
-
}
|
|
53
|
-
if (!cap.ok)
|
|
54
|
-
return { ok: false, error: cap.error };
|
|
55
|
-
if (cap.skippedVanished > 0 && this.onSkippedVanished) {
|
|
56
|
-
try {
|
|
57
|
-
void Promise.resolve(this.onSkippedVanished(cap.skippedVanished)).catch(() => { });
|
|
58
|
-
}
|
|
59
|
-
catch { }
|
|
60
|
-
}
|
|
61
|
-
await this.tx(async (conn) => {
|
|
62
|
-
const exists = await conn.query(this.q("SELECT 1 FROM snapshot_manifest WHERE scope = ? AND snap_key = ? LIMIT 1", "SELECT 1 FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2 LIMIT 1"), [scope, key]);
|
|
63
|
-
if (exists.rows[0])
|
|
64
|
-
return;
|
|
65
|
-
for (const [relPath, hash] of cap.value) {
|
|
66
|
-
await conn.query(this.q("INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES (?,?,?,?)", "INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES ($1,$2,$3,$4)"), [scope, key, relPath, hash]);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
return { ok: true };
|
|
70
|
-
}
|
|
71
|
-
catch (err) {
|
|
72
|
-
return { ok: false, error: { code: "restore_failed", message: `snapshot persist failed: ${err.message}` } };
|
|
73
|
-
}
|
|
74
|
-
finally {
|
|
75
|
-
this.inFlight.delete(flightKey);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async importManifest(scope, key, manifest, srcGetBlob) {
|
|
79
|
-
const flightKey = `${scope}\x00${key}`;
|
|
80
|
-
if (this.inFlight.has(flightKey))
|
|
81
|
-
return { ok: true };
|
|
82
|
-
this.inFlight.add(flightKey);
|
|
83
|
-
try {
|
|
84
|
-
if (await this.has(scope, key))
|
|
85
|
-
return { ok: true };
|
|
86
|
-
for (const hash of new Set(manifest.values())) {
|
|
87
|
-
if (hash === "" || hash === "." || hash === ".." || !/^[A-Za-z0-9_.-]+$/.test(hash)) {
|
|
88
|
-
return { ok: false, error: { code: "read_failed", message: `unsafe blob hash ${JSON.stringify(hash)}` } };
|
|
89
|
-
}
|
|
90
|
-
let bytes;
|
|
91
|
-
try {
|
|
92
|
-
bytes = await srcGetBlob(hash);
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
return { ok: false, error: { code: "read_failed", message: `source blob ${hash} fetch failed: ${err.message}` } };
|
|
96
|
-
}
|
|
97
|
-
if (!bytes)
|
|
98
|
-
return { ok: false, error: { code: "read_failed", message: `missing source blob ${hash}` } };
|
|
99
|
-
if (createHash("sha256").update(bytes).digest("hex") !== hash) {
|
|
100
|
-
return { ok: false, error: { code: "read_failed", message: `source blob ${hash} fails content-address integrity` } };
|
|
101
|
-
}
|
|
102
|
-
await this.putBlobRow(hash, bytes);
|
|
103
|
-
}
|
|
104
|
-
await this.tx(async (conn) => {
|
|
105
|
-
const exists = await conn.query(this.q("SELECT 1 FROM snapshot_manifest WHERE scope = ? AND snap_key = ? LIMIT 1", "SELECT 1 FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2 LIMIT 1"), [scope, key]);
|
|
106
|
-
if (exists.rows[0])
|
|
107
|
-
return;
|
|
108
|
-
for (const [relPath, hash] of manifest) {
|
|
109
|
-
await conn.query(this.q("INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES (?,?,?,?)", "INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES ($1,$2,$3,$4)"), [scope, key, relPath, hash]);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
return { ok: true };
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
return { ok: false, error: { code: "restore_failed", message: `import persist failed: ${err.message}` } };
|
|
116
|
-
}
|
|
117
|
-
finally {
|
|
118
|
-
this.inFlight.delete(flightKey);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
async restore(scope, key, env, root, signal) {
|
|
122
|
-
try {
|
|
123
|
-
const { rows } = await this.db.query(this.q("SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = ? AND snap_key = ?", "SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2"), [scope, key]);
|
|
124
|
-
if (rows.length === 0)
|
|
125
|
-
return { ok: false, error: { code: "not_found", message: `no file snapshot for ${scope}/${key}` } };
|
|
126
|
-
const manifest = new Map(rows.map((r) => [String(r.rel_path), String(r.blob_hash)]));
|
|
127
|
-
return await applyManifest(env, root, this.bounds, signal, manifest, (hash) => this.blobs.getBlob(hash));
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
return { ok: false, error: { code: "restore_failed", message: `restore blob read failed: ${err.message}` } };
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
async has(scope, key) {
|
|
134
|
-
const { rows } = await this.db.query(this.q("SELECT 1 FROM snapshot_manifest WHERE scope = ? AND snap_key = ? LIMIT 1", "SELECT 1 FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2 LIMIT 1"), [scope, key]);
|
|
135
|
-
return Boolean(rows[0]);
|
|
136
|
-
}
|
|
137
|
-
async listKeys(scope) {
|
|
138
|
-
const { rows } = await this.db.query(this.q("SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = ?", "SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = $1"), [scope]);
|
|
139
|
-
return rows.map((r) => String(r.snap_key));
|
|
140
|
-
}
|
|
141
|
-
async exportManifest(scope, key) {
|
|
142
|
-
const { rows } = await this.db.query(this.q("SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = ? AND snap_key = ?", "SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2"), [scope, key]);
|
|
143
|
-
if (rows.length === 0)
|
|
144
|
-
return null;
|
|
145
|
-
return new Map(rows.map((r) => [String(r.rel_path), String(r.blob_hash)]));
|
|
146
|
-
}
|
|
147
|
-
async getBlob(hash) {
|
|
148
|
-
return this.blobs.getBlob(hash);
|
|
149
|
-
}
|
|
150
|
-
async hasBlobs(hashes) {
|
|
151
|
-
return this.blobs.hasBlobs(hashes);
|
|
152
|
-
}
|
|
153
|
-
async blobSizes(hashes) {
|
|
154
|
-
return sqlBlobSizes(this.db.dialect, this.pool, hashes);
|
|
155
|
-
}
|
|
156
|
-
async reap(scope, keepKeys) {
|
|
157
|
-
return this.tx(async (conn) => {
|
|
158
|
-
const { rows: keys } = await conn.query(this.q("SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = ?", "SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = $1"), [scope]);
|
|
159
|
-
const drop = keys.map((k) => String(k.snap_key)).filter((k) => !keepKeys.includes(k));
|
|
160
|
-
for (const k of drop) {
|
|
161
|
-
await conn.query(this.q("DELETE FROM snapshot_manifest WHERE scope = ? AND snap_key = ?", "DELETE FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2"), [scope, k]);
|
|
162
|
-
}
|
|
163
|
-
return drop.length;
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
async deleteBySession(scope) {
|
|
167
|
-
const { affected } = await this.db.query(this.q("DELETE FROM snapshot_manifest WHERE scope = ?", "DELETE FROM snapshot_manifest WHERE scope = $1"), [scope]);
|
|
168
|
-
return affected;
|
|
169
|
-
}
|
|
170
|
-
async gcOrphanBlobs() {
|
|
171
|
-
const { rows } = await this.db
|
|
172
|
-
.query("SELECT blob_hash FROM snapshot_blob WHERE blob_hash NOT IN (SELECT blob_hash FROM snapshot_manifest)")
|
|
173
|
-
.catch(() => ({ rows: [], affected: 0 }));
|
|
174
|
-
const orphans = rows.map((r) => String(r.blob_hash));
|
|
175
|
-
if (orphans.length === 0)
|
|
176
|
-
return 0;
|
|
177
|
-
return this.blobs.deleteBlobs(orphans);
|
|
178
|
-
}
|
|
179
|
-
async sweepOrphanBlobs() {
|
|
180
|
-
return this.gcOrphanBlobs();
|
|
181
|
-
}
|
|
182
|
-
async tx(fn) {
|
|
183
|
-
const conn = await this.db.connect();
|
|
184
|
-
try {
|
|
185
|
-
await conn.begin();
|
|
186
|
-
const out = await fn(conn);
|
|
187
|
-
await conn.commit();
|
|
188
|
-
return out;
|
|
189
|
-
}
|
|
190
|
-
catch (err) {
|
|
191
|
-
try {
|
|
192
|
-
await conn.rollback();
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
}
|
|
196
|
-
throw err;
|
|
197
|
-
}
|
|
198
|
-
finally {
|
|
199
|
-
conn.release();
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
export class TiDBFileSnapshotStore extends SqlFileSnapshotStore {
|
|
204
|
-
constructor(pool, blobBackend, bounds = DEFAULT_SNAPSHOT_BOUNDS) {
|
|
205
|
-
super(mysqlDriver(pool), pool, blobBackend, bounds);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
export class PgFileSnapshotStore extends SqlFileSnapshotStore {
|
|
209
|
-
constructor(pool, blobBackend, bounds = DEFAULT_SNAPSHOT_BOUNDS) {
|
|
210
|
-
super(pgDriver(pool), pool, blobBackend, bounds);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
//# sourceMappingURL=file-snapshot-store-sql.js.map
|