@ait-co/console-cli 0.1.15 → 0.1.16

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/cli.mjs CHANGED
@@ -1017,13 +1017,37 @@ var AitBundleError = class extends Error {
1017
1017
  this.reason = args.reason;
1018
1018
  }
1019
1019
  };
1020
+ const AIT_MAGIC = new Uint8Array([
1021
+ 65,
1022
+ 73,
1023
+ 84,
1024
+ 66,
1025
+ 85,
1026
+ 78,
1027
+ 68,
1028
+ 76
1029
+ ]);
1030
+ const ZIP_MAGIC = new Uint8Array([
1031
+ 80,
1032
+ 75,
1033
+ 3,
1034
+ 4
1035
+ ]);
1036
+ function startsWith(bytes, prefix) {
1037
+ if (bytes.length < prefix.length) return false;
1038
+ for (let i = 0; i < prefix.length; i++) if (bytes[i] !== prefix[i]) return false;
1039
+ return true;
1040
+ }
1041
+ function detectBundleFormat(bytes) {
1042
+ if (startsWith(bytes, AIT_MAGIC)) return "ait";
1043
+ if (startsWith(bytes, ZIP_MAGIC)) return "zip";
1044
+ return "unknown";
1045
+ }
1020
1046
  /**
1021
- * Read the `.ait` at `path`, extract `app.json`, and pull out
1022
- * `_metadata.deploymentId`. Returns both the id and the raw bytes so the
1023
- * caller can forward them to the S3 upload without re-reading the file.
1024
- *
1025
- * Errors are all surfaced as `AitBundleError` with a structured `reason`
1026
- * so the command layer can render a typed `--json` failure.
1047
+ * Read the `.ait` at `path` and pull out `deploymentId`, auto-detecting
1048
+ * whether the file is the modern AIT header format or a legacy plain
1049
+ * zip. Returns the raw bytes so the caller can forward them to S3
1050
+ * without re-reading the file.
1027
1051
  */
1028
1052
  async function readAitBundle(path) {
1029
1053
  let buf;
@@ -1037,16 +1061,109 @@ async function readAitBundle(path) {
1037
1061
  });
1038
1062
  }
1039
1063
  const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
1064
+ const { deploymentId, format } = deploymentIdFromBundleBytes(bytes, path);
1040
1065
  return {
1041
- deploymentId: deploymentIdFromBundleBytes(bytes, path),
1042
- bytes
1066
+ deploymentId,
1067
+ bytes,
1068
+ format
1043
1069
  };
1044
1070
  }
1045
1071
  /**
1046
- * Pure helper split out so tests can feed raw zip bytes without a tmp
1047
- * file. Throws `AitBundleError` on any parse failure.
1072
+ * Pure helper split out so tests can feed raw bytes without a tmp file.
1073
+ * Throws `AitBundleError` on any parse failure. Returns the detected
1074
+ * format so callers that want to log it can.
1048
1075
  */
