expandai 0.0.2 → 0.0.3

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 (271) hide show
  1. package/README.md +42 -5
  2. package/build/dts/ApiClient.d.ts +128 -194
  3. package/build/dts/ApiClient.d.ts.map +1 -1
  4. package/build/dts/Auth/Domain.d.ts +95 -0
  5. package/build/dts/Auth/Domain.d.ts.map +1 -0
  6. package/build/dts/Auth/ManualAuthorization.d.ts +107 -0
  7. package/build/dts/Auth/ManualAuthorization.d.ts.map +1 -0
  8. package/build/dts/Auth/PendingInit.d.ts +35 -0
  9. package/build/dts/Auth/PendingInit.d.ts.map +1 -0
  10. package/build/dts/Auth/Pkce.d.ts +10 -0
  11. package/build/dts/Auth/Pkce.d.ts.map +1 -0
  12. package/build/dts/Auth/SetupGrantAuthorization.d.ts +34 -0
  13. package/build/dts/Auth/SetupGrantAuthorization.d.ts.map +1 -0
  14. package/build/dts/Authentication.d.ts +10 -9
  15. package/build/dts/Authentication.d.ts.map +1 -1
  16. package/build/dts/BrowserOAuth.d.ts +7 -7
  17. package/build/dts/BrowserOAuth.d.ts.map +1 -1
  18. package/build/dts/CommandPath.d.ts +2 -0
  19. package/build/dts/CommandPath.d.ts.map +1 -0
  20. package/build/dts/Commands/Fetch.d.ts.map +1 -1
  21. package/build/dts/Commands/Hook.d.ts +5 -5
  22. package/build/dts/Commands/Init.d.ts +55 -0
  23. package/build/dts/Commands/Init.d.ts.map +1 -0
  24. package/build/dts/Commands/Mcp.d.ts +6 -6
  25. package/build/dts/Commands/Search.d.ts.map +1 -1
  26. package/build/dts/Commands/Skill.d.ts +4 -4
  27. package/build/dts/Commands/Status.d.ts +27 -0
  28. package/build/dts/Commands/Status.d.ts.map +1 -0
  29. package/build/dts/Commands/Telemetry.d.ts +7 -0
  30. package/build/dts/Commands/Telemetry.d.ts.map +1 -0
  31. package/build/dts/Commands/Uninstall.d.ts +4 -4
  32. package/build/dts/Config.d.ts +108 -17
  33. package/build/dts/Config.d.ts.map +1 -1
  34. package/build/dts/Hook/SnapshotNote.d.ts +8 -4
  35. package/build/dts/Hook/SnapshotNote.d.ts.map +1 -1
  36. package/build/dts/Init/ClaudeAdapter.d.ts +61 -0
  37. package/build/dts/Init/ClaudeAdapter.d.ts.map +1 -0
  38. package/build/dts/Init/ClaudeProfile.d.ts +78 -0
  39. package/build/dts/Init/ClaudeProfile.d.ts.map +1 -0
  40. package/build/dts/Init/Domain.d.ts +139 -0
  41. package/build/dts/Init/Domain.d.ts.map +1 -0
  42. package/build/dts/Init/HarnessAdapter.d.ts +33 -0
  43. package/build/dts/Init/HarnessAdapter.d.ts.map +1 -0
  44. package/build/dts/Init/HarnessDetector.d.ts +19 -0
  45. package/build/dts/Init/HarnessDetector.d.ts.map +1 -0
  46. package/build/dts/Init/HarnessRegistry.d.ts +50 -0
  47. package/build/dts/Init/HarnessRegistry.d.ts.map +1 -0
  48. package/build/dts/Init/MutationTransaction.d.ts +96 -0
  49. package/build/dts/Init/MutationTransaction.d.ts.map +1 -0
  50. package/build/dts/Init/Service.d.ts +72 -0
  51. package/build/dts/Init/Service.d.ts.map +1 -0
  52. package/build/dts/Init/StandardAdapters.d.ts +42 -0
  53. package/build/dts/Init/StandardAdapters.d.ts.map +1 -0
  54. package/build/dts/Init/Status.d.ts +149 -0
  55. package/build/dts/Init/Status.d.ts.map +1 -0
  56. package/build/dts/Install/Claude/Hook.d.ts +85 -25
  57. package/build/dts/Install/Claude/Hook.d.ts.map +1 -1
  58. package/build/dts/Install/Claude/Mcp.d.ts +21 -18
  59. package/build/dts/Install/Claude/Mcp.d.ts.map +1 -1
  60. package/build/dts/Install/Claude/Skill.d.ts +14 -15
  61. package/build/dts/Install/Claude/Skill.d.ts.map +1 -1
  62. package/build/dts/Install/Codex/Mcp.d.ts +14 -8
  63. package/build/dts/Install/Codex/Mcp.d.ts.map +1 -1
  64. package/build/dts/Install/Codex/Skill.d.ts +4 -2
  65. package/build/dts/Install/Codex/Skill.d.ts.map +1 -1
  66. package/build/dts/Install/Cursor/Mcp.d.ts +13 -13
  67. package/build/dts/Install/Cursor/Mcp.d.ts.map +1 -1
  68. package/build/dts/Install/Cursor/Skill.d.ts +4 -2
  69. package/build/dts/Install/Cursor/Skill.d.ts.map +1 -1
  70. package/build/dts/Install/Domain.d.ts +35 -5
  71. package/build/dts/Install/Domain.d.ts.map +1 -1
  72. package/build/dts/Install/Hook.d.ts +4 -4
  73. package/build/dts/Install/Hook.d.ts.map +1 -1
  74. package/build/dts/Install/Mcp.d.ts +5 -5
  75. package/build/dts/Install/Mcp.d.ts.map +1 -1
  76. package/build/dts/Install/Opencode/Hook.d.ts +4 -3
  77. package/build/dts/Install/Opencode/Hook.d.ts.map +1 -1
  78. package/build/dts/Install/Opencode/Mcp.d.ts +11 -11
  79. package/build/dts/Install/Opencode/Mcp.d.ts.map +1 -1
  80. package/build/dts/Install/Opencode/Skill.d.ts +4 -2
  81. package/build/dts/Install/Opencode/Skill.d.ts.map +1 -1
  82. package/build/dts/Install/Skill.d.ts +3 -3
  83. package/build/dts/Install/Uninstall.d.ts +6 -6
  84. package/build/dts/Install/Uninstall.d.ts.map +1 -1
  85. package/build/dts/MarkdownAnsi.d.ts +29 -0
  86. package/build/dts/MarkdownAnsi.d.ts.map +1 -0
  87. package/build/dts/Mcp/ExpandDocs.d.ts +17 -5
  88. package/build/dts/Mcp/ExpandDocs.d.ts.map +1 -1
  89. package/build/dts/Mcp/Server.d.ts +1 -1
  90. package/build/dts/Mcp/Server.d.ts.map +1 -1
  91. package/build/dts/Mcp/tools/Citations.d.ts +100 -71
  92. package/build/dts/Mcp/tools/Citations.d.ts.map +1 -1
  93. package/build/dts/Mcp/tools/Fetch.d.ts +16 -15
  94. package/build/dts/Mcp/tools/Fetch.d.ts.map +1 -1
  95. package/build/dts/SecureFile.d.ts +23 -0
  96. package/build/dts/SecureFile.d.ts.map +1 -0
  97. package/build/dts/Snippets.d.ts +11 -12
  98. package/build/dts/Snippets.d.ts.map +1 -1
  99. package/build/dts/Telemetry.d.ts +30 -0
  100. package/build/dts/Telemetry.d.ts.map +1 -0
  101. package/build/dts/Version.d.ts +2 -0
  102. package/build/dts/Version.d.ts.map +1 -0
  103. package/build/dts/Xdg.d.ts +11 -0
  104. package/build/dts/Xdg.d.ts.map +1 -0
  105. package/build/esm/ApiClient.js +133 -10
  106. package/build/esm/ApiClient.js.map +1 -1
  107. package/build/esm/Auth/Domain.js +56 -0
  108. package/build/esm/Auth/Domain.js.map +1 -0
  109. package/build/esm/Auth/ManualAuthorization.js +253 -0
  110. package/build/esm/Auth/ManualAuthorization.js.map +1 -0
  111. package/build/esm/Auth/PendingInit.js +267 -0
  112. package/build/esm/Auth/PendingInit.js.map +1 -0
  113. package/build/esm/Auth/Pkce.js +23 -0
  114. package/build/esm/Auth/Pkce.js.map +1 -0
  115. package/build/esm/Auth/SetupGrantAuthorization.js +91 -0
  116. package/build/esm/Auth/SetupGrantAuthorization.js.map +1 -0
  117. package/build/esm/Authentication.js +38 -7
  118. package/build/esm/Authentication.js.map +1 -1
  119. package/build/esm/BrowserOAuth.js +7 -10
  120. package/build/esm/BrowserOAuth.js.map +1 -1
  121. package/build/esm/CommandPath.js +31 -0
  122. package/build/esm/CommandPath.js.map +1 -0
  123. package/build/esm/Commands/Fetch.js +30 -6
  124. package/build/esm/Commands/Fetch.js.map +1 -1
  125. package/build/esm/Commands/Init.js +371 -0
  126. package/build/esm/Commands/Init.js.map +1 -0
  127. package/build/esm/Commands/Search.js +6 -3
  128. package/build/esm/Commands/Search.js.map +1 -1
  129. package/build/esm/Commands/Status.js +186 -0
  130. package/build/esm/Commands/Status.js.map +1 -0
  131. package/build/esm/Commands/Telemetry.js +23 -0
  132. package/build/esm/Commands/Telemetry.js.map +1 -0
  133. package/build/esm/Config.js +502 -28
  134. package/build/esm/Config.js.map +1 -1
  135. package/build/esm/Hook/Claude.js +1 -1
  136. package/build/esm/Hook/Claude.js.map +1 -1
  137. package/build/esm/Hook/SnapshotNote.js +54 -7
  138. package/build/esm/Hook/SnapshotNote.js.map +1 -1
  139. package/build/esm/Init/ClaudeAdapter.js +137 -0
  140. package/build/esm/Init/ClaudeAdapter.js.map +1 -0
  141. package/build/esm/Init/ClaudeProfile.js +380 -0
  142. package/build/esm/Init/ClaudeProfile.js.map +1 -0
  143. package/build/esm/Init/Domain.js +118 -0
  144. package/build/esm/Init/Domain.js.map +1 -0
  145. package/build/esm/Init/HarnessAdapter.js +21 -0
  146. package/build/esm/Init/HarnessAdapter.js.map +1 -0
  147. package/build/esm/Init/HarnessDetector.js +65 -0
  148. package/build/esm/Init/HarnessDetector.js.map +1 -0
  149. package/build/esm/Init/HarnessRegistry.js +58 -0
  150. package/build/esm/Init/HarnessRegistry.js.map +1 -0
  151. package/build/esm/Init/MutationTransaction.js +494 -0
  152. package/build/esm/Init/MutationTransaction.js.map +1 -0
  153. package/build/esm/Init/Service.js +319 -0
  154. package/build/esm/Init/Service.js.map +1 -0
  155. package/build/esm/Init/StandardAdapters.js +116 -0
  156. package/build/esm/Init/StandardAdapters.js.map +1 -0
  157. package/build/esm/Init/Status.js +215 -0
  158. package/build/esm/Init/Status.js.map +1 -0
  159. package/build/esm/Install/Claude/Hook.js +182 -50
  160. package/build/esm/Install/Claude/Hook.js.map +1 -1
  161. package/build/esm/Install/Claude/Mcp.js +56 -26
  162. package/build/esm/Install/Claude/Mcp.js.map +1 -1
  163. package/build/esm/Install/Claude/Skill.js +49 -27
  164. package/build/esm/Install/Claude/Skill.js.map +1 -1
  165. package/build/esm/Install/Codex/Mcp.js +9 -4
  166. package/build/esm/Install/Codex/Mcp.js.map +1 -1
  167. package/build/esm/Install/Codex/Skill.js +4 -1
  168. package/build/esm/Install/Codex/Skill.js.map +1 -1
  169. package/build/esm/Install/Cursor/Mcp.js +7 -4
  170. package/build/esm/Install/Cursor/Mcp.js.map +1 -1
  171. package/build/esm/Install/Cursor/Skill.js +4 -1
  172. package/build/esm/Install/Cursor/Skill.js.map +1 -1
  173. package/build/esm/Install/Domain.js +60 -3
  174. package/build/esm/Install/Domain.js.map +1 -1
  175. package/build/esm/Install/Hook.js.map +1 -1
  176. package/build/esm/Install/Mcp.js +1 -1
  177. package/build/esm/Install/Mcp.js.map +1 -1
  178. package/build/esm/Install/Opencode/Hook.js +1 -1
  179. package/build/esm/Install/Opencode/Hook.js.map +1 -1
  180. package/build/esm/Install/Opencode/Mcp.js +6 -3
  181. package/build/esm/Install/Opencode/Mcp.js.map +1 -1
  182. package/build/esm/Install/Opencode/Skill.js +4 -1
  183. package/build/esm/Install/Opencode/Skill.js.map +1 -1
  184. package/build/esm/Install/Opencode/expandai-webfetch.js +27 -9
  185. package/build/esm/Install/Skill.js.map +1 -1
  186. package/build/esm/Install/Uninstall.js +2 -2
  187. package/build/esm/Install/Uninstall.js.map +1 -1
  188. package/build/esm/MarkdownAnsi.js +142 -0
  189. package/build/esm/MarkdownAnsi.js.map +1 -0
  190. package/build/esm/Mcp/ExpandDocs.js +44 -52
  191. package/build/esm/Mcp/ExpandDocs.js.map +1 -1
  192. package/build/esm/Mcp/RequestHeaders.js.map +1 -1
  193. package/build/esm/Mcp/Server.js +2 -1
  194. package/build/esm/Mcp/Server.js.map +1 -1
  195. package/build/esm/Mcp/tools/Citations.js +109 -8
  196. package/build/esm/Mcp/tools/Citations.js.map +1 -1
  197. package/build/esm/Mcp/tools/Fetch.js +36 -12
  198. package/build/esm/Mcp/tools/Fetch.js.map +1 -1
  199. package/build/esm/SecureFile.js +36 -0
  200. package/build/esm/SecureFile.js.map +1 -0
  201. package/build/esm/Snippets.js +12 -1
  202. package/build/esm/Snippets.js.map +1 -1
  203. package/build/esm/Telemetry.js +157 -0
  204. package/build/esm/Telemetry.js.map +1 -0
  205. package/build/esm/Version.js +4 -0
  206. package/build/esm/Version.js.map +1 -0
  207. package/build/esm/Xdg.js +15 -0
  208. package/build/esm/Xdg.js.map +1 -0
  209. package/build/esm/main.js +122 -14
  210. package/build/esm/main.js.map +1 -1
  211. package/build/skills/expandai/SKILL.md +26 -3
  212. package/package.json +4 -4
  213. package/src/ApiClient.ts +206 -13
  214. package/src/Auth/Domain.ts +63 -0
  215. package/src/Auth/ManualAuthorization.ts +296 -0
  216. package/src/Auth/PendingInit.ts +338 -0
  217. package/src/Auth/Pkce.ts +26 -0
  218. package/src/Auth/SetupGrantAuthorization.ts +116 -0
  219. package/src/Authentication.ts +49 -11
  220. package/src/BrowserOAuth.ts +10 -12
  221. package/src/CommandPath.ts +30 -0
  222. package/src/Commands/Fetch.ts +42 -11
  223. package/src/Commands/Init.ts +489 -0
  224. package/src/Commands/Search.ts +8 -3
  225. package/src/Commands/Status.ts +228 -0
  226. package/src/Commands/Telemetry.ts +42 -0
  227. package/src/Config.ts +695 -50
  228. package/src/Hook/Claude.ts +1 -1
  229. package/src/Hook/SnapshotNote.ts +55 -7
  230. package/src/Init/ClaudeAdapter.ts +191 -0
  231. package/src/Init/ClaudeProfile.ts +496 -0
  232. package/src/Init/Domain.ts +153 -0
  233. package/src/Init/HarnessAdapter.ts +35 -0
  234. package/src/Init/HarnessDetector.ts +77 -0
  235. package/src/Init/HarnessRegistry.ts +87 -0
  236. package/src/Init/MutationTransaction.ts +747 -0
  237. package/src/Init/Service.ts +385 -0
  238. package/src/Init/StandardAdapters.ts +141 -0
  239. package/src/Init/Status.ts +253 -0
  240. package/src/Install/Claude/Hook.ts +234 -81
  241. package/src/Install/Claude/Mcp.ts +80 -33
  242. package/src/Install/Claude/Skill.ts +70 -37
  243. package/src/Install/Codex/Mcp.ts +14 -9
  244. package/src/Install/Codex/Skill.ts +9 -5
  245. package/src/Install/Cursor/Mcp.ts +12 -9
  246. package/src/Install/Cursor/Skill.ts +9 -5
  247. package/src/Install/Domain.ts +77 -4
  248. package/src/Install/Hook.ts +3 -3
  249. package/src/Install/Mcp.ts +6 -6
  250. package/src/Install/Opencode/Hook.ts +5 -5
  251. package/src/Install/Opencode/Mcp.ts +11 -8
  252. package/src/Install/Opencode/Skill.ts +9 -5
  253. package/src/Install/Opencode/expandai-webfetch.js +27 -9
  254. package/src/Install/Skill.ts +3 -3
  255. package/src/Install/Uninstall.ts +3 -3
  256. package/src/MarkdownAnsi.ts +162 -0
  257. package/src/Mcp/ExpandDocs.ts +46 -55
  258. package/src/Mcp/Server.ts +2 -1
  259. package/src/Mcp/tools/Citations.ts +117 -8
  260. package/src/Mcp/tools/Fetch.ts +40 -14
  261. package/src/SecureFile.ts +70 -0
  262. package/src/Snippets.ts +19 -12
  263. package/src/Telemetry.ts +186 -0
  264. package/src/Version.ts +3 -0
  265. package/src/Xdg.ts +18 -0
  266. package/src/main.ts +171 -12
  267. package/build/dts/DeviceAuth.d.ts +0 -87
  268. package/build/dts/DeviceAuth.d.ts.map +0 -1
  269. package/build/esm/DeviceAuth.js +0 -127
  270. package/build/esm/DeviceAuth.js.map +0 -1
  271. package/src/DeviceAuth.ts +0 -193
