@better-update/cli 0.29.1 → 0.30.0

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/index.mjs CHANGED
@@ -34,7 +34,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
34
 
35
35
  //#endregion
36
36
  //#region package.json
37
- var version = "0.29.1";
37
+ var version = "0.30.0";
38
38
 
39
39
  //#endregion
40
40
  //#region src/lib/interactive-mode.ts
@@ -484,6 +484,7 @@ var AndroidUploadKeystore = class extends Schema.Class("AndroidUploadKeystore")(
484
484
  md5Fingerprint: Schema.NullOr(Schema.String),
485
485
  sha1Fingerprint: Schema.NullOr(Schema.String),
486
486
  sha256Fingerprint: Schema.NullOr(Schema.String),
487
+ keystoreType: Schema.NullOr(Schema.Literal("JKS", "PKCS12")),
487
488
  createdAt: DateTimeString,
488
489
  updatedAt: DateTimeString
489
490
  }) {};
@@ -498,7 +499,8 @@ const UploadAndroidUploadKeystoreBody = Schema.Struct({
498
499
  keyAlias: Schema.String.pipe(Schema.minLength(1), Schema.maxLength(200)),
499
500
  md5Fingerprint: Schema.optional(Schema.String.pipe(Schema.maxLength(200))),
500
501
  sha1Fingerprint: Schema.optional(Schema.String.pipe(Schema.maxLength(200))),
501
- sha256Fingerprint: Schema.optional(Schema.String.pipe(Schema.maxLength(200)))
502
+ sha256Fingerprint: Schema.optional(Schema.String.pipe(Schema.maxLength(200))),
503
+ keystoreType: Schema.optional(Schema.Literal("JKS", "PKCS12"))
502
504
  });
503
505
  const DeleteAndroidUploadKeystoreResult = DeletedResult;
504
506
  /** Encrypted envelope plus metadata; the CLI decrypts `ciphertext` to recover `{ keystoreBase64, keystorePassword, keyPassword }`. */
