@hasna/connectors 0.5.0 → 0.5.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 (223) hide show
  1. package/bin/index.js +147 -6
  2. package/bin/mcp.js +92 -1
  3. package/bin/serve.js +91 -0
  4. package/connectors/connect-ably/.env.example +11 -0
  5. package/connectors/connect-ably/CLAUDE.md +111 -0
  6. package/connectors/connect-ably/README.md +193 -0
  7. package/connectors/connect-ably/package.json +54 -0
  8. package/connectors/connect-ably/scripts/release.ts +179 -0
  9. package/connectors/connect-ably/src/api/channels.ts +33 -0
  10. package/connectors/connect-ably/src/api/client.ts +203 -0
  11. package/connectors/connect-ably/src/api/index.ts +59 -0
  12. package/connectors/connect-ably/src/api/messages.ts +48 -0
  13. package/connectors/connect-ably/src/api/presence.ts +39 -0
  14. package/connectors/connect-ably/src/api/stats.ts +29 -0
  15. package/connectors/connect-ably/src/cli/index.ts +397 -0
  16. package/connectors/connect-ably/src/index.ts +102 -0
  17. package/connectors/connect-ably/src/types/index.ts +294 -0
  18. package/connectors/connect-ably/src/utils/auth.ts +274 -0
  19. package/connectors/connect-ably/src/utils/bulk.ts +212 -0
  20. package/connectors/connect-ably/src/utils/config.ts +323 -0
  21. package/connectors/connect-ably/src/utils/output.ts +175 -0
  22. package/connectors/connect-ably/src/utils/settings.ts +114 -0
  23. package/connectors/connect-ably/src/utils/storage.ts +198 -0
  24. package/connectors/connect-ably/tsconfig.json +16 -0
  25. package/connectors/connect-box/.env.example +11 -0
  26. package/connectors/connect-box/CLAUDE.md +272 -0
  27. package/connectors/connect-box/README.md +193 -0
  28. package/connectors/connect-box/package.json +51 -0
  29. package/connectors/connect-box/scripts/release.ts +179 -0
  30. package/connectors/connect-box/src/api/client.ts +213 -0
  31. package/connectors/connect-box/src/api/example.ts +48 -0
  32. package/connectors/connect-box/src/api/index.ts +51 -0
  33. package/connectors/connect-box/src/cli/index.ts +254 -0
  34. package/connectors/connect-box/src/index.ts +103 -0
  35. package/connectors/connect-box/src/types/index.ts +237 -0
  36. package/connectors/connect-box/src/utils/auth.ts +274 -0
  37. package/connectors/connect-box/src/utils/bulk.ts +212 -0
  38. package/connectors/connect-box/src/utils/config.ts +326 -0
  39. package/connectors/connect-box/src/utils/output.ts +175 -0
  40. package/connectors/connect-box/src/utils/settings.ts +114 -0
  41. package/connectors/connect-box/src/utils/storage.ts +198 -0
  42. package/connectors/connect-box/tsconfig.json +16 -0
  43. package/connectors/connect-clearbit/.env.example +11 -0
  44. package/connectors/connect-clearbit/CLAUDE.md +272 -0
  45. package/connectors/connect-clearbit/README.md +193 -0
  46. package/connectors/connect-clearbit/package.json +51 -0
  47. package/connectors/connect-clearbit/scripts/release.ts +179 -0
  48. package/connectors/connect-clearbit/src/api/client.ts +213 -0
  49. package/connectors/connect-clearbit/src/api/example.ts +48 -0
  50. package/connectors/connect-clearbit/src/api/index.ts +51 -0
  51. package/connectors/connect-clearbit/src/cli/index.ts +254 -0
  52. package/connectors/connect-clearbit/src/index.ts +103 -0
  53. package/connectors/connect-clearbit/src/types/index.ts +237 -0
  54. package/connectors/connect-clearbit/src/utils/auth.ts +274 -0
  55. package/connectors/connect-clearbit/src/utils/bulk.ts +212 -0
  56. package/connectors/connect-clearbit/src/utils/config.ts +326 -0
  57. package/connectors/connect-clearbit/src/utils/output.ts +175 -0
  58. package/connectors/connect-clearbit/src/utils/settings.ts +114 -0
  59. package/connectors/connect-clearbit/src/utils/storage.ts +198 -0
  60. package/connectors/connect-clearbit/tsconfig.json +16 -0
  61. package/connectors/connect-coda/.env.example +11 -0
  62. package/connectors/connect-coda/CLAUDE.md +272 -0
  63. package/connectors/connect-coda/README.md +193 -0
  64. package/connectors/connect-coda/package.json +51 -0
  65. package/connectors/connect-coda/scripts/release.ts +179 -0
  66. package/connectors/connect-coda/src/api/client.ts +213 -0
  67. package/connectors/connect-coda/src/api/example.ts +48 -0
  68. package/connectors/connect-coda/src/api/index.ts +51 -0
  69. package/connectors/connect-coda/src/cli/index.ts +254 -0
  70. package/connectors/connect-coda/src/index.ts +103 -0
  71. package/connectors/connect-coda/src/types/index.ts +237 -0
  72. package/connectors/connect-coda/src/utils/auth.ts +274 -0
  73. package/connectors/connect-coda/src/utils/bulk.ts +212 -0
  74. package/connectors/connect-coda/src/utils/config.ts +326 -0
  75. package/connectors/connect-coda/src/utils/output.ts +175 -0
  76. package/connectors/connect-coda/src/utils/settings.ts +114 -0
  77. package/connectors/connect-coda/src/utils/storage.ts +198 -0
  78. package/connectors/connect-coda/tsconfig.json +16 -0
  79. package/connectors/connect-dropbox/.env.example +11 -0
  80. package/connectors/connect-dropbox/CLAUDE.md +119 -0
  81. package/connectors/connect-dropbox/README.md +193 -0
  82. package/connectors/connect-dropbox/package.json +51 -0
  83. package/connectors/connect-dropbox/src/api/client.ts +222 -0
  84. package/connectors/connect-dropbox/src/api/index.ts +395 -0
  85. package/connectors/connect-dropbox/src/cli/index.ts +627 -0
  86. package/connectors/connect-dropbox/src/index.ts +20 -0
  87. package/connectors/connect-dropbox/src/types/index.ts +516 -0
  88. package/connectors/connect-dropbox/src/utils/config.ts +197 -0
  89. package/connectors/connect-dropbox/tsconfig.json +16 -0
  90. package/connectors/connect-linode/.env.example +11 -0
  91. package/connectors/connect-linode/CLAUDE.md +272 -0
  92. package/connectors/connect-linode/README.md +193 -0
  93. package/connectors/connect-linode/package.json +51 -0
  94. package/connectors/connect-linode/scripts/release.ts +179 -0
  95. package/connectors/connect-linode/src/api/client.ts +213 -0
  96. package/connectors/connect-linode/src/api/example.ts +48 -0
  97. package/connectors/connect-linode/src/api/index.ts +51 -0
  98. package/connectors/connect-linode/src/cli/index.ts +254 -0
  99. package/connectors/connect-linode/src/index.ts +103 -0
  100. package/connectors/connect-linode/src/types/index.ts +237 -0
  101. package/connectors/connect-linode/src/utils/auth.ts +274 -0
  102. package/connectors/connect-linode/src/utils/bulk.ts +212 -0
  103. package/connectors/connect-linode/src/utils/config.ts +326 -0
  104. package/connectors/connect-linode/src/utils/output.ts +175 -0
  105. package/connectors/connect-linode/src/utils/settings.ts +114 -0
  106. package/connectors/connect-linode/src/utils/storage.ts +198 -0
  107. package/connectors/connect-linode/tsconfig.json +16 -0
  108. package/connectors/connect-mailgun/.env.example +11 -0
  109. package/connectors/connect-mailgun/CLAUDE.md +272 -0
  110. package/connectors/connect-mailgun/README.md +193 -0
  111. package/connectors/connect-mailgun/package.json +51 -0
  112. package/connectors/connect-mailgun/scripts/release.ts +179 -0
  113. package/connectors/connect-mailgun/src/api/client.ts +213 -0
  114. package/connectors/connect-mailgun/src/api/example.ts +48 -0
  115. package/connectors/connect-mailgun/src/api/index.ts +51 -0
  116. package/connectors/connect-mailgun/src/cli/index.ts +254 -0
  117. package/connectors/connect-mailgun/src/index.ts +103 -0
  118. package/connectors/connect-mailgun/src/types/index.ts +237 -0
  119. package/connectors/connect-mailgun/src/utils/auth.ts +274 -0
  120. package/connectors/connect-mailgun/src/utils/bulk.ts +212 -0
  121. package/connectors/connect-mailgun/src/utils/config.ts +326 -0
  122. package/connectors/connect-mailgun/src/utils/output.ts +175 -0
  123. package/connectors/connect-mailgun/src/utils/settings.ts +114 -0
  124. package/connectors/connect-mailgun/src/utils/storage.ts +198 -0
  125. package/connectors/connect-mailgun/tsconfig.json +16 -0
  126. package/connectors/connect-messagebird/.env.example +11 -0
  127. package/connectors/connect-messagebird/CLAUDE.md +272 -0
  128. package/connectors/connect-messagebird/README.md +193 -0
  129. package/connectors/connect-messagebird/package.json +51 -0
  130. package/connectors/connect-messagebird/scripts/release.ts +179 -0
  131. package/connectors/connect-messagebird/src/api/client.ts +213 -0
  132. package/connectors/connect-messagebird/src/api/example.ts +48 -0
  133. package/connectors/connect-messagebird/src/api/index.ts +51 -0
  134. package/connectors/connect-messagebird/src/cli/index.ts +254 -0
  135. package/connectors/connect-messagebird/src/index.ts +103 -0
  136. package/connectors/connect-messagebird/src/types/index.ts +237 -0
  137. package/connectors/connect-messagebird/src/utils/auth.ts +274 -0
  138. package/connectors/connect-messagebird/src/utils/bulk.ts +212 -0
  139. package/connectors/connect-messagebird/src/utils/config.ts +326 -0
  140. package/connectors/connect-messagebird/src/utils/output.ts +175 -0
  141. package/connectors/connect-messagebird/src/utils/settings.ts +114 -0
  142. package/connectors/connect-messagebird/src/utils/storage.ts +198 -0
  143. package/connectors/connect-messagebird/tsconfig.json +16 -0
  144. package/connectors/connect-miro/.env.example +11 -0
  145. package/connectors/connect-miro/CLAUDE.md +272 -0
  146. package/connectors/connect-miro/README.md +193 -0
  147. package/connectors/connect-miro/package.json +51 -0
  148. package/connectors/connect-miro/scripts/release.ts +179 -0
  149. package/connectors/connect-miro/src/api/client.ts +213 -0
  150. package/connectors/connect-miro/src/api/example.ts +48 -0
  151. package/connectors/connect-miro/src/api/index.ts +51 -0
  152. package/connectors/connect-miro/src/cli/index.ts +254 -0
  153. package/connectors/connect-miro/src/index.ts +103 -0
  154. package/connectors/connect-miro/src/types/index.ts +237 -0
  155. package/connectors/connect-miro/src/utils/auth.ts +274 -0
  156. package/connectors/connect-miro/src/utils/bulk.ts +212 -0
  157. package/connectors/connect-miro/src/utils/config.ts +326 -0
  158. package/connectors/connect-miro/src/utils/output.ts +175 -0
  159. package/connectors/connect-miro/src/utils/settings.ts +114 -0
  160. package/connectors/connect-miro/src/utils/storage.ts +198 -0
  161. package/connectors/connect-miro/tsconfig.json +16 -0
  162. package/connectors/connect-monday/.env.example +11 -0
  163. package/connectors/connect-monday/CLAUDE.md +128 -0
  164. package/connectors/connect-monday/README.md +193 -0
  165. package/connectors/connect-monday/package.json +52 -0
  166. package/connectors/connect-monday/src/api/client.ts +59 -0
  167. package/connectors/connect-monday/src/api/index.ts +539 -0
  168. package/connectors/connect-monday/src/cli/index.ts +479 -0
  169. package/connectors/connect-monday/src/index.ts +19 -0
  170. package/connectors/connect-monday/src/types/index.ts +274 -0
  171. package/connectors/connect-monday/src/utils/config.ts +197 -0
  172. package/connectors/connect-monday/src/utils/output.ts +119 -0
  173. package/connectors/connect-monday/tsconfig.json +16 -0
  174. package/connectors/connect-pipedrive/.env.example +11 -0
  175. package/connectors/connect-pipedrive/CLAUDE.md +128 -0
  176. package/connectors/connect-pipedrive/README.md +193 -0
  177. package/connectors/connect-pipedrive/package.json +52 -0
  178. package/connectors/connect-pipedrive/src/api/client.ts +121 -0
  179. package/connectors/connect-pipedrive/src/api/index.ts +306 -0
  180. package/connectors/connect-pipedrive/src/cli/index.ts +824 -0
  181. package/connectors/connect-pipedrive/src/index.ts +19 -0
  182. package/connectors/connect-pipedrive/src/types/index.ts +335 -0
  183. package/connectors/connect-pipedrive/src/utils/config.ts +171 -0
  184. package/connectors/connect-pipedrive/src/utils/output.ts +119 -0
  185. package/connectors/connect-pipedrive/tsconfig.json +16 -0
  186. package/connectors/connect-pusher/.env.example +11 -0
  187. package/connectors/connect-pusher/CLAUDE.md +272 -0
  188. package/connectors/connect-pusher/README.md +193 -0
  189. package/connectors/connect-pusher/package.json +51 -0
  190. package/connectors/connect-pusher/scripts/release.ts +179 -0
  191. package/connectors/connect-pusher/src/api/client.ts +213 -0
  192. package/connectors/connect-pusher/src/api/example.ts +48 -0
  193. package/connectors/connect-pusher/src/api/index.ts +51 -0
  194. package/connectors/connect-pusher/src/cli/index.ts +254 -0
  195. package/connectors/connect-pusher/src/index.ts +103 -0
  196. package/connectors/connect-pusher/src/types/index.ts +237 -0
  197. package/connectors/connect-pusher/src/utils/auth.ts +274 -0
  198. package/connectors/connect-pusher/src/utils/bulk.ts +212 -0
  199. package/connectors/connect-pusher/src/utils/config.ts +326 -0
  200. package/connectors/connect-pusher/src/utils/output.ts +175 -0
  201. package/connectors/connect-pusher/src/utils/settings.ts +114 -0
  202. package/connectors/connect-pusher/src/utils/storage.ts +198 -0
  203. package/connectors/connect-pusher/tsconfig.json +16 -0
  204. package/connectors/connect-vonage/.env.example +11 -0
  205. package/connectors/connect-vonage/CLAUDE.md +272 -0
  206. package/connectors/connect-vonage/README.md +193 -0
  207. package/connectors/connect-vonage/package.json +51 -0
  208. package/connectors/connect-vonage/scripts/release.ts +179 -0
  209. package/connectors/connect-vonage/src/api/client.ts +213 -0
  210. package/connectors/connect-vonage/src/api/example.ts +48 -0
  211. package/connectors/connect-vonage/src/api/index.ts +51 -0
  212. package/connectors/connect-vonage/src/cli/index.ts +254 -0
  213. package/connectors/connect-vonage/src/index.ts +103 -0
  214. package/connectors/connect-vonage/src/types/index.ts +237 -0
  215. package/connectors/connect-vonage/src/utils/auth.ts +274 -0
  216. package/connectors/connect-vonage/src/utils/bulk.ts +212 -0
  217. package/connectors/connect-vonage/src/utils/config.ts +326 -0
  218. package/connectors/connect-vonage/src/utils/output.ts +175 -0
  219. package/connectors/connect-vonage/src/utils/settings.ts +114 -0
  220. package/connectors/connect-vonage/src/utils/storage.ts +198 -0
  221. package/connectors/connect-vonage/tsconfig.json +16 -0
  222. package/dist/index.js +91 -0
  223. package/package.json +1 -1
