@claudetools/cli 0.13.12 → 0.13.15

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 (368) hide show
  1. package/dist/__tests__/factories.d.ts +173 -0
  2. package/dist/__tests__/factories.d.ts.map +1 -0
  3. package/dist/__tests__/factories.js +150 -0
  4. package/dist/__tests__/factories.js.map +1 -0
  5. package/dist/__tests__/helpers.d.ts +36 -0
  6. package/dist/__tests__/helpers.d.ts.map +1 -0
  7. package/dist/__tests__/helpers.js +52 -0
  8. package/dist/__tests__/helpers.js.map +1 -0
  9. package/dist/analytics/index.d.ts +14 -0
  10. package/dist/analytics/index.d.ts.map +1 -0
  11. package/dist/analytics/index.js +259 -0
  12. package/dist/analytics/index.js.map +1 -0
  13. package/dist/analytics/session.d.ts +17 -0
  14. package/dist/analytics/session.d.ts.map +1 -0
  15. package/dist/analytics/session.js +130 -0
  16. package/dist/analytics/session.js.map +1 -0
  17. package/dist/analytics/token-tracker.d.ts +48 -0
  18. package/dist/analytics/token-tracker.d.ts.map +1 -0
  19. package/dist/analytics/token-tracker.js +269 -0
  20. package/dist/analytics/token-tracker.js.map +1 -0
  21. package/dist/analytics/tracker.d.ts +33 -0
  22. package/dist/analytics/tracker.d.ts.map +1 -0
  23. package/dist/analytics/tracker.js +210 -0
  24. package/dist/analytics/tracker.js.map +1 -0
  25. package/dist/api-keys/index.d.ts +15 -0
  26. package/dist/api-keys/index.d.ts.map +1 -0
  27. package/dist/api-keys/index.js +228 -0
  28. package/dist/api-keys/index.js.map +1 -0
  29. package/dist/auth/config.d.ts +15 -0
  30. package/dist/auth/config.d.ts.map +1 -0
  31. package/dist/auth/config.js +67 -0
  32. package/dist/auth/config.js.map +1 -0
  33. package/dist/auth/index.d.ts +8 -0
  34. package/dist/auth/index.d.ts.map +1 -0
  35. package/dist/auth/index.js +299 -0
  36. package/dist/auth/index.js.map +1 -0
  37. package/dist/auth/keychain.d.ts +21 -0
  38. package/dist/auth/keychain.d.ts.map +1 -0
  39. package/dist/auth/keychain.js +256 -0
  40. package/dist/auth/keychain.js.map +1 -0
  41. package/dist/billing/index.d.ts +7 -0
  42. package/dist/billing/index.d.ts.map +1 -0
  43. package/dist/billing/index.js +233 -0
  44. package/dist/billing/index.js.map +1 -0
  45. package/dist/cli.js +291 -43
  46. package/dist/cli.js.map +1 -1
  47. package/dist/commands/hook.d.ts +12 -0
  48. package/dist/commands/hook.d.ts.map +1 -0
  49. package/dist/commands/hook.js +190 -0
  50. package/dist/commands/hook.js.map +1 -0
  51. package/dist/commands/keys.d.ts +4 -0
  52. package/dist/commands/keys.d.ts.map +1 -0
  53. package/dist/commands/keys.js +43 -0
  54. package/dist/commands/keys.js.map +1 -0
  55. package/dist/commands/mcp.d.ts +4 -0
  56. package/dist/commands/mcp.d.ts.map +1 -0
  57. package/dist/commands/mcp.js +43 -0
  58. package/dist/commands/mcp.js.map +1 -0
  59. package/dist/commands/project.d.ts +4 -0
  60. package/dist/commands/project.d.ts.map +1 -0
  61. package/dist/commands/project.js +68 -0
  62. package/dist/commands/project.js.map +1 -0
  63. package/dist/commands/skill.d.ts +4 -0
  64. package/dist/commands/skill.d.ts.map +1 -0
  65. package/dist/commands/skill.js +37 -0
  66. package/dist/commands/skill.js.map +1 -0
  67. package/dist/commands/stacks.d.ts +4 -0
  68. package/dist/commands/stacks.d.ts.map +1 -0
  69. package/dist/commands/stacks.js +103 -0
  70. package/dist/commands/stacks.js.map +1 -0
  71. package/dist/commands/stats.d.ts +4 -0
  72. package/dist/commands/stats.d.ts.map +1 -0
  73. package/dist/commands/stats.js +6 -0
  74. package/dist/commands/stats.js.map +1 -0
  75. package/dist/commands/sync.d.ts +4 -0
  76. package/dist/commands/sync.d.ts.map +1 -0
  77. package/dist/commands/sync.js +60 -0
  78. package/dist/commands/sync.js.map +1 -0
  79. package/dist/commands/update.d.ts +4 -0
  80. package/dist/commands/update.d.ts.map +1 -0
  81. package/dist/commands/update.js +63 -0
  82. package/dist/commands/update.js.map +1 -0
  83. package/dist/daemon/client.d.ts +107 -0
  84. package/dist/daemon/client.d.ts.map +1 -0
  85. package/dist/daemon/client.js +250 -0
  86. package/dist/daemon/client.js.map +1 -0
  87. package/dist/daemon/health.d.ts +38 -0
  88. package/dist/daemon/health.d.ts.map +1 -0
  89. package/dist/daemon/health.js +212 -0
  90. package/dist/daemon/health.js.map +1 -0
  91. package/dist/daemon/index.d.ts +34 -0
  92. package/dist/daemon/index.d.ts.map +1 -0
  93. package/dist/daemon/index.js +197 -0
  94. package/dist/daemon/index.js.map +1 -0
  95. package/dist/daemon/protocol.d.ts +144 -0
  96. package/dist/daemon/protocol.d.ts.map +1 -0
  97. package/dist/daemon/protocol.js +5 -0
  98. package/dist/daemon/protocol.js.map +1 -0
  99. package/dist/gamification/index.d.ts +13 -0
  100. package/dist/gamification/index.d.ts.map +1 -0
  101. package/dist/gamification/index.js +120 -0
  102. package/dist/gamification/index.js.map +1 -0
  103. package/dist/gamification/types.d.ts +34 -0
  104. package/dist/gamification/types.d.ts.map +1 -0
  105. package/dist/gamification/types.js +5 -0
  106. package/dist/gamification/types.js.map +1 -0
  107. package/dist/hooks/index.d.ts +65 -0
  108. package/dist/hooks/index.d.ts.map +1 -0
  109. package/dist/hooks/index.js +403 -0
  110. package/dist/hooks/index.js.map +1 -0
  111. package/dist/lib/api.d.ts +29 -0
  112. package/dist/lib/api.d.ts.map +1 -0
  113. package/dist/lib/api.js +213 -0
  114. package/dist/lib/api.js.map +1 -0
  115. package/dist/lib/browser.d.ts +6 -0
  116. package/dist/lib/browser.d.ts.map +1 -0
  117. package/dist/lib/browser.js +14 -0
  118. package/dist/lib/browser.js.map +1 -0
  119. package/dist/lib/channel-config.d.ts +10 -0
  120. package/dist/lib/channel-config.d.ts.map +1 -0
  121. package/dist/lib/channel-config.js +48 -0
  122. package/dist/lib/channel-config.js.map +1 -0
  123. package/dist/lib/command-runner.d.ts +16 -0
  124. package/dist/lib/command-runner.d.ts.map +1 -0
  125. package/dist/lib/command-runner.js +59 -0
  126. package/dist/lib/command-runner.js.map +1 -0
  127. package/dist/lib/command-utils.d.ts +22 -0
  128. package/dist/lib/command-utils.d.ts.map +1 -0
  129. package/dist/lib/command-utils.js +88 -0
  130. package/dist/lib/command-utils.js.map +1 -0
  131. package/dist/lib/error-handler.d.ts +13 -0
  132. package/dist/lib/error-handler.d.ts.map +1 -0
  133. package/dist/lib/error-handler.js +70 -0
  134. package/dist/lib/error-handler.js.map +1 -0
  135. package/dist/lib/errors.d.ts +35 -0
  136. package/dist/lib/errors.d.ts.map +1 -0
  137. package/dist/lib/errors.js +70 -0
  138. package/dist/lib/errors.js.map +1 -0
  139. package/dist/lib/exit.d.ts +10 -0
  140. package/dist/lib/exit.d.ts.map +1 -0
  141. package/dist/lib/exit.js +21 -0
  142. package/dist/lib/exit.js.map +1 -0
  143. package/dist/lib/formatters.d.ts +65 -0
  144. package/dist/lib/formatters.d.ts.map +1 -0
  145. package/dist/lib/formatters.js +180 -0
  146. package/dist/lib/formatters.js.map +1 -0
  147. package/dist/lib/hybrid-data.d.ts +47 -0
  148. package/dist/lib/hybrid-data.d.ts.map +1 -0
  149. package/dist/lib/hybrid-data.js +326 -0
  150. package/dist/lib/hybrid-data.js.map +1 -0
  151. package/dist/lib/local-store.d.ts +113 -0
  152. package/dist/lib/local-store.d.ts.map +1 -0
  153. package/dist/lib/local-store.js +220 -0
  154. package/dist/lib/local-store.js.map +1 -0
  155. package/dist/lib/machine-id.d.ts +8 -0
  156. package/dist/lib/machine-id.d.ts.map +1 -0
  157. package/dist/lib/machine-id.js +39 -0
  158. package/dist/lib/machine-id.js.map +1 -0
  159. package/dist/lib/network.d.ts +15 -0
  160. package/dist/lib/network.d.ts.map +1 -0
  161. package/dist/lib/network.js +46 -0
  162. package/dist/lib/network.js.map +1 -0
  163. package/dist/lib/theme.d.ts +77 -0
  164. package/dist/lib/theme.d.ts.map +1 -0
  165. package/dist/lib/theme.js +137 -0
  166. package/dist/lib/theme.js.map +1 -0
  167. package/dist/lib/tool-availability.d.ts +13 -0
  168. package/dist/lib/tool-availability.d.ts.map +1 -0
  169. package/dist/lib/tool-availability.js +48 -0
  170. package/dist/lib/tool-availability.js.map +1 -0
  171. package/dist/lib/update-checker.d.ts +21 -0
  172. package/dist/lib/update-checker.d.ts.map +1 -0
  173. package/dist/lib/update-checker.js +110 -0
  174. package/dist/lib/update-checker.js.map +1 -0
  175. package/dist/lib/validation.d.ts +30 -0
  176. package/dist/lib/validation.d.ts.map +1 -0
  177. package/dist/lib/validation.js +82 -0
  178. package/dist/lib/validation.js.map +1 -0
  179. package/dist/lib/validators.d.ts +18 -0
  180. package/dist/lib/validators.d.ts.map +1 -0
  181. package/dist/lib/validators.js +30 -0
  182. package/dist/lib/validators.js.map +1 -0
  183. package/dist/marketplace/api.d.ts +24 -0
  184. package/dist/marketplace/api.d.ts.map +1 -0
  185. package/dist/marketplace/api.js +92 -0
  186. package/dist/marketplace/api.js.map +1 -0
  187. package/dist/marketplace/index.d.ts +13 -0
  188. package/dist/marketplace/index.d.ts.map +1 -0
  189. package/dist/marketplace/index.js +155 -0
  190. package/dist/marketplace/index.js.map +1 -0
  191. package/dist/marketplace/installer.d.ts +18 -0
  192. package/dist/marketplace/installer.d.ts.map +1 -0
  193. package/dist/marketplace/installer.js +184 -0
  194. package/dist/marketplace/installer.js.map +1 -0
  195. package/dist/mcp/api.d.ts +93 -0
  196. package/dist/mcp/api.d.ts.map +1 -0
  197. package/dist/mcp/api.js +106 -0
  198. package/dist/mcp/api.js.map +1 -0
  199. package/dist/mcp/config.d.ts +72 -0
  200. package/dist/mcp/config.d.ts.map +1 -0
  201. package/dist/mcp/config.js +156 -0
  202. package/dist/mcp/config.js.map +1 -0
  203. package/dist/mcp/index.d.ts +54 -0
  204. package/dist/mcp/index.d.ts.map +1 -0
  205. package/dist/mcp/index.js +381 -0
  206. package/dist/mcp/index.js.map +1 -0
  207. package/dist/mcp/prompt.clean.d.ts +25 -0
  208. package/dist/mcp/prompt.clean.d.ts.map +1 -0
  209. package/dist/mcp/prompt.clean.js +206 -0
  210. package/dist/mcp/prompt.clean.js.map +1 -0
  211. package/dist/mcp/prompt.d.ts +52 -0
  212. package/dist/mcp/prompt.d.ts.map +1 -0
  213. package/dist/mcp/prompt.js +210 -0
  214. package/dist/mcp/prompt.js.map +1 -0
  215. package/dist/mcp/secrets.clean.d.ts +18 -0
  216. package/dist/mcp/secrets.clean.d.ts.map +1 -0
  217. package/dist/mcp/secrets.clean.js +357 -0
  218. package/dist/mcp/secrets.clean.js.map +1 -0
  219. package/dist/mcp/secrets.d.ts +46 -0
  220. package/dist/mcp/secrets.d.ts.map +1 -0
  221. package/dist/mcp/secrets.js +339 -0
  222. package/dist/mcp/secrets.js.map +1 -0
  223. package/dist/memory/index.d.ts +14 -0
  224. package/dist/memory/index.d.ts.map +1 -0
  225. package/dist/memory/index.js +98 -0
  226. package/dist/memory/index.js.map +1 -0
  227. package/dist/onboard/agents-md-builder.d.ts.map +1 -1
  228. package/dist/onboard/agents-md-builder.js +45 -0
  229. package/dist/onboard/agents-md-builder.js.map +1 -1
  230. package/dist/onboard/claude-inference.d.ts.map +1 -1
  231. package/dist/onboard/claude-inference.js +31 -5
  232. package/dist/onboard/claude-inference.js.map +1 -1
  233. package/dist/onboard/context7-fetcher.d.ts +1 -1
  234. package/dist/onboard/context7-fetcher.d.ts.map +1 -1
  235. package/dist/onboard/context7-fetcher.js +50 -16
  236. package/dist/onboard/context7-fetcher.js.map +1 -1
  237. package/dist/onboard/docs-builder.d.ts.map +1 -1
  238. package/dist/onboard/docs-builder.js +523 -50
  239. package/dist/onboard/docs-builder.js.map +1 -1
  240. package/dist/onboard/index.d.ts.map +1 -1
  241. package/dist/onboard/index.js +74 -25
  242. package/dist/onboard/index.js.map +1 -1
  243. package/dist/onboard/stack-detector.d.ts.map +1 -1
  244. package/dist/onboard/stack-detector.js +5 -55
  245. package/dist/onboard/stack-detector.js.map +1 -1
  246. package/dist/project/constants.d.ts +21 -0
  247. package/dist/project/constants.d.ts.map +1 -0
  248. package/dist/project/constants.js +21 -0
  249. package/dist/project/constants.js.map +1 -0
  250. package/dist/project/format.d.ts +16 -0
  251. package/dist/project/format.d.ts.map +1 -0
  252. package/dist/project/format.js +40 -0
  253. package/dist/project/format.js.map +1 -0
  254. package/dist/project/git.d.ts +28 -0
  255. package/dist/project/git.d.ts.map +1 -0
  256. package/dist/project/git.js +93 -0
  257. package/dist/project/git.js.map +1 -0
  258. package/dist/project/index.d.ts +36 -0
  259. package/dist/project/index.d.ts.map +1 -0
  260. package/dist/project/index.js +272 -0
  261. package/dist/project/index.js.map +1 -0
  262. package/dist/project/mapper.d.ts +27 -0
  263. package/dist/project/mapper.d.ts.map +1 -0
  264. package/dist/project/mapper.js +64 -0
  265. package/dist/project/mapper.js.map +1 -0
  266. package/dist/project/storage.d.ts +71 -0
  267. package/dist/project/storage.d.ts.map +1 -0
  268. package/dist/project/storage.js +274 -0
  269. package/dist/project/storage.js.map +1 -0
  270. package/dist/project/sync-bridge.d.ts +33 -0
  271. package/dist/project/sync-bridge.d.ts.map +1 -0
  272. package/dist/project/sync-bridge.js +155 -0
  273. package/dist/project/sync-bridge.js.map +1 -0
  274. package/dist/project/types.d.ts +107 -0
  275. package/dist/project/types.d.ts.map +1 -0
  276. package/dist/project/types.js +77 -0
  277. package/dist/project/types.js.map +1 -0
  278. package/dist/publish/index.d.ts +4 -0
  279. package/dist/publish/index.d.ts.map +1 -0
  280. package/dist/publish/index.js +92 -0
  281. package/dist/publish/index.js.map +1 -0
  282. package/dist/setup.d.ts.map +1 -1
  283. package/dist/setup.js +29 -10
  284. package/dist/setup.js.map +1 -1
  285. package/dist/skills/index.d.ts +51 -0
  286. package/dist/skills/index.d.ts.map +1 -0
  287. package/dist/skills/index.js +509 -0
  288. package/dist/skills/index.js.map +1 -0
  289. package/dist/stacks/check.d.ts +2 -0
  290. package/dist/stacks/check.d.ts.map +1 -0
  291. package/dist/stacks/check.js +144 -0
  292. package/dist/stacks/check.js.map +1 -0
  293. package/dist/stacks/diff.d.ts +11 -0
  294. package/dist/stacks/diff.d.ts.map +1 -0
  295. package/dist/stacks/diff.js +123 -0
  296. package/dist/stacks/diff.js.map +1 -0
  297. package/dist/stacks/index.d.ts +17 -0
  298. package/dist/stacks/index.d.ts.map +1 -0
  299. package/dist/stacks/index.js +525 -0
  300. package/dist/stacks/index.js.map +1 -0
  301. package/dist/stacks/index.refactored.d.ts.map +1 -0
  302. package/dist/stacks/index.refactored.js.map +1 -0
  303. package/dist/stacks/io.d.ts +11 -0
  304. package/dist/stacks/io.d.ts.map +1 -0
  305. package/dist/stacks/io.js +179 -0
  306. package/dist/stacks/io.js.map +1 -0
  307. package/dist/stacks/rollback.d.ts +5 -0
  308. package/dist/stacks/rollback.d.ts.map +1 -0
  309. package/dist/stacks/rollback.js +162 -0
  310. package/dist/stacks/rollback.js.map +1 -0
  311. package/dist/stacks/types.d.ts +70 -0
  312. package/dist/stacks/types.d.ts.map +1 -0
  313. package/dist/stacks/types.js +6 -0
  314. package/dist/stacks/types.js.map +1 -0
  315. package/dist/stacks/utils.d.ts +9 -0
  316. package/dist/stacks/utils.d.ts.map +1 -0
  317. package/dist/stacks/utils.js +11 -0
  318. package/dist/stacks/utils.js.map +1 -0
  319. package/dist/start/index.d.ts +23 -0
  320. package/dist/start/index.d.ts.map +1 -0
  321. package/dist/start/index.js +386 -0
  322. package/dist/start/index.js.map +1 -0
  323. package/dist/sync/index.d.ts +49 -0
  324. package/dist/sync/index.d.ts.map +1 -0
  325. package/dist/sync/index.js +207 -0
  326. package/dist/sync/index.js.map +1 -0
  327. package/dist/sync-engine/__tests__/test-helpers.d.ts +14 -0
  328. package/dist/sync-engine/__tests__/test-helpers.d.ts.map +1 -0
  329. package/dist/sync-engine/__tests__/test-helpers.js +73 -0
  330. package/dist/sync-engine/__tests__/test-helpers.js.map +1 -0
  331. package/dist/sync-engine/client.d.ts +128 -0
  332. package/dist/sync-engine/client.d.ts.map +1 -0
  333. package/dist/sync-engine/client.js +289 -0
  334. package/dist/sync-engine/client.js.map +1 -0
  335. package/dist/sync-engine/health.d.ts +38 -0
  336. package/dist/sync-engine/health.d.ts.map +1 -0
  337. package/dist/sync-engine/health.js +259 -0
  338. package/dist/sync-engine/health.js.map +1 -0
  339. package/dist/sync-engine/index.d.ts +34 -0
  340. package/dist/sync-engine/index.d.ts.map +1 -0
  341. package/dist/sync-engine/index.js +197 -0
  342. package/dist/sync-engine/index.js.map +1 -0
  343. package/dist/sync-engine/protocol.d.ts +153 -0
  344. package/dist/sync-engine/protocol.d.ts.map +1 -0
  345. package/dist/sync-engine/protocol.js +5 -0
  346. package/dist/sync-engine/protocol.js.map +1 -0
  347. package/dist/tasks/index.d.ts +14 -0
  348. package/dist/tasks/index.d.ts.map +1 -0
  349. package/dist/tasks/index.js +109 -0
  350. package/dist/tasks/index.js.map +1 -0
  351. package/dist/team/index.d.ts +12 -0
  352. package/dist/team/index.d.ts.map +1 -0
  353. package/dist/team/index.js +151 -0
  354. package/dist/team/index.js.map +1 -0
  355. package/dist/updater.d.ts +5 -5
  356. package/dist/updater.d.ts.map +1 -1
  357. package/dist/updater.js +24 -88
  358. package/dist/updater.js.map +1 -1
  359. package/dist/usage/index.d.ts +10 -0
  360. package/dist/usage/index.d.ts.map +1 -0
  361. package/dist/usage/index.js +104 -0
  362. package/dist/usage/index.js.map +1 -0
  363. package/dist/webhooks/index.d.ts +7 -0
  364. package/dist/webhooks/index.d.ts.map +1 -0
  365. package/dist/webhooks/index.js +81 -0
  366. package/dist/webhooks/index.js.map +1 -0
  367. package/package.json +26 -15
  368. package/scripts/postinstall.js +282 -0
