@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.
@@ -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. Read-only: offload never writes a journal.
1072
+ // are relative to the HQ root. The only journal write is recordCloudOnly.
1051
1073
  syncRecord: async (target) => {
1052
- const slug = target.vault === "company" ? target.slug : hqCloud.PERSONAL_VAULT_JOURNAL_SLUG;
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 = (await activeCompanies()).find((c) => c.slug === target.slug);
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
  /**