1049
1076
  function deploymentIdFromBundleBytes(bytes, pathForError) {
1077
+ const format = detectBundleFormat(bytes);
1078
+ if (format === "ait") return {
1079
+ deploymentId: deploymentIdFromAitHeader(bytes, pathForError),
1080
+ format
1081
+ };
1082
+ if (format === "zip") return {
1083
+ deploymentId: deploymentIdFromLegacyZip(bytes, pathForError),
1084
+ format
1085
+ };
1086
+ throw new AitBundleError({
1087
+ path: pathForError,
1088
+ reason: "unrecognized-format",
1089
+ message: "bundle does not start with AITBUNDL or PK magic bytes — not a valid .ait or legacy zip bundle"
1090
+ });
1091
+ }
1092
+ const AIT_MAGIC_SIZE = 8;
1093
+ const AIT_VERSION_SIZE = 4;
1094
+ const AIT_HEADER_SIZE = AIT_MAGIC_SIZE + AIT_VERSION_SIZE + 8;
1095
+ function deploymentIdFromAitHeader(bytes, pathForError) {
1096
+ if (bytes.length < AIT_HEADER_SIZE) throw new AitBundleError({
1097
+ path: pathForError,
1098
+ reason: "invalid-ait",
1099
+ message: "buffer too small to be a valid AIT file"
1100
+ });
1101
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1102
+ const bundleLen = Number(view.getBigUint64(AIT_MAGIC_SIZE + AIT_VERSION_SIZE, false));
1103
+ if (!Number.isFinite(bundleLen) || bundleLen <= 0) throw new AitBundleError({
1104
+ path: pathForError,
1105
+ reason: "invalid-ait",
1106
+ message: `AIT bundle length is invalid (${bundleLen})`
1107
+ });
1108
+ const bundleStart = AIT_HEADER_SIZE;
1109
+ const bundleEnd = bundleStart + bundleLen;
1110
+ if (bytes.length < bundleEnd) throw new AitBundleError({
1111
+ path: pathForError,
1112
+ reason: "invalid-ait",
1113
+ message: "unexpected end of buffer reading AIT bundle protobuf"
1114
+ });
1115
+ const deploymentId = readProtobufStringFields(bytes.subarray(bundleStart, bundleEnd), [2, 3]).get(2);
1116
+ if (typeof deploymentId !== "string" || deploymentId === "") throw new AitBundleError({
1117
+ path: pathForError,
1118
+ reason: "missing-deployment-id",
1119
+ message: "AIT bundle protobuf is missing deploymentId (field 2)"
1120
+ });
1121
+ return deploymentId;
1122
+ }
1123
+ function readProtobufStringFields(bytes, wantedFieldNumbers) {
1124
+ const wanted = new Set(wantedFieldNumbers);
1125
+ const out = /* @__PURE__ */ new Map();
1126
+ const decoder = new TextDecoder("utf-8", { fatal: false });
1127
+ let offset = 0;
1128
+ while (offset < bytes.length) {
1129
+ const { value: tag, next } = readVarint(bytes, offset);
1130
+ offset = next;
1131
+ const fieldNumber = Number(tag >> 3n);
1132
+ const wireType = Number(tag & 7n);
1133
+ if (wireType === 0) offset = readVarint(bytes, offset).next;
1134
+ else if (wireType === 1) offset += 8;
1135
+ else if (wireType === 2) {
1136
+ const { value: len, next: afterLen } = readVarint(bytes, offset);
1137
+ offset = afterLen;
1138
+ const payloadEnd = offset + Number(len);
1139
+ if (payloadEnd > bytes.length) break;
1140
+ if (wanted.has(fieldNumber)) out.set(fieldNumber, decoder.decode(bytes.subarray(offset, payloadEnd)));
1141
+ offset = payloadEnd;
1142
+ } else if (wireType === 5) offset += 4;
1143
+ else break;
1144
+ }
1145
+ return out;
1146
+ }
1147
+ function readVarint(bytes, start) {
1148
+ let value = 0n;
1149
+ let shift = 0n;
1150
+ let i = start;
1151
+ for (let n = 0; n < 10 && i < bytes.length; n++, i++) {
1152
+ const byte = bytes[i];
1153
+ if (byte === void 0) break;
1154
+ value |= BigInt(byte & 127) << shift;
1155
+ if ((byte & 128) === 0) return {
1156
+ value,
1157
+ next: i + 1
1158
+ };
1159
+ shift += 7n;
1160
+ }
1161
+ return {
1162
+ value,
1163
+ next: i
1164
+ };
1165
+ }
1166
+ function deploymentIdFromLegacyZip(bytes, pathForError) {
1050
1167
  let entries;
1051
1168
  try {
1052
1169
  entries = unzipSync(bytes, { filter: (file) => file.name === "app.json" });
@@ -1195,6 +1312,7 @@ async function runDeploy(args, deps = {}) {
1195
1312
  workspaceId,
1196
1313
  appId,
1197
1314
  deploymentId,
1315
+ bundleFormat: bundleInfo.format,
1198
1316
  bytes: bundleInfo.bytes.byteLength,
1199
1317
  steps,
1200
1318
  memo: memo ?? null,
@@ -1279,6 +1397,7 @@ async function runDeploy(args, deps = {}) {
1279
1397
  workspaceId,
1280
1398
  appId,
1281
1399
  deploymentId,
1400
+ bundleFormat: bundleInfo.format,
1282
1401
  uploaded,
1283
1402
  reviewed,
1284
1403
  released: release,
@@ -5410,7 +5529,7 @@ function resolveVersion() {
5410
5529
  if (typeof injected === "string" && injected.length > 0) return injected;
5411
5530
  } catch {}
5412
5531
  try {
5413
- return "0.1.15";
5532
+ return "0.1.16";
5414
5533
  } catch {}
5415
5534
  return "0.0.0-dev";
5416
5535
  }