@@ -19130,6 +19132,43 @@ const openFromDownload = (args) => {
19130
19132
  //#region src/lib/android-keystore.ts
19131
19133
  const DEFAULT_KEYSTORE_VALIDITY_DAYS = 1e4;
19132
19134
  const renderDistinguishedName = (params) => `CN=${params.commonName}, O=${params.organization}`;
19135
+ const FINGERPRINT_PATTERNS = {
19136
+ md5: /MD5:\s*(?<value>[0-9A-F:]+)/iu,
19137
+ sha1: /SHA-?1:\s*(?<value>[0-9A-F:]+)/iu,
19138
+ sha256: /SHA-?256:\s*(?<value>[0-9A-F:]+)/iu
19139
+ };
19140
+ /**
19141
+ * Parse certificate fingerprints out of `keytool -list -v` output. The fingerprint
19142
+ * labels (`MD5:`, `SHA1:`, `SHA256:`) are stable across keytool locales — only the
19143
+ * surrounding prose is translated — so label-anchored regexes are robust. MD5 is
19144
+ * absent on modern JDKs (dropped from `-v` output); that field stays `undefined`.
19145
+ * keytool already emits the canonical uppercase, colon-separated form the dashboard
19146
+ * displays verbatim, so no normalization is needed.
19147
+ */
19148
+ const parseKeystoreFingerprints = (output) => ({
19149
+ md5: output.match(FINGERPRINT_PATTERNS.md5)?.groups?.["value"],
19150
+ sha1: output.match(FINGERPRINT_PATTERNS.sha1)?.groups?.["value"],
19151
+ sha256: output.match(FINGERPRINT_PATTERNS.sha256)?.groups?.["value"]
19152
+ });
19153
+ /**
19154
+ * Run `keytool -list -v` against an on-disk keystore and extract its certificate
19155
+ * fingerprints. Only the store password is required to read a certificate. Used at
19156
+ * upload/generate time to populate the public, server-visible fingerprint metadata
19157
+ * the dashboard renders.
19158
+ */
19159
+ const extractKeystoreFingerprints = (params) => Command.string(Command.make("keytool", "-list", "-v", "-keystore", params.keystorePath, "-alias", params.keyAlias, "-storepass", params.storePassword).pipe(Command.env({ LC_ALL: "C" }))).pipe(Effect.mapError((cause) => new BuildFailedError({
19160
+ step: "extract keystore fingerprints",
19161
+ exitCode: 1,
19162
+ message: `keytool -list failed to run (is the JDK installed?): ${String(cause)}`
19163
+ })), Effect.flatMap((output) => {
19164
+ const fingerprints = parseKeystoreFingerprints(output);
19165
+ if (fingerprints.sha1 === void 0 && fingerprints.sha256 === void 0) return Effect.fail(new BuildFailedError({
19166
+ step: "extract keystore fingerprints",
19167
+ exitCode: 1,
19168
+ message: "keytool produced no SHA-1/SHA-256 fingerprints — verify the key alias and keystore password"
19169
+ }));
19170
+ return Effect.succeed(fingerprints);
19171
+ }));
19133
19172
  const generateAndroidKeystore = (input) => Command.exitCode(Command.make("keytool", "-genkeypair", "-v", "-storetype", "JKS", "-keystore", input.outputPath, "-alias", input.keyAlias, "-keyalg", "RSA", "-keysize", "2048", "-validity", String(input.validityDays ?? DEFAULT_KEYSTORE_VALIDITY_DAYS), "-storepass", input.storePassword, "-keypass", input.keyPassword, "-dname", renderDistinguishedName({
19134
19173
  commonName: input.commonName,
19135
19174
  organization: input.organization
@@ -19611,8 +19650,19 @@ const generateAndUploadKeystore = (api, input) => Effect.scoped(Effect.gen(funct
19611
19650
  ...compact({ validityDays: input.validityDays })
19612
19651
  });
19613
19652
  const bytes = yield* fs.readFile(keystorePath);
19653
+ const fingerprints = yield* extractKeystoreFingerprints({
19654
+ keystorePath,
19655
+ keyAlias: input.keyAlias,
19656
+ storePassword: input.storePassword
19657
+ });
19614
19658
  const session = yield* openVaultSessionInteractive(api);
19615
- const metadata = { keyAlias: input.keyAlias };
19659
+ const metadata = compact({
19660
+ keyAlias: input.keyAlias,
19661
+ md5Fingerprint: fingerprints.md5,
19662
+ sha1Fingerprint: fingerprints.sha1,
19663
+ sha256Fingerprint: fingerprints.sha256,
19664
+ keystoreType: "JKS"
19665
+ });
19616
19666
  const envelope = yield* sealForUpload({
19617
19667
  session,
19618
19668
  credentialType: "keystore",
@@ -25904,8 +25954,9 @@ const detectFormat = (bytes) => {
25904
25954
  /**
25905
25955
  * Validate keystore bytes + alias/passwords locally before sealing. Mirrors the
25906
25956
  * old server check: magic-byte format detection + required-field validation.
25907
- * Fingerprints are not derived from the bytes (keytool never surfaced them here
25908
- * either) they remain optional, user-supplied metadata.
25957
+ * Fingerprints cannot be derived from the raw bytes; they are extracted separately
25958
+ * via keytool (`extractKeystoreFingerprints` in ./android-keystore) at upload/generate
25959
+ * time and attached to the public metadata.
25909
25960
  */
25910
25961
  const validateAndroidKeystore = (params) => Effect.gen(function* () {
25911
25962
  if (params.bytes.byteLength < 16) return yield* new CredentialValidationError({ message: "Keystore file too small" });
@@ -26135,12 +26186,24 @@ const uploadAndroidKeystore = (api, input, bytes) => Effect.gen(function* () {
26135
26186
  if (input.password === void 0) return yield* missing("password");
26136
26187
  if (!input.keyAlias) return yield* missing("key-alias");
26137
26188
  if (!input.keyPassword) return yield* missing("key-password");
26138
- const metadata = { keyAlias: (yield* validateAndroidKeystore({
26189
+ const parsed = yield* validateAndroidKeystore({
26139
26190
  bytes,
26140
26191
  keyAlias: input.keyAlias,
26141
26192
  keystorePassword: input.password,
26142
26193
  keyPassword: input.keyPassword
26143
- })).keyAlias };
26194
+ });
26195
+ const fingerprints = yield* extractKeystoreFingerprints({
26196
+ keystorePath: input.filePath,
26197
+ keyAlias: parsed.keyAlias,
26198
+ storePassword: input.password
26199
+ });
26200
+ const metadata = compact({
26201
+ keyAlias: parsed.keyAlias,
26202
+ md5Fingerprint: fingerprints.md5,
26203
+ sha1Fingerprint: fingerprints.sha1,
26204
+ sha256Fingerprint: fingerprints.sha256,
26205
+ keystoreType: parsed.format
26206
+ });
26144
26207
  const envelope = yield* sealForUpload({
26145
26208
  session: yield* openVaultSessionInteractive(api),
26146
26209
  credentialType: "keystore",