@better-update/cli 0.29.1 → 0.29.2

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.29.2";
38
38
 
39
39
  //#endregion
40
40
  //#region src/lib/interactive-mode.ts
@@ -19130,6 +19130,43 @@ const openFromDownload = (args) => {
19130
19130
  //#region src/lib/android-keystore.ts
19131
19131
  const DEFAULT_KEYSTORE_VALIDITY_DAYS = 1e4;
19132
19132
  const renderDistinguishedName = (params) => `CN=${params.commonName}, O=${params.organization}`;
19133
+ const FINGERPRINT_PATTERNS = {
19134
+ md5: /MD5:\s*(?<value>[0-9A-F:]+)/iu,
19135
+ sha1: /SHA-?1:\s*(?<value>[0-9A-F:]+)/iu,
19136
+ sha256: /SHA-?256:\s*(?<value>[0-9A-F:]+)/iu
19137
+ };
19138
+ /**
19139
+ * Parse certificate fingerprints out of `keytool -list -v` output. The fingerprint
19140
+ * labels (`MD5:`, `SHA1:`, `SHA256:`) are stable across keytool locales — only the
19141
+ * surrounding prose is translated — so label-anchored regexes are robust. MD5 is
19142
+ * absent on modern JDKs (dropped from `-v` output); that field stays `undefined`.
19143
+ * keytool already emits the canonical uppercase, colon-separated form the dashboard
19144
+ * displays verbatim, so no normalization is needed.
19145
+ */
19146
+ const parseKeystoreFingerprints = (output) => ({
19147
+ md5: output.match(FINGERPRINT_PATTERNS.md5)?.groups?.["value"],
19148
+ sha1: output.match(FINGERPRINT_PATTERNS.sha1)?.groups?.["value"],
19149
+ sha256: output.match(FINGERPRINT_PATTERNS.sha256)?.groups?.["value"]
19150
+ });
19151
+ /**
19152
+ * Run `keytool -list -v` against an on-disk keystore and extract its certificate
19153
+ * fingerprints. Only the store password is required to read a certificate. Used at
19154
+ * upload/generate time to populate the public, server-visible fingerprint metadata
19155
+ * the dashboard renders.
19156
+ */
19157
+ 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({
19158
+ step: "extract keystore fingerprints",
19159
+ exitCode: 1,
19160
+ message: `keytool -list failed to run (is the JDK installed?): ${String(cause)}`
19161
+ })), Effect.flatMap((output) => {
19162
+ const fingerprints = parseKeystoreFingerprints(output);
19163
+ if (fingerprints.sha1 === void 0 && fingerprints.sha256 === void 0) return Effect.fail(new BuildFailedError({
19164
+ step: "extract keystore fingerprints",
19165
+ exitCode: 1,
19166
+ message: "keytool produced no SHA-1/SHA-256 fingerprints — verify the key alias and keystore password"
19167
+ }));
19168
+ return Effect.succeed(fingerprints);
19169
+ }));
19133
19170
  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
19171
  commonName: input.commonName,
19135
19172
  organization: input.organization
@@ -19611,8 +19648,18 @@ const generateAndUploadKeystore = (api, input) => Effect.scoped(Effect.gen(funct
19611
19648
  ...compact({ validityDays: input.validityDays })
19612
19649
  });
19613
19650
  const bytes = yield* fs.readFile(keystorePath);
19651
+ const fingerprints = yield* extractKeystoreFingerprints({
19652
+ keystorePath,
19653
+ keyAlias: input.keyAlias,
19654
+ storePassword: input.storePassword
19655
+ });
19614
19656
  const session = yield* openVaultSessionInteractive(api);
19615
- const metadata = { keyAlias: input.keyAlias };
19657
+ const metadata = compact({
19658
+ keyAlias: input.keyAlias,
19659
+ md5Fingerprint: fingerprints.md5,
19660
+ sha1Fingerprint: fingerprints.sha1,
19661
+ sha256Fingerprint: fingerprints.sha256
19662
+ });
19616
19663
  const envelope = yield* sealForUpload({
19617
19664
  session,
19618
19665
  credentialType: "keystore",
@@ -25904,8 +25951,9 @@ const detectFormat = (bytes) => {
25904
25951
  /**
25905
25952
  * Validate keystore bytes + alias/passwords locally before sealing. Mirrors the
25906
25953
  * 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.
25954
+ * Fingerprints cannot be derived from the raw bytes; they are extracted separately
25955
+ * via keytool (`extractKeystoreFingerprints` in ./android-keystore) at upload/generate
25956
+ * time and attached to the public metadata.
25909
25957
  */
25910
25958
  const validateAndroidKeystore = (params) => Effect.gen(function* () {
25911
25959
  if (params.bytes.byteLength < 16) return yield* new CredentialValidationError({ message: "Keystore file too small" });
@@ -26135,12 +26183,23 @@ const uploadAndroidKeystore = (api, input, bytes) => Effect.gen(function* () {
26135
26183
  if (input.password === void 0) return yield* missing("password");
26136
26184
  if (!input.keyAlias) return yield* missing("key-alias");
26137
26185
  if (!input.keyPassword) return yield* missing("key-password");
26138
- const metadata = { keyAlias: (yield* validateAndroidKeystore({
26186
+ const parsed = yield* validateAndroidKeystore({
26139
26187
  bytes,
26140
26188
  keyAlias: input.keyAlias,
26141
26189
  keystorePassword: input.password,
26142
26190
  keyPassword: input.keyPassword
26143
- })).keyAlias };
26191
+ });
26192
+ const fingerprints = yield* extractKeystoreFingerprints({
26193
+ keystorePath: input.filePath,
26194
+ keyAlias: parsed.keyAlias,
26195
+ storePassword: input.password
26196
+ });
26197
+ const metadata = compact({
26198
+ keyAlias: parsed.keyAlias,
26199
+ md5Fingerprint: fingerprints.md5,
26200
+ sha1Fingerprint: fingerprints.sha1,
26201
+ sha256Fingerprint: fingerprints.sha256
26202
+ });
26144
26203
  const envelope = yield* sealForUpload({
26145
26204
  session: yield* openVaultSessionInteractive(api),
26146
26205
  credentialType: "keystore",