@cursor/july 0.1.31 → 0.1.32
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/dist/bin/agent-serve.js +0 -0
- package/dist/channels/slack/post-update-delivery.d.ts +85 -0
- package/dist/channels/slack/post-update-delivery.d.ts.map +1 -0
- package/dist/docs/404.html +1 -1
- package/dist/docs/ab.html +2 -2
- package/dist/docs/assets/{app.DigB_9cQ.js → app.C2aVczmo.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.xgMnlUSO.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BCMX25Bv.js → VPLocalSearchBox.DLEzetbD.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BDDWeELx.js → theme.BkOH1oMp.js} +2 -2
- package/dist/docs/building-with-agents.html +2 -2
- package/dist/docs/concepts.html +2 -2
- package/dist/docs/deployment.html +2 -2
- package/dist/docs/evals.html +2 -2
- package/dist/docs/example-agents/approval-buddy.html +2 -2
- package/dist/docs/example-agents/benny.html +2 -2
- package/dist/docs/example-agents/bugbot.html +2 -2
- package/dist/docs/example-agents/codebase-wiki.html +2 -2
- package/dist/docs/example-agents/codeowners-review.html +2 -2
- package/dist/docs/example-agents/concierge.html +2 -2
- package/dist/docs/example-agents/fsd.html +2 -2
- package/dist/docs/example-agents/index.html +2 -2
- package/dist/docs/example-agents/knowledge-base.html +2 -2
- package/dist/docs/example-agents/oncall.html +2 -2
- package/dist/docs/example-agents/security-reviewer.html +2 -2
- package/dist/docs/example-agents/slack-agent.html +2 -2
- package/dist/docs/example-agents/weather-agent.html +2 -2
- package/dist/docs/guides/agent-to-agent.html +2 -2
- package/dist/docs/guides/cloud-runtime.html +2 -2
- package/dist/docs/guides/github.html +2 -2
- package/dist/docs/guides/human-in-the-loop.html +2 -2
- package/dist/docs/guides/mcp-oauth.html +2 -2
- package/dist/docs/guides/slack.html +2 -2
- package/dist/docs/guides/webhooks.html +2 -2
- package/dist/docs/hillclimbing.html +2 -2
- package/dist/docs/index.html +2 -2
- package/dist/docs/quickstart.html +2 -2
- package/dist/docs/reference/agent-config.html +2 -2
- package/dist/docs/reference/channels.html +2 -2
- package/dist/docs/reference/cli.html +2 -2
- package/dist/docs/reference/connections.html +2 -2
- package/dist/docs/reference/hooks.html +2 -2
- package/dist/docs/reference/http-api.html +2 -2
- package/dist/docs/reference/instructions.html +2 -2
- package/dist/docs/reference/playground.html +2 -2
- package/dist/docs/reference/project-layout.html +2 -2
- package/dist/docs/reference/prompt.html +2 -2
- package/dist/docs/reference/schedules.html +2 -2
- package/dist/docs/reference/sessions.html +2 -2
- package/dist/docs/reference/skills.html +2 -2
- package/dist/docs/reference/subagents.html +2 -2
- package/dist/docs/reference/tools.html +2 -2
- package/dist/docs/scaffolding-agents.html +2 -2
- package/dist/docs/storage.html +2 -2
- package/dist/docs/troubleshooting.html +2 -2
- package/dist/files-backends/agent-store-presigned-url.d.ts +3 -100
- package/dist/files-backends/agent-store-presigned-url.d.ts.map +1 -1
- package/dist/files-backends/agent-store-presigned-url.js +990 -314
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +25 -2
- package/dist/internal/json-dir-store.d.ts +32 -0
- package/dist/internal/json-dir-store.d.ts.map +1 -0
- package/dist/internal/persistence-coordinator.d.ts +127 -0
- package/dist/internal/persistence-coordinator.d.ts.map +1 -0
- package/dist/multi-tenant.d.ts +80 -0
- package/dist/multi-tenant.d.ts.map +1 -0
- package/dist/multi-tenant.js +69 -0
- package/dist/persistence.d.ts +184 -0
- package/dist/persistence.d.ts.map +1 -0
- package/package.json +3 -2
- package/src/files-backends/agent-store-presigned-url.ts +6 -400
- package/src/files-backends/cursor-hosted.ts +32 -6
- package/dist/docs/assets/chunks/@localSearchIndexroot.CnFFl07y.js +0 -1
|
@@ -1,347 +1,1023 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
1
|
+
// ../../node_modules/.pnpm/@connectrpc+connect@1.6.1_patch_hash=c9c7616ccfc0246b19c6537f56676d8501713cb6c94b440d13_ef7275568c8b1f1f499006dc986c70d2/node_modules/@connectrpc/connect/dist/esm/code.js
|
|
2
|
+
var Code;
|
|
3
|
+
(function(Code2) {
|
|
4
|
+
Code2[Code2["Canceled"] = 1] = "Canceled";
|
|
5
|
+
Code2[Code2["Unknown"] = 2] = "Unknown";
|
|
6
|
+
Code2[Code2["InvalidArgument"] = 3] = "InvalidArgument";
|
|
7
|
+
Code2[Code2["DeadlineExceeded"] = 4] = "DeadlineExceeded";
|
|
8
|
+
Code2[Code2["NotFound"] = 5] = "NotFound";
|
|
9
|
+
Code2[Code2["AlreadyExists"] = 6] = "AlreadyExists";
|
|
10
|
+
Code2[Code2["PermissionDenied"] = 7] = "PermissionDenied";
|
|
11
|
+
Code2[Code2["ResourceExhausted"] = 8] = "ResourceExhausted";
|
|
12
|
+
Code2[Code2["FailedPrecondition"] = 9] = "FailedPrecondition";
|
|
13
|
+
Code2[Code2["Aborted"] = 10] = "Aborted";
|
|
14
|
+
Code2[Code2["OutOfRange"] = 11] = "OutOfRange";
|
|
15
|
+
Code2[Code2["Unimplemented"] = 12] = "Unimplemented";
|
|
16
|
+
Code2[Code2["Internal"] = 13] = "Internal";
|
|
17
|
+
Code2[Code2["Unavailable"] = 14] = "Unavailable";
|
|
18
|
+
Code2[Code2["DataLoss"] = 15] = "DataLoss";
|
|
19
|
+
Code2[Code2["Unauthenticated"] = 16] = "Unauthenticated";
|
|
20
|
+
})(Code || (Code = {}));
|
|
21
|
+
|
|
22
|
+
// ../agent-store-sync/dist/bcs-transport.js
|
|
23
|
+
var DIRECTORY_LIST_PAGE_SIZE = 1e3;
|
|
24
|
+
var DIRECTORY_LIST_MAX_PAGES = 10;
|
|
25
|
+
var DIRECTORY_LIST_MAX_ENTRIES = DIRECTORY_LIST_PAGE_SIZE * DIRECTORY_LIST_MAX_PAGES;
|
|
26
|
+
|
|
27
|
+
// ../agent-store-sync/dist/conflict-events.js
|
|
28
|
+
import * as fs2 from "node:fs";
|
|
29
|
+
|
|
30
|
+
// ../agent-store-sync/dist/paths.js
|
|
31
|
+
import * as fs from "node:fs";
|
|
32
|
+
import * as os from "node:os";
|
|
33
|
+
import * as path from "node:path";
|
|
34
|
+
var PRIVATE_DIR_MODE = 448;
|
|
35
|
+
var AgentStorePathError = class extends Error {
|
|
36
|
+
constructor({ code, message, failedPath }) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = "AgentStorePathError";
|
|
39
|
+
this.code = code;
|
|
40
|
+
this.failedPath = failedPath;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function assertNoSymlinkInPath(targetPath, options = {}) {
|
|
44
|
+
const deps = getDeps(options);
|
|
45
|
+
const resolved = deps.path.resolve(targetPath);
|
|
46
|
+
const segments = getPathSegments(resolved, deps.path);
|
|
47
|
+
for (const segment of segments) {
|
|
48
|
+
const stat = tryLstat(segment, deps.fs);
|
|
49
|
+
if (!stat) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (stat.isSymbolicLink()) {
|
|
53
|
+
throw new AgentStorePathError({
|
|
54
|
+
code: "symlink_refused",
|
|
55
|
+
message: `Refusing to use symlinked agent store path: ${segment}`,
|
|
56
|
+
failedPath: segment
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function ensureSecureDirectoryChain(targetDir, options = {}) {
|
|
62
|
+
const deps = getDeps(options);
|
|
63
|
+
const resolvedTarget = deps.path.resolve(targetDir);
|
|
64
|
+
const trustedBase = options.trustedBase !== void 0 ? deps.path.resolve(options.trustedBase) : findTrustedDirectoryAncestor(resolvedTarget, deps);
|
|
65
|
+
if (resolvedTarget === trustedBase) {
|
|
66
|
+
assertRealDirectorySegment(trustedBase, deps);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const relative = deps.path.relative(trustedBase, resolvedTarget);
|
|
70
|
+
if (relative.length === 0 || relative.startsWith("..") || deps.path.isAbsolute(relative)) {
|
|
71
|
+
throw new AgentStorePathError({
|
|
72
|
+
code: "invalid_store_base",
|
|
73
|
+
message: `Refusing to create ${resolvedTarget} outside trusted base ${trustedBase}`,
|
|
74
|
+
failedPath: resolvedTarget
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const segments = relative.split(deps.path.sep).filter((segment) => segment.length > 0);
|
|
78
|
+
let current = trustedBase;
|
|
79
|
+
for (const segment of segments) {
|
|
80
|
+
assertRealDirectorySegment(current, deps);
|
|
81
|
+
current = deps.path.join(current, segment);
|
|
82
|
+
ensureRealDirectorySegment(current, deps);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function getDeps(options) {
|
|
86
|
+
var _a;
|
|
87
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
88
|
+
return {
|
|
89
|
+
env: (_b = options.env) !== null && _b !== void 0 ? _b : process.env,
|
|
90
|
+
fs: (_c = options.fs) !== null && _c !== void 0 ? _c : fs,
|
|
91
|
+
geteuid: (_d = options.geteuid) !== null && _d !== void 0 ? _d : (_a = process.geteuid) === null || _a === void 0 ? void 0 : _a.bind(process),
|
|
92
|
+
osTmpdir: (_e = options.osTmpdir) !== null && _e !== void 0 ? _e : os.tmpdir,
|
|
93
|
+
path: (_f = options.path) !== null && _f !== void 0 ? _f : path,
|
|
94
|
+
platform: (_g = options.platform) !== null && _g !== void 0 ? _g : process.platform,
|
|
95
|
+
windowsAcl: (_h = options.windowsAcl) !== null && _h !== void 0 ? _h : {}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function assertDirectoryStat(targetPath, stat) {
|
|
99
|
+
if (stat.isSymbolicLink()) {
|
|
100
|
+
throw new AgentStorePathError({
|
|
101
|
+
code: "symlink_refused",
|
|
102
|
+
message: `Refusing to use symlinked agent store path: ${targetPath}`,
|
|
103
|
+
failedPath: targetPath
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (!stat.isDirectory()) {
|
|
107
|
+
throw new AgentStorePathError({
|
|
108
|
+
code: "not_directory",
|
|
109
|
+
message: `Agent store path is not a directory: ${targetPath}`,
|
|
110
|
+
failedPath: targetPath
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function findTrustedDirectoryAncestor(targetPath, deps) {
|
|
115
|
+
var _a;
|
|
116
|
+
const segments = getPathSegments(deps.path.resolve(targetPath), deps.path);
|
|
117
|
+
let trusted = (_a = segments[0]) !== null && _a !== void 0 ? _a : deps.path.resolve(targetPath);
|
|
118
|
+
for (let i = 1; i < segments.length; i++) {
|
|
119
|
+
const segment = segments[i];
|
|
120
|
+
const stat = tryLstat(segment, deps.fs);
|
|
121
|
+
if (!stat) {
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
if (stat.isSymbolicLink()) {
|
|
125
|
+
throw new AgentStorePathError({
|
|
126
|
+
code: "symlink_refused",
|
|
127
|
+
message: `Refusing to use symlinked agent store path: ${segment}`,
|
|
128
|
+
failedPath: segment
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (!stat.isDirectory()) {
|
|
132
|
+
throw new AgentStorePathError({
|
|
133
|
+
code: "not_directory",
|
|
134
|
+
message: `Agent store path is not a directory: ${segment}`,
|
|
135
|
+
failedPath: segment
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
trusted = segment;
|
|
139
|
+
}
|
|
140
|
+
return trusted;
|
|
141
|
+
}
|
|
142
|
+
function assertRealDirectorySegment(targetPath, deps) {
|
|
143
|
+
const stat = tryLstat(targetPath, deps.fs);
|
|
144
|
+
if (!stat) {
|
|
145
|
+
throw new AgentStorePathError({
|
|
146
|
+
code: "not_directory",
|
|
147
|
+
message: `Trusted directory does not exist: ${targetPath}`,
|
|
148
|
+
failedPath: targetPath
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
assertDirectoryStat(targetPath, stat);
|
|
152
|
+
}
|
|
153
|
+
function ensureRealDirectorySegment(targetPath, deps) {
|
|
154
|
+
let stat = tryLstat(targetPath, deps.fs);
|
|
155
|
+
if (!stat) {
|
|
156
|
+
try {
|
|
157
|
+
deps.fs.mkdirSync(targetPath, { mode: PRIVATE_DIR_MODE });
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (!isNodeError(error) || error.code !== "EEXIST") {
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
stat = tryLstat(targetPath, deps.fs);
|
|
164
|
+
if (!stat) {
|
|
165
|
+
throw new AgentStorePathError({
|
|
166
|
+
code: "not_directory",
|
|
167
|
+
message: `Failed to create directory: ${targetPath}`,
|
|
168
|
+
failedPath: targetPath
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
assertDirectoryStat(targetPath, stat);
|
|
173
|
+
}
|
|
174
|
+
function getPathSegments(targetPath, pathModule) {
|
|
175
|
+
const root = pathModule.parse(targetPath).root;
|
|
176
|
+
const rest = targetPath.slice(root.length);
|
|
177
|
+
const parts = rest.split(/[\\/]+/).filter(Boolean);
|
|
178
|
+
const segments = [];
|
|
179
|
+
let current = root;
|
|
180
|
+
if (root) {
|
|
181
|
+
segments.push(root);
|
|
182
|
+
}
|
|
183
|
+
for (const part of parts) {
|
|
184
|
+
current = current === root ? pathModule.join(root, part) : pathModule.join(current, part);
|
|
185
|
+
segments.push(current);
|
|
186
|
+
}
|
|
187
|
+
return segments;
|
|
188
|
+
}
|
|
189
|
+
function tryLstat(targetPath, fsModule) {
|
|
190
|
+
try {
|
|
191
|
+
return fsModule.lstatSync(targetPath);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
if (isNodeError(err) && err.code === "ENOENT") {
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function isNodeError(err) {
|
|
200
|
+
return err instanceof Error && typeof err.code === "string";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ../agent-store-sync/dist/conflict-events.js
|
|
204
|
+
var CONFLICT_JOURNAL_ROTATE_BYTES = 4 * 1024 * 1024;
|
|
205
|
+
var O_NOFOLLOW_FLAG = fs2.constants.O_NOFOLLOW;
|
|
206
|
+
|
|
207
|
+
// ../agent-store-sync/dist/local-index.js
|
|
208
|
+
import * as fs4 from "node:fs";
|
|
209
|
+
import * as path3 from "node:path";
|
|
210
|
+
|
|
211
|
+
// ../agent-store-sync/dist/secure-open.js
|
|
212
|
+
import * as fs3 from "node:fs";
|
|
213
|
+
import * as path2 from "node:path";
|
|
214
|
+
function openSecureSqlitePath(resolvedPath, mode) {
|
|
215
|
+
const parentDir = path2.dirname(resolvedPath);
|
|
216
|
+
const parentDirFd = openParentDirectoryNoFollow(parentDir);
|
|
217
|
+
let cleanupParentFd = true;
|
|
218
|
+
try {
|
|
219
|
+
const parentStat = fs3.fstatSync(parentDirFd);
|
|
220
|
+
if (!parentStat.isDirectory()) {
|
|
221
|
+
throw new AgentStorePathError({
|
|
222
|
+
code: "symlink_refused",
|
|
223
|
+
message: `Refusing to open sqlite under non-directory parent: ${parentDir}`,
|
|
224
|
+
failedPath: parentDir
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
const parentIdentity = {
|
|
228
|
+
dev: parentStat.dev,
|
|
229
|
+
ino: parentStat.ino
|
|
230
|
+
};
|
|
231
|
+
assertParentDirInodeMatchesPath(parentIdentity, parentDir);
|
|
232
|
+
const leafFd = openRegularFileNoFollowCreate(resolvedPath, mode);
|
|
233
|
+
let leafIdentity;
|
|
234
|
+
try {
|
|
235
|
+
const leafStat = fs3.fstatSync(leafFd);
|
|
236
|
+
if (!leafStat.isFile()) {
|
|
237
|
+
throw new AgentStorePathError({
|
|
238
|
+
code: "symlink_refused",
|
|
239
|
+
message: `Refusing to open non-regular sqlite path: ${resolvedPath}`,
|
|
240
|
+
failedPath: resolvedPath
|
|
41
241
|
});
|
|
242
|
+
}
|
|
243
|
+
assertResolvedPathStillCanonical(resolvedPath);
|
|
244
|
+
leafIdentity = { dev: leafStat.dev, ino: leafStat.ino };
|
|
245
|
+
} finally {
|
|
246
|
+
fs3.closeSync(leafFd);
|
|
247
|
+
}
|
|
248
|
+
let verified = false;
|
|
249
|
+
let closed = false;
|
|
250
|
+
const closeParent = () => {
|
|
251
|
+
if (closed) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
closed = true;
|
|
255
|
+
fs3.closeSync(parentDirFd);
|
|
256
|
+
};
|
|
257
|
+
const verifyOpenedInode = () => {
|
|
258
|
+
if (verified) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
assertParentDirInodeMatchesPath(parentIdentity, parentDir);
|
|
262
|
+
assertLeafInodeUnchanged(resolvedPath, leafIdentity);
|
|
263
|
+
verified = true;
|
|
264
|
+
};
|
|
265
|
+
cleanupParentFd = false;
|
|
266
|
+
return {
|
|
267
|
+
sqlitePath: resolvedPath,
|
|
268
|
+
verifyOpenedInode,
|
|
269
|
+
close: closeParent
|
|
270
|
+
};
|
|
271
|
+
} finally {
|
|
272
|
+
if (cleanupParentFd) {
|
|
273
|
+
fs3.closeSync(parentDirFd);
|
|
42
274
|
}
|
|
275
|
+
}
|
|
43
276
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
const labels = lowered.split(".");
|
|
60
|
-
return labels.some(label => S3_OBJECT_LABELS.has(label));
|
|
61
|
-
}
|
|
62
|
-
const S3_OBJECT_LABELS = new Set(["s3", "s3-accelerate", "s3-fips"]);
|
|
63
|
-
/**
|
|
64
|
-
* Like {@link defaultPresignedUrlValidator} plus a case-insensitive
|
|
65
|
-
* exact-match check against `allowedHosts` (no subdomain wildcards).
|
|
66
|
-
*/
|
|
67
|
-
export function createHostAllowlistValidator(allowedHosts) {
|
|
68
|
-
const allowed = new Set(allowedHosts.map(host => host.toLowerCase()));
|
|
69
|
-
return (url) => {
|
|
70
|
-
defaultPresignedUrlValidator(url);
|
|
71
|
-
if (!allowed.has(url.hostname.toLowerCase())) {
|
|
72
|
-
throw new PresignedUrlRejectedError({
|
|
73
|
-
code: "host_not_allowed",
|
|
74
|
-
message: `Refused presigned URL whose host is not in the allowlist: ${url.hostname}`,
|
|
75
|
-
url: redactPresignedUrlString(url),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
};
|
|
277
|
+
function openRegularFileNoFollowCreate(filePath, mode) {
|
|
278
|
+
const flags = symlinkSafeOpenCreateFlags();
|
|
279
|
+
try {
|
|
280
|
+
return fs3.openSync(filePath, flags, mode);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (isNofollowSymlinkRefusal(error)) {
|
|
283
|
+
throw new AgentStorePathError({
|
|
284
|
+
code: "symlink_refused",
|
|
285
|
+
message: `Refusing to open symlinked path: ${filePath}`,
|
|
286
|
+
failedPath: filePath
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
79
291
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
292
|
+
function openParentDirectoryNoFollow(parentDir) {
|
|
293
|
+
const flags = symlinkSafeDirectoryOpenFlags();
|
|
294
|
+
try {
|
|
295
|
+
return fs3.openSync(parentDir, flags);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (isNofollowSymlinkRefusal(error) || isNodeError2(error) && error.code === "ENOTDIR" && isSymlink(parentDir)) {
|
|
298
|
+
throw new AgentStorePathError({
|
|
299
|
+
code: "symlink_refused",
|
|
300
|
+
message: `Refusing to open sqlite under symlinked parent: ${parentDir}`,
|
|
301
|
+
failedPath: parentDir
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function isSymlink(targetPath) {
|
|
308
|
+
try {
|
|
309
|
+
return fs3.lstatSync(targetPath).isSymbolicLink();
|
|
310
|
+
} catch (_a) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function assertResolvedPathStillCanonical(resolvedPath) {
|
|
315
|
+
let realpath;
|
|
316
|
+
try {
|
|
317
|
+
realpath = fs3.realpathSync.native(resolvedPath);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
throw new AgentStorePathError({
|
|
320
|
+
code: "symlink_refused",
|
|
321
|
+
message: `Unable to resolve realpath for ${resolvedPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
322
|
+
failedPath: resolvedPath
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
if (realpath !== resolvedPath) {
|
|
326
|
+
throw new AgentStorePathError({
|
|
327
|
+
code: "symlink_refused",
|
|
328
|
+
message: `Refusing path with symlinked parent: ${resolvedPath} -> ${realpath}`,
|
|
329
|
+
failedPath: resolvedPath
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function assertLeafInodeUnchanged(resolvedPath, expected) {
|
|
334
|
+
const flags = symlinkSafeReopenFlags();
|
|
335
|
+
let fd;
|
|
336
|
+
try {
|
|
337
|
+
fd = fs3.openSync(resolvedPath, flags);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
if (isNofollowSymlinkRefusal(error)) {
|
|
340
|
+
throw new AgentStorePathError({
|
|
341
|
+
code: "symlink_refused",
|
|
342
|
+
message: `Sqlite path became symlinked between open and verification: ${resolvedPath}`,
|
|
343
|
+
failedPath: resolvedPath
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const stat = fs3.fstatSync(fd);
|
|
350
|
+
assertResolvedPathStillCanonical(resolvedPath);
|
|
351
|
+
if (stat.dev !== expected.dev || stat.ino !== expected.ino) {
|
|
352
|
+
throw new AgentStorePathError({
|
|
353
|
+
code: "symlink_refused",
|
|
354
|
+
message: `Sqlite path inode drifted between open and verification (expected dev=${expected.dev} ino=${expected.ino}, got dev=${stat.dev} ino=${stat.ino}): ${resolvedPath}`,
|
|
355
|
+
failedPath: resolvedPath
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
} finally {
|
|
359
|
+
fs3.closeSync(fd);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function assertParentDirInodeMatchesPath(expected, parentDir) {
|
|
363
|
+
let pathStat;
|
|
364
|
+
try {
|
|
365
|
+
pathStat = fs3.lstatSync(parentDir);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
throw new AgentStorePathError({
|
|
368
|
+
code: "symlink_refused",
|
|
369
|
+
message: `Unable to lstat parent directory ${parentDir}: ${error instanceof Error ? error.message : String(error)}`,
|
|
370
|
+
failedPath: parentDir
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
if (pathStat.isSymbolicLink()) {
|
|
374
|
+
throw new AgentStorePathError({
|
|
375
|
+
code: "symlink_refused",
|
|
376
|
+
message: `Parent directory became symlinked between open and verification: ${parentDir}`,
|
|
377
|
+
failedPath: parentDir
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
if (pathStat.dev !== expected.dev || pathStat.ino !== expected.ino) {
|
|
381
|
+
throw new AgentStorePathError({
|
|
382
|
+
code: "symlink_refused",
|
|
383
|
+
message: `Parent directory inode drifted between open and verification (expected dev=${expected.dev} ino=${expected.ino}, got dev=${pathStat.dev} ino=${pathStat.ino}): ${parentDir}`,
|
|
384
|
+
failedPath: parentDir
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function symlinkSafeOpenCreateFlags() {
|
|
389
|
+
var _a;
|
|
390
|
+
const constants3 = fs3.constants;
|
|
391
|
+
let flags = (_a = constants3.O_RDWR) !== null && _a !== void 0 ? _a : 0;
|
|
392
|
+
if (typeof constants3.O_CREAT === "number") {
|
|
393
|
+
flags |= constants3.O_CREAT;
|
|
394
|
+
}
|
|
395
|
+
if (typeof constants3.O_NOFOLLOW === "number") {
|
|
396
|
+
flags |= constants3.O_NOFOLLOW;
|
|
397
|
+
}
|
|
398
|
+
if (typeof constants3.O_CLOEXEC === "number") {
|
|
399
|
+
flags |= constants3.O_CLOEXEC;
|
|
400
|
+
}
|
|
401
|
+
return flags;
|
|
402
|
+
}
|
|
403
|
+
function symlinkSafeReopenFlags() {
|
|
404
|
+
var _a;
|
|
405
|
+
const constants3 = fs3.constants;
|
|
406
|
+
let flags = (_a = constants3.O_RDONLY) !== null && _a !== void 0 ? _a : 0;
|
|
407
|
+
if (typeof constants3.O_NOFOLLOW === "number") {
|
|
408
|
+
flags |= constants3.O_NOFOLLOW;
|
|
409
|
+
}
|
|
410
|
+
if (typeof constants3.O_CLOEXEC === "number") {
|
|
411
|
+
flags |= constants3.O_CLOEXEC;
|
|
412
|
+
}
|
|
413
|
+
return flags;
|
|
414
|
+
}
|
|
415
|
+
function symlinkSafeDirectoryOpenFlags() {
|
|
416
|
+
var _a;
|
|
417
|
+
const constants3 = fs3.constants;
|
|
418
|
+
let flags = (_a = constants3.O_RDONLY) !== null && _a !== void 0 ? _a : 0;
|
|
419
|
+
if (typeof constants3.O_DIRECTORY === "number") {
|
|
420
|
+
flags |= constants3.O_DIRECTORY;
|
|
421
|
+
}
|
|
422
|
+
if (typeof constants3.O_NOFOLLOW === "number") {
|
|
423
|
+
flags |= constants3.O_NOFOLLOW;
|
|
424
|
+
}
|
|
425
|
+
if (typeof constants3.O_CLOEXEC === "number") {
|
|
426
|
+
flags |= constants3.O_CLOEXEC;
|
|
427
|
+
}
|
|
428
|
+
return flags;
|
|
429
|
+
}
|
|
430
|
+
function isNofollowSymlinkRefusal(error) {
|
|
431
|
+
if (!isNodeError2(error)) {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
if (error.code === "ELOOP" || error.code === "EMLINK") {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
if (error.code === "EPERM" && hasNofollowConstant()) {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
function hasNofollowConstant() {
|
|
443
|
+
return typeof fs3.constants.O_NOFOLLOW === "number";
|
|
444
|
+
}
|
|
445
|
+
function isNodeError2(error) {
|
|
446
|
+
return error instanceof Error && typeof error.code === "string";
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// ../agent-store-sync/dist/local-index.js
|
|
450
|
+
var CURRENT_SCHEMA_VERSION = 3;
|
|
451
|
+
var PRIVATE_FILE_MODE = 384;
|
|
452
|
+
var META_DELETION_ARMED = "deletion_armed";
|
|
453
|
+
var META_FILES_ROOT_DEV = "files_root_dev";
|
|
454
|
+
var META_FILES_ROOT_INO = "files_root_ino";
|
|
455
|
+
var LocalIndexError = class extends Error {
|
|
456
|
+
constructor(code, message) {
|
|
457
|
+
super(message);
|
|
458
|
+
this.code = code;
|
|
459
|
+
this.name = "LocalIndexError";
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
var LocalAgentStoreIndex = class _LocalAgentStoreIndex {
|
|
463
|
+
static open(indexPath, options) {
|
|
464
|
+
const resolvedPath = path3.resolve(indexPath);
|
|
465
|
+
const existing = _LocalAgentStoreIndex.openHandles.get(resolvedPath);
|
|
466
|
+
if (existing !== void 0 && !existing.closed) {
|
|
467
|
+
return existing;
|
|
468
|
+
}
|
|
469
|
+
assertNoSymlinkInPath(resolvedPath);
|
|
470
|
+
ensureSecureDirectoryChain(path3.dirname(resolvedPath));
|
|
471
|
+
assertNoSymlinkInPath(resolvedPath);
|
|
472
|
+
const opened = openSecureSqlitePath(resolvedPath, PRIVATE_FILE_MODE);
|
|
473
|
+
let db;
|
|
125
474
|
try {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
475
|
+
db = new options.sqlite(opened.sqlitePath);
|
|
476
|
+
opened.verifyOpenedInode();
|
|
477
|
+
restrictIndexFileModes(resolvedPath);
|
|
478
|
+
const index = new _LocalAgentStoreIndex(resolvedPath, db);
|
|
479
|
+
index.opened = opened;
|
|
480
|
+
index.migrate();
|
|
481
|
+
index.restrictFileModes();
|
|
482
|
+
_LocalAgentStoreIndex.openHandles.set(resolvedPath, index);
|
|
483
|
+
return index;
|
|
484
|
+
} catch (error) {
|
|
485
|
+
db === null || db === void 0 ? void 0 : db.close();
|
|
486
|
+
opened.close();
|
|
487
|
+
throw error;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
constructor(indexPath, db) {
|
|
491
|
+
this.indexPath = indexPath;
|
|
492
|
+
this.db = db;
|
|
493
|
+
this.closed = false;
|
|
494
|
+
}
|
|
495
|
+
close() {
|
|
496
|
+
var _a;
|
|
497
|
+
if (this.closed) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
this.db.close();
|
|
501
|
+
(_a = this.opened) === null || _a === void 0 ? void 0 : _a.close();
|
|
502
|
+
this.opened = void 0;
|
|
503
|
+
this.closed = true;
|
|
504
|
+
_LocalAgentStoreIndex.openHandles.delete(this.indexPath);
|
|
505
|
+
}
|
|
506
|
+
getFile(relPath) {
|
|
507
|
+
const row = this.db.prepare("SELECT rel_path, last_synced_sha, last_seen_etag, size, last_synced_ms, direction, state, tombstone_etag, deleted_at_ms FROM files WHERE rel_path = ?").get(relPath);
|
|
508
|
+
return row === void 0 ? void 0 : fileEntryFromRow(row);
|
|
509
|
+
}
|
|
510
|
+
listFiles() {
|
|
511
|
+
const rows = this.db.prepare("SELECT rel_path, last_synced_sha, last_seen_etag, size, last_synced_ms, direction, state, tombstone_etag, deleted_at_ms FROM files ORDER BY rel_path ASC").all();
|
|
512
|
+
return rows.map(fileEntryFromRow);
|
|
513
|
+
}
|
|
514
|
+
upsertFile(entry) {
|
|
515
|
+
var _a, _b;
|
|
516
|
+
const state = (_a = entry.state) !== null && _a !== void 0 ? _a : "live";
|
|
517
|
+
const tombstoneEtag = state === "tombstoned" ? entry.tombstoneEtag : null;
|
|
518
|
+
const deletedAtMs = state === "tombstoned" ? entry.deletedAtMs : null;
|
|
519
|
+
this.db.prepare(`
|
|
520
|
+
INSERT INTO files (
|
|
521
|
+
rel_path,
|
|
522
|
+
last_synced_sha,
|
|
523
|
+
last_seen_etag,
|
|
524
|
+
size,
|
|
525
|
+
last_synced_ms,
|
|
526
|
+
direction,
|
|
527
|
+
state,
|
|
528
|
+
tombstone_etag,
|
|
529
|
+
deleted_at_ms
|
|
530
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
531
|
+
ON CONFLICT(rel_path) DO UPDATE SET
|
|
532
|
+
last_synced_sha = excluded.last_synced_sha,
|
|
533
|
+
last_seen_etag = excluded.last_seen_etag,
|
|
534
|
+
size = excluded.size,
|
|
535
|
+
last_synced_ms = excluded.last_synced_ms,
|
|
536
|
+
direction = excluded.direction,
|
|
537
|
+
state = excluded.state,
|
|
538
|
+
tombstone_etag = excluded.tombstone_etag,
|
|
539
|
+
deleted_at_ms = excluded.deleted_at_ms
|
|
540
|
+
`).run(entry.relPath, entry.lastSyncedSha, (_b = entry.lastSeenEtag) !== null && _b !== void 0 ? _b : null, entry.size, entry.lastSyncedMs, entry.direction, state, tombstoneEtag !== null && tombstoneEtag !== void 0 ? tombstoneEtag : null, deletedAtMs !== null && deletedAtMs !== void 0 ? deletedAtMs : null);
|
|
541
|
+
this.restrictFileModes();
|
|
542
|
+
}
|
|
543
|
+
listPendingDeletes() {
|
|
544
|
+
const rows = this.db.prepare("SELECT rel_path, base_etag, requested_at_ms, mutation_id FROM pending_deletes ORDER BY rel_path ASC").all();
|
|
545
|
+
return rows.map((row) => {
|
|
546
|
+
var _a;
|
|
547
|
+
return {
|
|
548
|
+
relPath: row.rel_path,
|
|
549
|
+
baseEtag: row.base_etag,
|
|
550
|
+
requestedAtMs: row.requested_at_ms,
|
|
551
|
+
mutationId: (_a = row.mutation_id) !== null && _a !== void 0 ? _a : void 0
|
|
552
|
+
};
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
hasPendingDelete(relPath) {
|
|
556
|
+
const row = this.db.prepare("SELECT rel_path FROM pending_deletes WHERE rel_path = ?").get(relPath);
|
|
557
|
+
return row !== void 0;
|
|
558
|
+
}
|
|
559
|
+
upsertPendingDelete(entry) {
|
|
560
|
+
var _a;
|
|
561
|
+
this.db.prepare(`
|
|
562
|
+
INSERT INTO pending_deletes (
|
|
563
|
+
rel_path,
|
|
564
|
+
base_etag,
|
|
565
|
+
requested_at_ms,
|
|
566
|
+
mutation_id
|
|
567
|
+
) VALUES (?, ?, ?, ?)
|
|
568
|
+
ON CONFLICT(rel_path) DO UPDATE SET
|
|
569
|
+
base_etag = excluded.base_etag,
|
|
570
|
+
requested_at_ms = excluded.requested_at_ms,
|
|
571
|
+
mutation_id = excluded.mutation_id
|
|
572
|
+
`).run(entry.relPath, entry.baseEtag, entry.requestedAtMs, (_a = entry.mutationId) !== null && _a !== void 0 ? _a : null);
|
|
573
|
+
this.restrictFileModes();
|
|
574
|
+
}
|
|
575
|
+
removePendingDelete(relPath) {
|
|
576
|
+
const result = this.db.prepare("DELETE FROM pending_deletes WHERE rel_path = ?").run(relPath);
|
|
577
|
+
return result.changes > 0;
|
|
578
|
+
}
|
|
579
|
+
/** Recovery-mode escape hatch: drop every unflushed deletion intent. */
|
|
580
|
+
clearPendingDeletes() {
|
|
581
|
+
const result = this.db.prepare("DELETE FROM pending_deletes").run();
|
|
582
|
+
return result.changes;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Durable recovery step: unarm scan-based deletion, forget the recorded
|
|
586
|
+
* `files/` root identity, and drop every unflushed deletion intent in one
|
|
587
|
+
* shot. Called when the identity guard trips so a subsequent round starts
|
|
588
|
+
* from the legacy restore semantics until a clean round re-arms. Returns
|
|
589
|
+
* the number of pending deletes cleared.
|
|
590
|
+
*/
|
|
591
|
+
disarmDeletionAndClearPending() {
|
|
592
|
+
const disarm = this.db.transaction(() => {
|
|
593
|
+
this.db.prepare(`
|
|
594
|
+
INSERT INTO meta (key, value)
|
|
595
|
+
VALUES (?, ?)
|
|
596
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
597
|
+
`).run(META_DELETION_ARMED, "0");
|
|
598
|
+
this.db.prepare("DELETE FROM meta WHERE key = ?").run(META_FILES_ROOT_DEV);
|
|
599
|
+
this.db.prepare("DELETE FROM meta WHERE key = ?").run(META_FILES_ROOT_INO);
|
|
600
|
+
return this.db.prepare("DELETE FROM pending_deletes").run().changes;
|
|
601
|
+
});
|
|
602
|
+
const cleared = disarm();
|
|
603
|
+
this.restrictFileModes();
|
|
604
|
+
return cleared;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Arms scan-based deletion and records the `files/` root identity in one
|
|
608
|
+
* SQLite transaction so a crash cannot leave `deletion_armed=1` without
|
|
609
|
+
* both identity keys (which the next round would treat as compromised).
|
|
610
|
+
*/
|
|
611
|
+
armDeletionWithIdentity(identity) {
|
|
612
|
+
const arm = this.db.transaction(() => {
|
|
613
|
+
const upsert = this.db.prepare(`
|
|
614
|
+
INSERT INTO meta (key, value)
|
|
615
|
+
VALUES (?, ?)
|
|
616
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
617
|
+
`);
|
|
618
|
+
upsert.run(META_DELETION_ARMED, "1");
|
|
619
|
+
upsert.run(META_FILES_ROOT_DEV, String(identity.dev));
|
|
620
|
+
upsert.run(META_FILES_ROOT_INO, String(identity.ino));
|
|
621
|
+
});
|
|
622
|
+
arm();
|
|
623
|
+
this.restrictFileModes();
|
|
624
|
+
}
|
|
625
|
+
deleteFile(relPath) {
|
|
626
|
+
const result = this.db.prepare("DELETE FROM files WHERE rel_path = ?").run(relPath);
|
|
627
|
+
if (result.changes > 0) {
|
|
628
|
+
this.restrictFileModes();
|
|
629
|
+
}
|
|
630
|
+
return result.changes > 0;
|
|
631
|
+
}
|
|
632
|
+
getMeta(key) {
|
|
633
|
+
const row = this.db.prepare("SELECT value FROM meta WHERE key = ?").get(key);
|
|
634
|
+
return row === null || row === void 0 ? void 0 : row.value;
|
|
635
|
+
}
|
|
636
|
+
setMeta(entry) {
|
|
637
|
+
this.db.prepare(`
|
|
638
|
+
INSERT INTO meta (key, value)
|
|
639
|
+
VALUES (?, ?)
|
|
640
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
641
|
+
`).run(entry.key, entry.value);
|
|
642
|
+
this.restrictFileModes();
|
|
643
|
+
}
|
|
644
|
+
migrate() {
|
|
645
|
+
this.db.pragma("journal_mode = WAL");
|
|
646
|
+
this.db.pragma("synchronous = NORMAL");
|
|
647
|
+
this.db.pragma("busy_timeout = 5000");
|
|
648
|
+
this.db.exec(`
|
|
649
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
650
|
+
key TEXT PRIMARY KEY,
|
|
651
|
+
value TEXT NOT NULL
|
|
652
|
+
);
|
|
653
|
+
`);
|
|
654
|
+
const version = this.getMeta("schema_version");
|
|
655
|
+
if (version === void 0) {
|
|
656
|
+
this.createCurrentSchema();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const parsedVersion = Number(version);
|
|
660
|
+
if (!Number.isInteger(parsedVersion) || parsedVersion < 1 || parsedVersion > CURRENT_SCHEMA_VERSION) {
|
|
661
|
+
throw new LocalIndexError("unsupported_schema_version", `Unsupported agent store index schema version: ${version}`);
|
|
662
|
+
}
|
|
663
|
+
this.createFilesTableIfNeeded();
|
|
664
|
+
if (parsedVersion < 2) {
|
|
665
|
+
this.migrateV1ToV2();
|
|
666
|
+
}
|
|
667
|
+
if (parsedVersion < 3) {
|
|
668
|
+
this.migrateV2ToV3();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
createCurrentSchema() {
|
|
672
|
+
this.createFilesTableIfNeeded();
|
|
673
|
+
this.createPendingDeletesTableIfNeeded();
|
|
674
|
+
this.setMeta({
|
|
675
|
+
key: "schema_version",
|
|
676
|
+
value: String(CURRENT_SCHEMA_VERSION)
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
createFilesTableIfNeeded() {
|
|
680
|
+
this.db.exec(`
|
|
681
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
682
|
+
rel_path TEXT PRIMARY KEY,
|
|
683
|
+
last_synced_sha TEXT NOT NULL,
|
|
684
|
+
last_seen_etag TEXT,
|
|
685
|
+
size INTEGER NOT NULL,
|
|
686
|
+
last_synced_ms INTEGER NOT NULL,
|
|
687
|
+
direction TEXT NOT NULL CHECK(direction IN ('pulled', 'pushed')),
|
|
688
|
+
state TEXT NOT NULL DEFAULT 'live' CHECK(state IN ('live', 'tombstoned')),
|
|
689
|
+
tombstone_etag TEXT,
|
|
690
|
+
deleted_at_ms INTEGER
|
|
691
|
+
);
|
|
692
|
+
`);
|
|
693
|
+
}
|
|
694
|
+
createPendingDeletesTableIfNeeded() {
|
|
695
|
+
this.db.exec(`
|
|
696
|
+
CREATE TABLE IF NOT EXISTS pending_deletes (
|
|
697
|
+
rel_path TEXT PRIMARY KEY,
|
|
698
|
+
base_etag TEXT NOT NULL,
|
|
699
|
+
requested_at_ms INTEGER NOT NULL,
|
|
700
|
+
mutation_id TEXT
|
|
701
|
+
);
|
|
702
|
+
`);
|
|
703
|
+
}
|
|
704
|
+
migrateV1ToV2() {
|
|
705
|
+
const columns = this.db.prepare("PRAGMA table_info(files)").all();
|
|
706
|
+
if (columns.some((column) => column.name === "last_seen_sha")) {
|
|
707
|
+
this.db.exec("ALTER TABLE files RENAME COLUMN last_seen_sha TO last_synced_sha");
|
|
708
|
+
}
|
|
709
|
+
if (!columns.some((column) => column.name === "last_seen_etag")) {
|
|
710
|
+
this.db.exec("ALTER TABLE files ADD COLUMN last_seen_etag TEXT");
|
|
711
|
+
}
|
|
712
|
+
this.setMeta({
|
|
713
|
+
key: "schema_version",
|
|
714
|
+
value: "2"
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
migrateV2ToV3() {
|
|
718
|
+
const columns = this.db.prepare("PRAGMA table_info(files)").all();
|
|
719
|
+
if (!columns.some((column) => column.name === "state")) {
|
|
720
|
+
this.db.exec("ALTER TABLE files ADD COLUMN state TEXT NOT NULL DEFAULT 'live' CHECK(state IN ('live', 'tombstoned'))");
|
|
721
|
+
}
|
|
722
|
+
if (!columns.some((column) => column.name === "tombstone_etag")) {
|
|
723
|
+
this.db.exec("ALTER TABLE files ADD COLUMN tombstone_etag TEXT");
|
|
724
|
+
}
|
|
725
|
+
if (!columns.some((column) => column.name === "deleted_at_ms")) {
|
|
726
|
+
this.db.exec("ALTER TABLE files ADD COLUMN deleted_at_ms INTEGER");
|
|
727
|
+
}
|
|
728
|
+
this.createPendingDeletesTableIfNeeded();
|
|
729
|
+
this.setMeta({
|
|
730
|
+
key: "schema_version",
|
|
731
|
+
value: String(CURRENT_SCHEMA_VERSION)
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
restrictFileModes() {
|
|
735
|
+
restrictIndexFileModes(this.indexPath);
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
LocalAgentStoreIndex.openHandles = /* @__PURE__ */ new Map();
|
|
739
|
+
function fileEntryFromRow(row) {
|
|
740
|
+
var _a, _b, _c;
|
|
741
|
+
return {
|
|
742
|
+
relPath: row.rel_path,
|
|
743
|
+
lastSyncedSha: row.last_synced_sha,
|
|
744
|
+
lastSeenEtag: (_a = row.last_seen_etag) !== null && _a !== void 0 ? _a : void 0,
|
|
745
|
+
size: row.size,
|
|
746
|
+
lastSyncedMs: row.last_synced_ms,
|
|
747
|
+
direction: row.direction,
|
|
748
|
+
state: row.state,
|
|
749
|
+
tombstoneEtag: (_b = row.tombstone_etag) !== null && _b !== void 0 ? _b : void 0,
|
|
750
|
+
deletedAtMs: (_c = row.deleted_at_ms) !== null && _c !== void 0 ? _c : void 0
|
|
751
|
+
};
|
|
133
752
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
* (LocalStack) and still pin all other URLs to {@link BCS_AGENT_STORE_BUCKET_HOSTS}.
|
|
139
|
-
*/
|
|
140
|
-
export function createAgentStorePresignedUrlValidatorForBackend(backendUrl) {
|
|
141
|
-
if (isLocalAgentStoreBackendUrl(backendUrl)) {
|
|
142
|
-
return createLocalDevAgentStorePresignedUrlValidator();
|
|
753
|
+
function restrictIndexFileModes(indexPath) {
|
|
754
|
+
for (const filePath of [indexPath, `${indexPath}-wal`, `${indexPath}-shm`]) {
|
|
755
|
+
if (fs4.existsSync(filePath)) {
|
|
756
|
+
fs4.chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
143
757
|
}
|
|
144
|
-
|
|
145
|
-
return createHostAllowlistValidator(PLAYGROUND_AGENT_STORE_BUCKET_HOSTS);
|
|
146
|
-
}
|
|
147
|
-
return createBcsPresignedUrlValidator();
|
|
758
|
+
}
|
|
148
759
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
760
|
+
|
|
761
|
+
// ../agent-store-sync/dist/metrics-emitter.js
|
|
762
|
+
var NOOP_METRICS_EMITTER = Object.freeze({
|
|
763
|
+
onDiskFull() {
|
|
764
|
+
},
|
|
765
|
+
onSymlinkRefused() {
|
|
766
|
+
},
|
|
767
|
+
onNetworkError() {
|
|
768
|
+
},
|
|
769
|
+
onRoundCompleted() {
|
|
770
|
+
},
|
|
771
|
+
onError() {
|
|
772
|
+
},
|
|
773
|
+
onConflict() {
|
|
774
|
+
},
|
|
775
|
+
tokenMinted() {
|
|
776
|
+
},
|
|
777
|
+
tokenRefreshUnauthorized() {
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
// ../agent-store-sync/dist/metrics-aggregator.js
|
|
782
|
+
var DISK_FULL_REPEAT_WINDOW_MS = 5 * 60 * 1e3;
|
|
783
|
+
|
|
784
|
+
// ../agent-store-sync/dist/presigned-url.js
|
|
785
|
+
var PresignedUrlRejectedError = class extends Error {
|
|
786
|
+
constructor(options) {
|
|
787
|
+
super(options.message);
|
|
788
|
+
this.name = "PresignedUrlRejectedError";
|
|
789
|
+
this.code = options.code;
|
|
790
|
+
this.url = options.url;
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
function defaultPresignedUrlValidator(url) {
|
|
794
|
+
assertSafeAuthority(url);
|
|
795
|
+
if (url.protocol !== "https:") {
|
|
796
|
+
throw new PresignedUrlRejectedError({
|
|
797
|
+
code: "scheme_not_allowed",
|
|
798
|
+
message: `Refused presigned URL with non-https scheme: ${url.protocol}`,
|
|
799
|
+
url: redactPresignedUrlString(url)
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
const hostname = stripIPv6Brackets(url.hostname);
|
|
803
|
+
if (isLocalOrInternalHost(hostname)) {
|
|
804
|
+
throw new PresignedUrlRejectedError({
|
|
805
|
+
code: "private_host_not_allowed",
|
|
806
|
+
message: `Refused https presigned URL with private/internal host: ${hostname}`,
|
|
807
|
+
url: redactPresignedUrlString(url)
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
if (!isAwsS3Hostname(hostname)) {
|
|
811
|
+
throw new PresignedUrlRejectedError({
|
|
812
|
+
code: "host_not_allowed",
|
|
813
|
+
message: `Refused presigned URL whose host is not an AWS S3 endpoint: ${hostname}`,
|
|
814
|
+
url: redactPresignedUrlString(url)
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function isAwsS3Hostname(hostname) {
|
|
819
|
+
const lowered = hostname.toLowerCase();
|
|
820
|
+
if (lowered !== "amazonaws.com" && !lowered.endsWith(".amazonaws.com")) {
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
const labels = lowered.split(".");
|
|
824
|
+
return labels.some((label) => S3_OBJECT_LABELS.has(label));
|
|
825
|
+
}
|
|
826
|
+
var S3_OBJECT_LABELS = /* @__PURE__ */ new Set(["s3", "s3-accelerate", "s3-fips"]);
|
|
827
|
+
function createHostAllowlistValidator(allowedHosts) {
|
|
828
|
+
const allowed = new Set(allowedHosts.map((host) => host.toLowerCase()));
|
|
829
|
+
return (url) => {
|
|
830
|
+
defaultPresignedUrlValidator(url);
|
|
831
|
+
if (!allowed.has(url.hostname.toLowerCase())) {
|
|
832
|
+
throw new PresignedUrlRejectedError({
|
|
833
|
+
code: "host_not_allowed",
|
|
834
|
+
message: `Refused presigned URL whose host is not in the allowlist: ${url.hostname}`,
|
|
835
|
+
url: redactPresignedUrlString(url)
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
var BCS_AGENT_STORE_BUCKET_HOSTS = [
|
|
841
|
+
"agent-stores.s3.us-east-1.amazonaws.com",
|
|
842
|
+
"agent-stores.s3.amazonaws.com"
|
|
843
|
+
];
|
|
844
|
+
function createBcsPresignedUrlValidator() {
|
|
845
|
+
return createHostAllowlistValidator(BCS_AGENT_STORE_BUCKET_HOSTS);
|
|
159
846
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
847
|
+
function isLocalAgentStoreBackendUrl(backendUrl) {
|
|
848
|
+
const trimmed = backendUrl.trim();
|
|
849
|
+
if (trimmed === "") {
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
return trimmed.includes("localhost") || trimmed.includes("lclhst.build");
|
|
853
|
+
}
|
|
854
|
+
function createAgentStorePresignedUrlValidatorForBackend(backendUrl) {
|
|
855
|
+
return isLocalAgentStoreBackendUrl(backendUrl) ? createLocalDevAgentStorePresignedUrlValidator() : createBcsPresignedUrlValidator();
|
|
856
|
+
}
|
|
857
|
+
function createLocalDevAgentStorePresignedUrlValidator() {
|
|
858
|
+
const bcsValidator = createBcsPresignedUrlValidator();
|
|
859
|
+
return (url) => {
|
|
168
860
|
assertSafeAuthority(url);
|
|
169
861
|
const hostname = stripIPv6Brackets(url.hostname);
|
|
170
862
|
if (url.protocol === "http:" && isLoopbackHostName(hostname)) {
|
|
171
|
-
|
|
863
|
+
return;
|
|
172
864
|
}
|
|
173
|
-
|
|
865
|
+
bcsValidator(url);
|
|
866
|
+
};
|
|
174
867
|
}
|
|
175
868
|
function assertSafeAuthority(url) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Strip the query string and userinfo from a presigned URL so the AWS
|
|
186
|
-
* SigV4 signature never reaches error messages or telemetry.
|
|
187
|
-
*/
|
|
188
|
-
export function redactPresignedUrlString(url) {
|
|
189
|
-
try {
|
|
190
|
-
const parsed = typeof url === "string" ? new URL(url) : url;
|
|
191
|
-
return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
192
|
-
}
|
|
193
|
-
catch (_a) {
|
|
194
|
-
return "<unparseable-presigned-url>";
|
|
195
|
-
}
|
|
869
|
+
if (url.username !== "" || url.password !== "") {
|
|
870
|
+
throw new PresignedUrlRejectedError({
|
|
871
|
+
code: "userinfo_not_allowed",
|
|
872
|
+
message: "Refused presigned URL with embedded userinfo",
|
|
873
|
+
url: redactPresignedUrlString(url)
|
|
874
|
+
});
|
|
875
|
+
}
|
|
196
876
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
parsed = new URL(args.rawUrl);
|
|
206
|
-
}
|
|
207
|
-
catch (_a) {
|
|
208
|
-
throw new Error(`Refusing unparseable presigned URL for ${args.relPath}`);
|
|
209
|
-
}
|
|
210
|
-
try {
|
|
211
|
-
args.validatePresignedUrl(parsed);
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
throw new Error(`Refusing presigned URL for ${args.relPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
|
|
215
|
-
}
|
|
877
|
+
function redactPresignedUrlString(url) {
|
|
878
|
+
try {
|
|
879
|
+
const parsed = typeof url === "string" ? new URL(url) : url;
|
|
880
|
+
return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
881
|
+
} catch (_a) {
|
|
882
|
+
return "<unparseable-presigned-url>";
|
|
883
|
+
}
|
|
216
884
|
}
|
|
217
885
|
function isLoopbackHostName(hostname) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
lowered === "ip6-localhost" ||
|
|
221
|
-
lowered === "127.0.0.1" ||
|
|
222
|
-
lowered === "::1" ||
|
|
223
|
-
lowered === "0:0:0:0:0:0:0:1");
|
|
886
|
+
const lowered = hostname.toLowerCase();
|
|
887
|
+
return lowered === "localhost" || lowered === "ip6-localhost" || lowered === "127.0.0.1" || lowered === "::1" || lowered === "0:0:0:0:0:0:0:1";
|
|
224
888
|
}
|
|
225
889
|
function isLocalOrInternalHost(hostname) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// relies on transport-level allowlists for those.
|
|
240
|
-
return false;
|
|
890
|
+
if (hostname.length === 0) {
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
if (isLoopbackHostName(hostname)) {
|
|
894
|
+
return true;
|
|
895
|
+
}
|
|
896
|
+
if (isIPv4Address(hostname)) {
|
|
897
|
+
return isPrivateOrInternalIPv4(hostname);
|
|
898
|
+
}
|
|
899
|
+
if (isIPv6Address(hostname)) {
|
|
900
|
+
return isPrivateOrInternalIPv6(hostname);
|
|
901
|
+
}
|
|
902
|
+
return false;
|
|
241
903
|
}
|
|
242
904
|
function stripIPv6Brackets(hostname) {
|
|
243
|
-
|
|
244
|
-
? hostname.slice(1, -1)
|
|
245
|
-
: hostname;
|
|
905
|
+
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
246
906
|
}
|
|
247
907
|
function isIPv4Address(hostname) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
908
|
+
const parts = hostname.split(".");
|
|
909
|
+
if (parts.length !== 4) {
|
|
910
|
+
return false;
|
|
911
|
+
}
|
|
912
|
+
return parts.every((part) => {
|
|
913
|
+
if (part.length === 0 || part.length > 3) {
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
if (!/^\d+$/.test(part)) {
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
const n = Number(part);
|
|
920
|
+
return Number.isInteger(n) && n >= 0 && n <= 255;
|
|
921
|
+
});
|
|
262
922
|
}
|
|
263
923
|
function isPrivateOrInternalIPv4(hostname) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return true;
|
|
288
|
-
return false;
|
|
924
|
+
const [a, b] = hostname.split(".").map(Number);
|
|
925
|
+
if (a === void 0 || b === void 0) {
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
928
|
+
if (a === 0)
|
|
929
|
+
return true;
|
|
930
|
+
if (a === 10)
|
|
931
|
+
return true;
|
|
932
|
+
if (a === 127)
|
|
933
|
+
return true;
|
|
934
|
+
if (a === 169 && b === 254)
|
|
935
|
+
return true;
|
|
936
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
937
|
+
return true;
|
|
938
|
+
if (a === 192 && b === 168)
|
|
939
|
+
return true;
|
|
940
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
941
|
+
return true;
|
|
942
|
+
if (a >= 224 && a <= 239)
|
|
943
|
+
return true;
|
|
944
|
+
if (a >= 240)
|
|
945
|
+
return true;
|
|
946
|
+
return false;
|
|
289
947
|
}
|
|
290
948
|
function isIPv6Address(hostname) {
|
|
291
|
-
|
|
292
|
-
return hostname.includes(":");
|
|
949
|
+
return hostname.includes(":");
|
|
293
950
|
}
|
|
294
951
|
function isPrivateOrInternalIPv6(hostname) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return true;
|
|
317
|
-
}
|
|
318
|
-
return false;
|
|
952
|
+
const lowered = hostname.toLowerCase();
|
|
953
|
+
if (lowered === "::" || lowered === "::1") {
|
|
954
|
+
return true;
|
|
955
|
+
}
|
|
956
|
+
const mapped = extractIPv4MappedAddress(lowered);
|
|
957
|
+
if (mapped !== void 0) {
|
|
958
|
+
return isPrivateOrInternalIPv4(mapped);
|
|
959
|
+
}
|
|
960
|
+
if (lowered.startsWith("fc") || lowered.startsWith("fd")) {
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
if (lowered.startsWith("fe8") || lowered.startsWith("fe9")) {
|
|
964
|
+
return true;
|
|
965
|
+
}
|
|
966
|
+
if (lowered.startsWith("fea") || lowered.startsWith("feb")) {
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
if (lowered.startsWith("ff")) {
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
return false;
|
|
319
973
|
}
|
|
320
|
-
// Convert an IPv4-mapped IPv6 literal (`::ffff:a.b.c.d` or
|
|
321
|
-
// `::ffff:hhhh:hhhh`) to its dotted-quad form. Returns undefined otherwise.
|
|
322
974
|
function extractIPv4MappedAddress(hostname) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return undefined;
|
|
975
|
+
const dotted = hostname.match(/^(?:0:0:0:0:0|::):?ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i);
|
|
976
|
+
if (dotted) {
|
|
977
|
+
return dotted[1];
|
|
978
|
+
}
|
|
979
|
+
if (hostname.startsWith("::ffff:")) {
|
|
980
|
+
const tail = hostname.slice("::ffff:".length);
|
|
981
|
+
if (isIPv4Address(tail)) {
|
|
982
|
+
return tail;
|
|
983
|
+
}
|
|
984
|
+
const hex = tail.match(/^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
|
|
985
|
+
if (hex) {
|
|
986
|
+
const high = Number.parseInt(hex[1], 16);
|
|
987
|
+
const low = Number.parseInt(hex[2], 16);
|
|
988
|
+
if (Number.isFinite(high) && Number.isFinite(low) && high >= 0 && high <= 65535 && low >= 0 && low <= 65535) {
|
|
989
|
+
return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return void 0;
|
|
347
994
|
}
|
|
995
|
+
|
|
996
|
+
// ../agent-store-sync/dist/resilience-events.js
|
|
997
|
+
var NOOP_RESILIENCE_LISTENER = Object.freeze({
|
|
998
|
+
onDiskFull() {
|
|
999
|
+
},
|
|
1000
|
+
onSymlinkRefused() {
|
|
1001
|
+
},
|
|
1002
|
+
onNetworkError() {
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// ../agent-store-sync/dist/retrying-client.js
|
|
1007
|
+
var RETRYABLE_CONNECT_CODES = /* @__PURE__ */ new Set([
|
|
1008
|
+
Code.Unavailable,
|
|
1009
|
+
Code.ResourceExhausted,
|
|
1010
|
+
Code.Internal,
|
|
1011
|
+
Code.Aborted,
|
|
1012
|
+
Code.DeadlineExceeded
|
|
1013
|
+
]);
|
|
1014
|
+
|
|
1015
|
+
// ../agent-store-sync/dist/store-lock.js
|
|
1016
|
+
var STORE_LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
1017
|
+
|
|
1018
|
+
// ../agent-store-sync/dist/sync-engine.js
|
|
1019
|
+
var DEFAULT_MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
|
|
1020
|
+
var TOMBSTONE_ROW_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1021
|
+
export {
|
|
1022
|
+
createAgentStorePresignedUrlValidatorForBackend
|
|
1023
|
+
};
|