@hasna/connectors 0.4.0 → 0.4.2

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 (390) hide show
  1. package/bin/index.js +189 -2
  2. package/bin/mcp.js +176 -1
  3. package/bin/serve.js +175 -0
  4. package/connectors/connect-airtable/.env.example +11 -0
  5. package/connectors/connect-airtable/CLAUDE.md +121 -0
  6. package/connectors/connect-airtable/README.md +193 -0
  7. package/connectors/connect-airtable/package.json +50 -0
  8. package/connectors/connect-airtable/src/api/client.ts +139 -0
  9. package/connectors/connect-airtable/src/api/index.ts +405 -0
  10. package/connectors/connect-airtable/src/cli/index.ts +637 -0
  11. package/connectors/connect-airtable/src/index.ts +20 -0
  12. package/connectors/connect-airtable/src/types/index.ts +349 -0
  13. package/connectors/connect-airtable/src/utils/config.ts +197 -0
  14. package/connectors/connect-airtable/tsconfig.json +16 -0
  15. package/connectors/connect-amplitude/.env.example +11 -0
  16. package/connectors/connect-amplitude/CLAUDE.md +121 -0
  17. package/connectors/connect-amplitude/README.md +193 -0
  18. package/connectors/connect-amplitude/package.json +51 -0
  19. package/connectors/connect-amplitude/src/api/client.ts +144 -0
  20. package/connectors/connect-amplitude/src/api/index.ts +215 -0
  21. package/connectors/connect-amplitude/src/cli/index.ts +532 -0
  22. package/connectors/connect-amplitude/src/index.ts +22 -0
  23. package/connectors/connect-amplitude/src/types/index.ts +329 -0
  24. package/connectors/connect-amplitude/src/utils/config.ts +208 -0
  25. package/connectors/connect-amplitude/tsconfig.json +16 -0
  26. package/connectors/connect-calendly/.env.example +11 -0
  27. package/connectors/connect-calendly/CLAUDE.md +272 -0
  28. package/connectors/connect-calendly/README.md +193 -0
  29. package/connectors/connect-calendly/package.json +51 -0
  30. package/connectors/connect-calendly/scripts/release.ts +179 -0
  31. package/connectors/connect-calendly/src/api/client.ts +213 -0
  32. package/connectors/connect-calendly/src/api/example.ts +48 -0
  33. package/connectors/connect-calendly/src/api/index.ts +51 -0
  34. package/connectors/connect-calendly/src/cli/index.ts +254 -0
  35. package/connectors/connect-calendly/src/index.ts +103 -0
  36. package/connectors/connect-calendly/src/types/index.ts +237 -0
  37. package/connectors/connect-calendly/src/utils/auth.ts +274 -0
  38. package/connectors/connect-calendly/src/utils/bulk.ts +212 -0
  39. package/connectors/connect-calendly/src/utils/config.ts +326 -0
  40. package/connectors/connect-calendly/src/utils/output.ts +175 -0
  41. package/connectors/connect-calendly/src/utils/settings.ts +114 -0
  42. package/connectors/connect-calendly/src/utils/storage.ts +198 -0
  43. package/connectors/connect-calendly/tsconfig.json +16 -0
  44. package/connectors/connect-convertkit/.env.example +11 -0
  45. package/connectors/connect-convertkit/CLAUDE.md +272 -0
  46. package/connectors/connect-convertkit/README.md +193 -0
  47. package/connectors/connect-convertkit/package.json +51 -0
  48. package/connectors/connect-convertkit/scripts/release.ts +179 -0
  49. package/connectors/connect-convertkit/src/api/client.ts +213 -0
  50. package/connectors/connect-convertkit/src/api/example.ts +48 -0
  51. package/connectors/connect-convertkit/src/api/index.ts +51 -0
  52. package/connectors/connect-convertkit/src/cli/index.ts +254 -0
  53. package/connectors/connect-convertkit/src/index.ts +103 -0
  54. package/connectors/connect-convertkit/src/types/index.ts +237 -0
  55. package/connectors/connect-convertkit/src/utils/auth.ts +274 -0
  56. package/connectors/connect-convertkit/src/utils/bulk.ts +212 -0
  57. package/connectors/connect-convertkit/src/utils/config.ts +326 -0
  58. package/connectors/connect-convertkit/src/utils/output.ts +175 -0
  59. package/connectors/connect-convertkit/src/utils/settings.ts +114 -0
  60. package/connectors/connect-convertkit/src/utils/storage.ts +198 -0
  61. package/connectors/connect-convertkit/tsconfig.json +16 -0
  62. package/connectors/connect-crisp/.env.example +11 -0
  63. package/connectors/connect-crisp/CLAUDE.md +272 -0
  64. package/connectors/connect-crisp/README.md +193 -0
  65. package/connectors/connect-crisp/package.json +51 -0
  66. package/connectors/connect-crisp/scripts/release.ts +179 -0
  67. package/connectors/connect-crisp/src/api/client.ts +213 -0
  68. package/connectors/connect-crisp/src/api/example.ts +48 -0
  69. package/connectors/connect-crisp/src/api/index.ts +51 -0
  70. package/connectors/connect-crisp/src/cli/index.ts +254 -0
  71. package/connectors/connect-crisp/src/index.ts +103 -0
  72. package/connectors/connect-crisp/src/types/index.ts +237 -0
  73. package/connectors/connect-crisp/src/utils/auth.ts +274 -0
  74. package/connectors/connect-crisp/src/utils/bulk.ts +212 -0
  75. package/connectors/connect-crisp/src/utils/config.ts +326 -0
  76. package/connectors/connect-crisp/src/utils/output.ts +175 -0
  77. package/connectors/connect-crisp/src/utils/settings.ts +114 -0
  78. package/connectors/connect-crisp/src/utils/storage.ts +198 -0
  79. package/connectors/connect-crisp/tsconfig.json +16 -0
  80. package/connectors/connect-docusign/.env.example +11 -0
  81. package/connectors/connect-docusign/CLAUDE.md +128 -0
  82. package/connectors/connect-docusign/README.md +193 -0
  83. package/connectors/connect-docusign/package.json +52 -0
  84. package/connectors/connect-docusign/src/api/client.ts +133 -0
  85. package/connectors/connect-docusign/src/api/index.ts +178 -0
  86. package/connectors/connect-docusign/src/cli/index.ts +381 -0
  87. package/connectors/connect-docusign/src/index.ts +23 -0
  88. package/connectors/connect-docusign/src/types/index.ts +208 -0
  89. package/connectors/connect-docusign/src/utils/config.ts +125 -0
  90. package/connectors/connect-docusign/src/utils/output.ts +119 -0
  91. package/connectors/connect-docusign/tsconfig.json +16 -0
  92. package/connectors/connect-drift/.env.example +11 -0
  93. package/connectors/connect-drift/CLAUDE.md +272 -0
  94. package/connectors/connect-drift/README.md +193 -0
  95. package/connectors/connect-drift/package.json +51 -0
  96. package/connectors/connect-drift/scripts/release.ts +179 -0
  97. package/connectors/connect-drift/src/api/client.ts +213 -0
  98. package/connectors/connect-drift/src/api/example.ts +48 -0
  99. package/connectors/connect-drift/src/api/index.ts +51 -0
  100. package/connectors/connect-drift/src/cli/index.ts +254 -0
  101. package/connectors/connect-drift/src/index.ts +103 -0
  102. package/connectors/connect-drift/src/types/index.ts +237 -0
  103. package/connectors/connect-drift/src/utils/auth.ts +274 -0
  104. package/connectors/connect-drift/src/utils/bulk.ts +212 -0
  105. package/connectors/connect-drift/src/utils/config.ts +326 -0
  106. package/connectors/connect-drift/src/utils/output.ts +175 -0
  107. package/connectors/connect-drift/src/utils/settings.ts +114 -0
  108. package/connectors/connect-drift/src/utils/storage.ts +198 -0
  109. package/connectors/connect-drift/tsconfig.json +16 -0
  110. package/connectors/connect-freshdesk/.env.example +11 -0
  111. package/connectors/connect-freshdesk/CLAUDE.md +272 -0
  112. package/connectors/connect-freshdesk/README.md +193 -0
  113. package/connectors/connect-freshdesk/package.json +51 -0
  114. package/connectors/connect-freshdesk/scripts/release.ts +179 -0
  115. package/connectors/connect-freshdesk/src/api/client.ts +213 -0
  116. package/connectors/connect-freshdesk/src/api/example.ts +48 -0
  117. package/connectors/connect-freshdesk/src/api/index.ts +51 -0
  118. package/connectors/connect-freshdesk/src/cli/index.ts +254 -0
  119. package/connectors/connect-freshdesk/src/index.ts +103 -0
  120. package/connectors/connect-freshdesk/src/types/index.ts +237 -0
  121. package/connectors/connect-freshdesk/src/utils/auth.ts +274 -0
  122. package/connectors/connect-freshdesk/src/utils/bulk.ts +212 -0
  123. package/connectors/connect-freshdesk/src/utils/config.ts +326 -0
  124. package/connectors/connect-freshdesk/src/utils/output.ts +175 -0
  125. package/connectors/connect-freshdesk/src/utils/settings.ts +114 -0
  126. package/connectors/connect-freshdesk/src/utils/storage.ts +198 -0
  127. package/connectors/connect-freshdesk/tsconfig.json +16 -0
  128. package/connectors/connect-gumroad/.env.example +11 -0
  129. package/connectors/connect-gumroad/CLAUDE.md +272 -0
  130. package/connectors/connect-gumroad/README.md +193 -0
  131. package/connectors/connect-gumroad/package.json +51 -0
  132. package/connectors/connect-gumroad/scripts/release.ts +179 -0
  133. package/connectors/connect-gumroad/src/api/client.ts +213 -0
  134. package/connectors/connect-gumroad/src/api/example.ts +48 -0
  135. package/connectors/connect-gumroad/src/api/index.ts +51 -0
  136. package/connectors/connect-gumroad/src/cli/index.ts +254 -0
  137. package/connectors/connect-gumroad/src/index.ts +103 -0
  138. package/connectors/connect-gumroad/src/types/index.ts +237 -0
  139. package/connectors/connect-gumroad/src/utils/auth.ts +274 -0
  140. package/connectors/connect-gumroad/src/utils/bulk.ts +212 -0
  141. package/connectors/connect-gumroad/src/utils/config.ts +326 -0
  142. package/connectors/connect-gumroad/src/utils/output.ts +175 -0
  143. package/connectors/connect-gumroad/src/utils/settings.ts +114 -0
  144. package/connectors/connect-gumroad/src/utils/storage.ts +198 -0
  145. package/connectors/connect-gumroad/tsconfig.json +16 -0
  146. package/connectors/connect-hubspot/.env.example +11 -0
  147. package/connectors/connect-hubspot/CLAUDE.md +128 -0
  148. package/connectors/connect-hubspot/README.md +193 -0
  149. package/connectors/connect-hubspot/package.json +52 -0
  150. package/connectors/connect-hubspot/src/api/client.ts +136 -0
  151. package/connectors/connect-hubspot/src/api/index.ts +379 -0
  152. package/connectors/connect-hubspot/src/cli/index.ts +589 -0
  153. package/connectors/connect-hubspot/src/index.ts +21 -0
  154. package/connectors/connect-hubspot/src/types/index.ts +285 -0
  155. package/connectors/connect-hubspot/src/utils/config.ts +205 -0
  156. package/connectors/connect-hubspot/src/utils/output.ts +119 -0
  157. package/connectors/connect-hubspot/tsconfig.json +16 -0
  158. package/connectors/connect-intercom/.env.example +11 -0
  159. package/connectors/connect-intercom/CLAUDE.md +131 -0
  160. package/connectors/connect-intercom/README.md +193 -0
  161. package/connectors/connect-intercom/package.json +52 -0
  162. package/connectors/connect-intercom/src/api/client.ts +132 -0
  163. package/connectors/connect-intercom/src/api/index.ts +366 -0
  164. package/connectors/connect-intercom/src/cli/index.ts +831 -0
  165. package/connectors/connect-intercom/src/index.ts +21 -0
  166. package/connectors/connect-intercom/src/types/index.ts +650 -0
  167. package/connectors/connect-intercom/src/utils/config.ts +157 -0
  168. package/connectors/connect-intercom/src/utils/output.ts +119 -0
  169. package/connectors/connect-intercom/tsconfig.json +16 -0
  170. package/connectors/connect-lemonsqueezy/.env.example +11 -0
  171. package/connectors/connect-lemonsqueezy/CLAUDE.md +128 -0
  172. package/connectors/connect-lemonsqueezy/README.md +193 -0
  173. package/connectors/connect-lemonsqueezy/package.json +52 -0
  174. package/connectors/connect-lemonsqueezy/src/api/client.ts +133 -0
  175. package/connectors/connect-lemonsqueezy/src/api/index.ts +502 -0
  176. package/connectors/connect-lemonsqueezy/src/cli/index.ts +723 -0
  177. package/connectors/connect-lemonsqueezy/src/index.ts +21 -0
  178. package/connectors/connect-lemonsqueezy/src/types/index.ts +353 -0
  179. package/connectors/connect-lemonsqueezy/src/utils/config.ts +205 -0
  180. package/connectors/connect-lemonsqueezy/src/utils/output.ts +119 -0
  181. package/connectors/connect-lemonsqueezy/tsconfig.json +16 -0
  182. package/connectors/connect-linkedin/.env.example +11 -0
  183. package/connectors/connect-linkedin/CLAUDE.md +124 -0
  184. package/connectors/connect-linkedin/README.md +193 -0
  185. package/connectors/connect-linkedin/package.json +52 -0
  186. package/connectors/connect-linkedin/src/api/client.ts +132 -0
  187. package/connectors/connect-linkedin/src/api/index.ts +313 -0
  188. package/connectors/connect-linkedin/src/cli/index.ts +548 -0
  189. package/connectors/connect-linkedin/src/index.ts +21 -0
  190. package/connectors/connect-linkedin/src/types/index.ts +472 -0
  191. package/connectors/connect-linkedin/src/utils/config.ts +157 -0
  192. package/connectors/connect-linkedin/src/utils/output.ts +119 -0
  193. package/connectors/connect-linkedin/tsconfig.json +16 -0
  194. package/connectors/connect-mailchimp/.env.example +11 -0
  195. package/connectors/connect-mailchimp/CLAUDE.md +127 -0
  196. package/connectors/connect-mailchimp/README.md +193 -0
  197. package/connectors/connect-mailchimp/package.json +52 -0
  198. package/connectors/connect-mailchimp/src/api/client.ts +162 -0
  199. package/connectors/connect-mailchimp/src/api/index.ts +580 -0
  200. package/connectors/connect-mailchimp/src/cli/index.ts +822 -0
  201. package/connectors/connect-mailchimp/src/index.ts +23 -0
  202. package/connectors/connect-mailchimp/src/types/index.ts +585 -0
  203. package/connectors/connect-mailchimp/src/utils/config.ts +208 -0
  204. package/connectors/connect-mailchimp/src/utils/output.ts +119 -0
  205. package/connectors/connect-mailchimp/tsconfig.json +16 -0
  206. package/connectors/connect-mongodb/.env.example +11 -0
  207. package/connectors/connect-mongodb/CLAUDE.md +272 -0
  208. package/connectors/connect-mongodb/README.md +193 -0
  209. package/connectors/connect-mongodb/package.json +51 -0
  210. package/connectors/connect-mongodb/scripts/release.ts +179 -0
  211. package/connectors/connect-mongodb/src/api/client.ts +213 -0
  212. package/connectors/connect-mongodb/src/api/example.ts +48 -0
  213. package/connectors/connect-mongodb/src/api/index.ts +51 -0
  214. package/connectors/connect-mongodb/src/cli/index.ts +254 -0
  215. package/connectors/connect-mongodb/src/index.ts +103 -0
  216. package/connectors/connect-mongodb/src/types/index.ts +237 -0
  217. package/connectors/connect-mongodb/src/utils/auth.ts +274 -0
  218. package/connectors/connect-mongodb/src/utils/bulk.ts +212 -0
  219. package/connectors/connect-mongodb/src/utils/config.ts +326 -0
  220. package/connectors/connect-mongodb/src/utils/output.ts +175 -0
  221. package/connectors/connect-mongodb/src/utils/settings.ts +114 -0
  222. package/connectors/connect-mongodb/src/utils/storage.ts +198 -0
  223. package/connectors/connect-mongodb/tsconfig.json +16 -0
  224. package/connectors/connect-netlify/.env.example +11 -0
  225. package/connectors/connect-netlify/CLAUDE.md +170 -0
  226. package/connectors/connect-netlify/README.md +193 -0
  227. package/connectors/connect-netlify/package.json +53 -0
  228. package/connectors/connect-netlify/src/api/client.ts +132 -0
  229. package/connectors/connect-netlify/src/api/index.ts +533 -0
  230. package/connectors/connect-netlify/src/cli/index.ts +985 -0
  231. package/connectors/connect-netlify/src/index.ts +22 -0
  232. package/connectors/connect-netlify/src/types/index.ts +423 -0
  233. package/connectors/connect-netlify/src/utils/config.ts +171 -0
  234. package/connectors/connect-netlify/src/utils/output.ts +119 -0
  235. package/connectors/connect-netlify/tsconfig.json +16 -0
  236. package/connectors/connect-paypal/.env.example +11 -0
  237. package/connectors/connect-paypal/CLAUDE.md +128 -0
  238. package/connectors/connect-paypal/README.md +193 -0
  239. package/connectors/connect-paypal/package.json +51 -0
  240. package/connectors/connect-paypal/src/api/client.ts +182 -0
  241. package/connectors/connect-paypal/src/api/index.ts +235 -0
  242. package/connectors/connect-paypal/src/cli/index.ts +559 -0
  243. package/connectors/connect-paypal/src/index.ts +23 -0
  244. package/connectors/connect-paypal/src/types/index.ts +377 -0
  245. package/connectors/connect-paypal/src/utils/config.ts +125 -0
  246. package/connectors/connect-paypal/src/utils/output.ts +119 -0
  247. package/connectors/connect-paypal/tsconfig.json +16 -0
  248. package/connectors/connect-pinterest/.env.example +11 -0
  249. package/connectors/connect-pinterest/CLAUDE.md +115 -0
  250. package/connectors/connect-pinterest/README.md +193 -0
  251. package/connectors/connect-pinterest/package.json +52 -0
  252. package/connectors/connect-pinterest/src/api/client.ts +125 -0
  253. package/connectors/connect-pinterest/src/api/index.ts +203 -0
  254. package/connectors/connect-pinterest/src/cli/index.ts +653 -0
  255. package/connectors/connect-pinterest/src/index.ts +21 -0
  256. package/connectors/connect-pinterest/src/types/index.ts +268 -0
  257. package/connectors/connect-pinterest/src/utils/config.ts +157 -0
  258. package/connectors/connect-pinterest/src/utils/output.ts +119 -0
  259. package/connectors/connect-pinterest/tsconfig.json +16 -0
  260. package/connectors/connect-posthog/.env.example +11 -0
  261. package/connectors/connect-posthog/CLAUDE.md +272 -0
  262. package/connectors/connect-posthog/README.md +193 -0
  263. package/connectors/connect-posthog/package.json +51 -0
  264. package/connectors/connect-posthog/scripts/release.ts +179 -0
  265. package/connectors/connect-posthog/src/api/client.ts +213 -0
  266. package/connectors/connect-posthog/src/api/example.ts +48 -0
  267. package/connectors/connect-posthog/src/api/index.ts +51 -0
  268. package/connectors/connect-posthog/src/cli/index.ts +254 -0
  269. package/connectors/connect-posthog/src/index.ts +103 -0
  270. package/connectors/connect-posthog/src/types/index.ts +237 -0
  271. package/connectors/connect-posthog/src/utils/auth.ts +274 -0
  272. package/connectors/connect-posthog/src/utils/bulk.ts +212 -0
  273. package/connectors/connect-posthog/src/utils/config.ts +326 -0
  274. package/connectors/connect-posthog/src/utils/output.ts +175 -0
  275. package/connectors/connect-posthog/src/utils/settings.ts +114 -0
  276. package/connectors/connect-posthog/src/utils/storage.ts +198 -0
  277. package/connectors/connect-posthog/tsconfig.json +16 -0
  278. package/connectors/connect-salesforce/.env.example +11 -0
  279. package/connectors/connect-salesforce/CLAUDE.md +128 -0
  280. package/connectors/connect-salesforce/README.md +193 -0
  281. package/connectors/connect-salesforce/package.json +53 -0
  282. package/connectors/connect-salesforce/src/api/client.ts +150 -0
  283. package/connectors/connect-salesforce/src/api/index.ts +367 -0
  284. package/connectors/connect-salesforce/src/cli/index.ts +594 -0
  285. package/connectors/connect-salesforce/src/index.ts +21 -0
  286. package/connectors/connect-salesforce/src/types/index.ts +292 -0
  287. package/connectors/connect-salesforce/src/utils/config.ts +208 -0
  288. package/connectors/connect-salesforce/src/utils/output.ts +119 -0
  289. package/connectors/connect-salesforce/tsconfig.json +16 -0
  290. package/connectors/connect-segment/.env.example +11 -0
  291. package/connectors/connect-segment/CLAUDE.md +272 -0
  292. package/connectors/connect-segment/README.md +193 -0
  293. package/connectors/connect-segment/package.json +51 -0
  294. package/connectors/connect-segment/scripts/release.ts +179 -0
  295. package/connectors/connect-segment/src/api/client.ts +213 -0
  296. package/connectors/connect-segment/src/api/example.ts +48 -0
  297. package/connectors/connect-segment/src/api/index.ts +51 -0
  298. package/connectors/connect-segment/src/cli/index.ts +254 -0
  299. package/connectors/connect-segment/src/index.ts +103 -0
  300. package/connectors/connect-segment/src/types/index.ts +237 -0
  301. package/connectors/connect-segment/src/utils/auth.ts +274 -0
  302. package/connectors/connect-segment/src/utils/bulk.ts +212 -0
  303. package/connectors/connect-segment/src/utils/config.ts +326 -0
  304. package/connectors/connect-segment/src/utils/output.ts +175 -0
  305. package/connectors/connect-segment/src/utils/settings.ts +114 -0
  306. package/connectors/connect-segment/src/utils/storage.ts +198 -0
  307. package/connectors/connect-segment/tsconfig.json +16 -0
  308. package/connectors/connect-sendgrid/.env.example +11 -0
  309. package/connectors/connect-sendgrid/CLAUDE.md +125 -0
  310. package/connectors/connect-sendgrid/README.md +193 -0
  311. package/connectors/connect-sendgrid/package.json +53 -0
  312. package/connectors/connect-sendgrid/src/api/client.ts +137 -0
  313. package/connectors/connect-sendgrid/src/api/index.ts +588 -0
  314. package/connectors/connect-sendgrid/src/cli/index.ts +764 -0
  315. package/connectors/connect-sendgrid/src/index.ts +21 -0
  316. package/connectors/connect-sendgrid/src/types/index.ts +403 -0
  317. package/connectors/connect-sendgrid/src/utils/config.ts +197 -0
  318. package/connectors/connect-sendgrid/src/utils/output.ts +119 -0
  319. package/connectors/connect-sendgrid/tsconfig.json +16 -0
  320. package/connectors/connect-supabase/.env.example +11 -0
  321. package/connectors/connect-supabase/CLAUDE.md +132 -0
  322. package/connectors/connect-supabase/README.md +193 -0
  323. package/connectors/connect-supabase/package.json +52 -0
  324. package/connectors/connect-supabase/src/api/client.ts +231 -0
  325. package/connectors/connect-supabase/src/api/index.ts +439 -0
  326. package/connectors/connect-supabase/src/cli/index.ts +691 -0
  327. package/connectors/connect-supabase/src/index.ts +24 -0
  328. package/connectors/connect-supabase/src/types/index.ts +215 -0
  329. package/connectors/connect-supabase/src/utils/config.ts +219 -0
  330. package/connectors/connect-supabase/tsconfig.json +16 -0
  331. package/connectors/connect-vercel/.env.example +11 -0
  332. package/connectors/connect-vercel/CLAUDE.md +142 -0
  333. package/connectors/connect-vercel/README.md +193 -0
  334. package/connectors/connect-vercel/package.json +52 -0
  335. package/connectors/connect-vercel/src/api/client.ts +139 -0
  336. package/connectors/connect-vercel/src/api/index.ts +384 -0
  337. package/connectors/connect-vercel/src/cli/index.ts +740 -0
  338. package/connectors/connect-vercel/src/index.ts +24 -0
  339. package/connectors/connect-vercel/src/types/index.ts +350 -0
  340. package/connectors/connect-vercel/src/utils/config.ts +182 -0
  341. package/connectors/connect-vercel/src/utils/output.ts +119 -0
  342. package/connectors/connect-vercel/tsconfig.json +16 -0
  343. package/connectors/connect-zendesk/.env.example +10 -0
  344. package/connectors/connect-zendesk/.github/workflows/deploy.yml +51 -0
  345. package/connectors/connect-zendesk/CLAUDE.md +78 -0
  346. package/connectors/connect-zendesk/Makefile +129 -0
  347. package/connectors/connect-zendesk/README.md +295 -0
  348. package/connectors/connect-zendesk/SCAFFOLD.md +178 -0
  349. package/connectors/connect-zendesk/nginx-connector.conf +218 -0
  350. package/connectors/connect-zendesk/nginx.conf +61 -0
  351. package/connectors/connect-zendesk/package.json +71 -0
  352. package/connectors/connect-zendesk/scripts/init.sh +62 -0
  353. package/connectors/connect-zendesk/scripts/publish.ts +210 -0
  354. package/connectors/connect-zendesk/server/index.js +142 -0
  355. package/connectors/connect-zendesk/src/api/automations.ts +95 -0
  356. package/connectors/connect-zendesk/src/api/brands.ts +80 -0
  357. package/connectors/connect-zendesk/src/api/bulk.ts +838 -0
  358. package/connectors/connect-zendesk/src/api/client.test.ts +315 -0
  359. package/connectors/connect-zendesk/src/api/client.ts +173 -0
  360. package/connectors/connect-zendesk/src/api/example.ts +59 -0
  361. package/connectors/connect-zendesk/src/api/groups.ts +60 -0
  362. package/connectors/connect-zendesk/src/api/index.test.ts +111 -0
  363. package/connectors/connect-zendesk/src/api/index.ts +131 -0
  364. package/connectors/connect-zendesk/src/api/macros.ts +124 -0
  365. package/connectors/connect-zendesk/src/api/organizations.ts +85 -0
  366. package/connectors/connect-zendesk/src/api/slas.ts +80 -0
  367. package/connectors/connect-zendesk/src/api/ticket-fields.ts +98 -0
  368. package/connectors/connect-zendesk/src/api/tickets.test.ts +215 -0
  369. package/connectors/connect-zendesk/src/api/tickets.ts +103 -0
  370. package/connectors/connect-zendesk/src/api/triggers.test.ts +204 -0
  371. package/connectors/connect-zendesk/src/api/triggers.ts +125 -0
  372. package/connectors/connect-zendesk/src/api/users.test.ts +180 -0
  373. package/connectors/connect-zendesk/src/api/users.ts +108 -0
  374. package/connectors/connect-zendesk/src/api/views.test.ts +223 -0
  375. package/connectors/connect-zendesk/src/api/views.ts +145 -0
  376. package/connectors/connect-zendesk/src/api/webhooks.test.ts +208 -0
  377. package/connectors/connect-zendesk/src/api/webhooks.ts +118 -0
  378. package/connectors/connect-zendesk/src/cli/index.ts +1478 -0
  379. package/connectors/connect-zendesk/src/index.ts +36 -0
  380. package/connectors/connect-zendesk/src/server/index.ts +204 -0
  381. package/connectors/connect-zendesk/src/types/index.ts +910 -0
  382. package/connectors/connect-zendesk/src/utils/config.test.ts +193 -0
  383. package/connectors/connect-zendesk/src/utils/config.ts +526 -0
  384. package/connectors/connect-zendesk/src/utils/export.ts +338 -0
  385. package/connectors/connect-zendesk/src/utils/logger.ts +105 -0
  386. package/connectors/connect-zendesk/src/utils/output.test.ts +137 -0
  387. package/connectors/connect-zendesk/src/utils/output.ts +119 -0
  388. package/connectors/connect-zendesk/tsconfig.json +31 -0
  389. package/dist/index.js +175 -0
  390. package/package.json +1 -1
