whydid 1.0.0 → 1.0.1

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.
@@ -5,13 +5,13 @@ import { HttpLicenseApiClient } from "./license-client.js";
5
5
  import { createLicenseStore } from "./license-store.js";
6
6
  import { getMachineId } from "./machine-id.js";
7
7
  /**
8
- * PLACEHOLDER until license-server's real production keypair is generated
9
- * (license-server/scripts/generate-keypair.ts) — replace with the real
10
- * public key before this ever ships to a real customer. A build/release
11
- * check should refuse to publish while this constant is unchanged; add
12
- * that check as part of actual deployment, not covered by this plan.
8
+ * The real production Ed25519 public key, matching the private key held
9
+ * only in license-server's deployed .env (LICENSE_ED25519_PRIVATE_KEY) —
10
+ * generated once via license-server/scripts/generate-keypair.ts. Every
11
+ * signed license this public key verifies was signed by that exact
12
+ * deployment; swapping it invalidates every previously-issued license.
13
13
  */
14
- export const PUBLIC_KEY_DER_BASE64 = process.env.WHYDID_LICENSE_PUBLIC_KEY ?? "REPLACE_WITH_REAL_PRODUCTION_PUBLIC_KEY";
14
+ export const PUBLIC_KEY_DER_BASE64 = process.env.WHYDID_LICENSE_PUBLIC_KEY ?? "MCowBQYDK2VwAyEA2CpLqf2+q26q6q1k77bbJZYvQ1otG4E6iY19e+KNogc=";
15
15
  export const LICENSE_API_URL = process.env.WHYDID_LICENSE_API_URL ?? "https://license.whydid.dev";
16
16
  export const PRICING_URL = "https://whydid.dev/pricing";
17
17
  function globalLicenseDir() {
@@ -3,6 +3,24 @@ import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:
3
3
  import { dirname, join } from "node:path";
4
4
  const SERVICE_NAME = "whydid-license";
5
5
  const ACCOUNT_NAME = "license";
6
+ async function loadRealKeytar() {
7
+ return import("@napi-rs/keyring/keytar.js");
8
+ }
9
+ /**
10
+ * The OS keychain prompt (e.g. macOS's "wants to use your confidential
11
+ * information") asks for the user's own Mac login password, not anything
12
+ * whydid-specific — but denying/canceling it throws a raw native error
13
+ * ("User canceled the operation.") straight out of @napi-rs/keyring. Left
14
+ * unwrapped, that string reaches the user verbatim with no context. This
15
+ * wraps ANY keychain-access failure (denied, canceled, or an unexpected
16
+ * platform error) in one clear, actionable message.
17
+ */
18
+ function wrapKeychainError(err) {
19
+ const raw = err instanceof Error ? err.message : String(err);
20
+ return new Error(`whydid couldn't access your saved license in the system keychain (${raw}). ` +
21
+ `This is asking for your Mac's own login password, not a whydid password — ` +
22
+ `run the command again and click "Allow" (or "Always Allow") when prompted.`);
23
+ }
6
24
  /**
7
25
  * OS keychain (macOS Keychain / Windows Credential Manager / Linux Secret
8
26
  * Service), via @napi-rs/keyring's keytar-compatible API — the maintained
@@ -12,13 +30,28 @@ const ACCOUNT_NAME = "license";
12
30
  * this platform/architecture.
13
31
  */
14
32
  export class KeychainLicenseStore {
33
+ loadKeytar;
34
+ constructor(loadKeytar = loadRealKeytar) {
35
+ this.loadKeytar = loadKeytar;
36
+ }
15
37
  async save(signed) {
16
- const { setPassword } = await import("@napi-rs/keyring/keytar.js");
17
- await setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(signed));
38
+ const { setPassword } = await this.loadKeytar();
39
+ try {
40
+ await setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(signed));
41
+ }
42
+ catch (err) {
43
+ throw wrapKeychainError(err);
44
+ }
18
45
  }
19
46
  async load() {
20
- const { getPassword } = await import("@napi-rs/keyring/keytar.js");
21
- const raw = await getPassword(SERVICE_NAME, ACCOUNT_NAME);
47
+ const { getPassword } = await this.loadKeytar();
48
+ let raw;
49
+ try {
50
+ raw = await getPassword(SERVICE_NAME, ACCOUNT_NAME);
51
+ }
52
+ catch (err) {
53
+ throw wrapKeychainError(err);
54
+ }
22
55
  if (!raw)
23
56
  return null;
24
57
  try {
@@ -29,7 +62,7 @@ export class KeychainLicenseStore {
29
62
  }
30
63
  }
31
64
  async clear() {
32
- const { deletePassword } = await import("@napi-rs/keyring/keytar.js");
65
+ const { deletePassword } = await this.loadKeytar();
33
66
  await deletePassword(SERVICE_NAME, ACCOUNT_NAME).catch(() => false);
34
67
  }
35
68
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whydid",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "description": "A local-first AI coding time machine — captures meaningful changes, reasoning, and session context so you can see what happened across your coding sessions, even days later.",