@indigoai-us/hq-cloud 6.11.10 → 6.11.12
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/sync-runner.d.ts +2 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +231 -52
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +330 -11
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/reindex.d.ts.map +1 -1
- package/dist/cli/reindex.js +16 -1
- package/dist/cli/reindex.js.map +1 -1
- package/dist/cli/reindex.test.js +39 -1
- package/dist/cli/reindex.test.js.map +1 -1
- package/dist/cli/rescue-classify-ordering.test.js +58 -0
- package/dist/cli/rescue-classify-ordering.test.js.map +1 -1
- package/dist/cli/rescue-core.js +229 -15
- package/dist/cli/rescue-core.js.map +1 -1
- package/dist/cli/rescue-exec-bit-preserve.test.d.ts +2 -0
- package/dist/cli/rescue-exec-bit-preserve.test.d.ts.map +1 -0
- package/dist/cli/rescue-exec-bit-preserve.test.js +169 -0
- package/dist/cli/rescue-exec-bit-preserve.test.js.map +1 -0
- package/dist/cli/share.d.ts +2 -1
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +100 -32
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +30 -0
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +28 -1
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +188 -59
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +487 -1
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/cognito-auth.d.ts.map +1 -1
- package/dist/cognito-auth.js +55 -10
- package/dist/cognito-auth.js.map +1 -1
- package/dist/cognito-auth.test.js +61 -0
- package/dist/cognito-auth.test.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +93 -6
- package/dist/journal.js.map +1 -1
- package/dist/journal.test.js +59 -0
- package/dist/journal.test.js.map +1 -1
- package/dist/machine-auth.test.js +60 -2
- package/dist/machine-auth.test.js.map +1 -1
- package/dist/object-io.d.ts +37 -1
- package/dist/object-io.d.ts.map +1 -1
- package/dist/object-io.js +148 -29
- package/dist/object-io.js.map +1 -1
- package/dist/object-io.test.js +121 -0
- package/dist/object-io.test.js.map +1 -1
- package/dist/operation-lock.d.ts +8 -8
- package/dist/operation-lock.d.ts.map +1 -1
- package/dist/operation-lock.js +99 -32
- package/dist/operation-lock.js.map +1 -1
- package/dist/operation-lock.test.js +51 -4
- package/dist/operation-lock.test.js.map +1 -1
- package/dist/personal-vault.d.ts +8 -0
- package/dist/personal-vault.d.ts.map +1 -1
- package/dist/personal-vault.js +17 -3
- package/dist/personal-vault.js.map +1 -1
- package/dist/personal-vault.test.js +34 -0
- package/dist/personal-vault.test.js.map +1 -1
- package/dist/prefix-coalesce.d.ts +20 -9
- package/dist/prefix-coalesce.d.ts.map +1 -1
- package/dist/prefix-coalesce.js +124 -28
- package/dist/prefix-coalesce.js.map +1 -1
- package/dist/prefix-coalesce.test.js +57 -2
- package/dist/prefix-coalesce.test.js.map +1 -1
- package/dist/remote-pull.d.ts +6 -1
- package/dist/remote-pull.d.ts.map +1 -1
- package/dist/remote-pull.js +62 -13
- package/dist/remote-pull.js.map +1 -1
- package/dist/remote-pull.test.js +189 -0
- package/dist/remote-pull.test.js.map +1 -1
- package/dist/s3.d.ts +2 -0
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +197 -116
- package/dist/s3.js.map +1 -1
- package/dist/s3.test.js +109 -0
- package/dist/s3.test.js.map +1 -1
- package/dist/scope-shrink.d.ts +3 -2
- package/dist/scope-shrink.d.ts.map +1 -1
- package/dist/scope-shrink.js +1 -1
- package/dist/scope-shrink.js.map +1 -1
- package/dist/skill-telemetry.d.ts +1 -1
- package/dist/skill-telemetry.d.ts.map +1 -1
- package/dist/skill-telemetry.js +69 -9
- package/dist/skill-telemetry.js.map +1 -1
- package/dist/skill-telemetry.test.js +86 -0
- package/dist/skill-telemetry.test.js.map +1 -1
- package/dist/sync/event-sync.d.ts +6 -0
- package/dist/sync/event-sync.d.ts.map +1 -1
- package/dist/sync/event-sync.js +34 -1
- package/dist/sync/event-sync.js.map +1 -1
- package/dist/sync/event-sync.test.js +73 -0
- package/dist/sync/event-sync.test.js.map +1 -1
- package/dist/sync/metrics.d.ts +17 -1
- package/dist/sync/metrics.d.ts.map +1 -1
- package/dist/sync/metrics.js +32 -1
- package/dist/sync/metrics.js.map +1 -1
- package/dist/sync/metrics.test.js +74 -1
- package/dist/sync/metrics.test.js.map +1 -1
- package/dist/sync/pull-scope.d.ts.map +1 -1
- package/dist/sync/pull-scope.js +15 -7
- package/dist/sync/pull-scope.js.map +1 -1
- package/dist/sync/push-receiver.d.ts +6 -5
- package/dist/sync/push-receiver.d.ts.map +1 -1
- package/dist/sync/push-receiver.js +13 -15
- package/dist/sync/push-receiver.js.map +1 -1
- package/dist/sync/push-receiver.test.js +36 -1
- package/dist/sync/push-receiver.test.js.map +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +59 -6
- package/dist/telemetry.js.map +1 -1
- package/dist/telemetry.test.js +74 -0
- package/dist/telemetry.test.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/watcher.d.ts +36 -0
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +152 -30
- package/dist/watcher.js.map +1 -1
- package/dist/watcher.test.js +103 -0
- package/dist/watcher.test.js.map +1 -1
- package/package.json +1 -1
- package/src/bin/sync-runner.test.ts +396 -11
- package/src/bin/sync-runner.ts +254 -52
- package/src/cli/reindex.test.ts +47 -1
- package/src/cli/reindex.ts +17 -1
- package/src/cli/rescue-classify-ordering.test.ts +61 -0
- package/src/cli/rescue-core.ts +261 -15
- package/src/cli/rescue-exec-bit-preserve.test.ts +187 -0
- package/src/cli/share.test.ts +38 -0
- package/src/cli/share.ts +103 -34
- package/src/cli/sync.test.ts +594 -1
- package/src/cli/sync.ts +229 -65
- package/src/cognito-auth.test.ts +77 -0
- package/src/cognito-auth.ts +73 -11
- package/src/index.ts +8 -0
- package/src/journal.test.ts +72 -0
- package/src/journal.ts +95 -8
- package/src/machine-auth.test.ts +64 -2
- package/src/object-io.test.ts +142 -0
- package/src/object-io.ts +182 -30
- package/src/operation-lock.test.ts +63 -4
- package/src/operation-lock.ts +99 -31
- package/src/personal-vault.test.ts +42 -0
- package/src/personal-vault.ts +18 -3
- package/src/prefix-coalesce.test.ts +71 -1
- package/src/prefix-coalesce.ts +155 -30
- package/src/remote-pull.test.ts +205 -0
- package/src/remote-pull.ts +77 -14
- package/src/s3.test.ts +126 -0
- package/src/s3.ts +237 -122
- package/src/scope-shrink.ts +6 -3
- package/src/skill-telemetry.test.ts +109 -0
- package/src/skill-telemetry.ts +82 -14
- package/src/sync/event-sync.test.ts +75 -0
- package/src/sync/event-sync.ts +54 -1
- package/src/sync/metrics.test.ts +81 -0
- package/src/sync/metrics.ts +59 -4
- package/src/sync/pull-scope.ts +23 -7
- package/src/sync/push-receiver.test.ts +38 -1
- package/src/sync/push-receiver.ts +15 -18
- package/src/telemetry.test.ts +85 -0
- package/src/telemetry.ts +69 -6
- package/src/types.ts +8 -0
- package/src/watcher.test.ts +117 -0
- package/src/watcher.ts +209 -33
package/src/cli/share.ts
CHANGED
|
@@ -46,7 +46,11 @@ import {
|
|
|
46
46
|
fetchCompanyTombstones,
|
|
47
47
|
type CompanyTombstone,
|
|
48
48
|
} from "./tombstones.js";
|
|
49
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
isCoveredByAny,
|
|
51
|
+
isDirInScope,
|
|
52
|
+
type ScopePrefixInput,
|
|
53
|
+
} from "../prefix-coalesce.js";
|
|
50
54
|
import {
|
|
51
55
|
buildConflictId,
|
|
52
56
|
buildConflictPath,
|
|
@@ -637,7 +641,7 @@ export interface ShareOptions {
|
|
|
637
641
|
* filter — full access. An empty array means "no granted prefixes" → every
|
|
638
642
|
* path is out of scope (mirrors the pull side's `isCoveredByAny([])`).
|
|
639
643
|
*/
|
|
640
|
-
prefixSet?:
|
|
644
|
+
prefixSet?: ScopePrefixInput[];
|
|
641
645
|
/**
|
|
642
646
|
* Pre-fetched FILE_TOMBSTONE map (POSIX key → tombstone) for the push-side
|
|
643
647
|
* delete-resync consult. When omitted, share() fetches it itself via
|
|
@@ -778,7 +782,7 @@ function isPreconditionFailed(err: unknown): boolean {
|
|
|
778
782
|
function wrapFilterWithScope(
|
|
779
783
|
underlying: (absPath: string, isDir?: boolean) => boolean,
|
|
780
784
|
syncRoot: string,
|
|
781
|
-
prefixSet: readonly
|
|
785
|
+
prefixSet: readonly ScopePrefixInput[],
|
|
782
786
|
onScopeExcluded: (rel: string) => void,
|
|
783
787
|
): (absPath: string, isDir?: boolean) => boolean {
|
|
784
788
|
return (absPath: string, isDir?: boolean) => {
|
|
@@ -1279,13 +1283,15 @@ export async function share(options: ShareOptions): Promise<ShareResult> {
|
|
|
1279
1283
|
|
|
1280
1284
|
let isFreshCollision = false;
|
|
1281
1285
|
let multipartConverged = false;
|
|
1282
|
-
if (!journalEntry && item.kind === "
|
|
1286
|
+
if (!journalEntry && item.kind === "symlink") {
|
|
1287
|
+
// A HEAD on an existing object does not expose the symlink target.
|
|
1288
|
+
// On a first sync, treat the existing remote as a fresh collision
|
|
1289
|
+
// instead of replacing it with the local link record.
|
|
1290
|
+
isFreshCollision = true;
|
|
1291
|
+
} else if (!journalEntry && item.kind === "file") {
|
|
1283
1292
|
// Single-part S3 PUT etag is MD5 of the body. Multipart uploads
|
|
1284
|
-
// produce
|
|
1285
|
-
//
|
|
1286
|
-
// prefix + target) isn't a pure byte mirror and would mis-
|
|
1287
|
-
// classify; symlink overwrites are rare and an audit pass after
|
|
1288
|
-
// the broader bug-cleanup wave can extend coverage if needed.
|
|
1293
|
+
// produce `<md5>-<partCount>`. Symlink records cannot be classified
|
|
1294
|
+
// from HEAD alone, so the branch above fails closed.
|
|
1289
1295
|
const remoteEtagNormalized = normalizeEtag(remoteMeta.etag);
|
|
1290
1296
|
const isMultipart = /-\d+$/.test(remoteEtagNormalized);
|
|
1291
1297
|
if (!isMultipart) {
|
|
@@ -1392,17 +1398,25 @@ export async function share(options: ShareOptions): Promise<ShareResult> {
|
|
|
1392
1398
|
machineId,
|
|
1393
1399
|
);
|
|
1394
1400
|
const conflictAbs = path.join(hqRoot, conflictRelative);
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1401
|
+
if (!isMaterializationPathStillContained(syncRoot, conflictAbs)) {
|
|
1402
|
+
emit({
|
|
1403
|
+
type: "error",
|
|
1404
|
+
path: relativePath,
|
|
1405
|
+
message: "conflict mirror skipped: local parent escaped the sync root",
|
|
1406
|
+
});
|
|
1407
|
+
} else {
|
|
1408
|
+
await downloadFile(ctx, relativePath, conflictAbs);
|
|
1409
|
+
appendConflictEntry(hqRoot, {
|
|
1410
|
+
id: buildConflictId(originalRelative, detectedAt),
|
|
1411
|
+
originalPath: originalRelative,
|
|
1412
|
+
conflictPath: conflictRelative,
|
|
1413
|
+
detectedAt,
|
|
1414
|
+
side: "push",
|
|
1415
|
+
machineId,
|
|
1416
|
+
localHash,
|
|
1417
|
+
remoteHash: normalizeEtag(remoteMeta.etag),
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1406
1420
|
} catch (mirrorErr) {
|
|
1407
1421
|
emit({
|
|
1408
1422
|
type: "error",
|
|
@@ -1539,20 +1553,28 @@ export async function share(options: ShareOptions): Promise<ShareResult> {
|
|
|
1539
1553
|
machineId,
|
|
1540
1554
|
);
|
|
1541
1555
|
const conflictAbs = path.join(hqRoot, conflictRelative);
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
+
if (!isMaterializationPathStillContained(syncRoot, conflictAbs)) {
|
|
1557
|
+
emit({
|
|
1558
|
+
type: "error",
|
|
1559
|
+
path: relativePath,
|
|
1560
|
+
message: "conflict mirror skipped: local parent escaped the sync root",
|
|
1561
|
+
});
|
|
1562
|
+
} else {
|
|
1563
|
+
await downloadFile(ctx, relativePath, conflictAbs);
|
|
1564
|
+
appendConflictEntry(hqRoot, {
|
|
1565
|
+
id: buildConflictId(originalRelative, detectedAt),
|
|
1566
|
+
originalPath: originalRelative,
|
|
1567
|
+
conflictPath: conflictRelative,
|
|
1568
|
+
detectedAt,
|
|
1569
|
+
side: "push",
|
|
1570
|
+
machineId,
|
|
1571
|
+
localHash,
|
|
1572
|
+
// remoteMeta (if any) predates the racing write that fired the
|
|
1573
|
+
// fence — record what we knew ("" when the key was believed
|
|
1574
|
+
// absent); the mirror file carries the authoritative remote bytes.
|
|
1575
|
+
remoteHash: remoteMeta ? normalizeEtag(remoteMeta.etag) : "",
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1556
1578
|
} catch (mirrorErr) {
|
|
1557
1579
|
emit({
|
|
1558
1580
|
type: "error",
|
|
@@ -2082,6 +2104,11 @@ function isWithin(parent: string, child: string): boolean {
|
|
|
2082
2104
|
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
2083
2105
|
}
|
|
2084
2106
|
|
|
2107
|
+
function isPathWithin(parent: string, child: string): boolean {
|
|
2108
|
+
const rel = path.relative(parent, child);
|
|
2109
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2085
2112
|
function realpathSafe(p: string): string {
|
|
2086
2113
|
try {
|
|
2087
2114
|
return fs.realpathSync.native(p);
|
|
@@ -2090,6 +2117,48 @@ function realpathSafe(p: string): string {
|
|
|
2090
2117
|
}
|
|
2091
2118
|
}
|
|
2092
2119
|
|
|
2120
|
+
function deepestExistingAncestor(start: string): string | null {
|
|
2121
|
+
let current = start;
|
|
2122
|
+
for (;;) {
|
|
2123
|
+
try {
|
|
2124
|
+
fs.lstatSync(current);
|
|
2125
|
+
return current;
|
|
2126
|
+
} catch (err: unknown) {
|
|
2127
|
+
const code =
|
|
2128
|
+
err && typeof err === "object" && "code" in err
|
|
2129
|
+
? (err as { code?: string }).code
|
|
2130
|
+
: undefined;
|
|
2131
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") return null;
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
const parent = path.dirname(current);
|
|
2135
|
+
if (parent === current) return null;
|
|
2136
|
+
current = parent;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
function isMaterializationPathStillContained(root: string, localPath: string): boolean {
|
|
2141
|
+
const resolvedRoot = path.resolve(root);
|
|
2142
|
+
const resolvedLocal = path.resolve(localPath);
|
|
2143
|
+
if (!isPathWithin(resolvedRoot, resolvedLocal)) return false;
|
|
2144
|
+
|
|
2145
|
+
let realRoot: string;
|
|
2146
|
+
try {
|
|
2147
|
+
realRoot = fs.realpathSync.native(resolvedRoot);
|
|
2148
|
+
} catch {
|
|
2149
|
+
return false;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
const existingAncestor = deepestExistingAncestor(path.dirname(resolvedLocal));
|
|
2153
|
+
if (existingAncestor === null) return false;
|
|
2154
|
+
try {
|
|
2155
|
+
const realAncestor = fs.realpathSync.native(existingAncestor);
|
|
2156
|
+
return isPathWithin(realRoot, realAncestor);
|
|
2157
|
+
} catch {
|
|
2158
|
+
return false;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2093
2162
|
/**
|
|
2094
2163
|
* Containment check tailored for symlinks. Canonicalizes the link's
|
|
2095
2164
|
* PARENT DIR (which is a real dir, not the link), then compares the
|