@aurora-foundation/obsidian-next 0.4.7 → 0.4.9

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.
Files changed (232) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +628 -190
  3. package/README.md +23 -9
  4. package/dist/auditLog-6WDBDNYL.js +8 -0
  5. package/dist/auditLog-HGPVDSDC.js +8 -0
  6. package/dist/auditLog-TDIKFBM4.js +8 -0
  7. package/dist/auditLog-XC2KY3ZZ.js +8 -0
  8. package/dist/chunk-2I235WNB.js +133 -0
  9. package/dist/chunk-2JWDGXTR.js +42 -0
  10. package/dist/chunk-2NOB6W2B.js +133 -0
  11. package/dist/chunk-3LFKVKKL.js +7199 -0
  12. package/dist/chunk-3U6WHPDX.js +4695 -0
  13. package/dist/chunk-3UCL6RYE.js +7272 -0
  14. package/dist/chunk-4GN2UQLI.js +130 -0
  15. package/dist/chunk-4MW33MZD.js +516 -0
  16. package/dist/chunk-4PUJBUKZ.js +4716 -0
  17. package/dist/chunk-4QHK6H6O.js +130 -0
  18. package/dist/chunk-55CQIHCO.js +133 -0
  19. package/dist/chunk-5LWINFWI.js +676 -0
  20. package/dist/chunk-5OKGLNQW.js +439 -0
  21. package/dist/chunk-5T6ETZEO.js +6183 -0
  22. package/dist/chunk-5WGIFUVL.js +4234 -0
  23. package/dist/chunk-66EW47T3.js +4237 -0
  24. package/dist/chunk-6TXUOTT2.js +581 -0
  25. package/dist/chunk-6YUYSYDA.js +130 -0
  26. package/dist/chunk-74VPNFMX.js +133 -0
  27. package/dist/chunk-77CGJRGV.js +6188 -0
  28. package/dist/chunk-7DS3VT4C.js +7135 -0
  29. package/dist/chunk-7FHX3VBT.js +133 -0
  30. package/dist/chunk-7MHF56YU.js +6178 -0
  31. package/dist/chunk-ABLPMV7G.js +133 -0
  32. package/dist/chunk-B77K6OQZ.js +687 -0
  33. package/dist/chunk-BKOXH66O.js +133 -0
  34. package/dist/chunk-BPP76UN2.js +130 -0
  35. package/dist/chunk-C4D56GRC.js +5936 -0
  36. package/dist/chunk-CCDPY4WE.js +370 -0
  37. package/dist/chunk-CHNVBJN3.js +7272 -0
  38. package/dist/chunk-CKBZI576.js +7229 -0
  39. package/dist/chunk-CW5HBSJ2.js +7198 -0
  40. package/dist/chunk-DGHDJEY7.js +133 -0
  41. package/dist/chunk-DPNIQWKZ.js +439 -0
  42. package/dist/chunk-DU4T3V2T.js +214 -0
  43. package/dist/chunk-DV3WFKNB.js +4679 -0
  44. package/dist/chunk-DZI2OVN2.js +516 -0
  45. package/dist/chunk-E45VILML.js +7198 -0
  46. package/dist/chunk-ECEUUYXC.js +7199 -0
  47. package/dist/chunk-EJRRSHPW.js +685 -0
  48. package/dist/chunk-EMBMLZFE.js +370 -0
  49. package/dist/chunk-EPG5V5OO.js +285 -0
  50. package/dist/chunk-F2R4HXXW.js +130 -0
  51. package/dist/chunk-FK6N66ES.js +581 -0
  52. package/dist/chunk-G3CZKGYA.js +197 -0
  53. package/dist/chunk-GUUPG4A7.js +7111 -0
  54. package/dist/chunk-HBAAUGUN.js +7230 -0
  55. package/dist/chunk-HHFJMK2Q.js +6177 -0
  56. package/dist/chunk-HINRQTCZ.js +196 -0
  57. package/dist/chunk-HRKJ3R2U.js +288 -0
  58. package/dist/chunk-HWVK4CVE.js +439 -0
  59. package/dist/chunk-JEYSADNZ.js +581 -0
  60. package/dist/chunk-JNEIL7UN.js +4252 -0
  61. package/dist/chunk-JTWSK277.js +676 -0
  62. package/dist/chunk-K4CHTTCJ.js +942 -0
  63. package/dist/chunk-K7R5KUDS.js +4695 -0
  64. package/dist/chunk-KNJFOURE.js +7151 -0
  65. package/dist/chunk-KY22FIT3.js +7256 -0
  66. package/dist/chunk-L2OTIJSF.js +4228 -0
  67. package/dist/chunk-LEEBUHP6.js +4655 -0
  68. package/dist/chunk-LK7UP2T7.js +130 -0
  69. package/dist/chunk-LPGNO3PK.js +284 -0
  70. package/dist/chunk-LYQYJMWS.js +133 -0
  71. package/dist/chunk-MBYFJXR3.js +130 -0
  72. package/dist/chunk-N3WX44L3.js +130 -0
  73. package/dist/chunk-N6AQWES3.js +6197 -0
  74. package/dist/chunk-NW4XSTQZ.js +130 -0
  75. package/dist/chunk-NWG2XURH.js +130 -0
  76. package/dist/chunk-O3GF3LJD.js +6142 -0
  77. package/dist/chunk-OHP5LD3Y.js +6188 -0
  78. package/dist/chunk-P5PQSFZT.js +6182 -0
  79. package/dist/chunk-PAADOWNP.js +130 -0
  80. package/dist/chunk-PERGND7L.js +7213 -0
  81. package/dist/chunk-PWA7V4XX.js +179 -0
  82. package/dist/chunk-QGCWEP6L.js +7111 -0
  83. package/dist/chunk-QVT2IHNJ.js +175 -0
  84. package/dist/chunk-QZNGYPMS.js +6161 -0
  85. package/dist/chunk-R6P2E2ZQ.js +207 -0
  86. package/dist/chunk-ROSDMGIL.js +4679 -0
  87. package/dist/chunk-RQZP7IKG.js +196 -0
  88. package/dist/chunk-RUQSPX3U.js +133 -0
  89. package/dist/chunk-S3BYHP5M.js +130 -0
  90. package/dist/chunk-S6GNETVE.js +438 -0
  91. package/dist/chunk-SDT2ZE2R.js +133 -0
  92. package/dist/chunk-SHQBXJFC.js +6166 -0
  93. package/dist/chunk-TJNISYTE.js +42 -0
  94. package/dist/chunk-TJW74HFF.js +130 -0
  95. package/dist/chunk-TPP72DTK.js +7096 -0
  96. package/dist/chunk-UOESII6R.js +42 -0
  97. package/dist/chunk-UWEDGLYJ.js +6142 -0
  98. package/dist/chunk-V5FYNAFX.js +133 -0
  99. package/dist/chunk-VPURF6UT.js +7198 -0
  100. package/dist/chunk-VQH6LWIZ.js +6184 -0
  101. package/dist/chunk-VS22YVX6.js +7111 -0
  102. package/dist/chunk-VSF5KBW7.js +367 -0
  103. package/dist/chunk-VV3JMCKY.js +214 -0
  104. package/dist/chunk-W5L7HOE3.js +133 -0
  105. package/dist/chunk-WFEVQISK.js +676 -0
  106. package/dist/chunk-WJZPSCEP.js +516 -0
  107. package/dist/chunk-WLV4MKEF.js +16 -0
  108. package/dist/chunk-WSEVQFFI.js +5428 -0
  109. package/dist/chunk-X7N2RNR3.js +5428 -0
  110. package/dist/chunk-XKZNMRNO.js +133 -0
  111. package/dist/chunk-Y7BVEC36.js +130 -0
  112. package/dist/chunk-YG7YSNNU.js +4226 -0
  113. package/dist/chunk-YHM62466.js +261 -0
  114. package/dist/chunk-YLTYJLDZ.js +7208 -0
  115. package/dist/chunk-YPMJD4YE.js +56 -0
  116. package/dist/chunk-YTX3FU2A.js +7199 -0
  117. package/dist/chunk-ZEQ3EBBN.js +214 -0
  118. package/dist/chunk-ZIWLQSLK.js +42 -0
  119. package/dist/chunk-ZJELNTEO.js +516 -0
  120. package/dist/chunk-ZOSSVNGK.js +370 -0
  121. package/dist/config-EYK32F2E.js +10 -0
  122. package/dist/config-FJPPPYTY.js +10 -0
  123. package/dist/config-VAHPVILX.js +10 -0
  124. package/dist/context-2YGE4U75.js +10 -0
  125. package/dist/context-5UFVYKES.js +9 -0
  126. package/dist/context-ANZF4J72.js +10 -0
  127. package/dist/context-GLUNCUBQ.js +10 -0
  128. package/dist/context-M5ULPZKQ.js +10 -0
  129. package/dist/context-NYOIRZKV.js +10 -0
  130. package/dist/context-YP2REI6A.js +10 -0
  131. package/dist/database-MP2JBLMF.js +8 -0
  132. package/dist/index.js +6177 -4688
  133. package/dist/keyManager-P2SZONKE.js +8 -0
  134. package/dist/mcp/index.js +127 -47
  135. package/dist/memory-BPGJAL4J.js +13 -0
  136. package/dist/memory-FRQOUI6W.js +13 -0
  137. package/dist/memory-OAMK27IZ.js +13 -0
  138. package/dist/memory-OZ734ALL.js +13 -0
  139. package/dist/memory-PQ2EWRMU.js +13 -0
  140. package/dist/memory-PXL45M6W.js +13 -0
  141. package/dist/memory-QZTBTYPH.js +13 -0
  142. package/dist/migrations-TLJ3WRVW.js +188 -0
  143. package/dist/resume-2NHDK6EI.js +17 -0
  144. package/dist/resume-44L2PDB2.js +17 -0
  145. package/dist/resume-4MXIWUJO.js +7 -0
  146. package/dist/resume-72VJX66I.js +17 -0
  147. package/dist/resume-7ZW4XM3B.js +15 -0
  148. package/dist/resume-AZHYQ657.js +17 -0
  149. package/dist/resume-CNLXSYHV.js +17 -0
  150. package/dist/resume-DPN4Q777.js +16 -0
  151. package/dist/resume-DSFHVNPI.js +15 -0
  152. package/dist/resume-GHLQJJTO.js +17 -0
  153. package/dist/resume-HJ6SBWTF.js +17 -0
  154. package/dist/resume-HRLYHY2L.js +17 -0
  155. package/dist/resume-ISIQFKO6.js +17 -0
  156. package/dist/resume-JQDEA6PS.js +15 -0
  157. package/dist/resume-KP3Y3Y7P.js +17 -0
  158. package/dist/resume-M4KHR5OI.js +17 -0
  159. package/dist/resume-N62OAMBG.js +17 -0
  160. package/dist/resume-NNMA5POT.js +17 -0
  161. package/dist/resume-ODYT3J4H.js +17 -0
  162. package/dist/resume-PCFJXA5O.js +17 -0
  163. package/dist/resume-PLF4XGBD.js +15 -0
  164. package/dist/resume-Q2PFX57V.js +17 -0
  165. package/dist/resume-QB4XI2J5.js +17 -0
  166. package/dist/resume-R5MFTUPF.js +17 -0
  167. package/dist/resume-SVA7223Z.js +17 -0
  168. package/dist/resume-TYKKDJZI.js +17 -0
  169. package/dist/resume-XIS45HKV.js +17 -0
  170. package/dist/resume-YCSEJTU7.js +17 -0
  171. package/dist/resume-YD76GI2J.js +15 -0
  172. package/dist/resume-YDN7EL77.js +17 -0
  173. package/dist/resume-YE7DB4ZA.js +17 -0
  174. package/dist/resume-YKAKOXWV.js +15 -0
  175. package/dist/resume-ZHBCVFDY.js +17 -0
  176. package/dist/scheduler-2CK24A2Q.js +14 -0
  177. package/dist/scheduler-7OAF2XKX.js +14 -0
  178. package/dist/scheduler-AS23AAB5.js +14 -0
  179. package/dist/scheduler-PCOYQJA5.js +14 -0
  180. package/dist/scheduler-V2ECBQPK.js +14 -0
  181. package/dist/scheduler-VEWZ6L7V.js +13 -0
  182. package/dist/scheduler-W37QMGDQ.js +14 -0
  183. package/dist/session-2E2JKPD7.js +15 -0
  184. package/dist/session-2VSF257B.js +14 -0
  185. package/dist/session-4APFTDJU.js +14 -0
  186. package/dist/session-5YS5LNNL.js +16 -0
  187. package/dist/session-6MO5ZPOB.js +16 -0
  188. package/dist/session-6XMGPRTQ.js +14 -0
  189. package/dist/session-7DJR77R7.js +16 -0
  190. package/dist/session-7DQHPWTR.js +14 -0
  191. package/dist/session-ADKIQCR5.js +16 -0
  192. package/dist/session-AOGH2GGI.js +16 -0
  193. package/dist/session-C4W6GDYG.js +16 -0
  194. package/dist/session-DCGNGGMV.js +14 -0
  195. package/dist/session-F5JKZAN2.js +16 -0
  196. package/dist/session-G6F3O2FQ.js +16 -0
  197. package/dist/session-GFBSARRO.js +16 -0
  198. package/dist/session-H5IWAIUI.js +16 -0
  199. package/dist/session-IPFA6AHC.js +14 -0
  200. package/dist/session-IWG2UOAX.js +14 -0
  201. package/dist/session-KJ2K4Y4M.js +14 -0
  202. package/dist/session-KPXFBW6Q.js +14 -0
  203. package/dist/session-KR256UL5.js +16 -0
  204. package/dist/session-M72LJXPR.js +16 -0
  205. package/dist/session-MBK3FODN.js +14 -0
  206. package/dist/session-MOUFAU7G.js +16 -0
  207. package/dist/session-NRC6ZXFQ.js +16 -0
  208. package/dist/session-NRPQMV4K.js +16 -0
  209. package/dist/session-O5IFFJZQ.js +14 -0
  210. package/dist/session-OF5BGKDE.js +16 -0
  211. package/dist/session-OGRZMIM7.js +14 -0
  212. package/dist/session-OJOFAJG3.js +16 -0
  213. package/dist/session-OKU4N3SP.js +16 -0
  214. package/dist/session-P2VAOSFB.js +14 -0
  215. package/dist/session-PKOVZD4M.js +16 -0
  216. package/dist/session-POAIMUVN.js +16 -0
  217. package/dist/session-PSHFONFE.js +16 -0
  218. package/dist/session-QKYVVZFV.js +16 -0
  219. package/dist/session-QPWGBMUS.js +14 -0
  220. package/dist/session-R5UG5PZR.js +14 -0
  221. package/dist/session-RAY6BZRQ.js +16 -0
  222. package/dist/session-S3VATHMU.js +16 -0
  223. package/dist/session-SYTD7RHW.js +14 -0
  224. package/dist/session-UHMMVO4J.js +16 -0
  225. package/dist/session-WEX5K3ZY.js +14 -0
  226. package/dist/session-XFLOXGU3.js +14 -0
  227. package/dist/session-XV2A4HHG.js +14 -0
  228. package/dist/settings-3VPJYD4D.js +8 -0
  229. package/dist/settings-GZTJJTBK.js +8 -0
  230. package/dist/settings-YKJFSKMO.js +8 -0
  231. package/dist/shell-FM34624T.js +8 -0
  232. package/package.json +14 -4
