@vault77/summon 2.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.
@@ -0,0 +1,86 @@
1
+ import keytar from "keytar";
2
+ import { KeychainError } from "../lib/errors.js";
3
+ import { logger } from "./logger.js";
4
+
5
+ const SERVICE = "summonTheWarlord";
6
+ const ACCOUNT = "wallet-private-key";
7
+
8
+ /**
9
+ * Normalize a pasted secret by coercing to string and trimming whitespace/newlines.
10
+ * @param {any} secret
11
+ * @returns {string}
12
+ */
13
+ function normalizeSecret(secret) {
14
+ return String(secret ?? "").trim();
15
+ }
16
+
17
+ /**
18
+ * Stores the Solana private key in the macOS Keychain.
19
+ * Accepts Base58 or a JSON array string; we store it as-is.
20
+ * @param {string} secret - The private key string.
21
+ */
22
+ export async function storePrivateKey(secret) {
23
+ const normalized = normalizeSecret(secret);
24
+ if (!normalized) {
25
+ throw new KeychainError("No private key provided. Paste your Base58 string or JSON array.");
26
+ }
27
+ try {
28
+ await keytar.setPassword(SERVICE, ACCOUNT, normalized);
29
+ console.log("🔐 Private key securely stored in macOS Keychain.");
30
+ } catch (err) {
31
+ logger.error("Failed to store private key.", { error: err?.message });
32
+ throw new KeychainError("Failed to store private key in Keychain.", { cause: err });
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Retrieves the private key from the macOS Keychain.
38
+ * Throws if not found.
39
+ * @returns {Promise<string>}
40
+ */
41
+ export async function getPrivateKey() {
42
+ try {
43
+ const key = await keytar.getPassword(SERVICE, ACCOUNT);
44
+ if (!key) {
45
+ throw new KeychainError("Private key not found. Run `summon keychain store` to save it.");
46
+ }
47
+ return key.trim();
48
+ } catch (err) {
49
+ if (err instanceof KeychainError) throw err;
50
+ logger.error("Failed to read private key from Keychain.", { error: err?.message });
51
+ throw new KeychainError("Failed to read private key from Keychain.", { cause: err });
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Returns true if a private key exists in the Keychain.
57
+ * @returns {Promise<boolean>}
58
+ */
59
+ export async function hasPrivateKey() {
60
+ try {
61
+ const key = await keytar.getPassword(SERVICE, ACCOUNT);
62
+ return typeof key === "string" && key.length > 0;
63
+ } catch (err) {
64
+ logger.error("Failed to check Keychain for private key.", { error: err?.message });
65
+ return false;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Deletes the private key from Keychain.
71
+ * @returns {Promise<boolean>} true if an entry was deleted, false if nothing existed
72
+ */
73
+ export async function deletePrivateKey() {
74
+ try {
75
+ const deleted = await keytar.deletePassword(SERVICE, ACCOUNT);
76
+ if (deleted) {
77
+ console.log("đŸ’Ĩ Private key removed from macOS Keychain.");
78
+ } else {
79
+ console.log("â„šī¸ No private key found in macOS Keychain.");
80
+ }
81
+ return deleted;
82
+ } catch (err) {
83
+ logger.error("Failed to delete private key from Keychain.", { error: err?.message });
84
+ throw new KeychainError("Failed to delete private key from Keychain.", { cause: err });
85
+ }
86
+ }
@@ -0,0 +1,45 @@
1
+ const LEVELS = {
2
+ debug: 10,
3
+ info: 20,
4
+ warn: 30,
5
+ error: 40,
6
+ };
7
+
8
+ const DEFAULT_LEVEL = (process.env.WARLORD_LOG_LEVEL || (process.env.NODE_ENV === "development" ? "debug" : "info")).toLowerCase();
9
+ const LOG_JSON = process.env.WARLORD_LOG_JSON === "1";
10
+
11
+ function shouldLog(level) {
12
+ const current = LEVELS[DEFAULT_LEVEL] ?? LEVELS.info;
13
+ return LEVELS[level] >= current;
14
+ }
15
+
16
+ function formatPayload(level, message, meta) {
17
+ if (LOG_JSON) {
18
+ return JSON.stringify({ level, message, ...meta });
19
+ }
20
+ if (meta && Object.keys(meta).length) {
21
+ return `[${level.toUpperCase()}] ${message} ${JSON.stringify(meta)}`;
22
+ }
23
+ return `[${level.toUpperCase()}] ${message}`;
24
+ }
25
+
26
+ function log(level, message, meta = {}) {
27
+ if (!shouldLog(level)) return;
28
+ const payload = formatPayload(level, message, meta);
29
+ if (level === "error") {
30
+ console.error(payload);
31
+ } else if (level === "warn") {
32
+ console.warn(payload);
33
+ } else if (level === "debug") {
34
+ console.debug(payload);
35
+ } else {
36
+ console.log(payload);
37
+ }
38
+ }
39
+
40
+ export const logger = {
41
+ debug: (message, meta) => log("debug", message, meta),
42
+ info: (message, meta) => log("info", message, meta),
43
+ warn: (message, meta) => log("warn", message, meta),
44
+ error: (message, meta) => log("error", message, meta),
45
+ };
@@ -0,0 +1,58 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { NotificationError } from "../lib/errors.js";
3
+ import { logger } from "./logger.js";
4
+
5
+ function escapeAppleScriptString(str) {
6
+ return str
7
+ .replace(/\\/g, "\\\\")
8
+ .replace(/"/g, "\\\"")
9
+ .replace(/\n/g, "\\n");
10
+ }
11
+
12
+ /**
13
+ * Display a macOS native notification using osascript.
14
+ * @param {Object} options
15
+ * @param {string} [options.title="summonTheWarlord"]
16
+ * @param {string} [options.message=""]
17
+ * @param {string} [options.subtitle=""]
18
+ * @param {string} [options.sound]
19
+ */
20
+ export function notify({
21
+ title = "summonTheWarlord",
22
+ message = "",
23
+ subtitle = "",
24
+ sound,
25
+ throwOnError = false,
26
+ } = {}) {
27
+ if (process.platform !== "darwin") {
28
+ const bell = "\u0007";
29
+ console.log(`${bell} ${title}: ${message}${subtitle ? ` - ${subtitle}` : ""}`);
30
+ return false;
31
+ }
32
+
33
+ try {
34
+ const escTitle = escapeAppleScriptString(title);
35
+ const escMessage = escapeAppleScriptString(message);
36
+ const escSubtitle = escapeAppleScriptString(subtitle);
37
+ let script = `display notification "${escMessage}" with title "${escTitle}"`;
38
+ if (subtitle) {
39
+ script += ` subtitle "${escSubtitle}"`;
40
+ }
41
+ if (sound) {
42
+ script += ` sound name "${escapeAppleScriptString(sound)}"`;
43
+ }
44
+
45
+ const result = spawnSync("osascript", ["-e", script], { stdio: "ignore" });
46
+ if (result.error || result.status !== 0) {
47
+ throw new NotificationError(`osascript exited with ${result.status}`, { cause: result.error });
48
+ }
49
+ return true;
50
+ } catch (err) {
51
+ logger.warn("Notification failed.", { error: err?.message });
52
+ if (throwOnError) {
53
+ throw err;
54
+ }
55
+ console.log(`🔔 ${title}: ${message}${subtitle ? ` - ${subtitle}` : ""}`);
56
+ return false;
57
+ }
58
+ }