@@ -0,0 +1,403 @@
1
+ /**
2
+ * Hooks management for ClaudeTools CLI
3
+ *
4
+ * Hooks are automation scripts that run in response to Claude events.
5
+ * The sync engine acts as a single dispatcher that executes hook chains.
6
+ */
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, readdirSync, copyFileSync, chmodSync } from 'fs';
8
+ import { homedir } from 'os';
9
+ import { join, basename } from 'path';
10
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
11
+ const HOOKS_DIR = join(homedir(), '.claude', 'hooks');
12
+ const SETTINGS_PATH = join(homedir(), '.claude', 'settings.json');
13
+ // Ensure hooks directory exists
14
+ if (!existsSync(HOOKS_DIR)) {
15
+ mkdirSync(HOOKS_DIR, { recursive: true });
16
+ }
17
+ /**
18
+ * List installed hooks
19
+ */
20
+ export async function runHooksList(options) {
21
+ const hooks = listInstalledHooks();
22
+ if (options?.json) {
23
+ console.log(JSON.stringify(hooks, null, 2));
24
+ return;
25
+ }
26
+ console.log();
27
+ if (hooks.length === 0) {
28
+ console.log(' No hooks installed.');
29
+ console.log(' Add with: claudetools hook add <name>');
30
+ console.log();
31
+ return;
32
+ }
33
+ console.log(` Installed hooks (${hooks.length}):`);
34
+ console.log();
35
+ for (const hook of hooks) {
36
+ const eventIcon = getEventIcon(hook.event);
37
+ const asyncBadge = hook.async ? ' [async]' : '';
38
+ console.log(` ${eventIcon} ${hook.name}${asyncBadge}`);
39
+ console.log(` Event: ${hook.event}`);
40
+ if (hook.matcher && hook.matcher !== '*') {
41
+ console.log(` Matcher: ${hook.matcher}`);
42
+ }
43
+ console.log(` Command: ${hook.command}`);
44
+ console.log();
45
+ }
46
+ }
47
+ /**
48
+ * Add a hook to the hooks directory
49
+ */
50
+ export async function runHookAdd(name, options) {
51
+ if (!name) {
52
+ console.error('Error: Missing hook name');
53
+ console.log('Usage: claudetools hook add <name> [options]');
54
+ process.exit(1);
55
+ }
56
+ // If it's a path, copy the hook script
57
+ if (name.startsWith('./') || name.startsWith('/') || name.startsWith('~')) {
58
+ await copyHookScript(name);
59
+ return;
60
+ }
61
+ // Otherwise create a new hook config
62
+ const event = options?.event || 'PreToolUse';
63
+ const matcher = options?.matcher || '*';
64
+ const command = options?.command;
65
+ if (!command) {
66
+ console.error('Error: Missing --command option');
67
+ console.log('Usage: claudetools hook add <name> --event <event> --command "<command>"');
68
+ process.exit(1);
69
+ }
70
+ const config = {
71
+ name,
72
+ event,
73
+ matcher,
74
+ command,
75
+ priority: options?.priority,
76
+ async: options?.async,
77
+ onFailure: options?.onFailure,
78
+ };
79
+ const configPath = join(HOOKS_DIR, `${name}.yaml`);
80
+ writeFileSync(configPath, stringifyYaml(config), 'utf-8');
81
+ console.log();
82
+ console.log(` Added hook: ${name}`);
83
+ console.log(` Event: ${event}`);
84
+ console.log(` Command: ${command}`);
85
+ console.log();
86
+ }
87
+ /**
88
+ * Remove a hook
89
+ */
90
+ export async function runHookRemove(name) {
91
+ if (!name) {
92
+ console.error('Error: Missing hook name');
93
+ console.log('Usage: claudetools hook remove <name>');
94
+ process.exit(1);
95
+ }
96
+ const configPath = join(HOOKS_DIR, `${name}.yaml`);
97
+ const scriptPath = join(HOOKS_DIR, `${name}.sh`);
98
+ let removed = false;
99
+ if (existsSync(configPath)) {
100
+ rmSync(configPath);
101
+ removed = true;
102
+ }
103
+ if (existsSync(scriptPath)) {
104
+ rmSync(scriptPath);
105
+ removed = true;
106
+ }
107
+ if (removed) {
108
+ console.log();
109
+ console.log(` Removed hook: ${name}`);
110
+ console.log();
111
+ }
112
+ else {
113
+ console.error(`Hook not found: ${name}`);
114
+ process.exit(1);
115
+ }
116
+ }
117
+ /**
118
+ * Sanitize hook command for safe execution
119
+ * Validates against command injection patterns
120
+ */
121
+ function sanitizeHookCommand(command) {
122
+ // Block shell injection patterns
123
+ const dangerousPatterns = [
124
+ /[;&|`$(){}[\]<>]/, // Shell metacharacters
125
+ /\n|\r/, // Newlines
126
+ ];
127
+ for (const pattern of dangerousPatterns) {
128
+ if (pattern.test(command)) {
129
+ throw new Error('Hook command contains potentially dangerous characters');
130
+ }
131
+ }
132
+ return command.trim();
133
+ }
134
+ /**
135
+ * Test a hook
136
+ */
137
+ export async function runHookTest(name, options) {
138
+ if (!name) {
139
+ console.error('Error: Missing hook name');
140
+ console.log('Usage: claudetools hook test <name> --tool <tool>');
141
+ process.exit(1);
142
+ }
143
+ const config = loadHookConfig(name);
144
+ if (!config) {
145
+ console.error(`Hook not found: ${name}`);
146
+ process.exit(1);
147
+ }
148
+ const event = options?.event || config.event;
149
+ const tool = options?.tool || 'TestTool';
150
+ console.log();
151
+ console.log(` Testing hook: ${name}`);
152
+ console.log(` Event: ${event}`);
153
+ console.log(` Tool: ${tool}`);
154
+ console.log();
155
+ // Check if matcher would match
156
+ const wouldMatch = matchesPattern(config.matcher || '*', tool);
157
+ console.log(` Matcher: ${config.matcher || '*'}`);
158
+ console.log(` Would match: ${wouldMatch ? 'Yes' : 'No'}`);
159
+ console.log();
160
+ if (!wouldMatch) {
161
+ console.log(' Hook would not execute for this tool.');
162
+ return;
163
+ }
164
+ // Execute the hook
165
+ console.log(' Executing command...');
166
+ console.log();
167
+ try {
168
+ const sanitizedCommand = sanitizeHookCommand(config.command);
169
+ const { spawn } = await import('child_process');
170
+ const child = spawn('sh', ['-c', sanitizedCommand], {
171
+ env: {
172
+ ...process.env,
173
+ CLAUDETOOLS_EVENT: event,
174
+ CLAUDETOOLS_TOOL: tool,
175
+ },
176
+ stdio: 'inherit',
177
+ });
178
+ await new Promise((resolve) => {
179
+ child.on('close', (code) => {
180
+ console.log();
181
+ console.log(` Exit code: ${code}`);
182
+ console.log();
183
+ resolve();
184
+ });
185
+ });
186
+ }
187
+ catch (error) {
188
+ console.log();
189
+ console.error(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
190
+ console.log();
191
+ process.exit(1);
192
+ }
193
+ }
194
+ /**
195
+ * Install hook dispatchers in settings.json
196
+ */
197
+ export async function runHooksInstall() {
198
+ console.log();
199
+ console.log(' Installing hook dispatchers...');
200
+ let settings = {};
201
+ if (existsSync(SETTINGS_PATH)) {
202
+ const content = readFileSync(SETTINGS_PATH, 'utf-8');
203
+ settings = JSON.parse(content);
204
+ }
205
+ // Create hooks section with single dispatcher per event
206
+ settings.hooks = {
207
+ PreToolUse: [
208
+ {
209
+ matcher: '*',
210
+ hooks: [{ type: 'command', command: 'claudetools hook dispatch PreToolUse' }],
211
+ },
212
+ ],
213
+ PostToolUse: [
214
+ {
215
+ matcher: '*',
216
+ hooks: [{ type: 'command', command: 'claudetools hook dispatch PostToolUse' }],
217
+ },
218
+ ],
219
+ };
220
+ writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf-8');
221
+ console.log(' Hook dispatchers installed in settings.json');
222
+ console.log();
223
+ console.log(' The sync engine will now execute hook chains for:');
224
+ console.log(' - PreToolUse: Before each tool execution');
225
+ console.log(' - PostToolUse: After each tool execution');
226
+ console.log();
227
+ }
228
+ /**
229
+ * Uninstall hook dispatchers from settings.json
230
+ */
231
+ export async function runHooksUninstall() {
232
+ console.log();
233
+ console.log(' Removing hook dispatchers...');
234
+ if (!existsSync(SETTINGS_PATH)) {
235
+ console.log(' settings.json not found');
236
+ return;
237
+ }
238
+ const content = readFileSync(SETTINGS_PATH, 'utf-8');
239
+ const settings = JSON.parse(content);
240
+ delete settings.hooks;
241
+ writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf-8');
242
+ console.log(' Hook dispatchers removed from settings.json');
243
+ console.log();
244
+ }
245
+ /**
246
+ * Dispatch hook execution (called by Claude Code)
247
+ */
248
+ export async function runHookDispatch(event) {
249
+ if (!event) {
250
+ return;
251
+ }
252
+ // This would communicate with the sync engine to execute the hook chain
253
+ // For now, just execute hooks directly
254
+ const hooks = listInstalledHooks().filter((h) => h.event === event);
255
+ if (hooks.length === 0) {
256
+ return;
257
+ }
258
+ // Sort by priority
259
+ hooks.sort((a, b) => (a.priority || 100) - (b.priority || 100));
260
+ const tool = process.env.CLAUDE_TOOL_NAME || '';
261
+ for (const hook of hooks) {
262
+ if (!matchesPattern(hook.matcher || '*', tool)) {
263
+ continue;
264
+ }
265
+ try {
266
+ const sanitizedCommand = sanitizeHookCommand(hook.command);
267
+ const { spawnSync } = await import('child_process');
268
+ const result = spawnSync('sh', ['-c', sanitizedCommand], {
269
+ env: {
270
+ ...process.env,
271
+ CLAUDETOOLS_EVENT: event,
272
+ CLAUDETOOLS_TOOL: tool,
273
+ },
274
+ timeout: (hook.timeout || 30) * 1000,
275
+ });
276
+ if (result.status !== 0 && hook.onFailure === 'block') {
277
+ process.stderr.write(result.stderr || '');
278
+ process.exit(2);
279
+ }
280
+ }
281
+ catch (error) {
282
+ // Hook command validation failed
283
+ const message = error instanceof Error ? error.message : 'Unknown error';
284
+ if (hook.onFailure === 'block') {
285
+ process.stderr.write(`Hook '${hook.name}' failed: ${message}\n`);
286
+ process.exit(2);
287
+ }
288
+ // Warn or ignore based on onFailure setting
289
+ }
290
+ }
291
+ }
292
+ // Helper functions
293
+ function listInstalledHooks() {
294
+ const hooks = [];
295
+ if (!existsSync(HOOKS_DIR)) {
296
+ return hooks;
297
+ }
298
+ const entries = readdirSync(HOOKS_DIR);
299
+ for (const entry of entries) {
300
+ if (entry.endsWith('.yaml') || entry.endsWith('.yml')) {
301
+ const config = loadHookConfig(entry.replace(/\.(yaml|yml)$/, ''));
302
+ if (config) {
303
+ hooks.push(config);
304
+ }
305
+ }
306
+ }
307
+ return hooks;
308
+ }
309
+ function loadHookConfig(name) {
310
+ const yamlPath = join(HOOKS_DIR, `${name}.yaml`);
311
+ const ymlPath = join(HOOKS_DIR, `${name}.yml`);
312
+ let configPath = null;
313
+ if (existsSync(yamlPath)) {
314
+ configPath = yamlPath;
315
+ }
316
+ else if (existsSync(ymlPath)) {
317
+ configPath = ymlPath;
318
+ }
319
+ if (!configPath) {
320
+ return null;
321
+ }
322
+ const content = readFileSync(configPath, 'utf-8');
323
+ return parseYaml(content);
324
+ }
325
+ async function copyHookScript(path) {
326
+ const resolvedPath = path.startsWith('~')
327
+ ? join(homedir(), path.slice(1))
328
+ : path;
329
+ if (!existsSync(resolvedPath)) {
330
+ throw new Error(`File not found: ${resolvedPath}`);
331
+ }
332
+ const name = basename(resolvedPath, '.sh');
333
+ const targetPath = join(HOOKS_DIR, `${name}.sh`);
334
+ copyFileSync(resolvedPath, targetPath);
335
+ chmodSync(targetPath, 0o755);
336
+ console.log();
337
+ console.log(` Copied hook script: ${name}`);
338
+ console.log(` Location: ${targetPath}`);
339
+ console.log();
340
+ }
341
+ function matchesPattern(pattern, value) {
342
+ if (pattern === '*') {
343
+ return true;
344
+ }
345
+ // Support pipe-separated patterns like "Write|Edit"
346
+ const patterns = pattern.split('|').map((p) => p.trim());
347
+ for (const p of patterns) {
348
+ if (p === value) {
349
+ return true;
350
+ }
351
+ // Basic glob support
352
+ const regex = new RegExp('^' + p.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
353
+ if (regex.test(value)) {
354
+ return true;
355
+ }
356
+ }
357
+ return false;
358
+ }
359
+ function getEventIcon(event) {
360
+ switch (event) {
361
+ case 'PreToolUse':
362
+ return '⏵';
363
+ case 'PostToolUse':
364
+ return '⏹';
365
+ default:
366
+ return '•';
367
+ }
368
+ }
369
+ /**
370
+ * Show hooks help
371
+ */
372
+ export function showHookHelp() {
373
+ console.log(`
374
+ claudetools hook - Manage hooks (automation scripts)
375
+
376
+ Usage:
377
+ claudetools hook list List installed hooks
378
+ claudetools hook add <name> Add a hook
379
+ claudetools hook remove <name> Remove a hook
380
+ claudetools hook test <name> Test a hook
381
+ claudetools hooks install Install dispatchers in settings.json
382
+ claudetools hooks uninstall Remove dispatchers from settings.json
383
+
384
+ Hook Events:
385
+ PreToolUse - Runs before each tool execution
386
+ PostToolUse - Runs after each tool execution
387
+
388
+ Options (for add):
389
+ --event <event> Event to trigger on (default: PreToolUse)
390
+ --matcher <pattern> Tool pattern to match (default: *)
391
+ --command "<cmd>" Command to execute
392
+ --priority <n> Execution order (lower = first)
393
+ --async Run asynchronously
394
+ --on-failure <action> block|warn|ignore (default: warn)
395
+
396
+ Examples:
397
+ claudetools hook add pre-lint --event PreToolUse --matcher "Write|Edit" --command "eslint \${FILE}"
398
+ claudetools hook test pre-lint --tool Write
399
+ claudetools hook remove pre-lint
400
+ claudetools hooks install
401
+ `);
402
+ }
403
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export interface ApiClientOptions {
2
+ authenticated?: boolean;
3
+ }
4
+ export interface ApiResponse<T> {
5
+ success: boolean;
6
+ data?: T;
7
+ error?: {
8
+ code: string;
9
+ message: string;
10
+ details?: Record<string, unknown>;
11
+ };
12
+ }
13
+ export declare class ApiError extends Error {
14
+ code: string;
15
+ details?: Record<string, unknown> | undefined;
16
+ constructor(code: string, message: string, details?: Record<string, unknown> | undefined);
17
+ }
18
+ export declare function getApiClient(options?: ApiClientOptions): {
19
+ get<T>(endpoint: string): Promise<ApiResponse<T>>;
20
+ post<T>(endpoint: string, body: unknown): Promise<ApiResponse<T>>;
21
+ put<T>(endpoint: string, body: unknown): Promise<ApiResponse<T>>;
22
+ delete<T>(endpoint: string): Promise<ApiResponse<T>>;
23
+ getRaw(endpoint: string): Promise<{
24
+ status: number;
25
+ body: string;
26
+ headers: Headers;
27
+ }>;
28
+ };
29
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,CAAC;CACH;AAED,qBAAa,QAAS,SAAQ,KAAK;IAExB,IAAI,EAAE,MAAM;IAEZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFjC,IAAI,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACR,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAK3C;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAE,gBAA0C;QAElE,CAAC,YAAY,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;SAI5C,CAAC,YAAY,MAAM,QAAQ,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QAI7D,CAAC,YAAY,MAAM,QAAQ,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;WAIzD,CAAC,YAAY,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;qBAInC,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;EAI9F"}
@@ -0,0 +1,213 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join } from 'node:path';
4
+ import { getCredential, getRefreshToken, storeCredential } from '../auth/keychain.js';
5
+ import { loadConfig, saveConfig } from '../auth/config.js';
6
+ const API_BASE = process.env.CLAUDETOOLS_API_URL || 'https://claudetools.com/api';
7
+ // Read CLI version from package.json for User-Agent header
8
+ const __api_filename = fileURLToPath(import.meta.url);
9
+ const __api_dirname = dirname(__api_filename);
10
+ const cliVersion = (() => {
11
+ try {
12
+ const pkg = JSON.parse(readFileSync(join(__api_dirname, '..', '..', 'package.json'), 'utf-8'));
13
+ return pkg.version;
14
+ }
15
+ catch {
16
+ return '0.0.0';
17
+ }
18
+ })();
19
+ const USER_AGENT = `claudetools-cli/${cliVersion}`;
20
+ export class ApiError extends Error {
21
+ code;
22
+ details;
23
+ constructor(code, message, details) {
24
+ super(message);
25
+ this.code = code;
26
+ this.details = details;
27
+ this.name = 'ApiError';
28
+ }
29
+ }
30
+ export function getApiClient(options = { authenticated: true }) {
31
+ return {
32
+ async get(endpoint) {
33
+ return makeRequest('GET', endpoint, undefined, options.authenticated);
34
+ },
35
+ async post(endpoint, body) {
36
+ return makeRequest('POST', endpoint, body, options.authenticated);
37
+ },
38
+ async put(endpoint, body) {
39
+ return makeRequest('PUT', endpoint, body, options.authenticated);
40
+ },
41
+ async delete(endpoint) {
42
+ return makeRequest('DELETE', endpoint, undefined, options.authenticated);
43
+ },
44
+ async getRaw(endpoint) {
45
+ return makeRawRequest('GET', endpoint, undefined, options.authenticated);
46
+ },
47
+ };
48
+ }
49
+ /**
50
+ * Refresh access token using refresh token
51
+ */
52
+ async function refreshAccessToken() {
53
+ const refreshToken = await getRefreshToken();
54
+ if (!refreshToken) {
55
+ return null;
56
+ }
57
+ try {
58
+ const response = await fetch(`${API_BASE}/auth/refresh`, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ 'User-Agent': USER_AGENT,
63
+ },
64
+ body: JSON.stringify({ refresh_token: refreshToken }),
65
+ });
66
+ if (!response.ok) {
67
+ // Refresh token is invalid or expired
68
+ return null;
69
+ }
70
+ const json = (await response.json());
71
+ if (!json.data) {
72
+ return null;
73
+ }
74
+ const { access_token, expires_in } = json.data;
75
+ // Calculate new expiration
76
+ const expiresAt = Date.now() + (expires_in * 1000);
77
+ // Store new access token (keep same refresh token)
78
+ await storeCredential(access_token, refreshToken);
79
+ saveConfig({
80
+ tokenExpiresAt: expiresAt,
81
+ });
82
+ return access_token;
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
88
+ /**
89
+ * Check if token is expired or will expire soon (within 5 minutes)
90
+ */
91
+ function isTokenExpired() {
92
+ const config = loadConfig();
93
+ if (!config.tokenExpiresAt) {
94
+ return false; // Let server validate — don't assume expired
95
+ }
96
+ const bufferMs = 5 * 60 * 1000; // 5 minutes
97
+ return Date.now() >= (config.tokenExpiresAt - bufferMs);
98
+ }
99
+ const MAX_RETRIES = 3;
100
+ /**
101
+ * Wait for the specified duration in milliseconds
102
+ */
103
+ function delay(ms) {
104
+ return new Promise(resolve => setTimeout(resolve, ms));
105
+ }
106
+ const MAX_RETRY_AFTER_MS = 60_000;
107
+ /**
108
+ * Parse Retry-After header value into milliseconds.
109
+ * Supports both delta-seconds (e.g. "5") and HTTP-date formats.
110
+ * Falls back to the provided default if unparseable.
111
+ * Capped at 60 seconds to prevent a malicious server from blocking the client.
112
+ */
113
+ function parseRetryAfter(headerValue, fallbackMs) {
114
+ if (!headerValue) {
115
+ return Math.min(fallbackMs, MAX_RETRY_AFTER_MS);
116
+ }
117
+ const seconds = Number(headerValue);
118
+ if (!Number.isNaN(seconds) && seconds >= 0) {
119
+ return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);
120
+ }
121
+ // Try HTTP-date format
122
+ const dateMs = Date.parse(headerValue);
123
+ if (!Number.isNaN(dateMs)) {
124
+ const waitMs = dateMs - Date.now();
125
+ return Math.min(Math.max(waitMs, 0), MAX_RETRY_AFTER_MS);
126
+ }
127
+ return Math.min(fallbackMs, MAX_RETRY_AFTER_MS);
128
+ }
129
+ /**
130
+ * Shared logic: refresh token if needed, build headers, execute fetch with
131
+ * 429 retry. Returns the raw Response for callers to process.
132
+ */
133
+ async function fetchWithRetry(method, endpoint, body, authenticated = true) {
134
+ // Check if token needs refresh before making request
135
+ if (authenticated && isTokenExpired()) {
136
+ await refreshAccessToken();
137
+ // If refresh fails, still try with existing token — server may accept it
138
+ // (session tokens with 7-day expiry may still be valid even if client thinks expired)
139
+ }
140
+ const requestHeaders = {
141
+ 'Content-Type': 'application/json',
142
+ 'User-Agent': USER_AGENT,
143
+ };
144
+ if (authenticated) {
145
+ const accessToken = await getCredential();
146
+ if (!accessToken) {
147
+ throw new ApiError('AUTH_REQUIRED', 'No stored credentials found. Run: claudetools login');
148
+ }
149
+ requestHeaders['Authorization'] = `Bearer ${accessToken}`;
150
+ }
151
+ const url = `${API_BASE}${endpoint}`;
152
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
153
+ const response = await fetch(url, {
154
+ method,
155
+ headers: requestHeaders,
156
+ body: body ? JSON.stringify(body) : undefined,
157
+ });
158
+ // Handle 429 rate limiting with retry
159
+ if (response.status === 429 && attempt < MAX_RETRIES) {
160
+ const retryAfterMs = parseRetryAfter(response.headers.get('Retry-After'), (attempt + 1) * 1000);
161
+ await delay(retryAfterMs);
162
+ continue;
163
+ }
164
+ return response;
165
+ }
166
+ // Should not reach here, but handle gracefully
167
+ throw new ApiError('RATE_LIMITED', 'Request rate limited after maximum retries. Try again later.');
168
+ }
169
+ /**
170
+ * Throw an ApiError from a JSON error payload (string or structured object).
171
+ */
172
+ function throwApiError(rawError, json, status, statusText) {
173
+ if (rawError) {
174
+ if (typeof rawError === 'string') {
175
+ throw new ApiError(rawError, rawError);
176
+ }
177
+ if (json.error && typeof json.error === 'object') {
178
+ throw new ApiError(json.error.code, json.error.message, json.error.details);
179
+ }
180
+ }
181
+ throw new ApiError('UNKNOWN_ERROR', `HTTP ${status}: ${statusText}`);
182
+ }
183
+ async function makeRawRequest(method, endpoint, body, authenticated = true) {
184
+ const response = await fetchWithRetry(method, endpoint, body, authenticated);
185
+ const responseBody = await response.text();
186
+ if (!response.ok) {
187
+ try {
188
+ const json = JSON.parse(responseBody);
189
+ throwApiError(json.error, json, response.status, response.statusText);
190
+ }
191
+ catch (parseError) {
192
+ if (parseError instanceof ApiError)
193
+ throw parseError;
194
+ // Not JSON, throw generic error
195
+ }
196
+ throw new ApiError('UNKNOWN_ERROR', `HTTP ${response.status}: ${response.statusText}`);
197
+ }
198
+ return {
199
+ status: response.status,
200
+ body: responseBody,
201
+ headers: response.headers,
202
+ };
203
+ }
204
+ async function makeRequest(method, endpoint, body, authenticated = true) {
205
+ const response = await fetchWithRetry(method, endpoint, body, authenticated);
206
+ const json = (await response.json());
207
+ if (!response.ok) {
208
+ const rawError = json.error;
209
+ throwApiError(rawError, json, response.status, response.statusText);
210
+ }
211
+ return json;
212
+ }
213
+ //# sourceMappingURL=api.js.map