@@ -0,0 +1,438 @@
1
+ // src/core/keyManager.ts
2
+ import { exec } from "child_process";
3
+ import { promisify } from "util";
4
+ import fs from "fs/promises";
5
+ import path from "path";
6
+ import os from "os";
7
+ import crypto from "crypto";
8
+ var execAsync = promisify(exec);
9
+ var ROTATION_CHECK_INTERVAL = 4 * 60 * 60 * 1e3;
10
+ var DEFAULT_SERVICE = "obsidian-next";
11
+ var DEFAULT_ACCOUNT = "anthropic-api-key";
12
+ var KeyManager = class {
13
+ keyCache = /* @__PURE__ */ new Map();
14
+ encryptedFilePath;
15
+ machineId = null;
16
+ constructor() {
17
+ this.encryptedFilePath = path.join(os.homedir(), ".obsidian", ".keystore");
18
+ }
19
+ getCacheKey(service, account) {
20
+ return `${service}:${account}`;
21
+ }
22
+ /**
23
+ * Load API key from the most secure available backend
24
+ */
25
+ async loadKey(options = {}) {
26
+ const service = options.service || DEFAULT_SERVICE;
27
+ const account = options.account || DEFAULT_ACCOUNT;
28
+ const cacheKey = this.getCacheKey(service, account);
29
+ const cached = this.keyCache.get(cacheKey);
30
+ if (cached && !this.shouldRotate(cached)) {
31
+ return cached.key;
32
+ }
33
+ let key = null;
34
+ let backend = "env";
35
+ if (account === DEFAULT_ACCOUNT) {
36
+ key = process.env.ANTHROPIC_API_KEY || null;
37
+ }
38
+ if (key) {
39
+ backend = "env";
40
+ }
41
+ if (!key && process.platform === "darwin") {
42
+ key = await this.loadFromKeychain(service, account);
43
+ if (key) backend = "keychain";
44
+ }
45
+ if (!key && process.platform === "linux") {
46
+ key = await this.loadFromSecretTool(service, account);
47
+ if (key) backend = "secret-tool";
48
+ }
49
+ if (!key) {
50
+ key = await this.loadFromEncryptedFile(service, account);
51
+ if (key) backend = "encrypted-file";
52
+ }
53
+ if (key) {
54
+ this.keyCache.set(cacheKey, {
55
+ key,
56
+ loadedAt: Date.now(),
57
+ backend
58
+ });
59
+ }
60
+ return key;
61
+ }
62
+ /**
63
+ * Store API key in the most secure available backend
64
+ */
65
+ async storeKey(key, options = {}) {
66
+ const service = options.service || DEFAULT_SERVICE;
67
+ const account = options.account || DEFAULT_ACCOUNT;
68
+ const cacheKey = this.getCacheKey(service, account);
69
+ if (process.platform === "darwin") {
70
+ const result2 = await this.storeInKeychain(key, service, account);
71
+ if (result2.success) {
72
+ this.keyCache.set(cacheKey, {
73
+ key,
74
+ loadedAt: Date.now(),
75
+ backend: "keychain"
76
+ });
77
+ return { success: true, backend: "keychain" };
78
+ }
79
+ }
80
+ if (process.platform === "linux") {
81
+ const result2 = await this.storeInSecretTool(key, service, account);
82
+ if (result2.success) {
83
+ this.keyCache.set(cacheKey, {
84
+ key,
85
+ loadedAt: Date.now(),
86
+ backend: "secret-tool"
87
+ });
88
+ return { success: true, backend: "secret-tool" };
89
+ }
90
+ }
91
+ const result = await this.storeInEncryptedFile(key, service, account);
92
+ if (result.success) {
93
+ this.keyCache.set(cacheKey, {
94
+ key,
95
+ loadedAt: Date.now(),
96
+ backend: "encrypted-file"
97
+ });
98
+ return { success: true, backend: "encrypted-file" };
99
+ }
100
+ return { success: false, backend: "env", error: result.error };
101
+ }
102
+ /**
103
+ * Delete stored key from all backends
104
+ */
105
+ async deleteKey(options = {}) {
106
+ const service = options.service || DEFAULT_SERVICE;
107
+ const account = options.account || DEFAULT_ACCOUNT;
108
+ const cacheKey = this.getCacheKey(service, account);
109
+ this.keyCache.delete(cacheKey);
110
+ if (process.platform === "darwin") {
111
+ await this.deleteFromKeychain(service, account);
112
+ }
113
+ if (process.platform === "linux") {
114
+ await this.deleteFromSecretTool(service, account);
115
+ }
116
+ await this.deleteFromEncryptedFile(service, account);
117
+ }
118
+ /**
119
+ * Check if key should be rotated (for long-running sessions)
120
+ */
121
+ shouldRotate(config = this.keyCache.get(
122
+ this.getCacheKey(DEFAULT_SERVICE, DEFAULT_ACCOUNT)
123
+ )) {
124
+ if (!config) return true;
125
+ return Date.now() - config.loadedAt > ROTATION_CHECK_INTERVAL;
126
+ }
127
+ /**
128
+ * Force reload key from backend
129
+ */
130
+ async refreshKey(options = {}) {
131
+ const service = options.service || DEFAULT_SERVICE;
132
+ const account = options.account || DEFAULT_ACCOUNT;
133
+ this.keyCache.delete(this.getCacheKey(service, account));
134
+ return this.loadKey(options);
135
+ }
136
+ /**
137
+ * Get current backend being used
138
+ */
139
+ getBackend(options = {}) {
140
+ const service = options.service || DEFAULT_SERVICE;
141
+ const account = options.account || DEFAULT_ACCOUNT;
142
+ return this.keyCache.get(this.getCacheKey(service, account))?.backend || null;
143
+ }
144
+ // ==================== macOS Keychain ====================
145
+ async loadFromKeychain(service, account) {
146
+ try {
147
+ const { stdout } = await execAsync(
148
+ `security find-generic-password -s "${service}" -a "${account}" -w 2>/dev/null`
149
+ );
150
+ return stdout.trim() || null;
151
+ } catch {
152
+ return null;
153
+ }
154
+ }
155
+ async storeInKeychain(key, service, account) {
156
+ try {
157
+ await this.deleteFromKeychain(service, account);
158
+ await execAsync(
159
+ `security add-generic-password -s "${service}" -a "${account}" -w "${key}" -U`
160
+ );
161
+ return { success: true };
162
+ } catch (error) {
163
+ return { success: false, error: error.message };
164
+ }
165
+ }
166
+ async deleteFromKeychain(service, account) {
167
+ try {
168
+ await execAsync(
169
+ `security delete-generic-password -s "${service}" -a "${account}" 2>/dev/null`
170
+ );
171
+ } catch {
172
+ }
173
+ }
174
+ // ==================== Linux secret-tool ====================
175
+ async loadFromSecretTool(service, account) {
176
+ try {
177
+ const { stdout } = await execAsync(
178
+ `secret-tool lookup service "${service}" account "${account}" 2>/dev/null`
179
+ );
180
+ return stdout.trim() || null;
181
+ } catch {
182
+ return null;
183
+ }
184
+ }
185
+ async storeInSecretTool(key, service, account) {
186
+ try {
187
+ await execAsync(
188
+ `echo -n "${key}" | secret-tool store --label="Obsidian Next API Key" service "${service}" account "${account}"`
189
+ );
190
+ return { success: true };
191
+ } catch (error) {
192
+ return { success: false, error: error.message };
193
+ }
194
+ }
195
+ async deleteFromSecretTool(service, account) {
196
+ try {
197
+ await execAsync(
198
+ `secret-tool clear service "${service}" account "${account}" 2>/dev/null`
199
+ );
200
+ } catch {
201
+ }
202
+ }
203
+ // ==================== Encrypted File ====================
204
+ async getMachineId() {
205
+ if (this.machineId) return this.machineId;
206
+ const components = [
207
+ os.hostname(),
208
+ os.userInfo().username,
209
+ os.platform(),
210
+ os.arch()
211
+ ];
212
+ if (process.platform === "darwin") {
213
+ try {
214
+ const { stdout } = await execAsync(
215
+ "ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID"
216
+ );
217
+ const match = stdout.match(/"IOPlatformUUID" = "([^"]+)"/);
218
+ if (match) components.push(match[1]);
219
+ } catch {
220
+ }
221
+ } else if (process.platform === "linux") {
222
+ try {
223
+ const machineId = await fs.readFile("/etc/machine-id", "utf-8");
224
+ components.push(machineId.trim());
225
+ } catch {
226
+ }
227
+ }
228
+ this.machineId = crypto.createHash("sha256").update(components.join(":")).digest("hex");
229
+ return this.machineId;
230
+ }
231
+ async deriveEncryptionKey() {
232
+ const machineId = await this.getMachineId();
233
+ return crypto.pbkdf2Sync(
234
+ machineId,
235
+ "obsidian-next-salt",
236
+ 1e5,
237
+ 32,
238
+ "sha256"
239
+ );
240
+ }
241
+ async loadFromEncryptedFile(service, account) {
242
+ try {
243
+ const encrypted = await fs.readFile(this.encryptedFilePath, "utf-8");
244
+ const data = JSON.parse(encrypted);
245
+ const key = await this.deriveEncryptionKey();
246
+ const iv = Buffer.from(data.iv, "hex");
247
+ const authTag = Buffer.from(data.tag, "hex");
248
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
249
+ decipher.setAuthTag(authTag);
250
+ let decrypted = decipher.update(data.encrypted, "hex", "utf-8");
251
+ decrypted += decipher.final("utf-8");
252
+ try {
253
+ const keyMap = JSON.parse(decrypted);
254
+ return keyMap[this.getCacheKey(service, account)] || null;
255
+ } catch {
256
+ if (service === DEFAULT_SERVICE && account === DEFAULT_ACCOUNT) {
257
+ return decrypted;
258
+ }
259
+ return null;
260
+ }
261
+ } catch {
262
+ return null;
263
+ }
264
+ }
265
+ async storeInEncryptedFile(apiKey, service, account) {
266
+ try {
267
+ const key = await this.deriveEncryptionKey();
268
+ const iv = crypto.randomBytes(16);
269
+ let keyMap = {};
270
+ try {
271
+ const existingEncrypted = await fs.readFile(
272
+ this.encryptedFilePath,
273
+ "utf-8"
274
+ );
275
+ const data2 = JSON.parse(existingEncrypted);
276
+ const exDecipher = crypto.createDecipheriv(
277
+ "aes-256-gcm",
278
+ key,
279
+ Buffer.from(data2.iv, "hex")
280
+ );
281
+ exDecipher.setAuthTag(Buffer.from(data2.tag, "hex"));
282
+ let exDecrypted = exDecipher.update(data2.encrypted, "hex", "utf-8");
283
+ exDecrypted += exDecipher.final("utf-8");
284
+ try {
285
+ keyMap = JSON.parse(exDecrypted);
286
+ } catch {
287
+ keyMap[this.getCacheKey(DEFAULT_SERVICE, DEFAULT_ACCOUNT)] = exDecrypted;
288
+ }
289
+ } catch {
290
+ }
291
+ keyMap[this.getCacheKey(service, account)] = apiKey;
292
+ const contentToEncrypt = JSON.stringify(keyMap);
293
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
294
+ let encrypted = cipher.update(contentToEncrypt, "utf-8", "hex");
295
+ encrypted += cipher.final("hex");
296
+ const authTag = cipher.getAuthTag();
297
+ const data = {
298
+ encrypted,
299
+ iv: iv.toString("hex"),
300
+ tag: authTag.toString("hex"),
301
+ version: 1
302
+ };
303
+ await fs.mkdir(path.dirname(this.encryptedFilePath), { recursive: true });
304
+ await fs.writeFile(this.encryptedFilePath, JSON.stringify(data), {
305
+ mode: 384
306
+ });
307
+ return { success: true };
308
+ } catch (error) {
309
+ return { success: false, error: error.message };
310
+ }
311
+ }
312
+ async deleteFromEncryptedFile(service, account) {
313
+ try {
314
+ const key = await this.deriveEncryptionKey();
315
+ const existingEncrypted = await fs.readFile(
316
+ this.encryptedFilePath,
317
+ "utf-8"
318
+ );
319
+ const data = JSON.parse(existingEncrypted);
320
+ const exDecipher = crypto.createDecipheriv(
321
+ "aes-256-gcm",
322
+ key,
323
+ Buffer.from(data.iv, "hex")
324
+ );
325
+ exDecipher.setAuthTag(Buffer.from(data.tag, "hex"));
326
+ let exDecrypted = exDecipher.update(data.encrypted, "hex", "utf-8");
327
+ exDecrypted += exDecipher.final("utf-8");
328
+ let keyMap = {};
329
+ try {
330
+ keyMap = JSON.parse(exDecrypted);
331
+ } catch {
332
+ if (service === DEFAULT_SERVICE && account === DEFAULT_ACCOUNT) {
333
+ await fs.unlink(this.encryptedFilePath);
334
+ return;
335
+ }
336
+ }
337
+ delete keyMap[this.getCacheKey(service, account)];
338
+ if (Object.keys(keyMap).length === 0) {
339
+ await fs.unlink(this.encryptedFilePath);
340
+ return;
341
+ }
342
+ const iv = crypto.randomBytes(16);
343
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
344
+ let encrypted = cipher.update(JSON.stringify(keyMap), "utf-8", "hex");
345
+ encrypted += cipher.final("hex");
346
+ const authTag = cipher.getAuthTag();
347
+ const newData = {
348
+ encrypted,
349
+ iv: iv.toString("hex"),
350
+ tag: authTag.toString("hex"),
351
+ version: 2
352
+ // Upgraded version
353
+ };
354
+ await fs.writeFile(this.encryptedFilePath, JSON.stringify(newData), {
355
+ mode: 384
356
+ });
357
+ } catch {
358
+ }
359
+ }
360
+ /**
361
+ * Clear key from memory (call when done with sensitive operations)
362
+ */
363
+ clearFromMemory() {
364
+ this.keyCache.clear();
365
+ }
366
+ /**
367
+ * Check if key exists in any backend (without loading it)
368
+ */
369
+ async hasKey(options = {}) {
370
+ const service = options.service || DEFAULT_SERVICE;
371
+ const account = options.account || DEFAULT_ACCOUNT;
372
+ if (account === DEFAULT_ACCOUNT && process.env.ANTHROPIC_API_KEY)
373
+ return true;
374
+ if (process.platform === "darwin") {
375
+ const key2 = await this.loadFromKeychain(service, account);
376
+ if (key2) return true;
377
+ }
378
+ if (process.platform === "linux") {
379
+ const key2 = await this.loadFromSecretTool(service, account);
380
+ if (key2) return true;
381
+ }
382
+ const key = await this.loadFromEncryptedFile(service, account);
383
+ return !!key;
384
+ }
385
+ /**
386
+ * Migrate key from environment variable to secure storage
387
+ * Returns true if migration was successful
388
+ */
389
+ async migrateFromEnv() {
390
+ const envKey = process.env.ANTHROPIC_API_KEY;
391
+ if (!envKey) {
392
+ return {
393
+ migrated: false,
394
+ error: "No ANTHROPIC_API_KEY found in environment"
395
+ };
396
+ }
397
+ const cached = this.keyCache.get(
398
+ this.getCacheKey(DEFAULT_SERVICE, DEFAULT_ACCOUNT)
399
+ );
400
+ if (cached && cached.backend !== "env") {
401
+ return { migrated: false, error: "Key already in secure storage" };
402
+ }
403
+ const result = await this.storeKey(envKey);
404
+ if (result.success) {
405
+ return { migrated: true, backend: result.backend };
406
+ }
407
+ return { migrated: false, error: result.error };
408
+ }
409
+ /**
410
+ * Validate an API key by making a test request
411
+ */
412
+ async validateKey(key) {
413
+ if (!key.startsWith("sk-ant-")) {
414
+ return false;
415
+ }
416
+ if (key.length < 50 || key.length > 200) {
417
+ return false;
418
+ }
419
+ return true;
420
+ }
421
+ };
422
+ async function detectEnvFile(workspaceRoot) {
423
+ const envPath = path.join(workspaceRoot, ".env");
424
+ try {
425
+ const content = await fs.readFile(envPath, "utf-8");
426
+ if (content.includes("ANTHROPIC_API_KEY")) {
427
+ return { found: true, path: envPath };
428
+ }
429
+ } catch {
430
+ }
431
+ return { found: false };
432
+ }
433
+ var keyManager = new KeyManager();
434
+
435
+ export {
436
+ detectEnvFile,
437
+ keyManager
438
+ };
@@ -0,0 +1,133 @@
1
+ import {
2
+ history,
3
+ session,
4
+ tasks
5
+ } from "./chunk-OHP5LD3Y.js";
6
+ import {
7
+ context
8
+ } from "./chunk-DPNIQWKZ.js";
9
+ import {
10
+ bus
11
+ } from "./chunk-WQM6FFSD.js";
12
+
13
+ // src/commands/resume.ts
14
+ import path from "path";
15
+ var resumeCommand = async (args) => {
16
+ if (args[0] === "--delete" || args[0] === "-d") {
17
+ const sessionId = args[1];
18
+ if (!sessionId) {
19
+ bus.emitAgent({
20
+ type: "error",
21
+ message: "Usage: /resume --delete <session_id>"
22
+ });
23
+ return;
24
+ }
25
+ const deleted = await session.delete(sessionId);
26
+ if (deleted) {
27
+ bus.emitAgent({
28
+ type: "done",
29
+ summary: `Session ${sessionId} deleted.`
30
+ });
31
+ } else {
32
+ bus.emitAgent({
33
+ type: "error",
34
+ message: `Session ${sessionId} not found.`
35
+ });
36
+ }
37
+ return;
38
+ }
39
+ if (args[0] === "--last" || args[0] === "-l") {
40
+ const sessions = await session.list();
41
+ if (sessions.length === 0) {
42
+ bus.emitAgent({
43
+ type: "error",
44
+ message: "No saved sessions found."
45
+ });
46
+ return;
47
+ }
48
+ args[0] = sessions[0].id;
49
+ }
50
+ if (!args[0]) {
51
+ const sessions = await session.list();
52
+ if (sessions.length === 0) {
53
+ bus.emitAgent({
54
+ type: "thought",
55
+ content: "No saved sessions found.\n\nSessions are created when you run /exit."
56
+ });
57
+ bus.emitAgent({
58
+ type: "done",
59
+ summary: "No sessions available."
60
+ });
61
+ return;
62
+ }
63
+ const content2 = [
64
+ "Saved Session Registry",
65
+ ...sessions.slice(0, 10).map((s) => {
66
+ const date = new Date(s.savedAt);
67
+ const dateStr = date.toLocaleDateString();
68
+ const timeStr = date.toLocaleTimeString([], {
69
+ hour: "2-digit",
70
+ minute: "2-digit"
71
+ });
72
+ const workspaceName = path.basename(s.workspace);
73
+ return [
74
+ ` \u23BF [${s.id}]`,
75
+ ` \u23BF Date ${dateStr} ${timeStr}`,
76
+ ` \u23BF Workspace ${workspaceName}`,
77
+ s.task ? ` \u23BF Task ${s.task}` : null,
78
+ ` \u23BF Activity ${s.filesModified} files handled`,
79
+ ""
80
+ ].filter(Boolean).join("\n");
81
+ }),
82
+ sessions.length > 10 ? ` ... and ${sessions.length - 10} more sessions
83
+ ` : "",
84
+ " [Usage]",
85
+ " \u23BF /resume <id> Restore session",
86
+ " \u23BF /resume --last Restore latest",
87
+ ""
88
+ ].join("\n");
89
+ bus.emitAgent({
90
+ type: "thought",
91
+ content: content2
92
+ });
93
+ bus.emitAgent({
94
+ type: "done",
95
+ summary: `${sessions.length} session(s) available.`
96
+ });
97
+ return;
98
+ }
99
+ const sessionIdArg = args[0];
100
+ const result = await session.restore(sessionIdArg);
101
+ if (!result.success) {
102
+ bus.emitAgent({
103
+ type: "error",
104
+ message: result.error || "Failed to restore session"
105
+ });
106
+ return;
107
+ }
108
+ const currentTask = tasks.get();
109
+ const currentContext = context.get();
110
+ const content = [
111
+ "Session Restored Successfully",
112
+ ` \u23BF ID ${sessionIdArg}`,
113
+ ` \u23BF Root ${path.basename(process.cwd())}`,
114
+ "",
115
+ " [Restored State]",
116
+ ` \u23BF Context ${currentContext.files_read.length} files in set`,
117
+ ` \u23BF History ${(await history.load()).length} events rehydrated`,
118
+ ` \u23BF Task ${currentTask?.title || "None"}`,
119
+ ""
120
+ ].join("\n");
121
+ bus.emitAgent({
122
+ type: "thought",
123
+ content
124
+ });
125
+ bus.emitAgent({
126
+ type: "done",
127
+ summary: `Session ${sessionIdArg} restored.`
128
+ });
129
+ };
130
+
131
+ export {
132
+ resumeCommand
133
+ };