@@ -0,0 +1,59 @@
1
+ import type { ConnectorConfig } from '../types';
2
+ import { ConnectorClient } from './client';
3
+ import { MessagesApi } from './messages';
4
+ import { ChannelsApi } from './channels';
5
+ import { PresenceApi } from './presence';
6
+ import { StatsApi } from './stats';
7
+
8
+ /**
9
+ * Ably REST API Connector
10
+ */
11
+ export class Connector {
12
+ private readonly client: ConnectorClient;
13
+
14
+ public readonly messages: MessagesApi;
15
+ public readonly channels: ChannelsApi;
16
+ public readonly presence: PresenceApi;
17
+ public readonly stats: StatsApi;
18
+
19
+ constructor(config: ConnectorConfig) {
20
+ this.client = new ConnectorClient(config);
21
+ this.messages = new MessagesApi(this.client);
22
+ this.channels = new ChannelsApi(this.client);
23
+ this.presence = new PresenceApi(this.client);
24
+ this.stats = new StatsApi(this.client);
25
+ }
26
+
27
+ /**
28
+ * Create a client from environment variables
29
+ * Looks for ABLY_API_KEY
30
+ */
31
+ static fromEnv(): Connector {
32
+ const apiKey = process.env.ABLY_API_KEY;
33
+
34
+ if (!apiKey) {
35
+ throw new Error('ABLY_API_KEY environment variable is required');
36
+ }
37
+ return new Connector({ apiKey });
38
+ }
39
+
40
+ /**
41
+ * Get a preview of the API key (for debugging)
42
+ */
43
+ getApiKeyPreview(): string {
44
+ return this.client.getApiKeyPreview();
45
+ }
46
+
47
+ /**
48
+ * Get the underlying client for direct API access
49
+ */
50
+ getClient(): ConnectorClient {
51
+ return this.client;
52
+ }
53
+ }
54
+
55
+ export { ConnectorClient } from './client';
56
+ export { MessagesApi } from './messages';
57
+ export { ChannelsApi } from './channels';
58
+ export { PresenceApi } from './presence';
59
+ export { StatsApi } from './stats';
@@ -0,0 +1,48 @@
1
+ import type { ConnectorClient } from './client';
2
+ import type {
3
+ Message,
4
+ PublishMessageParams,
5
+ PublishMessageResult,
6
+ MessageHistoryParams,
7
+ } from '../types';
8
+
9
+ export class MessagesApi {
10
+ constructor(private readonly client: ConnectorClient) {}
11
+
12
+ /**
13
+ * Publish a message to a channel
14
+ * POST /channels/{channelId}/messages
15
+ */
16
+ async publish(channelId: string, params: PublishMessageParams): Promise<PublishMessageResult> {
17
+ const body: Record<string, unknown> = {};
18
+ if (params.name !== undefined) body.name = params.name;
19
+ if (params.data !== undefined) body.data = params.data;
20
+ if (params.id !== undefined) body.id = params.id;
21
+ if (params.clientId !== undefined) body.clientId = params.clientId;
22
+ if (params.extras !== undefined) body.extras = params.extras;
23
+
24
+ await this.client.post(`/channels/${encodeURIComponent(channelId)}/messages`, body);
25
+
26
+ return {
27
+ channel: channelId,
28
+ messageId: params.id || '',
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Get message history for a channel
34
+ * GET /channels/{channelId}/messages
35
+ */
36
+ async history(channelId: string, params?: MessageHistoryParams): Promise<Message[]> {
37
+ const queryParams: Record<string, string | number | boolean | undefined> = {};
38
+ if (params?.start) queryParams.start = params.start;
39
+ if (params?.end) queryParams.end = params.end;
40
+ if (params?.limit) queryParams.limit = params.limit;
41
+ if (params?.direction) queryParams.direction = params.direction;
42
+
43
+ return this.client.get<Message[]>(
44
+ `/channels/${encodeURIComponent(channelId)}/messages`,
45
+ queryParams,
46
+ );
47
+ }
48
+ }
@@ -0,0 +1,39 @@
1
+ import type { ConnectorClient } from './client';
2
+ import type { PresenceMember, PresenceParams, PresenceHistoryParams } from '../types';
3
+
4
+ export class PresenceApi {
5
+ constructor(private readonly client: ConnectorClient) {}
6
+
7
+ /**
8
+ * Get current presence members for a channel
9
+ * GET /channels/{channelId}/presence
10
+ */
11
+ async get(channelId: string, params?: PresenceParams): Promise<PresenceMember[]> {
12
+ const queryParams: Record<string, string | number | boolean | undefined> = {};
13
+ if (params?.clientId) queryParams.clientId = params.clientId;
14
+ if (params?.connectionId) queryParams.connectionId = params.connectionId;
15
+ if (params?.limit) queryParams.limit = params.limit;
16
+
17
+ return this.client.get<PresenceMember[]>(
18
+ `/channels/${encodeURIComponent(channelId)}/presence`,
19
+ queryParams,
20
+ );
21
+ }
22
+
23
+ /**
24
+ * Get presence history for a channel
25
+ * GET /channels/{channelId}/presence/history
26
+ */
27
+ async history(channelId: string, params?: PresenceHistoryParams): Promise<PresenceMember[]> {
28
+ const queryParams: Record<string, string | number | boolean | undefined> = {};
29
+ if (params?.start) queryParams.start = params.start;
30
+ if (params?.end) queryParams.end = params.end;
31
+ if (params?.limit) queryParams.limit = params.limit;
32
+ if (params?.direction) queryParams.direction = params.direction;
33
+
34
+ return this.client.get<PresenceMember[]>(
35
+ `/channels/${encodeURIComponent(channelId)}/presence/history`,
36
+ queryParams,
37
+ );
38
+ }
39
+ }
@@ -0,0 +1,29 @@
1
+ import type { ConnectorClient } from './client';
2
+ import type { Stats, StatsParams } from '../types';
3
+
4
+ export class StatsApi {
5
+ constructor(private readonly client: ConnectorClient) {}
6
+
7
+ /**
8
+ * Get application statistics
9
+ * GET /stats
10
+ */
11
+ async get(params?: StatsParams): Promise<Stats[]> {
12
+ const queryParams: Record<string, string | number | boolean | undefined> = {};
13
+ if (params?.start) queryParams.start = params.start;
14
+ if (params?.end) queryParams.end = params.end;
15
+ if (params?.limit) queryParams.limit = params.limit;
16
+ if (params?.direction) queryParams.direction = params.direction;
17
+ if (params?.unit) queryParams.unit = params.unit;
18
+
19
+ return this.client.get<Stats[]>('/stats', queryParams);
20
+ }
21
+
22
+ /**
23
+ * Get server time
24
+ * GET /time
25
+ */
26
+ async time(): Promise<number[]> {
27
+ return this.client.get<number[]>('/time');
28
+ }
29
+ }
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env bun
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { Connector } from '../api';
5
+ import {
6
+ getApiKey,
7
+ setApiKey,
8
+ clearConfig,
9
+ getConfigDir,
10
+ setProfileOverride,
11
+ getCurrentProfile,
12
+ setCurrentProfile,
13
+ listProfiles,
14
+ createProfile,
15
+ deleteProfile,
16
+ profileExists,
17
+ loadProfile,
18
+ } from '../utils/config';
19
+ import type { OutputFormat } from '../utils/output';
20
+ import { success, error, info, print, warn, setVerboseMode, debug } from '../utils/output';
21
+
22
+ const CONNECTOR_NAME = 'connect-ably';
23
+ const VERSION = '0.0.1';
24
+
25
+ const program = new Command();
26
+
27
+ program
28
+ .name(CONNECTOR_NAME)
29
+ .description('Ably REST API connector CLI')
30
+ .version(VERSION)
31
+ .option('-k, --api-key <key>', 'API key (overrides config)')
32
+ .option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
33
+ .option('-p, --profile <profile>', 'Use a specific profile')
34
+ .option('-v, --verbose', 'Enable verbose output for debugging')
35
+ .hook('preAction', (thisCommand) => {
36
+ const opts = thisCommand.opts();
37
+
38
+ if (opts.verbose) {
39
+ setVerboseMode(true);
40
+ debug('Verbose mode enabled');
41
+ }
42
+
43
+ if (opts.profile) {
44
+ if (!profileExists(opts.profile)) {
45
+ error(`Profile "${opts.profile}" does not exist. Create it with "${CONNECTOR_NAME} profile create ${opts.profile}"`);
46
+ process.exit(1);
47
+ }
48
+ setProfileOverride(opts.profile);
49
+ debug(`Using profile: ${opts.profile}`);
50
+ }
51
+
52
+ if (opts.apiKey) {
53
+ process.env.ABLY_API_KEY = opts.apiKey;
54
+ debug('API key set from command line flag');
55
+ }
56
+ });
57
+
58
+ // Helper to get output format
59
+ function getFormat(cmd: Command): OutputFormat {
60
+ const parent = cmd.parent;
61
+ return (parent?.opts().format || 'pretty') as OutputFormat;
62
+ }
63
+
64
+ // Helper to get authenticated client
65
+ function getClient(): Connector {
66
+ const apiKey = getApiKey();
67
+ if (!apiKey) {
68
+ error(`No API key configured. Run "${CONNECTOR_NAME} config set-key <key>" or set ABLY_API_KEY environment variable.`);
69
+ process.exit(1);
70
+ }
71
+ return new Connector({ apiKey });
72
+ }
73
+
74
+ // ============================================
75
+ // Profile Commands
76
+ // ============================================
77
+ const profileCmd = program
78
+ .command('profile')
79
+ .description('Manage configuration profiles');
80
+
81
+ profileCmd
82
+ .command('list')
83
+ .description('List all profiles')
84
+ .action(() => {
85
+ const profiles = listProfiles();
86
+ const current = getCurrentProfile();
87
+
88
+ if (profiles.length === 0) {
89
+ info('No profiles found. Use "profile create <name>" to create one.');
90
+ return;
91
+ }
92
+
93
+ success(`Profiles:`);
94
+ profiles.forEach(p => {
95
+ const isActive = p === current ? chalk.green(' (active)') : '';
96
+ console.log(` ${p}${isActive}`);
97
+ });
98
+ });
99
+
100
+ profileCmd
101
+ .command('use <name>')
102
+ .description('Switch to a profile')
103
+ .action((name: string) => {
104
+ if (!profileExists(name)) {
105
+ error(`Profile "${name}" does not exist. Create it with "profile create ${name}"`);
106
+ process.exit(1);
107
+ }
108
+ setCurrentProfile(name);
109
+ success(`Switched to profile: ${name}`);
110
+ });
111
+
112
+ profileCmd
113
+ .command('create <name>')
114
+ .description('Create a new profile')
115
+ .option('--api-key <key>', 'API key')
116
+ .option('--use', 'Switch to this profile after creation')
117
+ .action((name: string, opts) => {
118
+ if (profileExists(name)) {
119
+ error(`Profile "${name}" already exists`);
120
+ process.exit(1);
121
+ }
122
+
123
+ createProfile(name, {
124
+ apiKey: opts.apiKey,
125
+ });
126
+ success(`Profile "${name}" created`);
127
+
128
+ if (opts.use) {
129
+ setCurrentProfile(name);
130
+ info(`Switched to profile: ${name}`);
131
+ }
132
+ });
133
+
134
+ profileCmd
135
+ .command('delete <name>')
136
+ .description('Delete a profile')
137
+ .action((name: string) => {
138
+ if (name === 'default') {
139
+ error('Cannot delete the default profile');
140
+ process.exit(1);
141
+ }
142
+ if (deleteProfile(name)) {
143
+ success(`Profile "${name}" deleted`);
144
+ } else {
145
+ error(`Profile "${name}" not found`);
146
+ process.exit(1);
147
+ }
148
+ });
149
+
150
+ profileCmd
151
+ .command('show [name]')
152
+ .description('Show profile configuration')
153
+ .action((name?: string) => {
154
+ const profileName = name || getCurrentProfile();
155
+ const config = loadProfile(profileName);
156
+ const active = getCurrentProfile();
157
+
158
+ console.log(chalk.bold(`Profile: ${profileName}${profileName === active ? chalk.green(' (active)') : ''}`));
159
+ info(`API Key: ${config.apiKey ? `${config.apiKey.substring(0, 8)}...` : chalk.gray('not set')}`);
160
+ });
161
+
162
+ // ============================================
163
+ // Config Commands
164
+ // ============================================
165
+ const configCmd = program
166
+ .command('config')
167
+ .description('Manage CLI configuration (for active profile)');
168
+
169
+ configCmd
170
+ .command('set-key <apiKey>')
171
+ .description('Set API key (format: appId.keyId:keySecret)')
172
+ .action((apiKey: string) => {
173
+ setApiKey(apiKey);
174
+ success(`API key saved to profile: ${getCurrentProfile()}`);
175
+ });
176
+
177
+ configCmd
178
+ .command('show')
179
+ .description('Show current configuration')
180
+ .action(() => {
181
+ const profileName = getCurrentProfile();
182
+ const apiKey = getApiKey();
183
+
184
+ console.log(chalk.bold(`Active Profile: ${profileName}`));
185
+ info(`Config directory: ${getConfigDir()}`);
186
+ info(`API Key: ${apiKey ? `${apiKey.substring(0, 8)}...` : chalk.gray('not set')}`);
187
+ });
188
+
189
+ configCmd
190
+ .command('clear')
191
+ .description('Clear configuration for active profile')
192
+ .action(() => {
193
+ clearConfig();
194
+ success(`Configuration cleared for profile: ${getCurrentProfile()}`);
195
+ });
196
+
197
+ // ============================================
198
+ // Messages Commands
199
+ // ============================================
200
+ const messagesCmd = program
201
+ .command('messages')
202
+ .description('Publish and retrieve channel messages');
203
+
204
+ messagesCmd
205
+ .command('publish <channel>')
206
+ .description('Publish a message to a channel')
207
+ .requiredOption('-n, --name <name>', 'Event name')
208
+ .option('-d, --data <data>', 'Message data (JSON string)')
209
+ .option('--client-id <clientId>', 'Client ID')
210
+ .action(async (channel: string, opts) => {
211
+ try {
212
+ const client = getClient();
213
+ let data: unknown = opts.data;
214
+ if (typeof data === 'string') {
215
+ try {
216
+ data = JSON.parse(data);
217
+ } catch {
218
+ // Keep as string
219
+ }
220
+ }
221
+ const result = await client.messages.publish(channel, {
222
+ name: opts.name,
223
+ data,
224
+ clientId: opts.clientId,
225
+ });
226
+ success(`Message published to channel: ${channel}`);
227
+ print(result, getFormat(messagesCmd));
228
+ } catch (err) {
229
+ error(String(err));
230
+ process.exit(1);
231
+ }
232
+ });
233
+
234
+ messagesCmd
235
+ .command('history <channel>')
236
+ .description('Get message history for a channel')
237
+ .option('-l, --limit <number>', 'Maximum results', '100')
238
+ .option('--start <time>', 'Start time (ms since epoch)')
239
+ .option('--end <time>', 'End time (ms since epoch)')
240
+ .option('--direction <dir>', 'Sort direction (forwards, backwards)', 'backwards')
241
+ .action(async (channel: string, opts) => {
242
+ try {
243
+ const client = getClient();
244
+ const result = await client.messages.history(channel, {
245
+ limit: parseInt(opts.limit),
246
+ start: opts.start,
247
+ end: opts.end,
248
+ direction: opts.direction,
249
+ });
250
+ print(result, getFormat(messagesCmd));
251
+ } catch (err) {
252
+ error(String(err));
253
+ process.exit(1);
254
+ }
255
+ });
256
+
257
+ // ============================================
258
+ // Channels Commands
259
+ // ============================================
260
+ const channelsCmd = program
261
+ .command('channels')
262
+ .description('List and inspect channels');
263
+
264
+ channelsCmd
265
+ .command('list')
266
+ .description('List active channels')
267
+ .option('-l, --limit <number>', 'Maximum results', '100')
268
+ .option('--prefix <prefix>', 'Filter by channel name prefix')
269
+ .option('--by <type>', 'Response type (id, value)', 'value')
270
+ .action(async (opts) => {
271
+ try {
272
+ const client = getClient();
273
+ const result = await client.channels.list({
274
+ limit: parseInt(opts.limit),
275
+ prefix: opts.prefix,
276
+ by: opts.by,
277
+ });
278
+ print(result, getFormat(channelsCmd));
279
+ } catch (err) {
280
+ error(String(err));
281
+ process.exit(1);
282
+ }
283
+ });
284
+
285
+ channelsCmd
286
+ .command('get <channelId>')
287
+ .description('Get channel details')
288
+ .action(async (channelId: string) => {
289
+ try {
290
+ const client = getClient();
291
+ const result = await client.channels.get(channelId);
292
+ print(result, getFormat(channelsCmd));
293
+ } catch (err) {
294
+ error(String(err));
295
+ process.exit(1);
296
+ }
297
+ });
298
+
299
+ // ============================================
300
+ // Presence Commands
301
+ // ============================================
302
+ const presenceCmd = program
303
+ .command('presence')
304
+ .description('Channel presence members');
305
+
306
+ presenceCmd
307
+ .command('get <channel>')
308
+ .description('Get current presence members for a channel')
309
+ .option('-l, --limit <number>', 'Maximum results', '100')
310
+ .option('--client-id <clientId>', 'Filter by client ID')
311
+ .option('--connection-id <connectionId>', 'Filter by connection ID')
312
+ .action(async (channel: string, opts) => {
313
+ try {
314
+ const client = getClient();
315
+ const result = await client.presence.get(channel, {
316
+ limit: parseInt(opts.limit),
317
+ clientId: opts.clientId,
318
+ connectionId: opts.connectionId,
319
+ });
320
+ print(result, getFormat(presenceCmd));
321
+ } catch (err) {
322
+ error(String(err));
323
+ process.exit(1);
324
+ }
325
+ });
326
+
327
+ presenceCmd
328
+ .command('history <channel>')
329
+ .description('Get presence history for a channel')
330
+ .option('-l, --limit <number>', 'Maximum results', '100')
331
+ .option('--start <time>', 'Start time (ms since epoch)')
332
+ .option('--end <time>', 'End time (ms since epoch)')
333
+ .option('--direction <dir>', 'Sort direction (forwards, backwards)', 'backwards')
334
+ .action(async (channel: string, opts) => {
335
+ try {
336
+ const client = getClient();
337
+ const result = await client.presence.history(channel, {
338
+ limit: parseInt(opts.limit),
339
+ start: opts.start,
340
+ end: opts.end,
341
+ direction: opts.direction,
342
+ });
343
+ print(result, getFormat(presenceCmd));
344
+ } catch (err) {
345
+ error(String(err));
346
+ process.exit(1);
347
+ }
348
+ });
349
+
350
+ // ============================================
351
+ // Stats Commands
352
+ // ============================================
353
+ const statsCmd = program
354
+ .command('stats')
355
+ .description('Application statistics');
356
+
357
+ statsCmd
358
+ .command('get')
359
+ .description('Get application statistics')
360
+ .option('-l, --limit <number>', 'Maximum results', '100')
361
+ .option('--start <time>', 'Start time')
362
+ .option('--end <time>', 'End time')
363
+ .option('--direction <dir>', 'Sort direction (forwards, backwards)', 'backwards')
364
+ .option('--unit <unit>', 'Interval granularity (minute, hour, day, month)', 'minute')
365
+ .action(async (opts) => {
366
+ try {
367
+ const client = getClient();
368
+ const result = await client.stats.get({
369
+ limit: parseInt(opts.limit),
370
+ start: opts.start,
371
+ end: opts.end,
372
+ direction: opts.direction,
373
+ unit: opts.unit,
374
+ });
375
+ print(result, getFormat(statsCmd));
376
+ } catch (err) {
377
+ error(String(err));
378
+ process.exit(1);
379
+ }
380
+ });
381
+
382
+ statsCmd
383
+ .command('time')
384
+ .description('Get server time')
385
+ .action(async () => {
386
+ try {
387
+ const client = getClient();
388
+ const result = await client.stats.time();
389
+ print(result, getFormat(statsCmd));
390
+ } catch (err) {
391
+ error(String(err));
392
+ process.exit(1);
393
+ }
394
+ });
395
+
396
+ // Parse and execute
397
+ program.parse();
@@ -0,0 +1,102 @@
1
+ // Ably REST API Connector
2
+
3
+ export { Connector } from './api';
4
+ export * from './types';
5
+
6
+ // Re-export individual API classes for advanced usage
7
+ export { ConnectorClient, MessagesApi, ChannelsApi, PresenceApi, StatsApi } from './api';
8
+
9
+ // Export config utilities
10
+ export {
11
+ getApiKey,
12
+ setApiKey,
13
+ getApiSecret,
14
+ setApiSecret,
15
+ getToken,
16
+ setToken,
17
+ getCurrentProfile,
18
+ setCurrentProfile,
19
+ listProfiles,
20
+ createProfile,
21
+ deleteProfile,
22
+ loadProfile,
23
+ saveProfile,
24
+ clearConfig,
25
+ getOAuthConfig,
26
+ setOAuthConfig,
27
+ loadOAuthTokens,
28
+ saveOAuthTokens,
29
+ clearOAuthTokens,
30
+ getAccessToken,
31
+ } from './utils/config';
32
+
33
+ // Export OAuth2 utilities
34
+ export {
35
+ getAuthUrl,
36
+ exchangeCodeForTokens,
37
+ refreshAccessToken,
38
+ startCallbackServer,
39
+ getValidAccessToken,
40
+ isAuthenticated,
41
+ getRedirectUri,
42
+ getRedirectPort,
43
+ type AuthResult,
44
+ type AuthUrlOptions,
45
+ } from './utils/auth';
46
+
47
+ // Export settings utilities
48
+ export {
49
+ loadSettings,
50
+ saveSettings,
51
+ getSetting,
52
+ setSetting,
53
+ resetSettings,
54
+ getDefaultSettings,
55
+ isVerbose,
56
+ needsConfirmation,
57
+ type Settings,
58
+ } from './utils/settings';
59
+
60
+ // Export storage utilities
61
+ export {
62
+ saveEntity,
63
+ getEntity,
64
+ entityExists,
65
+ getAllEntities,
66
+ deleteEntity,
67
+ searchEntities,
68
+ searchEntitiesByText,
69
+ countEntities,
70
+ clearEntities,
71
+ createStorage,
72
+ type Storable,
73
+ } from './utils/storage';
74
+
75
+ // Export bulk operation utilities
76
+ export {
77
+ executeBulk,
78
+ executeSequential,
79
+ chunkArray,
80
+ sleep,
81
+ createProgressReporter,
82
+ formatBulkResult,
83
+ type BulkOperationOptions,
84
+ type BulkOperationResult,
85
+ } from './utils/bulk';
86
+
87
+ // Export output utilities
88
+ export {
89
+ formatOutput,
90
+ success,
91
+ error,
92
+ warn,
93
+ info,
94
+ heading,
95
+ print,
96
+ setVerboseMode,
97
+ isVerboseMode,
98
+ debug,
99
+ debugRequest,
100
+ debugResponse,
101
+ type OutputFormat,
102
+ } from './utils/output';