@indigoai-us/hq-cli 5.345.136 → 5.345.138
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/CHANGELOG.md +6 -0
- package/dist/commands/storage-offload.d.ts +110 -31
- package/dist/commands/storage-offload.js +239 -43
- package/dist/commands/storage.js +62 -7
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +577 -441
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/package.json +2 -2
package/dist/commands/storage.js
CHANGED
|
@@ -838,12 +838,16 @@ export function formatOffload(r) {
|
|
|
838
838
|
const CURRENT_ACTION_LABEL = {
|
|
839
839
|
"point-at-vault": "point at vault",
|
|
840
840
|
upload: "upload",
|
|
841
|
+
"upload+offload": "upload+offload",
|
|
841
842
|
skip: "skip",
|
|
842
843
|
};
|
|
843
844
|
/** One `--current` plan line: path, size, owning vault, sync state, action. */
|
|
844
845
|
function formatCurrentFile(f) {
|
|
845
846
|
const vault = f.vault === "company" ? `company ${f.company ?? "?"}` : "personal vault";
|
|
846
847
|
const parts = [f.path, formatBytes(f.bytes), vault, f.synced ? "synced" : "not synced", CURRENT_ACTION_LABEL[f.action]];
|
|
848
|
+
if (f.action === "upload+offload") {
|
|
849
|
+
parts.push(`(${f.bytes} bytes to the company ${f.company ?? "?"} vault at ${f.vault_key ?? f.path})`);
|
|
850
|
+
}
|
|
847
851
|
if (f.action === "skip" && f.reason)
|
|
848
852
|
parts.push(`(${f.reason})`);
|
|
849
853
|
return parts.join(" ");
|
|
@@ -1026,6 +1030,24 @@ async function buildOffloadDeps() {
|
|
|
1026
1030
|
return companiesFromMemberships(await res.json());
|
|
1027
1031
|
};
|
|
1028
1032
|
const journals = new Map();
|
|
1033
|
+
const journalSlug = (target) => target.vault === "company" ? target.slug : hqCloud.PERSONAL_VAULT_JOURNAL_SLUG;
|
|
1034
|
+
const companyVault = async (slug) => {
|
|
1035
|
+
const company = (await activeCompanies()).find((c) => c.slug === slug);
|
|
1036
|
+
if (!company)
|
|
1037
|
+
throw new Error(`You are not an active member of '${slug}'.`);
|
|
1038
|
+
const { vault } = await signedInVault();
|
|
1039
|
+
const entity = await vault.entity.get(company.uid);
|
|
1040
|
+
return { company, bucket: entity.bucketName ?? `hq-vault-cmp-${company.slug}` };
|
|
1041
|
+
};
|
|
1042
|
+
// Company upload, the ignore explainer and the cloud-only journal row ship
|
|
1043
|
+
// in a newer @indigoai-us/hq-cloud. Until the bundled one exports them,
|
|
1044
|
+
// company files over the sync limit report "update HQ sync".
|
|
1045
|
+
const cloud = hqCloud;
|
|
1046
|
+
const explainIgnore = cloud.createIgnoreExplainer;
|
|
1047
|
+
const uploadLarge = cloud.uploadLargeObjectVerified;
|
|
1048
|
+
const multipartClient = cloud.s3MultipartClient;
|
|
1049
|
+
const recordOffloaded = cloud.recordOffloadedObject;
|
|
1050
|
+
let explainer;
|
|
1029
1051
|
return {
|
|
1030
1052
|
hqRoot,
|
|
1031
1053
|
now: () => new Date(),
|
|
@@ -1047,9 +1069,9 @@ async function buildOffloadDeps() {
|
|
|
1047
1069
|
syncHoldsPlaceholders: () => hqCloud.CLOUD_PLACEHOLDER_HOLDS_ANY_SIBLING === true,
|
|
1048
1070
|
// What HQ sync last stored for a file, read from its local journal.
|
|
1049
1071
|
// Company journal keys are relative to the company folder; personal keys
|
|
1050
|
-
// are relative to the HQ root.
|
|
1072
|
+
// are relative to the HQ root. The only journal write is recordCloudOnly.
|
|
1051
1073
|
syncRecord: async (target) => {
|
|
1052
|
-
const slug = target
|
|
1074
|
+
const slug = journalSlug(target);
|
|
1053
1075
|
let journal = journals.get(slug);
|
|
1054
1076
|
if (!journal) {
|
|
1055
1077
|
journal = hqCloud.readJournal(slug);
|
|
@@ -1067,15 +1089,48 @@ async function buildOffloadDeps() {
|
|
|
1067
1089
|
const { bucket, credentials } = await personalVault();
|
|
1068
1090
|
return s3VaultObjectStore(bucket, new S3Client({ region: DEFAULT_COGNITO.region, credentials }));
|
|
1069
1091
|
}
|
|
1070
|
-
const company =
|
|
1071
|
-
if (!company)
|
|
1072
|
-
throw new Error(`You are not an active member of '${target.slug}'.`);
|
|
1092
|
+
const { company, bucket } = await companyVault(target.slug);
|
|
1073
1093
|
const { vault } = await signedInVault();
|
|
1074
1094
|
const vend = await vault.sts.vend({ companyUid: company.uid });
|
|
1075
|
-
const entity = await vault.entity.get(company.uid);
|
|
1076
|
-
const bucket = entity.bucketName ?? `hq-vault-cmp-${company.slug}`;
|
|
1077
1095
|
return s3VaultObjectStore(bucket, new S3Client({ region: DEFAULT_COGNITO.region, credentials: vend.credentials }));
|
|
1078
1096
|
},
|
|
1097
|
+
syncMaxFileBytes: typeof cloud.SYNC_MAX_FILE_BYTES === "number" ? cloud.SYNC_MAX_FILE_BYTES : undefined,
|
|
1098
|
+
syncExclusion: explainIgnore
|
|
1099
|
+
? (rel) => {
|
|
1100
|
+
explainer ??= explainIgnore(hqRoot);
|
|
1101
|
+
return explainer(rel);
|
|
1102
|
+
}
|
|
1103
|
+
: undefined,
|
|
1104
|
+
// Multipart upload into the company vault, through the caller's
|
|
1105
|
+
// membership-scoped /sts/vend credentials (re-vended before they expire,
|
|
1106
|
+
// since a multi-GB upload can outlast one STS session).
|
|
1107
|
+
openCompanyUploader: uploadLarge && multipartClient && recordOffloaded
|
|
1108
|
+
? async (slug) => {
|
|
1109
|
+
const { company, bucket } = await companyVault(slug);
|
|
1110
|
+
if (bucket.startsWith("hq-vault-prs-")) {
|
|
1111
|
+
throw new Error(`'${bucket}' is a personal vault, not the company vault for '${slug}'.`);
|
|
1112
|
+
}
|
|
1113
|
+
const credentials = refreshingVendedCredentials(async () => {
|
|
1114
|
+
const { vault } = await signedInVault();
|
|
1115
|
+
const vend = await vault.sts.vend({ companyUid: company.uid });
|
|
1116
|
+
return { ...vend.credentials, expiresAt: vend.expiresAt };
|
|
1117
|
+
});
|
|
1118
|
+
const client = multipartClient(new S3Client({ region: DEFAULT_COGNITO.region, credentials }), bucket);
|
|
1119
|
+
return {
|
|
1120
|
+
slug: company.slug,
|
|
1121
|
+
bucket,
|
|
1122
|
+
upload: ({ key, file, stateFile }) => uploadLarge({ client, key, filePath: file, stateFile }),
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
: undefined,
|
|
1126
|
+
recordCloudOnly: recordOffloaded
|
|
1127
|
+
? async (target, object) => {
|
|
1128
|
+
const slug = journalSlug(target);
|
|
1129
|
+
recordOffloaded({ journalSlug: slug, key: target.key, ...object });
|
|
1130
|
+
// The cached journal is stale now; reread it on the next lookup.
|
|
1131
|
+
journals.delete(slug);
|
|
1132
|
+
}
|
|
1133
|
+
: undefined,
|
|
1079
1134
|
};
|
|
1080
1135
|
}
|
|
1081
1136
|
/**
|