@@ -0,0 +1,1478 @@
1
+ #!/usr/bin/env bun
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { Zendesk } from '../api';
5
+ import {
6
+ getEmail,
7
+ getApiToken,
8
+ getBaseUrl,
9
+ setEmail,
10
+ setApiToken,
11
+ setBaseUrl,
12
+ setDefaultAccount,
13
+ getDefaultAccount,
14
+ clearConfig,
15
+ initConfigDir,
16
+ getConfigDir,
17
+ getBaseConfigDir,
18
+ getExportsDir,
19
+ getRemoteApiUrl,
20
+ setRemoteApiUrl,
21
+ setProfileOverride,
22
+ getCurrentProfile,
23
+ setCurrentProfile,
24
+ listProfiles,
25
+ createProfile,
26
+ deleteProfile,
27
+ profileExists,
28
+ isAuthenticated,
29
+ } from '../utils/config';
30
+ import type { OutputFormat } from '../utils/output';
31
+ import { success, error, info, print } from '../utils/output';
32
+ import { logger } from '../utils/logger';
33
+ import {
34
+ exportData,
35
+ exportTicketsToCSV,
36
+ exportUsersToCSV,
37
+ exportOrganizationsToCSV,
38
+ exportGroupsToCSV,
39
+ exportViewsToCSV,
40
+ exportTriggersToCSV,
41
+ exportAutomationsToCSV,
42
+ exportMacrosToCSV,
43
+ exportWebhooksToCSV,
44
+ exportBrandsToCSV,
45
+ } from '../utils/export';
46
+
47
+ const program = new Command();
48
+
49
+ program
50
+ .name('connect-zendesk')
51
+ .description('Zendesk API connector CLI')
52
+ .version('1.0.0')
53
+ .option('-p, --profile <name>', 'Use specific profile')
54
+ .option('-e, --email <email>', 'Zendesk email address')
55
+ .option('-t, --api-token <token>', 'Zendesk API token')
56
+ .option('-u, --base-url <url>', 'Zendesk base URL (e.g., https://your-subdomain.zendesk.com/api/v2)')
57
+ .option('-f, --format <format>', 'Output format (json, table, pretty)', 'pretty')
58
+ .hook('preAction', (thisCommand) => {
59
+ const opts = thisCommand.opts();
60
+ if (opts.profile) {
61
+ setProfileOverride(opts.profile);
62
+ }
63
+ if (opts.email) {
64
+ process.env.ZENDESK_EMAIL = opts.email;
65
+ }
66
+ if (opts.apiToken) {
67
+ process.env.ZENDESK_API_TOKEN = opts.apiToken;
68
+ }
69
+ if (opts.baseUrl) {
70
+ process.env.ZENDESK_BASE_URL = opts.baseUrl;
71
+ }
72
+ });
73
+
74
+ // ============================================
75
+ // Init Command
76
+ // ============================================
77
+ program
78
+ .command('init')
79
+ .description('Initialize the connect-zendesk configuration directory')
80
+ .action(() => {
81
+ logger.command('init');
82
+ const result = initConfigDir();
83
+
84
+ if (result.created.length > 0) {
85
+ success('Created the following:');
86
+ result.created.forEach(path => info(` ${chalk.green('+')} ${path}`));
87
+ }
88
+
89
+ if (result.existing.length > 0) {
90
+ info('Already exists:');
91
+ result.existing.forEach(path => info(` ${chalk.gray('-')} ${path}`));
92
+ }
93
+
94
+ info(`\nConfiguration directory: ${getBaseConfigDir()}`);
95
+ info(`Current profile: ${getCurrentProfile()}`);
96
+ info(`Profile config: ${getConfigDir()}`);
97
+ info(`Exports directory: ${getExportsDir()}`);
98
+ success('\nRun "connect-zendesk config set-email <email>" and "connect-zendesk config set-token <token>" to configure your credentials.');
99
+ });
100
+
101
+ // Helper to get Zendesk client
102
+ function getClient(): Zendesk {
103
+ const email = getEmail();
104
+ const apiToken = getApiToken();
105
+ const baseUrl = getBaseUrl();
106
+
107
+ if (!email || !apiToken) {
108
+ error('Email and API token are required. Run "connect-zendesk config set-email <email>" and "connect-zendesk config set-token <token>" or set ZENDESK_EMAIL and ZENDESK_API_TOKEN environment variables.');
109
+ process.exit(1);
110
+ }
111
+
112
+ return new Zendesk({ email, apiToken, baseUrl });
113
+ }
114
+
115
+ // Helper to get output format
116
+ function getFormat(cmd: Command): OutputFormat {
117
+ const parent = cmd.parent;
118
+ return (parent?.opts().format || 'pretty') as OutputFormat;
119
+ }
120
+
121
+ // ============================================
122
+ // Profile Commands
123
+ // ============================================
124
+ const profileCmd = program
125
+ .command('profile')
126
+ .description('Manage profiles for multiple Zendesk accounts');
127
+
128
+ profileCmd
129
+ .command('list')
130
+ .description('List all profiles')
131
+ .action(() => {
132
+ const profiles = listProfiles();
133
+ const current = getCurrentProfile();
134
+
135
+ if (profiles.length === 0) {
136
+ info('No profiles found. Using default.');
137
+ return;
138
+ }
139
+
140
+ info('Profiles:');
141
+ profiles.forEach(p => {
142
+ const marker = p === current ? chalk.green(' (active)') : '';
143
+ console.log(` ${p}${marker}`);
144
+ });
145
+ });
146
+
147
+ profileCmd
148
+ .command('use <name>')
149
+ .description('Switch to a profile')
150
+ .action((name: string) => {
151
+ if (!profileExists(name)) {
152
+ error(`Profile "${name}" does not exist`);
153
+ process.exit(1);
154
+ }
155
+ setCurrentProfile(name);
156
+ success(`Switched to profile "${name}"`);
157
+ });
158
+
159
+ profileCmd
160
+ .command('create <name>')
161
+ .description('Create a new profile')
162
+ .action((name: string) => {
163
+ try {
164
+ createProfile(name);
165
+ success(`Profile "${name}" created`);
166
+ info(`Switch to it with: connect-zendesk profile use ${name}`);
167
+ } catch (e) {
168
+ error((e as Error).message);
169
+ process.exit(1);
170
+ }
171
+ });
172
+
173
+ profileCmd
174
+ .command('delete <name>')
175
+ .description('Delete a profile')
176
+ .action((name: string) => {
177
+ try {
178
+ deleteProfile(name);
179
+ success(`Profile "${name}" deleted`);
180
+ } catch (e) {
181
+ error((e as Error).message);
182
+ process.exit(1);
183
+ }
184
+ });
185
+
186
+ profileCmd
187
+ .command('show')
188
+ .description('Show current profile name and info')
189
+ .action(() => {
190
+ const profile = getCurrentProfile();
191
+ const email = getEmail();
192
+ const baseUrl = getBaseUrl();
193
+
194
+ info(`Current profile: ${chalk.cyan(profile)}`);
195
+ info(`Email: ${email || chalk.gray('not set')}`);
196
+ info(`Base URL: ${baseUrl || chalk.gray('not set')}`);
197
+ info(`Authenticated: ${isAuthenticated() ? chalk.green('yes') : chalk.red('no')}`);
198
+ });
199
+
200
+ // ============================================
201
+ // Config Commands
202
+ // ============================================
203
+ const configCmd = program
204
+ .command('config')
205
+ .description('Manage CLI configuration');
206
+
207
+ configCmd
208
+ .command('set-email <email>')
209
+ .description('Set Zendesk email address')
210
+ .action((email: string) => {
211
+ setEmail(email);
212
+ success(`Email saved to profile "${getCurrentProfile()}"`);
213
+ });
214
+
215
+ configCmd
216
+ .command('set-token <apiToken>')
217
+ .description('Set Zendesk API token')
218
+ .action((apiToken: string) => {
219
+ setApiToken(apiToken);
220
+ success(`API token saved to profile "${getCurrentProfile()}"`);
221
+ });
222
+
223
+ configCmd
224
+ .command('set-base-url <baseUrl>')
225
+ .description('Set Zendesk base URL (e.g., https://your-subdomain.zendesk.com/api/v2)')
226
+ .action((baseUrl: string) => {
227
+ setBaseUrl(baseUrl);
228
+ success(`Base URL saved to profile "${getCurrentProfile()}"`);
229
+ });
230
+
231
+ configCmd
232
+ .command('set-account <account>')
233
+ .description('Set default account')
234
+ .action((account: string) => {
235
+ setDefaultAccount(account);
236
+ success(`Default account set to: ${account}`);
237
+ });
238
+
239
+ configCmd
240
+ .command('set-remote-url <url>')
241
+ .description('Set remote API URL (default: https://connect.hasna.com/zendesk)')
242
+ .action((url: string) => {
243
+ setRemoteApiUrl(url);
244
+ success(`Remote API URL set to: ${url}`);
245
+ });
246
+
247
+ configCmd
248
+ .command('show')
249
+ .description('Show current configuration')
250
+ .action(() => {
251
+ const profile = getCurrentProfile();
252
+ const email = getEmail();
253
+ const apiToken = getApiToken();
254
+ const baseUrl = getBaseUrl();
255
+ const account = getDefaultAccount();
256
+ const remoteUrl = getRemoteApiUrl();
257
+ info(`Profile: ${chalk.cyan(profile)}`);
258
+ info(`Email: ${email || chalk.gray('not set')}`);
259
+ info(`API Token: ${apiToken ? `${apiToken.substring(0, 6)}...${apiToken.substring(apiToken.length - 4)}` : chalk.gray('not set')}`);
260
+ info(`Base URL: ${baseUrl || chalk.gray('not set')}`);
261
+ info(`Default Account: ${account || chalk.gray('not set')}`);
262
+ info(`Remote API URL: ${remoteUrl}`);
263
+ info(`Config Directory: ${getBaseConfigDir()}`);
264
+ info(`Profile Config: ${getConfigDir()}`);
265
+ info(`Exports Directory: ${getExportsDir()}`);
266
+ });
267
+
268
+ configCmd
269
+ .command('clear')
270
+ .description('Clear all configuration for current profile')
271
+ .action(() => {
272
+ clearConfig();
273
+ success(`Configuration cleared for profile "${getCurrentProfile()}"`);
274
+ });
275
+
276
+ // ============================================
277
+ // Remote API Commands (connect.hasna.com)
278
+ // ============================================
279
+ const remoteCmd = program
280
+ .command('remote')
281
+ .description('Interact with the remote Zendesk connector API');
282
+
283
+ remoteCmd
284
+ .command('status')
285
+ .description('Check remote API status')
286
+ .action(async () => {
287
+ const remoteUrl = getRemoteApiUrl();
288
+ logger.command('remote status', { remoteUrl });
289
+ try {
290
+ const response = await fetch(`${remoteUrl}/status`);
291
+ const data = await response.json();
292
+ print(data, getFormat(remoteCmd));
293
+ } catch (err) {
294
+ error(`Failed to connect to remote API at ${remoteUrl}: ${String(err)}`);
295
+ logger.error('Remote status check failed', { remoteUrl, error: String(err) });
296
+ process.exit(1);
297
+ }
298
+ });
299
+
300
+ remoteCmd
301
+ .command('health')
302
+ .description('Check remote API health')
303
+ .action(async () => {
304
+ const remoteUrl = getRemoteApiUrl();
305
+ logger.command('remote health', { remoteUrl });
306
+ try {
307
+ const response = await fetch(`${remoteUrl}/health`);
308
+ if (response.ok) {
309
+ success(`Remote API at ${remoteUrl} is healthy`);
310
+ } else {
311
+ error(`Remote API returned status: ${response.status}`);
312
+ }
313
+ } catch (err) {
314
+ error(`Failed to connect to remote API at ${remoteUrl}: ${String(err)}`);
315
+ logger.error('Remote health check failed', { remoteUrl, error: String(err) });
316
+ process.exit(1);
317
+ }
318
+ });
319
+
320
+ remoteCmd
321
+ .command('url')
322
+ .description('Show current remote API URL')
323
+ .action(() => {
324
+ info(`Remote API URL: ${getRemoteApiUrl()}`);
325
+ });
326
+
327
+ // ============================================
328
+ // Tickets Commands
329
+ // ============================================
330
+ const ticketsCmd = program
331
+ .command('tickets')
332
+ .description('Manage Zendesk tickets');
333
+
334
+ ticketsCmd
335
+ .command('list')
336
+ .description('List tickets')
337
+ .option('-p, --page <page>', 'Page number')
338
+ .option('-l, --per-page <perPage>', 'Items per page')
339
+ .option('-s, --sort-by <sortBy>', 'Sort by field (created_at, updated_at, priority, status, ticket_type)')
340
+ .option('-o, --sort-order <sortOrder>', 'Sort order (asc, desc)')
341
+ .action(async (opts) => {
342
+ try {
343
+ const client = getClient();
344
+ const tickets = await client.tickets.list({
345
+ page: opts.page ? parseInt(opts.page) : undefined,
346
+ per_page: opts.perPage ? parseInt(opts.perPage) : undefined,
347
+ sort_by: opts.sortBy,
348
+ sort_order: opts.sortOrder,
349
+ });
350
+ print(tickets, getFormat(ticketsCmd));
351
+ } catch (err) {
352
+ error(String(err));
353
+ process.exit(1);
354
+ }
355
+ });
356
+
357
+ ticketsCmd
358
+ .command('get <id>')
359
+ .description('Get ticket by ID')
360
+ .action(async (id: string) => {
361
+ try {
362
+ const client = getClient();
363
+ const ticket = await client.tickets.get(parseInt(id));
364
+ print(ticket, getFormat(ticketsCmd));
365
+ } catch (err) {
366
+ error(String(err));
367
+ process.exit(1);
368
+ }
369
+ });
370
+
371
+ ticketsCmd
372
+ .command('create')
373
+ .description('Create a new ticket')
374
+ .requiredOption('-s, --subject <subject>', 'Ticket subject')
375
+ .requiredOption('-b, --body <body>', 'Ticket description')
376
+ .option('-p, --priority <priority>', 'Priority (urgent, high, normal, low)')
377
+ .option('-t, --type <type>', 'Type (problem, incident, question, task)')
378
+ .option('--requester-id <requesterId>', 'Requester user ID')
379
+ .option('--assignee-id <assigneeId>', 'Assignee user ID')
380
+ .action(async (opts) => {
381
+ try {
382
+ const client = getClient();
383
+ const ticket = await client.tickets.create({
384
+ ticket: {
385
+ subject: opts.subject,
386
+ comment: { body: opts.body },
387
+ priority: opts.priority,
388
+ type: opts.type,
389
+ requester_id: opts.requesterId ? parseInt(opts.requesterId) : undefined,
390
+ assignee_id: opts.assigneeId ? parseInt(opts.assigneeId) : undefined,
391
+ },
392
+ });
393
+ success('Ticket created successfully');
394
+ print(ticket, getFormat(ticketsCmd));
395
+ } catch (err) {
396
+ error(String(err));
397
+ process.exit(1);
398
+ }
399
+ });
400
+
401
+ ticketsCmd
402
+ .command('update <id>')
403
+ .description('Update a ticket')
404
+ .option('-s, --subject <subject>', 'Ticket subject')
405
+ .option('-p, --priority <priority>', 'Priority (urgent, high, normal, low)')
406
+ .option('--status <status>', 'Status (new, open, pending, hold, solved, closed)')
407
+ .option('-c, --comment <comment>', 'Add a comment')
408
+ .action(async (id: string, opts) => {
409
+ try {
410
+ const client = getClient();
411
+ const updateData: any = { ticket: {} };
412
+
413
+ if (opts.subject) updateData.ticket.subject = opts.subject;
414
+ if (opts.priority) updateData.ticket.priority = opts.priority;
415
+ if (opts.status) updateData.ticket.status = opts.status;
416
+ if (opts.comment) updateData.ticket.comment = { body: opts.comment };
417
+
418
+ const ticket = await client.tickets.update(parseInt(id), updateData);
419
+ success('Ticket updated successfully');
420
+ print(ticket, getFormat(ticketsCmd));
421
+ } catch (err) {
422
+ error(String(err));
423
+ process.exit(1);
424
+ }
425
+ });
426
+
427
+ ticketsCmd
428
+ .command('delete <id>')
429
+ .description('Delete a ticket')
430
+ .action(async (id: string) => {
431
+ try {
432
+ const client = getClient();
433
+ await client.tickets.delete(parseInt(id));
434
+ success(`Ticket ${id} deleted successfully`);
435
+ } catch (err) {
436
+ error(String(err));
437
+ process.exit(1);
438
+ }
439
+ });
440
+
441
+ ticketsCmd
442
+ .command('export')
443
+ .description('Export tickets to CSV or JSON')
444
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
445
+ .option('-o, --output <filename>', 'Output filename', 'tickets')
446
+ .option('-l, --limit <limit>', 'Maximum number of tickets to export')
447
+ .action(async (opts) => {
448
+ try {
449
+ const client = getClient();
450
+ logger.command('tickets export', { format: opts.format, output: opts.output });
451
+ info('Fetching tickets...');
452
+
453
+ const allTickets: unknown[] = [];
454
+ let page = 1;
455
+ const limit = opts.limit ? parseInt(opts.limit) : undefined;
456
+
457
+ while (true) {
458
+ const response = await client.tickets.list({ page, per_page: 100 });
459
+ allTickets.push(...response.tickets);
460
+
461
+ if (limit && allTickets.length >= limit) {
462
+ allTickets.splice(limit);
463
+ break;
464
+ }
465
+
466
+ if (!response.next_page) break;
467
+ page++;
468
+ }
469
+
470
+ const filepath = exportData(allTickets, opts.output, opts.format);
471
+ logger.export('tickets', opts.format, filepath, allTickets.length);
472
+ success(`Exported ${allTickets.length} tickets to ${filepath}`);
473
+ } catch (err) {
474
+ error(String(err));
475
+ logger.error('Tickets export failed', { error: String(err) });
476
+ process.exit(1);
477
+ }
478
+ });
479
+
480
+ // ============================================
481
+ // Users Commands
482
+ // ============================================
483
+ const usersCmd = program
484
+ .command('users')
485
+ .description('Manage Zendesk users');
486
+
487
+ usersCmd
488
+ .command('list')
489
+ .description('List users')
490
+ .option('-p, --page <page>', 'Page number')
491
+ .option('-l, --per-page <perPage>', 'Items per page')
492
+ .option('-r, --role <role>', 'Filter by role (end-user, agent, admin)')
493
+ .action(async (opts) => {
494
+ try {
495
+ const client = getClient();
496
+ const users = await client.users.list({
497
+ page: opts.page ? parseInt(opts.page) : undefined,
498
+ per_page: opts.perPage ? parseInt(opts.perPage) : undefined,
499
+ role: opts.role,
500
+ });
501
+ print(users, getFormat(usersCmd));
502
+ } catch (err) {
503
+ error(String(err));
504
+ process.exit(1);
505
+ }
506
+ });
507
+
508
+ usersCmd
509
+ .command('get <id>')
510
+ .description('Get user by ID')
511
+ .action(async (id: string) => {
512
+ try {
513
+ const client = getClient();
514
+ const user = await client.users.get(parseInt(id));
515
+ print(user, getFormat(usersCmd));
516
+ } catch (err) {
517
+ error(String(err));
518
+ process.exit(1);
519
+ }
520
+ });
521
+
522
+ usersCmd
523
+ .command('me')
524
+ .description('Get current authenticated user')
525
+ .action(async () => {
526
+ try {
527
+ const client = getClient();
528
+ const user = await client.users.me();
529
+ print(user, getFormat(usersCmd));
530
+ } catch (err) {
531
+ error(String(err));
532
+ process.exit(1);
533
+ }
534
+ });
535
+
536
+ usersCmd
537
+ .command('create')
538
+ .description('Create a new user')
539
+ .requiredOption('-n, --name <name>', 'User name')
540
+ .option('-e, --email <email>', 'User email')
541
+ .option('-r, --role <role>', 'User role (end-user, agent, admin)')
542
+ .action(async (opts) => {
543
+ try {
544
+ const client = getClient();
545
+ const user = await client.users.create({
546
+ user: {
547
+ name: opts.name,
548
+ email: opts.email,
549
+ role: opts.role,
550
+ },
551
+ });
552
+ success('User created successfully');
553
+ print(user, getFormat(usersCmd));
554
+ } catch (err) {
555
+ error(String(err));
556
+ process.exit(1);
557
+ }
558
+ });
559
+
560
+ usersCmd
561
+ .command('search')
562
+ .description('Search users by email or name')
563
+ .requiredOption('-q, --query <query>', 'Search query (email or name)')
564
+ .action(async (opts) => {
565
+ try {
566
+ const client = getClient();
567
+ const users = await client.users.searchByEmail(opts.query);
568
+ print(users, getFormat(usersCmd));
569
+ } catch (err) {
570
+ error(String(err));
571
+ process.exit(1);
572
+ }
573
+ });
574
+
575
+ usersCmd
576
+ .command('export')
577
+ .description('Export users to CSV or JSON')
578
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
579
+ .option('-o, --output <filename>', 'Output filename', 'users')
580
+ .option('-l, --limit <limit>', 'Maximum number of users to export')
581
+ .option('-r, --role <role>', 'Filter by role (end-user, agent, admin)')
582
+ .action(async (opts) => {
583
+ try {
584
+ const client = getClient();
585
+ logger.command('users export', { format: opts.format, output: opts.output, role: opts.role });
586
+ info('Fetching users...');
587
+
588
+ const allUsers: unknown[] = [];
589
+ let page = 1;
590
+ const limit = opts.limit ? parseInt(opts.limit) : undefined;
591
+
592
+ while (true) {
593
+ const response = await client.users.list({ page, per_page: 100, role: opts.role });
594
+ allUsers.push(...response.users);
595
+
596
+ if (limit && allUsers.length >= limit) {
597
+ allUsers.splice(limit);
598
+ break;
599
+ }
600
+
601
+ if (!response.next_page) break;
602
+ page++;
603
+ }
604
+
605
+ const filepath = exportData(allUsers, opts.output, opts.format);
606
+ logger.export('users', opts.format, filepath, allUsers.length);
607
+ success(`Exported ${allUsers.length} users to ${filepath}`);
608
+ } catch (err) {
609
+ error(String(err));
610
+ logger.error('Users export failed', { error: String(err) });
611
+ process.exit(1);
612
+ }
613
+ });
614
+
615
+ // ============================================
616
+ // Organizations Commands
617
+ // ============================================
618
+ const organizationsCmd = program
619
+ .command('organizations')
620
+ .alias('orgs')
621
+ .description('Manage Zendesk organizations');
622
+
623
+ organizationsCmd
624
+ .command('list')
625
+ .description('List organizations')
626
+ .option('-p, --page <page>', 'Page number')
627
+ .option('-l, --per-page <perPage>', 'Items per page')
628
+ .action(async (opts) => {
629
+ try {
630
+ const client = getClient();
631
+ const organizations = await client.organizations.list({
632
+ page: opts.page ? parseInt(opts.page) : undefined,
633
+ per_page: opts.perPage ? parseInt(opts.perPage) : undefined,
634
+ });
635
+ print(organizations, getFormat(organizationsCmd));
636
+ } catch (err) {
637
+ error(String(err));
638
+ process.exit(1);
639
+ }
640
+ });
641
+
642
+ organizationsCmd
643
+ .command('get <id>')
644
+ .description('Get organization by ID')
645
+ .action(async (id: string) => {
646
+ try {
647
+ const client = getClient();
648
+ const organization = await client.organizations.get(parseInt(id));
649
+ print(organization, getFormat(organizationsCmd));
650
+ } catch (err) {
651
+ error(String(err));
652
+ process.exit(1);
653
+ }
654
+ });
655
+
656
+ organizationsCmd
657
+ .command('create')
658
+ .description('Create a new organization')
659
+ .requiredOption('-n, --name <name>', 'Organization name')
660
+ .option('-d, --details <details>', 'Organization details')
661
+ .action(async (opts) => {
662
+ try {
663
+ const client = getClient();
664
+ const organization = await client.organizations.create({
665
+ organization: {
666
+ name: opts.name,
667
+ details: opts.details,
668
+ },
669
+ });
670
+ success('Organization created successfully');
671
+ print(organization, getFormat(organizationsCmd));
672
+ } catch (err) {
673
+ error(String(err));
674
+ process.exit(1);
675
+ }
676
+ });
677
+
678
+ organizationsCmd
679
+ .command('search')
680
+ .description('Search organizations by name')
681
+ .requiredOption('-q, --query <query>', 'Search query (organization name)')
682
+ .action(async (opts) => {
683
+ try {
684
+ const client = getClient();
685
+ const organizations = await client.organizations.search(opts.query);
686
+ print(organizations, getFormat(organizationsCmd));
687
+ } catch (err) {
688
+ error(String(err));
689
+ process.exit(1);
690
+ }
691
+ });
692
+
693
+ organizationsCmd
694
+ .command('export')
695
+ .description('Export organizations to CSV or JSON')
696
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
697
+ .option('-o, --output <filename>', 'Output filename', 'organizations')
698
+ .option('-l, --limit <limit>', 'Maximum number of organizations to export')
699
+ .action(async (opts) => {
700
+ try {
701
+ const client = getClient();
702
+ logger.command('organizations export', { format: opts.format, output: opts.output });
703
+ info('Fetching organizations...');
704
+
705
+ const allOrgs: unknown[] = [];
706
+ let page = 1;
707
+ const limit = opts.limit ? parseInt(opts.limit) : undefined;
708
+
709
+ while (true) {
710
+ const response = await client.organizations.list({ page, per_page: 100 });
711
+ allOrgs.push(...response.organizations);
712
+
713
+ if (limit && allOrgs.length >= limit) {
714
+ allOrgs.splice(limit);
715
+ break;
716
+ }
717
+
718
+ if (!response.next_page) break;
719
+ page++;
720
+ }
721
+
722
+ const filepath = exportData(allOrgs, opts.output, opts.format);
723
+ logger.export('organizations', opts.format, filepath, allOrgs.length);
724
+ success(`Exported ${allOrgs.length} organizations to ${filepath}`);
725
+ } catch (err) {
726
+ error(String(err));
727
+ logger.error('Organizations export failed', { error: String(err) });
728
+ process.exit(1);
729
+ }
730
+ });
731
+
732
+ // ============================================
733
+ // Groups Commands
734
+ // ============================================
735
+ const groupsCmd = program
736
+ .command('groups')
737
+ .description('Manage Zendesk groups');
738
+
739
+ groupsCmd
740
+ .command('list')
741
+ .description('List groups')
742
+ .option('-p, --page <page>', 'Page number')
743
+ .option('-l, --per-page <perPage>', 'Items per page')
744
+ .action(async (opts) => {
745
+ try {
746
+ const client = getClient();
747
+ const groups = await client.groups.list({
748
+ page: opts.page ? parseInt(opts.page) : undefined,
749
+ per_page: opts.perPage ? parseInt(opts.perPage) : undefined,
750
+ });
751
+ print(groups, getFormat(groupsCmd));
752
+ } catch (err) {
753
+ error(String(err));
754
+ process.exit(1);
755
+ }
756
+ });
757
+
758
+ groupsCmd
759
+ .command('get <id>')
760
+ .description('Get group by ID')
761
+ .action(async (id: string) => {
762
+ try {
763
+ const client = getClient();
764
+ const group = await client.groups.get(parseInt(id));
765
+ print(group, getFormat(groupsCmd));
766
+ } catch (err) {
767
+ error(String(err));
768
+ process.exit(1);
769
+ }
770
+ });
771
+
772
+ groupsCmd
773
+ .command('export')
774
+ .description('Export groups to CSV or JSON')
775
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
776
+ .option('-o, --output <filename>', 'Output filename', 'groups')
777
+ .action(async (opts) => {
778
+ try {
779
+ const client = getClient();
780
+ logger.command('groups export', { format: opts.format, output: opts.output });
781
+ info('Fetching groups...');
782
+
783
+ const allGroups: unknown[] = [];
784
+ let page = 1;
785
+
786
+ while (true) {
787
+ const response = await client.groups.list({ page, per_page: 100 });
788
+ allGroups.push(...response.groups);
789
+
790
+ if (!response.next_page) break;
791
+ page++;
792
+ }
793
+
794
+ const filepath = exportData(allGroups, opts.output, opts.format);
795
+ logger.export('groups', opts.format, filepath, allGroups.length);
796
+ success(`Exported ${allGroups.length} groups to ${filepath}`);
797
+ } catch (err) {
798
+ error(String(err));
799
+ logger.error('Groups export failed', { error: String(err) });
800
+ process.exit(1);
801
+ }
802
+ });
803
+
804
+ // ============================================
805
+ // Views Commands
806
+ // ============================================
807
+ const viewsCmd = program
808
+ .command('views')
809
+ .description('Manage Zendesk views');
810
+
811
+ viewsCmd
812
+ .command('list')
813
+ .description('List views')
814
+ .option('-a, --active', 'Only show active views')
815
+ .action(async (opts) => {
816
+ try {
817
+ const client = getClient();
818
+ const views = opts.active
819
+ ? await client.views.listActive()
820
+ : await client.views.list();
821
+ print(views, getFormat(viewsCmd));
822
+ } catch (err) {
823
+ error(String(err));
824
+ process.exit(1);
825
+ }
826
+ });
827
+
828
+ viewsCmd
829
+ .command('get <id>')
830
+ .description('Get view by ID')
831
+ .action(async (id: string) => {
832
+ try {
833
+ const client = getClient();
834
+ const view = await client.views.get(parseInt(id));
835
+ print(view, getFormat(viewsCmd));
836
+ } catch (err) {
837
+ error(String(err));
838
+ process.exit(1);
839
+ }
840
+ });
841
+
842
+ viewsCmd
843
+ .command('export')
844
+ .description('Export views to CSV or JSON')
845
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
846
+ .option('-o, --output <filename>', 'Output filename', 'views')
847
+ .action(async (opts) => {
848
+ try {
849
+ const client = getClient();
850
+ logger.command('views export', { format: opts.format, output: opts.output });
851
+ info('Fetching views...');
852
+
853
+ const response = await client.views.list();
854
+ const filepath = exportData(response.views, opts.output, opts.format);
855
+ logger.export('views', opts.format, filepath, response.views.length);
856
+ success(`Exported ${response.views.length} views to ${filepath}`);
857
+ } catch (err) {
858
+ error(String(err));
859
+ logger.error('Views export failed', { error: String(err) });
860
+ process.exit(1);
861
+ }
862
+ });
863
+
864
+ // ============================================
865
+ // Triggers Commands
866
+ // ============================================
867
+ const triggersCmd = program
868
+ .command('triggers')
869
+ .description('Manage Zendesk triggers');
870
+
871
+ triggersCmd
872
+ .command('list')
873
+ .description('List triggers')
874
+ .option('-a, --active', 'Only show active triggers')
875
+ .action(async (opts) => {
876
+ try {
877
+ const client = getClient();
878
+ const triggers = opts.active
879
+ ? await client.triggers.listActive()
880
+ : await client.triggers.list();
881
+ print(triggers, getFormat(triggersCmd));
882
+ } catch (err) {
883
+ error(String(err));
884
+ process.exit(1);
885
+ }
886
+ });
887
+
888
+ triggersCmd
889
+ .command('get <id>')
890
+ .description('Get trigger by ID')
891
+ .action(async (id: string) => {
892
+ try {
893
+ const client = getClient();
894
+ const trigger = await client.triggers.get(parseInt(id));
895
+ print(trigger, getFormat(triggersCmd));
896
+ } catch (err) {
897
+ error(String(err));
898
+ process.exit(1);
899
+ }
900
+ });
901
+
902
+ triggersCmd
903
+ .command('export')
904
+ .description('Export triggers to CSV or JSON')
905
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
906
+ .option('-o, --output <filename>', 'Output filename', 'triggers')
907
+ .action(async (opts) => {
908
+ try {
909
+ const client = getClient();
910
+ logger.command('triggers export', { format: opts.format, output: opts.output });
911
+ info('Fetching triggers...');
912
+
913
+ const response = await client.triggers.list();
914
+ const filepath = exportData(response.triggers, opts.output, opts.format);
915
+ logger.export('triggers', opts.format, filepath, response.triggers.length);
916
+ success(`Exported ${response.triggers.length} triggers to ${filepath}`);
917
+ } catch (err) {
918
+ error(String(err));
919
+ logger.error('Triggers export failed', { error: String(err) });
920
+ process.exit(1);
921
+ }
922
+ });
923
+
924
+ // ============================================
925
+ // Automations Commands
926
+ // ============================================
927
+ const automationsCmd = program
928
+ .command('automations')
929
+ .description('Manage Zendesk automations');
930
+
931
+ automationsCmd
932
+ .command('list')
933
+ .description('List automations')
934
+ .option('-a, --active', 'Only show active automations')
935
+ .action(async (opts) => {
936
+ try {
937
+ const client = getClient();
938
+ const automations = opts.active
939
+ ? await client.automations.listActive()
940
+ : await client.automations.list();
941
+ print(automations, getFormat(automationsCmd));
942
+ } catch (err) {
943
+ error(String(err));
944
+ process.exit(1);
945
+ }
946
+ });
947
+
948
+ automationsCmd
949
+ .command('get <id>')
950
+ .description('Get automation by ID')
951
+ .action(async (id: string) => {
952
+ try {
953
+ const client = getClient();
954
+ const automation = await client.automations.get(parseInt(id));
955
+ print(automation, getFormat(automationsCmd));
956
+ } catch (err) {
957
+ error(String(err));
958
+ process.exit(1);
959
+ }
960
+ });
961
+
962
+ automationsCmd
963
+ .command('export')
964
+ .description('Export automations to CSV or JSON')
965
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
966
+ .option('-o, --output <filename>', 'Output filename', 'automations')
967
+ .action(async (opts) => {
968
+ try {
969
+ const client = getClient();
970
+ logger.command('automations export', { format: opts.format, output: opts.output });
971
+ info('Fetching automations...');
972
+
973
+ const response = await client.automations.list();
974
+ const filepath = exportData(response.automations, opts.output, opts.format);
975
+ logger.export('automations', opts.format, filepath, response.automations.length);
976
+ success(`Exported ${response.automations.length} automations to ${filepath}`);
977
+ } catch (err) {
978
+ error(String(err));
979
+ logger.error('Automations export failed', { error: String(err) });
980
+ process.exit(1);
981
+ }
982
+ });
983
+
984
+ // ============================================
985
+ // Macros Commands
986
+ // ============================================
987
+ const macrosCmd = program
988
+ .command('macros')
989
+ .description('Manage Zendesk macros');
990
+
991
+ macrosCmd
992
+ .command('list')
993
+ .description('List macros')
994
+ .option('-a, --active', 'Only show active macros')
995
+ .action(async (opts) => {
996
+ try {
997
+ const client = getClient();
998
+ const macros = opts.active
999
+ ? await client.macros.listActive()
1000
+ : await client.macros.list();
1001
+ print(macros, getFormat(macrosCmd));
1002
+ } catch (err) {
1003
+ error(String(err));
1004
+ process.exit(1);
1005
+ }
1006
+ });
1007
+
1008
+ macrosCmd
1009
+ .command('get <id>')
1010
+ .description('Get macro by ID')
1011
+ .action(async (id: string) => {
1012
+ try {
1013
+ const client = getClient();
1014
+ const macro = await client.macros.get(parseInt(id));
1015
+ print(macro, getFormat(macrosCmd));
1016
+ } catch (err) {
1017
+ error(String(err));
1018
+ process.exit(1);
1019
+ }
1020
+ });
1021
+
1022
+ macrosCmd
1023
+ .command('export')
1024
+ .description('Export macros to CSV or JSON')
1025
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
1026
+ .option('-o, --output <filename>', 'Output filename', 'macros')
1027
+ .action(async (opts) => {
1028
+ try {
1029
+ const client = getClient();
1030
+ logger.command('macros export', { format: opts.format, output: opts.output });
1031
+ info('Fetching macros...');
1032
+
1033
+ const response = await client.macros.list();
1034
+ const filepath = exportData(response.macros, opts.output, opts.format);
1035
+ logger.export('macros', opts.format, filepath, response.macros.length);
1036
+ success(`Exported ${response.macros.length} macros to ${filepath}`);
1037
+ } catch (err) {
1038
+ error(String(err));
1039
+ logger.error('Macros export failed', { error: String(err) });
1040
+ process.exit(1);
1041
+ }
1042
+ });
1043
+
1044
+ // ============================================
1045
+ // Webhooks Commands
1046
+ // ============================================
1047
+ const webhooksCmd = program
1048
+ .command('webhooks')
1049
+ .description('Manage Zendesk webhooks');
1050
+
1051
+ webhooksCmd
1052
+ .command('list')
1053
+ .description('List webhooks')
1054
+ .action(async () => {
1055
+ try {
1056
+ const client = getClient();
1057
+ const webhooks = await client.webhooks.list();
1058
+ print(webhooks, getFormat(webhooksCmd));
1059
+ } catch (err) {
1060
+ error(String(err));
1061
+ process.exit(1);
1062
+ }
1063
+ });
1064
+
1065
+ webhooksCmd
1066
+ .command('get <id>')
1067
+ .description('Get webhook by ID')
1068
+ .action(async (id: string) => {
1069
+ try {
1070
+ const client = getClient();
1071
+ const webhook = await client.webhooks.get(id);
1072
+ print(webhook, getFormat(webhooksCmd));
1073
+ } catch (err) {
1074
+ error(String(err));
1075
+ process.exit(1);
1076
+ }
1077
+ });
1078
+
1079
+ webhooksCmd
1080
+ .command('export')
1081
+ .description('Export webhooks to CSV or JSON')
1082
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
1083
+ .option('-o, --output <filename>', 'Output filename', 'webhooks')
1084
+ .action(async (opts) => {
1085
+ try {
1086
+ const client = getClient();
1087
+ logger.command('webhooks export', { format: opts.format, output: opts.output });
1088
+ info('Fetching webhooks...');
1089
+
1090
+ const response = await client.webhooks.list();
1091
+ const filepath = exportData(response.webhooks, opts.output, opts.format);
1092
+ logger.export('webhooks', opts.format, filepath, response.webhooks.length);
1093
+ success(`Exported ${response.webhooks.length} webhooks to ${filepath}`);
1094
+ } catch (err) {
1095
+ error(String(err));
1096
+ logger.error('Webhooks export failed', { error: String(err) });
1097
+ process.exit(1);
1098
+ }
1099
+ });
1100
+
1101
+ // ============================================
1102
+ // Brands Commands
1103
+ // ============================================
1104
+ const brandsCmd = program
1105
+ .command('brands')
1106
+ .description('Manage Zendesk brands');
1107
+
1108
+ brandsCmd
1109
+ .command('list')
1110
+ .description('List brands')
1111
+ .action(async () => {
1112
+ try {
1113
+ const client = getClient();
1114
+ const brands = await client.brands.list();
1115
+ print(brands, getFormat(brandsCmd));
1116
+ } catch (err) {
1117
+ error(String(err));
1118
+ process.exit(1);
1119
+ }
1120
+ });
1121
+
1122
+ brandsCmd
1123
+ .command('get <id>')
1124
+ .description('Get brand by ID')
1125
+ .action(async (id: string) => {
1126
+ try {
1127
+ const client = getClient();
1128
+ const brand = await client.brands.get(parseInt(id));
1129
+ print(brand, getFormat(brandsCmd));
1130
+ } catch (err) {
1131
+ error(String(err));
1132
+ process.exit(1);
1133
+ }
1134
+ });
1135
+
1136
+ brandsCmd
1137
+ .command('export')
1138
+ .description('Export brands to CSV or JSON')
1139
+ .option('-f, --format <format>', 'Export format (csv, json)', 'csv')
1140
+ .option('-o, --output <filename>', 'Output filename', 'brands')
1141
+ .action(async (opts) => {
1142
+ try {
1143
+ const client = getClient();
1144
+ logger.command('brands export', { format: opts.format, output: opts.output });
1145
+ info('Fetching brands...');
1146
+
1147
+ const response = await client.brands.list();
1148
+ const filepath = exportData(response.brands, opts.output, opts.format);
1149
+ logger.export('brands', opts.format, filepath, response.brands.length);
1150
+ success(`Exported ${response.brands.length} brands to ${filepath}`);
1151
+ } catch (err) {
1152
+ error(String(err));
1153
+ logger.error('Brands export failed', { error: String(err) });
1154
+ process.exit(1);
1155
+ }
1156
+ });
1157
+
1158
+ // ============================================
1159
+ // Bulk Operations Commands
1160
+ // ============================================
1161
+ const bulkCmd = program
1162
+ .command('bulk')
1163
+ .description('Bulk operations for tickets and users');
1164
+
1165
+ bulkCmd
1166
+ .command('update <resource>')
1167
+ .description('Bulk update tickets or users')
1168
+ .requiredOption('-w, --where <filter>', 'Filter expression (e.g., "status=open", "priority=high")')
1169
+ .requiredOption('-s, --set <updates...>', 'Updates to apply (e.g., "status=solved" "priority=low")')
1170
+ .option('--ids <ids>', 'Comma-separated IDs to update (alternative to --where)')
1171
+ .option('--dry-run', 'Preview changes without applying them')
1172
+ .option('--concurrency <number>', 'Number of concurrent operations', '3')
1173
+ .option('--wait', 'Wait for bulk job to complete and show results')
1174
+ .action(async (resource: string, opts) => {
1175
+ try {
1176
+ const client = getClient();
1177
+ const resourceType = resource.toLowerCase() as 'tickets' | 'users';
1178
+
1179
+ if (!['tickets', 'users'].includes(resourceType)) {
1180
+ error(`Invalid resource type: ${resource}. Must be "tickets" or "users".`);
1181
+ process.exit(1);
1182
+ }
1183
+
1184
+ // Parse IDs if provided
1185
+ const ids = opts.ids ? opts.ids.split(',').map((id: string) => parseInt(id.trim(), 10)) : undefined;
1186
+
1187
+ // Parse updates
1188
+ const { FilterParser } = await import('../api/bulk');
1189
+ const updates = (opts.set as string[]).map(s => FilterParser.parseUpdate(s));
1190
+
1191
+ logger.command('bulk update', {
1192
+ resource: resourceType,
1193
+ where: opts.where,
1194
+ ids,
1195
+ updates,
1196
+ dryRun: opts.dryRun,
1197
+ });
1198
+
1199
+ if (opts.dryRun) {
1200
+ info(`[DRY RUN] Would update ${resourceType} matching: ${opts.where || `IDs: ${ids?.join(', ')}`}`);
1201
+ info('Updates to apply:');
1202
+ updates.forEach(u => info(` ${u.field} = ${u.value}`));
1203
+ }
1204
+
1205
+ info(`Searching for ${resourceType}...`);
1206
+
1207
+ const result = await client.bulk.update({
1208
+ resourceType,
1209
+ where: opts.where,
1210
+ ids,
1211
+ updates,
1212
+ dryRun: opts.dryRun,
1213
+ concurrency: parseInt(opts.concurrency, 10),
1214
+ onProgress: (current, total) => {
1215
+ process.stdout.write(`\rProgress: ${current}/${total}`);
1216
+ },
1217
+ });
1218
+
1219
+ console.log(''); // New line after progress
1220
+
1221
+ if (opts.dryRun) {
1222
+ success(`[DRY RUN] Would update ${result.total} ${resourceType}`);
1223
+ if (result.updatedItems.length > 0) {
1224
+ info('\nSample items that would be updated:');
1225
+ result.updatedItems.slice(0, 5).forEach(item => {
1226
+ const title = resourceType === 'tickets'
1227
+ ? (item as any).subject || `Ticket #${item.id}`
1228
+ : (item as any).name || `User #${item.id}`;
1229
+ info(` - ${title} (ID: ${item.id})`);
1230
+ });
1231
+ if (result.updatedItems.length > 5) {
1232
+ info(` ... and ${result.updatedItems.length - 5} more`);
1233
+ }
1234
+ }
1235
+ } else {
1236
+ success(`Bulk update completed:`);
1237
+ info(` Total: ${result.total}`);
1238
+ info(` Success: ${chalk.green(result.success)}`);
1239
+ info(` Failed: ${result.failed > 0 ? chalk.red(result.failed) : result.failed}`);
1240
+
1241
+ if (result.jobStatus) {
1242
+ info(`\nJob Status:`);
1243
+ info(` ID: ${result.jobStatus.id}`);
1244
+ info(` Status: ${result.jobStatus.status}`);
1245
+ if (result.jobStatus.message) {
1246
+ info(` Message: ${result.jobStatus.message}`);
1247
+ }
1248
+
1249
+ // Wait for job completion if requested
1250
+ if (opts.wait && result.jobStatus.status !== 'completed') {
1251
+ info('\nWaiting for job to complete...');
1252
+ const finalStatus = await client.bulk.waitForJob(result.jobStatus.id, {
1253
+ onProgress: (progress, total) => {
1254
+ process.stdout.write(`\rJob progress: ${progress}/${total}`);
1255
+ },
1256
+ });
1257
+ console.log(''); // New line after progress
1258
+ info(`Final status: ${finalStatus.status}`);
1259
+ if (finalStatus.results) {
1260
+ const successes = finalStatus.results.filter(r => r.success).length;
1261
+ const failures = finalStatus.results.filter(r => !r.success).length;
1262
+ info(` Successes: ${chalk.green(successes)}`);
1263
+ info(` Failures: ${failures > 0 ? chalk.red(failures) : failures}`);
1264
+ }
1265
+ }
1266
+ }
1267
+
1268
+ if (result.errors.length > 0) {
1269
+ error('\nErrors:');
1270
+ result.errors.slice(0, 10).forEach(e => {
1271
+ error(` - ID ${e.id}: ${e.error}`);
1272
+ });
1273
+ if (result.errors.length > 10) {
1274
+ error(` ... and ${result.errors.length - 10} more errors`);
1275
+ }
1276
+ }
1277
+ }
1278
+ } catch (err) {
1279
+ error(String(err));
1280
+ logger.error('Bulk update failed', { error: String(err) });
1281
+ process.exit(1);
1282
+ }
1283
+ });
1284
+
1285
+ bulkCmd
1286
+ .command('preview <resource>')
1287
+ .description('Preview items that would be affected by a bulk operation')
1288
+ .requiredOption('-w, --where <filter>', 'Filter expression (e.g., "status=open", "priority=high")')
1289
+ .option('-s, --set <updates...>', 'Updates to preview (shows current values for these fields)')
1290
+ .option('--ids <ids>', 'Comma-separated IDs to preview (alternative to --where)')
1291
+ .option('-l, --limit <limit>', 'Maximum number of items to show', '20')
1292
+ .action(async (resource: string, opts) => {
1293
+ try {
1294
+ const client = getClient();
1295
+ const resourceType = resource.toLowerCase() as 'tickets' | 'users';
1296
+
1297
+ if (!['tickets', 'users'].includes(resourceType)) {
1298
+ error(`Invalid resource type: ${resource}. Must be "tickets" or "users".`);
1299
+ process.exit(1);
1300
+ }
1301
+
1302
+ // Parse IDs if provided
1303
+ const ids = opts.ids ? opts.ids.split(',').map((id: string) => parseInt(id.trim(), 10)) : undefined;
1304
+
1305
+ // Parse updates if provided
1306
+ const { FilterParser } = await import('../api/bulk');
1307
+ const updates = opts.set ? (opts.set as string[]).map(s => FilterParser.parseUpdate(s)) : [];
1308
+
1309
+ logger.command('bulk preview', { resource: resourceType, where: opts.where, ids });
1310
+
1311
+ info(`Searching for ${resourceType} matching: ${opts.where || `IDs: ${ids?.join(', ')}`}`);
1312
+
1313
+ const result = await client.bulk.preview({
1314
+ resourceType,
1315
+ where: opts.where,
1316
+ ids,
1317
+ updates,
1318
+ });
1319
+
1320
+ success(`Found ${result.count} ${resourceType}`);
1321
+
1322
+ if (result.items.length === 0) {
1323
+ info('No items match the filter.');
1324
+ return;
1325
+ }
1326
+
1327
+ info('\nItems:');
1328
+ const limit = parseInt(opts.limit, 10);
1329
+ result.items.slice(0, limit).forEach(item => {
1330
+ info(`\n ${chalk.cyan(item.title)} (ID: ${item.id})`);
1331
+ if (Object.keys(item.currentValues).length > 0) {
1332
+ info(' Current values:');
1333
+ Object.entries(item.currentValues).forEach(([key, value]) => {
1334
+ info(` ${key}: ${chalk.yellow(String(value ?? 'null'))}`);
1335
+ });
1336
+ }
1337
+ });
1338
+
1339
+ if (result.items.length > limit) {
1340
+ info(`\n ... and ${result.items.length - limit} more`);
1341
+ }
1342
+
1343
+ if (updates.length > 0) {
1344
+ info('\nProposed updates:');
1345
+ updates.forEach(u => {
1346
+ info(` ${u.field} -> ${chalk.green(u.value)}`);
1347
+ });
1348
+ }
1349
+ } catch (err) {
1350
+ error(String(err));
1351
+ logger.error('Bulk preview failed', { error: String(err) });
1352
+ process.exit(1);
1353
+ }
1354
+ });
1355
+
1356
+ bulkCmd
1357
+ .command('schema [resource]')
1358
+ .description('Show available fields and values for bulk operations')
1359
+ .option('--field <field>', 'Show details for a specific field')
1360
+ .action(async (resource: string | undefined, opts) => {
1361
+ try {
1362
+ const client = getClient();
1363
+ logger.command('bulk schema', { resource, field: opts.field });
1364
+
1365
+ info('Fetching schema...');
1366
+ const schema = await client.bulk.getSchema();
1367
+
1368
+ if (resource === 'tickets' || !resource) {
1369
+ info(chalk.bold('\nTickets Schema'));
1370
+ info(chalk.bold('=============='));
1371
+
1372
+ info('\nStatuses:');
1373
+ schema.tickets.statuses.forEach(s => info(` - ${s}`));
1374
+
1375
+ info('\nPriorities:');
1376
+ schema.tickets.priorities.forEach(p => info(` - ${p}`));
1377
+
1378
+ info('\nTypes:');
1379
+ schema.tickets.types.forEach(t => info(` - ${t}`));
1380
+
1381
+ info('\nFields:');
1382
+ schema.tickets.fields.forEach(field => {
1383
+ if (opts.field && field.name !== opts.field) return;
1384
+
1385
+ info(`\n ${chalk.cyan(field.name)} (${field.type})`);
1386
+ if (field.description) {
1387
+ info(` ${chalk.gray(field.description)}`);
1388
+ }
1389
+ if (field.options && field.options.length > 0) {
1390
+ info(' Options:');
1391
+ field.options.forEach(opt => {
1392
+ info(` - ${opt.name}: ${chalk.yellow(opt.value)}`);
1393
+ });
1394
+ }
1395
+ });
1396
+ }
1397
+
1398
+ if (resource === 'users' || !resource) {
1399
+ info(chalk.bold('\nUsers Schema'));
1400
+ info(chalk.bold('============'));
1401
+
1402
+ info('\nRoles:');
1403
+ schema.users.roles.forEach(r => info(` - ${r}`));
1404
+
1405
+ info('\nFields:');
1406
+ schema.users.fields.forEach(field => {
1407
+ if (opts.field && field.name !== opts.field) return;
1408
+
1409
+ info(`\n ${chalk.cyan(field.name)} (${field.type})`);
1410
+ if (field.description) {
1411
+ info(` ${chalk.gray(field.description)}`);
1412
+ }
1413
+ if (field.options && field.options.length > 0) {
1414
+ info(' Options:');
1415
+ field.options.forEach(opt => {
1416
+ info(` - ${opt.name}: ${chalk.yellow(opt.value)}`);
1417
+ });
1418
+ }
1419
+ });
1420
+ }
1421
+
1422
+ info('\n' + chalk.bold('Usage Examples:'));
1423
+ info(' # Update all open tickets to pending');
1424
+ info(' connect-zendesk bulk update tickets --where "status=open" --set "status=pending"');
1425
+ info('');
1426
+ info(' # Update priority for high-priority open tickets');
1427
+ info(' connect-zendesk bulk update tickets --where "status=open&priority=high" --set "priority=urgent"');
1428
+ info('');
1429
+ info(' # Update multiple fields at once');
1430
+ info(' connect-zendesk bulk update tickets --where "status=new" --set "status=open" "priority=normal"');
1431
+ info('');
1432
+ info(' # Preview changes before applying');
1433
+ info(' connect-zendesk bulk update tickets --where "status=pending" --set "status=solved" --dry-run');
1434
+ info('');
1435
+ info(' # Update specific tickets by ID');
1436
+ info(' connect-zendesk bulk update tickets --ids "123,456,789" --set "status=closed"');
1437
+ info('');
1438
+ info(' # Suspend users matching a filter');
1439
+ info(' connect-zendesk bulk update users --where "suspended=false" --set "suspended=true"');
1440
+
1441
+ } catch (err) {
1442
+ error(String(err));
1443
+ logger.error('Bulk schema failed', { error: String(err) });
1444
+ process.exit(1);
1445
+ }
1446
+ });
1447
+
1448
+ bulkCmd
1449
+ .command('job <jobId>')
1450
+ .description('Check the status of a bulk operation job')
1451
+ .option('--wait', 'Wait for job to complete')
1452
+ .action(async (jobId: string, opts) => {
1453
+ try {
1454
+ const client = getClient();
1455
+ logger.command('bulk job', { jobId, wait: opts.wait });
1456
+
1457
+ if (opts.wait) {
1458
+ info(`Waiting for job ${jobId} to complete...`);
1459
+ const status = await client.bulk.waitForJob(jobId, {
1460
+ onProgress: (progress, total) => {
1461
+ process.stdout.write(`\rProgress: ${progress}/${total}`);
1462
+ },
1463
+ });
1464
+ console.log(''); // New line after progress
1465
+ print(status, getFormat(bulkCmd));
1466
+ } else {
1467
+ const status = await client.bulk.getJobStatus(jobId);
1468
+ print(status, getFormat(bulkCmd));
1469
+ }
1470
+ } catch (err) {
1471
+ error(String(err));
1472
+ logger.error('Bulk job check failed', { error: String(err) });
1473
+ process.exit(1);
1474
+ }
1475
+ });
1476
+
1477
+ // Parse and execute
1478
+ program.parse();