@@ -0,0 +1,267 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { FileSystem, Path, Error as PlatformError } from '@effect/platform';
3
+ import { NodeContext } from '@effect/platform-node';
4
+ import { DateTime, Effect, Either, Option, Schema } from 'effect';
5
+ import { xdgConfigDirectory } from '../Xdg.js';
6
+ import { CLI_AUTHORIZATION_CLIENT_ID, PendingInitCorrupt, PendingInitRecord, PendingInitStorageError, } from './Domain.js';
7
+ const PendingInitRecordJson = Schema.parseJson(PendingInitRecord);
8
+ const COMPLETION_CODE_RETENTION_GRACE_MS = 5 * 60 * 1_000;
9
+ const PendingInitKeyTupleJson = Schema.parseJson(Schema.Tuple(Schema.String, Schema.Literal(CLI_AUTHORIZATION_CLIENT_ID), Schema.Literal('agent-harnesses'), Schema.Literal('project', 'global'), Schema.String));
10
+ function isErrnoCode(input, code) {
11
+ return typeof input === 'object' && input !== null && 'code' in input && input.code === code;
12
+ }
13
+ function isAlreadyExists(error) {
14
+ return (error instanceof PlatformError.SystemError &&
15
+ (error.reason === 'AlreadyExists' || isErrnoCode(error.cause, 'EEXIST')));
16
+ }
17
+ /** Canonical, collision-free serialization of all pending authorization key fields. */
18
+ export function serializePendingInitKey(key) {
19
+ return Schema.encodeSync(PendingInitKeyTupleJson)([
20
+ key.apiOrigin,
21
+ key.clientId,
22
+ key.harness,
23
+ key.scope,
24
+ key.canonicalScopeRoot,
25
+ ]);
26
+ }
27
+ function ancestorPaths(path, current) {
28
+ const parent = path.dirname(current);
29
+ return parent === current ? [current] : [current, ...ancestorPaths(path, parent)];
30
+ }
31
+ export class PendingInitStore extends Effect.Service()('@expandai/cli/Auth/PendingInitStore', {
32
+ dependencies: [NodeContext.layer],
33
+ effect: Effect.gen(function* () {
34
+ const fileSystem = yield* FileSystem.FileSystem;
35
+ const path = yield* Path.Path;
36
+ const configDirectory = path.join(yield* xdgConfigDirectory, 'expandai');
37
+ const pendingPath = path.join(configDirectory, 'pending-init');
38
+ const storageError = (targetPath, error) => new PendingInitStorageError({ path: targetPath, error });
39
+ /**
40
+ * Maps a platform failure to the domain error for `targetPath`.
41
+ *
42
+ * Deliberately applied per call rather than once around each function: a
43
+ * blanket `mapError` over a whole `Effect.fn` body would also swallow
44
+ * `PendingInitCorrupt`, which `decodeRecord` raises and which `Status.ts`
45
+ * reports differently — corrupt state is recoverable by deleting the file,
46
+ * a storage failure is not.
47
+ */
48
+ const onStorage = (targetPath, effect) => effect.pipe(Effect.mapError((error) => storageError(targetPath, error)));
49
+ const assertNotSymbolicLink = Effect.fnUntraced(function* (targetPath) {
50
+ const result = yield* Effect.either(fileSystem.readLink(targetPath));
51
+ if (Either.isRight(result)) {
52
+ return yield* storageError(targetPath, new Error('Symbolic links are not allowed for pending authorization'));
53
+ }
54
+ const error = result.left;
55
+ if (error instanceof PlatformError.SystemError &&
56
+ (error.reason === 'InvalidData' ||
57
+ error.reason === 'NotFound' ||
58
+ (error.method === 'readLink' && isErrnoCode(error.cause, 'EINVAL')))) {
59
+ return;
60
+ }
61
+ return yield* storageError(targetPath, error);
62
+ });
63
+ const ensureDirectory = Effect.fnUntraced(function* () {
64
+ yield* assertNotSymbolicLink(configDirectory);
65
+ yield* assertNotSymbolicLink(pendingPath);
66
+ yield* onStorage(pendingPath, fileSystem.makeDirectory(pendingPath, { recursive: true, mode: 0o700 }));
67
+ yield* assertNotSymbolicLink(configDirectory);
68
+ yield* assertNotSymbolicLink(pendingPath);
69
+ const directoryInfo = yield* onStorage(pendingPath, fileSystem.stat(pendingPath));
70
+ if (directoryInfo.type !== 'Directory') {
71
+ return yield* storageError(pendingPath, new Error('Pending authorization path is not a directory'));
72
+ }
73
+ if (process.platform !== 'win32') {
74
+ yield* onStorage(configDirectory, fileSystem.chmod(configDirectory, 0o700));
75
+ yield* onStorage(pendingPath, fileSystem.chmod(pendingPath, 0o700));
76
+ }
77
+ });
78
+ const recordPath = (key) => {
79
+ const digest = createHash('sha256').update(serializePendingInitKey(key)).digest('hex');
80
+ return path.join(pendingPath, `${digest}.json`);
81
+ };
82
+ const decodeRecord = Effect.fnUntraced(function* (targetPath, encoded) {
83
+ const record = yield* Schema.decode(PendingInitRecordJson)(encoded).pipe(Effect.mapError(() => new PendingInitCorrupt({
84
+ path: targetPath,
85
+ error: new Error('Stored pending authorization did not match the expected schema'),
86
+ })));
87
+ if (recordPath(record.key) !== targetPath) {
88
+ return yield* new PendingInitCorrupt({
89
+ path: targetPath,
90
+ error: new Error('Stored pending authorization key did not match its file'),
91
+ });
92
+ }
93
+ return record;
94
+ });
95
+ const readRecordPath = Effect.fnUntraced(function* (targetPath) {
96
+ yield* assertNotSymbolicLink(targetPath);
97
+ const exists = yield* onStorage(targetPath, fileSystem.exists(targetPath));
98
+ if (!exists)
99
+ return Option.none();
100
+ const info = yield* onStorage(targetPath, fileSystem.stat(targetPath));
101
+ if (info.type !== 'File')
102
+ return yield* storageError(targetPath, new Error('Pending state is not a regular file'));
103
+ if (process.platform !== 'win32' && (info.mode & 0o777) !== 0o600) {
104
+ return yield* storageError(targetPath, new Error('Pending state file mode is not 600'));
105
+ }
106
+ const encoded = yield* onStorage(targetPath, fileSystem.readFileString(targetPath));
107
+ return Option.some(yield* decodeRecord(targetPath, encoded));
108
+ });
109
+ const readRecord = (key) => readRecordPath(recordPath(key));
110
+ const isExpired = Effect.fnUntraced(function* (record) {
111
+ const now = DateTime.toEpochMillis(yield* DateTime.now);
112
+ return DateTime.toEpochMillis(record.approvalExpiresAt) + COMPLETION_CODE_RETENTION_GRACE_MS <= now;
113
+ });
114
+ const writeExclusive = Effect.fnUntraced(function* (record) {
115
+ yield* ensureDirectory();
116
+ const targetPath = recordPath(record.key);
117
+ const encoded = yield* Schema.encode(PendingInitRecordJson)(record).pipe(Effect.mapError((error) => storageError(targetPath, error)));
118
+ const created = yield* Effect.either(Effect.scoped(Effect.gen(function* () {
119
+ const file = yield* fileSystem.open(targetPath, { flag: 'wx', mode: 0o600 });
120
+ yield* file.writeAll(new TextEncoder().encode(encoded));
121
+ yield* file.sync;
122
+ })));
123
+ if (Either.isLeft(created)) {
124
+ // `wx` means an existing file belongs to another attempt — leave it.
125
+ if (isAlreadyExists(created.left))
126
+ return false;
127
+ // Anything else failed *after* we created the file, so a zero-byte or
128
+ // half-written record is now sitting at the final path. Left there it
129
+ // fails every later read of this key, and interrupting between `open`
130
+ // and `writeAll` is enough to cause it.
131
+ yield* fileSystem.remove(targetPath).pipe(Effect.ignore);
132
+ return yield* storageError(targetPath, created.left);
133
+ }
134
+ if (process.platform !== 'win32') {
135
+ const chmodResult = yield* Effect.either(fileSystem.chmod(targetPath, 0o600));
136
+ if (Either.isLeft(chmodResult)) {
137
+ yield* fileSystem.remove(targetPath).pipe(Effect.ignore);
138
+ return yield* storageError(targetPath, chmodResult.left);
139
+ }
140
+ }
141
+ return true;
142
+ });
143
+ /**
144
+ * Publishes `next` over the record for the same key, atomically.
145
+ *
146
+ * The caller used to do `removeIfMatch` then `putIfAbsent`. An interrupt
147
+ * between those two — Ctrl-C during the poll that runs on *every* `expandai
148
+ * init` while authorization is pending — deleted the pending record and
149
+ * wrote nothing back, so the next run minted a fresh code and asked the user
150
+ * to approve again with no error to explain it.
151
+ *
152
+ * `rename` over the target is atomic on POSIX, so an interrupt either leaves
153
+ * the old record or completes the new one. It is **not** a cross-process
154
+ * compare-and-swap: another process can publish between our read and our
155
+ * rename, and ours wins. That race is pre-existing and unaddressed here —
156
+ * closing it needs a per-key lock, and Windows replacement semantics need
157
+ * checking first. The name says `IfMatch` because it verifies before
158
+ * writing, not because it is a CAS.
159
+ */
160
+ const replaceIfMatch = Effect.fn('PendingInitStore.replaceIfMatch')(function* (previous, next) {
161
+ yield* ensureDirectory();
162
+ const targetPath = recordPath(next.key);
163
+ if (recordPath(previous.key) !== targetPath) {
164
+ return yield* storageError(targetPath, new Error('Pending authorization replacement changed its key'));
165
+ }
166
+ const current = yield* readRecordPath(targetPath);
167
+ if (Option.isNone(current) || current.value.userCode !== previous.userCode)
168
+ return false;
169
+ const encoded = yield* Schema.encode(PendingInitRecordJson)(next).pipe(Effect.mapError((error) => storageError(targetPath, error)));
170
+ // Same directory, so `rename` stays on one filesystem and is atomic.
171
+ //
172
+ // `acquireRelease` rather than manual cleanup on the typed-failure branches:
173
+ // a defect or an interrupt bypasses those branches entirely, and an
174
+ // abandoned `.tmp` is worse than it looks — it is a second copy of a live
175
+ // credential, `pruneExpired` only scans `.json` records so it never reaps
176
+ // one, and a fixed name made the next attempt fail on its own leftovers.
177
+ const staging = `${targetPath}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
178
+ const published = yield* Effect.scoped(Effect.acquireRelease(Effect.succeed(staging), () => fileSystem.remove(staging).pipe(Effect.ignore)).pipe(Effect.zipRight(Effect.gen(function* () {
179
+ yield* assertNotSymbolicLink(staging);
180
+ yield* Effect.scoped(Effect.gen(function* () {
181
+ const file = yield* fileSystem.open(staging, { flag: 'wx', mode: 0o600 });
182
+ yield* file.writeAll(new TextEncoder().encode(encoded));
183
+ yield* file.sync;
184
+ }));
185
+ yield* fileSystem.rename(staging, targetPath);
186
+ })))).pipe(Effect.either);
187
+ if (Either.isLeft(published))
188
+ return yield* storageError(targetPath, published.left);
189
+ return true;
190
+ });
191
+ const removeIfCurrent = Effect.fnUntraced(function* (record) {
192
+ const targetPath = recordPath(record.key);
193
+ const current = yield* readRecordPath(targetPath);
194
+ if (Option.isNone(current) || current.value.userCode !== record.userCode)
195
+ return false;
196
+ yield* onStorage(targetPath, fileSystem.remove(targetPath));
197
+ return true;
198
+ });
199
+ const get = Effect.fn('PendingInitStore.get')(function* (key) {
200
+ const record = yield* readRecord(key);
201
+ if (Option.isNone(record) || !(yield* isExpired(record.value)))
202
+ return record;
203
+ yield* removeIfCurrent(record.value);
204
+ return Option.none();
205
+ });
206
+ const peek = Effect.fn('PendingInitStore.peek')(function* (key) {
207
+ return yield* readRecord(key);
208
+ });
209
+ const putIfAbsent = Effect.fn('PendingInitStore.putIfAbsent')(function* (record) {
210
+ const existing = yield* readRecord(record.key);
211
+ if (Option.isSome(existing) && !(yield* isExpired(existing.value)))
212
+ return existing.value;
213
+ if (Option.isSome(existing))
214
+ yield* removeIfCurrent(existing.value);
215
+ if (yield* writeExclusive(record))
216
+ return record;
217
+ const winner = yield* readRecord(record.key);
218
+ return yield* Option.match(winner, {
219
+ onNone: () => storageError(recordPath(record.key), new Error('Pending record creation lost without a winner')),
220
+ onSome: Effect.succeed,
221
+ });
222
+ });
223
+ const removeIfMatch = Effect.fn('PendingInitStore.removeIfMatch')((record) => removeIfCurrent(record));
224
+ const pruneExpired = Effect.fn('PendingInitStore.pruneExpired')(function* () {
225
+ yield* assertNotSymbolicLink(pendingPath);
226
+ const exists = yield* onStorage(pendingPath, fileSystem.exists(pendingPath));
227
+ if (!exists) {
228
+ return 0;
229
+ }
230
+ const entries = yield* onStorage(pendingPath, fileSystem.readDirectory(pendingPath));
231
+ const removed = yield* Effect.forEach(entries.filter((entry) => entry.endsWith('.json')), (entry) => Effect.gen(function* () {
232
+ const record = yield* readRecordPath(path.join(pendingPath, entry));
233
+ if (Option.isNone(record) || !(yield* isExpired(record.value))) {
234
+ return 0;
235
+ }
236
+ return (yield* removeIfCurrent(record.value)) ? 1 : 0;
237
+ // One unreadable entry must not fail the sweep. Pruning runs
238
+ // on every begin, resume and refreshStatus, so a single
239
+ // corrupt or newer-format file would otherwise block all
240
+ // three — including the resume that would have recovered.
241
+ }).pipe(Effect.orElseSucceed(() => 0)));
242
+ return removed.reduce((total, count) => total + count, 0);
243
+ });
244
+ const canonicalScopeRoot = Effect.fn('PendingInitStore.canonicalScopeRoot')(function* (scope, cwd = process.cwd()) {
245
+ if (scope === 'global')
246
+ return 'global';
247
+ const canonicalCwd = yield* onStorage(cwd, fileSystem.realPath(cwd));
248
+ const inspected = yield* Effect.forEach(ancestorPaths(path, canonicalCwd), (candidate) => onStorage(candidate, fileSystem.exists(path.join(candidate, '.git'))).pipe(Effect.map((exists) => ({ candidate, exists }))));
249
+ const repositoryRoot = inspected.find(({ exists }) => exists)?.candidate;
250
+ return repositoryRoot === undefined
251
+ ? canonicalCwd
252
+ : yield* onStorage(repositoryRoot, fileSystem.realPath(repositoryRoot));
253
+ });
254
+ return {
255
+ pendingPath,
256
+ get,
257
+ peek,
258
+ putIfAbsent,
259
+ replaceIfMatch,
260
+ removeIfMatch,
261
+ pruneExpired,
262
+ canonicalScopeRoot,
263
+ };
264
+ }),
265
+ }) {
266
+ }
267
+ //# sourceMappingURL=PendingInit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PendingInit.js","sourceRoot":"","sources":["../../../src/Auth/PendingInit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { Redacted } from 'effect';
3
+ /**
4
+ * RFC 7636 requires 43–128 characters of unreserved alphabet; 32 random bytes
5
+ * encode to 43 base64url characters, which is also what the API's
6
+ * `^[A-Za-z0-9_-]{43}$` challenge check expects.
7
+ */
8
+ const VERIFIER_BYTES = 32;
9
+ /** PKCE code verifier. Redacted so it cannot reach a log or a JSON result. */
10
+ export function createCodeVerifier() {
11
+ return Redacted.make(randomBytes(VERIFIER_BYTES).toString('base64url'));
12
+ }
13
+ export function codeChallengeFor(verifier) {
14
+ return createHash('sha256').update(Redacted.value(verifier)).digest('base64url');
15
+ }
16
+ /**
17
+ * Opaque high-entropy value for non-PKCE uses such as the OAuth `state`
18
+ * parameter. Same entropy and encoding, different purpose.
19
+ */
20
+ export function createHighEntropySecret() {
21
+ return randomBytes(VERIFIER_BYTES).toString('base64url');
22
+ }
23
+ //# sourceMappingURL=Pkce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pkce.js","sourceRoot":"","sources":["../../../src/Auth/Pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC;;;;GAIG;AACH,MAAM,cAAc,GAAG,EAAE,CAAA;AAEzB,8EAA8E;AAC9E,MAAM,UAAU,kBAAkB;IACjC,OAAO,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAA;AACxE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAmC;IACnE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACtC,OAAO,WAAW,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AACzD,CAAC"}
@@ -0,0 +1,91 @@
1
+ import { Effect, Schema } from 'effect';
2
+ import { ConfigManager } from '../Config.js';
3
+ import { ManualAuthorization, ManualAuthorizationCredentialSaveFailed, ManualAuthorizationDenied, ManualAuthorizationRefused, } from './ManualAuthorization.js';
4
+ export class SetupGrantAuthorizationError extends Schema.TaggedError()('SetupGrantAuthorizationError', {
5
+ code: Schema.Literal('invalid_or_expired', 'stored_credential_conflict', 'request_failed', 'denied', 'credential_save_failed', 'refused', 'config_unreadable'),
6
+ recoverable: Schema.Boolean,
7
+ }) {
8
+ get message() {
9
+ switch (this.code) {
10
+ // Checked before redemption, so the token is untouched and the user can
11
+ // retry with the same one after clearing the stored credential.
12
+ case 'stored_credential_conflict':
13
+ return 'An Expand API key is already stored on this machine, and completing this setup would replace it with no way to recover it. Nothing was changed and your setup prompt is still valid. Run `expandai logout` to clear the stored key, then run this setup command again.';
14
+ case 'request_failed':
15
+ return 'Could not reach Expand to redeem the setup token. Nothing was sent, so the same token is still good — check the network and try again.';
16
+ case 'denied':
17
+ return 'This setup authorization was denied. Copy a fresh setup prompt from the Expand dashboard.';
18
+ // Checked before redemption, so like stored_credential_conflict the token is
19
+ // untouched and the same prompt works once the file is dealt with.
20
+ case 'config_unreadable':
21
+ return 'The Expand config file on this machine could not be read, so setup cannot tell whether completing it would overwrite an existing API key. Nothing was redeemed and your setup prompt is still valid. Run `expandai whoami` to see which file it is, fix or delete it, then run this setup command again.';
22
+ // Server-side misconfiguration: a fresh prompt is refused identically.
23
+ case 'refused':
24
+ return 'Expand refused to redeem this setup token for a reason a fresh prompt cannot fix — most likely an OAuth client, resource, or scope misconfiguration on the server. Do not request another prompt; report this.';
25
+ // The token redeemed — it is spent — but the credentials could not be
26
+ // written locally. A fresh prompt hits the same wall until the local
27
+ // storage problem is fixed, so say that instead of blaming the token.
28
+ case 'credential_save_failed':
29
+ return 'The setup token was redeemed, but the credentials could not be saved on this machine. Fix credential storage — run `expandai whoami` to see the path it uses, and check its permissions — then copy a fresh setup prompt from the Expand dashboard.';
30
+ default:
31
+ return 'The setup token is invalid, expired, or already used. Copy a fresh setup prompt from the Expand dashboard.';
32
+ }
33
+ }
34
+ }
35
+ function setupGrantError(code) {
36
+ return new SetupGrantAuthorizationError({
37
+ code,
38
+ recoverable: code !== 'denied' && code !== 'credential_save_failed' && code !== 'refused',
39
+ });
40
+ }
41
+ /**
42
+ * Dashboard-minted setup: the "token" in the prompt is a pre-approved RFC 8628
43
+ * device code, already bound to the minting user and pinned to their
44
+ * organization server-side. Redeeming is a single poll of the OAuth token
45
+ * endpoint — better-auth owns generation, expiry and the single-use burn, so
46
+ * there is no redemption protocol of ours left to get wrong.
47
+ */
48
+ export class SetupGrantAuthorization extends Effect.Service()('@expandai/cli/Auth/SetupGrantAuthorization', {
49
+ dependencies: [ConfigManager.Default, ManualAuthorization.Default],
50
+ effect: Effect.gen(function* () {
51
+ const configManager = yield* ConfigManager;
52
+ const manualAuthorization = yield* ManualAuthorization;
53
+ const authorize = Effect.fn('SetupGrantAuthorization.authorize')(function* (input) {
54
+ // The *file*, specifically. `loadConfig` collapses env and file into one
55
+ // value, so it cannot tell an ambient `EXPAND_API_KEY` — which must
56
+ // never block setup — from a key written by `expandai login`, which
57
+ // saving new credentials would overwrite with no way to recover it.
58
+ //
59
+ // Refused before redemption: nothing has been spent, so the same prompt
60
+ // still works once the user clears the stored key.
61
+ // `readConfigFile` never fails — it reports an unreadable file through
62
+ // `parseFailed` and hands back an empty config — so `Effect.either` here was
63
+ // always Right and the ApiKey check silently passed on a corrupt file. That
64
+ // matters: redemption is single-use, so proceeding would spend the token and
65
+ // *then* fail to save. Refuse first, while the prompt is still good.
66
+ const storedFile = yield* configManager.readConfigFile();
67
+ if (storedFile.parseFailed)
68
+ return yield* setupGrantError('config_unreadable');
69
+ if (storedFile.config.authMethod?._tag === 'ApiKey') {
70
+ return yield* setupGrantError('stored_credential_conflict');
71
+ }
72
+ yield* Effect.annotateCurrentSpan({ harness: input.harness, scope: input.scope });
73
+ yield* manualAuthorization.redeemPreApproved(input.setupToken).pipe(Effect.mapError((error) => {
74
+ if (error instanceof ManualAuthorizationDenied)
75
+ return setupGrantError('denied');
76
+ if (error instanceof ManualAuthorizationCredentialSaveFailed) {
77
+ return setupGrantError('credential_save_failed');
78
+ }
79
+ if (error instanceof ManualAuthorizationRefused)
80
+ return setupGrantError('refused');
81
+ if (error._tag === 'ManualAuthorizationApiError' && error.kind === 'request_failed') {
82
+ return setupGrantError('request_failed');
83
+ }
84
+ return setupGrantError('invalid_or_expired');
85
+ }));
86
+ });
87
+ return { authorize };
88
+ }),
89
+ }) {
90
+ }
91
+ //# sourceMappingURL=SetupGrantAuthorization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SetupGrantAuthorization.js","sourceRoot":"","sources":["../../../src/Auth/SetupGrantAuthorization.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,10 @@ import { Command, Prompt } from '@effect/cli';
2
2
  import { Console, DateTime, Duration, Effect, Either, Match, Schema, String } from 'effect';
3
3
  import open from 'open';
4
4
  import { BrowserOAuthService } from './BrowserOAuth.js';
5
- import { ApiKey, ApiKeyConfig, ConfigManager } from './Config.js';
5
+ import { ApiKey, ApiKeyConfig, ConfigManager, isAuthMethodLocallyUsable } from './Config.js';
6
+ // Re-exported: these moved to Config.ts, where the credential types live, so
7
+ // that credential precedence can consult usability without a cycle.
8
+ export { authMethodState, isAuthMethodLocallyUsable } from './Config.js';
6
9
  import { Uninstall } from './Install/Uninstall.js';
7
10
  /** Render a user-facing description of the current auth state. Pure given DateTime.now. */
8
11
  export const formatAuthStatus = Effect.fn('Authentication.formatAuthStatus')(function* (authMethod) {
@@ -21,6 +24,17 @@ export class Authentication extends Effect.Service()('@expandai/cli/Authenticati
21
24
  const configManager = yield* ConfigManager;
22
25
  const browserOAuth = yield* BrowserOAuthService;
23
26
  const uninstall = yield* Uninstall;
27
+ const hasLocallyUsableCredentials = Effect.fn('Authentication.hasLocallyUsableCredentials')(function* () {
28
+ yield* configManager.ensureSecureStorage();
29
+ const loaded = yield* Effect.either(configManager.loadConfig());
30
+ if (Either.isLeft(loaded)) {
31
+ if (loaded.left._tag === 'NotAuthenticated')
32
+ return false;
33
+ return yield* Effect.fail(loaded.left);
34
+ }
35
+ const now = yield* DateTime.now;
36
+ return isAuthMethodLocallyUsable(loaded.right.authMethod, now);
37
+ });
24
38
  const apiKeyLogin = Effect.fn('Authentication.apiKeyLogin')(function* () {
25
39
  const apiKey = yield* Prompt.password({ message: 'Enter your API Key' });
26
40
  yield* configManager.updateConfig({
@@ -28,11 +42,30 @@ export class Authentication extends Effect.Service()('@expandai/cli/Authenticati
28
42
  });
29
43
  yield* Console.log('API key saved successfully!');
30
44
  });
31
- const oauthLogin = Effect.fn('Authentication.oauthLogin')(function* () {
45
+ const browserLogin = Effect.fnUntraced(function* (showAuthorizeUrl) {
32
46
  yield* Console.log(String.stripMargin(`
33
47
  |
34
48
  |Starting browser authorization...`));
35
- yield* browserOAuth.login((authorizeUrl) => Effect.gen(function* () {
49
+ yield* browserOAuth.login(showAuthorizeUrl);
50
+ yield* Console.log(String.stripMargin(`
51
+ |
52
+ |Successfully authenticated!`));
53
+ });
54
+ const printAuthorizeUrl = Effect.fnUntraced(function* (authorizeUrl) {
55
+ yield* Console.log(String.stripMargin(`
56
+ |
57
+ |Open this URL in a browser:
58
+ | ${authorizeUrl.toString()}
59
+ |
60
+ |Waiting for browser authorization...`));
61
+ });
62
+ // `init` already asks for harness and scope. Keep browser login on the
63
+ // existing loopback OAuth path without introducing a third Prompt choice.
64
+ const loginWithBrowser = Effect.fn('Authentication.loginWithBrowser')(function* () {
65
+ yield* browserLogin(printAuthorizeUrl);
66
+ });
67
+ const oauthLogin = Effect.fn('Authentication.oauthLogin')(function* () {
68
+ yield* browserLogin((authorizeUrl) => Effect.gen(function* () {
36
69
  yield* Console.log(String.stripMargin(`
37
70
  |
38
71
  |Open this URL in a browser:
@@ -46,11 +79,9 @@ export class Authentication extends Effect.Service()('@expandai/cli/Authenticati
46
79
  |
47
80
  |Waiting for browser authorization...`));
48
81
  }));
49
- yield* Console.log(String.stripMargin(`
50
- |
51
- |Successfully authenticated!`));
52
82
  });
53
83
  const login = Effect.fn('Authentication.login')(function* () {
84
+ yield* configManager.ensureSecureStorage();
54
85
  const existing = yield* Effect.either(configManager.loadConfig());
55
86
  if (Either.isRight(existing)) {
56
87
  const status = yield* formatAuthStatus(existing.right.authMethod);
@@ -102,7 +133,7 @@ export class Authentication extends Effect.Service()('@expandai/cli/Authenticati
102
133
  }
103
134
  yield* Console.log('Logged out.');
104
135
  });
105
- return { login, whoami, logout };
136
+ return { hasLocallyUsableCredentials, loginWithBrowser, login, whoami, logout };
106
137
  }),
107
138
  }) {
108
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Authentication.js","sourceRoot":"","sources":["../../src/Authentication.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Authentication.js","sourceRoot":"","sources":["../../src/Authentication.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { createHash, randomBytes } from 'node:crypto';
2
1
  import { createServer } from 'node:http';
3
2
  import { HttpClient, HttpClientRequest, HttpClientResponse } from '@effect/platform';
4
3
  import { NodeHttpClient } from '@effect/platform-node';
5
4
  import { DateTime, Duration, Effect, Inspectable, Redacted, Schema } from 'effect';
5
+ import { codeChallengeFor, createHighEntropySecret } from './Auth/Pkce.js';
6
6
  import { ConfigManager, OAuthConfig } from './Config.js';
7
7
  const CLIENT_ID = 'expandai-cli';
8
8
  const OAUTH_AUTHORIZE_ENDPOINT = '/api/auth/oauth2/authorize';
@@ -62,14 +62,8 @@ export class BrowserOAuthTimedOut extends Schema.TaggedError()('BrowserOAuthTime
62
62
  return 'OAuth browser login timed out before the local callback completed.';
63
63
  }
64
64
  }
65
- function base64Url(bytes) {
66
- return Buffer.from(bytes).toString('base64').replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
67
- }
68
- function createHighEntropySecret() {
69
- return base64Url(randomBytes(32));
70
- }
71
65
  export const createCodeChallenge = Effect.fn('BrowserOAuth.createCodeChallenge')(function* (codeVerifier) {
72
- return base64Url(createHash('sha256').update(codeVerifier).digest());
66
+ return codeChallengeFor(Redacted.make(codeVerifier));
73
67
  });
74
68
  export const buildAuthorizeUrl = Effect.fn('BrowserOAuth.buildAuthorizeUrl')(function* ({ apiUrl, redirectUri, state, codeChallenge, }) {
75
69
  const authorizeUrl = new URL(OAUTH_AUTHORIZE_ENDPOINT, apiUrl);
@@ -179,6 +173,7 @@ export class BrowserOAuthService extends Effect.Service()('@expandai/cli/Browser
179
173
  const httpClient = yield* HttpClient.HttpClient;
180
174
  const configManager = yield* ConfigManager;
181
175
  const exchangeToken = Effect.fn('BrowserOAuthService.exchangeToken')(function* (form) {
176
+ yield* configManager.ensureSecureStorage();
182
177
  const apiUrl = yield* configManager.getApiUrl();
183
178
  const resource = apiUrl.toString();
184
179
  const request = HttpClientRequest.post(new URL(OAUTH_TOKEN_ENDPOINT, apiUrl)).pipe(HttpClientRequest.bodyUrlParams([...form, ['resource', resource]]));
@@ -204,10 +199,10 @@ export class BrowserOAuthService extends Effect.Service()('@expandai/cli/Browser
204
199
  ['code_verifier', codeVerifier],
205
200
  ]);
206
201
  });
207
- const refreshToken = Effect.fn('BrowserOAuthService.refreshToken')(function* (refreshToken) {
202
+ const refreshToken = Effect.fn('BrowserOAuthService.refreshToken')(function* (refreshToken, clientId = CLIENT_ID) {
208
203
  return yield* exchangeToken([
209
204
  ['grant_type', 'refresh_token'],
210
- ['client_id', CLIENT_ID],
205
+ ['client_id', clientId],
211
206
  ['refresh_token', refreshToken],
212
207
  ]);
213
208
  });
@@ -219,10 +214,12 @@ export class BrowserOAuthService extends Effect.Service()('@expandai/cli/Browser
219
214
  accessToken: Redacted.make(tokenResponse.access_token),
220
215
  refreshToken: tokenResponse.refresh_token === undefined ? undefined : Redacted.make(tokenResponse.refresh_token),
221
216
  expiresAt,
217
+ clientId: CLIENT_ID,
222
218
  }),
223
219
  });
224
220
  });
225
221
  const login = Effect.fn('BrowserOAuthService.login')(function* (openAuthorizeUrl) {
222
+ yield* configManager.ensureSecureStorage();
226
223
  const codeVerifier = createHighEntropySecret();
227
224
  const state = createHighEntropySecret();
228
225
  const codeChallenge = yield* createCodeChallenge(codeVerifier);