@hasna/connectors 1.3.45 → 1.4.0

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 (118) hide show
  1. package/README.md +25 -10
  2. package/bin/index.js +1362 -11796
  3. package/bin/mcp.js +751 -10328
  4. package/bin/serve.js +2252 -11829
  5. package/connectors/cloudflare-workers/.env.example +5 -0
  6. package/connectors/cloudflare-workers/AGENTS.md +11 -0
  7. package/connectors/cloudflare-workers/CLAUDE.md +11 -0
  8. package/connectors/cloudflare-workers/README.md +45 -0
  9. package/connectors/cloudflare-workers/package.json +47 -0
  10. package/connectors/cloudflare-workers/src/api/client.test.ts +144 -0
  11. package/connectors/cloudflare-workers/src/api/client.ts +386 -0
  12. package/connectors/cloudflare-workers/src/api/index.ts +1 -0
  13. package/connectors/cloudflare-workers/src/cli/index.ts +206 -0
  14. package/connectors/cloudflare-workers/src/index.ts +16 -0
  15. package/connectors/cloudflare-workers/src/types/index.ts +53 -0
  16. package/connectors/cloudflare-workers/src/utils/config.ts +61 -0
  17. package/connectors/cloudflare-workers/src/utils/output.ts +26 -0
  18. package/connectors/cloudflare-workers/tsconfig.build.json +4 -0
  19. package/connectors/cloudflare-workers/tsconfig.json +14 -0
  20. package/connectors/tidio/.env.example +3 -2
  21. package/connectors/tidio/CLAUDE.md +21 -96
  22. package/connectors/tidio/README.md +22 -18
  23. package/connectors/tidio/package.json +4 -2
  24. package/connectors/tidio/src/api/client.ts +26 -15
  25. package/connectors/tidio/src/api/index.ts +185 -128
  26. package/connectors/tidio/src/api/tidio.test.ts +175 -44
  27. package/connectors/tidio/src/cli/index.ts +154 -388
  28. package/connectors/tidio/src/index.ts +5 -3
  29. package/connectors/tidio/src/types/index.ts +135 -82
  30. package/connectors/tidio/src/utils/config.ts +90 -44
  31. package/connectors/wandb/.env.example +7 -0
  32. package/connectors/wandb/AGENTS.md +43 -0
  33. package/connectors/wandb/CLAUDE.md +67 -0
  34. package/connectors/wandb/README.md +65 -0
  35. package/connectors/wandb/package.json +52 -0
  36. package/connectors/wandb/src/api/client.test.ts +116 -0
  37. package/connectors/wandb/src/api/client.ts +83 -0
  38. package/connectors/wandb/src/api/graphql.ts +9 -0
  39. package/connectors/wandb/src/api/index.ts +36 -0
  40. package/connectors/wandb/src/api/projects.ts +36 -0
  41. package/connectors/wandb/src/api/viewer.ts +20 -0
  42. package/connectors/wandb/src/cli/index.ts +265 -0
  43. package/connectors/wandb/src/index.ts +22 -0
  44. package/connectors/wandb/src/types/index.ts +75 -0
  45. package/connectors/wandb/src/utils/config.ts +164 -0
  46. package/connectors/wandb/src/utils/output.ts +119 -0
  47. package/connectors/wandb/tsconfig.json +16 -0
  48. package/connectors/weights-biases/.env.example +7 -0
  49. package/connectors/weights-biases/CLAUDE.md +65 -0
  50. package/connectors/weights-biases/README.md +62 -0
  51. package/connectors/weights-biases/package.json +53 -0
  52. package/connectors/weights-biases/src/api/client.test.ts +46 -0
  53. package/connectors/weights-biases/src/api/client.ts +119 -0
  54. package/connectors/weights-biases/src/api/events.ts +18 -0
  55. package/connectors/weights-biases/src/api/index.ts +56 -0
  56. package/connectors/weights-biases/src/api/runs.ts +28 -0
  57. package/connectors/weights-biases/src/api/search.ts +10 -0
  58. package/connectors/weights-biases/src/cli/index.ts +289 -0
  59. package/connectors/weights-biases/src/index.ts +19 -0
  60. package/connectors/weights-biases/src/types/index.ts +92 -0
  61. package/connectors/weights-biases/src/utils/config.ts +168 -0
  62. package/connectors/weights-biases/src/utils/output.ts +116 -0
  63. package/connectors/weights-biases/tsconfig.json +16 -0
  64. package/connectors/x/src/api/oauth.test.ts +205 -0
  65. package/connectors/zoominfo/.env.example +4 -0
  66. package/connectors/zoominfo/AGENTS.md +26 -0
  67. package/connectors/zoominfo/CLAUDE.md +20 -0
  68. package/connectors/zoominfo/README.md +69 -0
  69. package/connectors/zoominfo/package.json +52 -0
  70. package/connectors/zoominfo/src/api/client.test.ts +140 -0
  71. package/connectors/zoominfo/src/api/client.ts +228 -0
  72. package/connectors/zoominfo/src/api/index.ts +1 -0
  73. package/connectors/zoominfo/src/cli/index.ts +179 -0
  74. package/connectors/zoominfo/src/index.ts +3 -0
  75. package/connectors/zoominfo/src/types/index.ts +39 -0
  76. package/connectors/zoominfo/src/utils/config.ts +61 -0
  77. package/connectors/zoominfo/src/utils/output.ts +27 -0
  78. package/connectors/zoominfo/tsconfig.json +20 -0
  79. package/connectors/zotero/.env.example +7 -0
  80. package/connectors/zotero/.npmrc.example +2 -0
  81. package/connectors/zotero/AGENTS.md +40 -0
  82. package/connectors/zotero/CLAUDE.md +69 -0
  83. package/connectors/zotero/README.md +91 -0
  84. package/connectors/zotero/package.json +43 -0
  85. package/connectors/zotero/src/api/attachments.ts +130 -0
  86. package/connectors/zotero/src/api/client.test.ts +166 -0
  87. package/connectors/zotero/src/api/client.ts +169 -0
  88. package/connectors/zotero/src/api/collections.ts +26 -0
  89. package/connectors/zotero/src/api/index.ts +45 -0
  90. package/connectors/zotero/src/api/items.ts +64 -0
  91. package/connectors/zotero/src/cli/index.ts +386 -0
  92. package/connectors/zotero/src/index.ts +28 -0
  93. package/connectors/zotero/src/types/index.ts +129 -0
  94. package/connectors/zotero/src/utils/config.ts +245 -0
  95. package/connectors/zotero/src/utils/output.ts +119 -0
  96. package/connectors/zotero/tsconfig.json +16 -0
  97. package/connectors/zymbly/.env.example +2 -0
  98. package/connectors/zymbly/AGENTS.md +11 -0
  99. package/connectors/zymbly/CLAUDE.md +5 -0
  100. package/connectors/zymbly/README.md +35 -0
  101. package/connectors/zymbly/package.json +52 -0
  102. package/connectors/zymbly/src/api/client.test.ts +110 -0
  103. package/connectors/zymbly/src/api/client.ts +166 -0
  104. package/connectors/zymbly/src/api/index.ts +1 -0
  105. package/connectors/zymbly/src/cli/index.ts +156 -0
  106. package/connectors/zymbly/src/index.ts +12 -0
  107. package/connectors/zymbly/src/types/index.ts +34 -0
  108. package/connectors/zymbly/src/utils/config.ts +52 -0
  109. package/connectors/zymbly/src/utils/output.ts +17 -0
  110. package/connectors/zymbly/tsconfig.json +13 -0
  111. package/dist/db/database.d.ts +2 -1
  112. package/dist/db/sqlite-adapter.d.ts +43 -0
  113. package/dist/index.js +172 -9653
  114. package/dist/lib/compact-output.d.ts +40 -0
  115. package/dist/mcp/tools/jobs.d.ts +32 -0
  116. package/dist/mcp/tools/jobs.test.d.ts +1 -0
  117. package/dist/no-cloud-boundary.test.d.ts +1 -0
  118. package/package.json +3 -3
@@ -0,0 +1,65 @@
1
+ # CLAUDE.md
2
+
3
+ Guidance for Claude Code when working with the Weights & Biases connector.
4
+
5
+ ## Project Overview
6
+
7
+ `@hasna/connect-weights-biases` is a TypeScript connector for the [Weights & Biases REST API](https://docs.wandb.ai/). It provides multi-profile configuration, API key authentication, and CLI commands for runs, events, search, and raw requests.
8
+
9
+ ## Build & Run Commands
10
+
11
+ ```bash
12
+ bun install
13
+ bun run dev
14
+ bun run build
15
+ bun run typecheck
16
+ bun test
17
+ ```
18
+
19
+ ## Authentication
20
+
21
+ API key authentication. Set credentials via:
22
+
23
+ - Environment variable `WANDB_API_KEY`
24
+ - Profile config: `weights-biases config set-key <key>`
25
+
26
+ The HTTP client sends the key as `Authorization: Bearer <api_key>`.
27
+
28
+ ## Environment Variables
29
+
30
+ | Variable | Description |
31
+ |----------|-------------|
32
+ | `WANDB_API_KEY` | API key (overrides profile) |
33
+ | `WANDB_BASE_URL` | Override base URL (default `https://api.wandb.ai/v1`) |
34
+
35
+ ## CLI Commands
36
+
37
+ ```bash
38
+ weights-biases config show
39
+ weights-biases config set-key <key>
40
+ weights-biases runs list --entity <entity> --project <project>
41
+ weights-biases runs get <runId>
42
+ weights-biases runs create --body '<json>'
43
+ weights-biases events list --run-id <runId>
44
+ weights-biases search query --body '<json>'
45
+ weights-biases request /runs -X GET
46
+ ```
47
+
48
+ ## Data Storage
49
+
50
+ ```
51
+ ~/.hasna/connectors/weights-biases/
52
+ ├── current_profile
53
+ └── profiles/
54
+ └── default.json
55
+ ```
56
+
57
+ ## Project Structure
58
+
59
+ ```
60
+ src/
61
+ ├── api/ # client, runs, events, search
62
+ ├── cli/ # Commander CLI
63
+ ├── types/
64
+ └── utils/ # config, output
65
+ ```
@@ -0,0 +1,62 @@
1
+ # @hasna/connect-weights-biases
2
+
3
+ TypeScript connector for the [Weights & Biases](https://wandb.ai/) REST API — ML experiment tracking with runs, events, and search.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ bun add @hasna/connect-weights-biases
9
+ ```
10
+
11
+ Or use via the main `@hasna/connectors` CLI/MCP.
12
+
13
+ ## Authentication
14
+
15
+ Get an API key from [wandb.ai/authorize](https://wandb.ai/authorize).
16
+
17
+ ```bash
18
+ export WANDB_API_KEY=your-api-key-here
19
+ # or
20
+ weights-biases config set-key your-api-key-here
21
+ ```
22
+
23
+ ## CLI
24
+
25
+ ```bash
26
+ # Configuration
27
+ weights-biases config show
28
+ weights-biases profile list
29
+
30
+ # Runs
31
+ weights-biases runs list --entity my-team --project my-project
32
+ weights-biases runs get <run-id>
33
+ weights-biases runs create --body '{"entity":"team","project":"demo","displayName":"test"}'
34
+
35
+ # Events
36
+ weights-biases events list --run-id <run-id>
37
+
38
+ # Search
39
+ weights-biases search query --body '{"entity":"team","project":"demo","filters":{}}'
40
+
41
+ # Raw request
42
+ weights-biases request /runs -X GET --query '{"perPage":10}'
43
+ ```
44
+
45
+ ## Library
46
+
47
+ ```typescript
48
+ import { WeightsBiases } from '@hasna/connect-weights-biases';
49
+
50
+ const wb = WeightsBiases.fromEnv();
51
+ const runs = await wb.runs.list({ entity: 'team', project: 'demo' });
52
+ ```
53
+
54
+ ## API base URL
55
+
56
+ Default: `https://api.wandb.ai/v1`
57
+
58
+ Override with `WANDB_BASE_URL` or `weights-biases config set-base-url`.
59
+
60
+ ## License
61
+
62
+ Apache-2.0
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@hasna/connect-weights-biases",
3
+ "version": "0.1.0",
4
+ "description": "Weights & Biases API connector - ML experiment tracking (runs, events, search)",
5
+ "type": "module",
6
+ "bin": {
7
+ "weights-biases": "./bin/index.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "scripts": {
18
+ "build": "bun build ./src/index.ts --outdir ./dist --target bun && bun build ./src/cli/index.ts --outdir ./bin --target bun",
19
+ "dev": "bun run ./src/cli/index.ts",
20
+ "typecheck": "tsc --noEmit",
21
+ "test": "bun test",
22
+ "prepublishOnly": "bun run build"
23
+ },
24
+ "keywords": [
25
+ "weights-biases",
26
+ "wandb",
27
+ "ml",
28
+ "experiment-tracking",
29
+ "api",
30
+ "connector",
31
+ "cli",
32
+ "typescript",
33
+ "bun",
34
+ "hasna"
35
+ ],
36
+ "author": "Hasna",
37
+ "license": "Apache-2.0",
38
+ "devDependencies": {
39
+ "@types/bun": "latest",
40
+ "typescript": "^5"
41
+ },
42
+ "dependencies": {
43
+ "commander": "^12.1.0",
44
+ "chalk": "^5.3.0"
45
+ },
46
+ "engines": {
47
+ "bun": ">=1.0.0"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/hasna/connectors.git"
52
+ }
53
+ }
@@ -0,0 +1,46 @@
1
+ import { describe, expect, test, mock, afterEach } from 'bun:test';
2
+ import { WeightsBiasesClient } from './client';
3
+
4
+ describe('WeightsBiasesClient', () => {
5
+ const originalFetch = globalThis.fetch;
6
+
7
+ afterEach(() => {
8
+ globalThis.fetch = originalFetch;
9
+ mock.restore();
10
+ });
11
+
12
+ test('sends Bearer auth header and builds /runs URL', async () => {
13
+ let capturedUrl = '';
14
+ let capturedHeaders: HeadersInit | undefined;
15
+
16
+ globalThis.fetch = mock(async (input: RequestInfo | URL, init?: RequestInit) => {
17
+ capturedUrl = String(input);
18
+ capturedHeaders = init?.headers;
19
+ return new Response(JSON.stringify({ runs: [] }), {
20
+ status: 200,
21
+ headers: { 'content-type': 'application/json' },
22
+ });
23
+ }) as unknown as typeof fetch;
24
+
25
+ const client = new WeightsBiasesClient({ apiKey: 'test-key-12345' });
26
+ await client.get('/runs', { entity: 'team', project: 'demo' });
27
+
28
+ expect(capturedUrl).toBe('https://api.wandb.ai/v1/runs?entity=team&project=demo');
29
+ expect(capturedHeaders).toMatchObject({
30
+ Authorization: 'Bearer test-key-12345',
31
+ Accept: 'application/json',
32
+ });
33
+ });
34
+
35
+ test('uses custom base URL when configured', () => {
36
+ const client = new WeightsBiasesClient({
37
+ apiKey: 'key',
38
+ baseUrl: 'https://custom.example/v1',
39
+ });
40
+ expect(client.getBaseUrl()).toBe('https://custom.example/v1');
41
+ });
42
+
43
+ test('throws when API key is missing', () => {
44
+ expect(() => new WeightsBiasesClient({ apiKey: '' })).toThrow('API key is required');
45
+ });
46
+ });
@@ -0,0 +1,119 @@
1
+ import type { WeightsBiasesConfig } from '../types';
2
+ import { WeightsBiasesApiError } from '../types';
3
+
4
+ const DEFAULT_BASE_URL = 'https://api.wandb.ai/v1';
5
+
6
+ export interface RequestOptions {
7
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
8
+ params?: Record<string, string | number | boolean | undefined>;
9
+ body?: Record<string, unknown> | unknown[] | string;
10
+ headers?: Record<string, string>;
11
+ }
12
+
13
+ export class WeightsBiasesClient {
14
+ private readonly apiKey: string;
15
+ private readonly baseUrl: string;
16
+
17
+ constructor(config: WeightsBiasesConfig) {
18
+ if (!config.apiKey) {
19
+ throw new Error('API key is required');
20
+ }
21
+ this.apiKey = config.apiKey;
22
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
23
+ }
24
+
25
+ getBaseUrl(): string {
26
+ return this.baseUrl;
27
+ }
28
+
29
+ private buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {
30
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
31
+ const url = new URL(`${this.baseUrl}${normalizedPath}`);
32
+
33
+ if (params) {
34
+ Object.entries(params).forEach(([key, value]) => {
35
+ if (value !== undefined && value !== null && value !== '') {
36
+ url.searchParams.append(key, String(value));
37
+ }
38
+ });
39
+ }
40
+
41
+ return url.toString();
42
+ }
43
+
44
+ async request<T>(path: string, options: RequestOptions = {}): Promise<T> {
45
+ const { method = 'GET', params, body, headers = {} } = options;
46
+ const url = this.buildUrl(path, params);
47
+
48
+ const requestHeaders: Record<string, string> = {
49
+ Authorization: `Bearer ${this.apiKey}`,
50
+ Accept: 'application/json',
51
+ ...headers,
52
+ };
53
+
54
+ if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
55
+ requestHeaders['Content-Type'] = 'application/json';
56
+ }
57
+
58
+ const fetchOptions: RequestInit = {
59
+ method,
60
+ headers: requestHeaders,
61
+ };
62
+
63
+ if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
64
+ fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
65
+ }
66
+
67
+ const response = await fetch(url, fetchOptions);
68
+
69
+ if (response.status === 204) {
70
+ return {} as T;
71
+ }
72
+
73
+ let data: unknown;
74
+ const contentType = response.headers.get('content-type') || '';
75
+
76
+ if (contentType.includes('application/json')) {
77
+ const text = await response.text();
78
+ if (text) {
79
+ try {
80
+ data = JSON.parse(text);
81
+ } catch {
82
+ data = text;
83
+ }
84
+ }
85
+ } else {
86
+ data = await response.text();
87
+ }
88
+
89
+ if (!response.ok) {
90
+ let errorMessage = String(data || response.statusText);
91
+ if (typeof data === 'object' && data !== null) {
92
+ const errorObj = data as { detail?: string; error?: string; message?: string };
93
+ errorMessage = errorObj.detail || errorObj.error || errorObj.message || JSON.stringify(data);
94
+ }
95
+ throw new WeightsBiasesApiError(errorMessage, response.status);
96
+ }
97
+
98
+ return data as T;
99
+ }
100
+
101
+ async get<T>(path: string, params?: Record<string, string | number | boolean | undefined>): Promise<T> {
102
+ return this.request<T>(path, { method: 'GET', params });
103
+ }
104
+
105
+ async post<T>(
106
+ path: string,
107
+ body?: Record<string, unknown> | unknown[] | string | object,
108
+ params?: Record<string, string | number | boolean | undefined>,
109
+ ): Promise<T> {
110
+ return this.request<T>(path, { method: 'POST', body: body as Record<string, unknown>, params });
111
+ }
112
+
113
+ getApiKeyPreview(): string {
114
+ if (this.apiKey.length > 10) {
115
+ return `${this.apiKey.substring(0, 6)}...${this.apiKey.substring(this.apiKey.length - 4)}`;
116
+ }
117
+ return '***';
118
+ }
119
+ }
@@ -0,0 +1,18 @@
1
+ import type { EventsListResponse } from '../types';
2
+ import type { WeightsBiasesClient } from './client';
3
+
4
+ export interface ListEventsParams {
5
+ entity?: string;
6
+ project?: string;
7
+ runId?: string;
8
+ perPage?: number;
9
+ [key: string]: string | number | boolean | undefined;
10
+ }
11
+
12
+ export class EventsApi {
13
+ constructor(private readonly client: WeightsBiasesClient) {}
14
+
15
+ list(params?: ListEventsParams): Promise<EventsListResponse> {
16
+ return this.client.get<EventsListResponse>('/events', params);
17
+ }
18
+ }
@@ -0,0 +1,56 @@
1
+ import type { WeightsBiasesConfig } from '../types';
2
+ import { WeightsBiasesClient } from './client';
3
+ import { RunsApi } from './runs';
4
+ import { EventsApi } from './events';
5
+ import { SearchApi } from './search';
6
+
7
+ export class WeightsBiases {
8
+ private readonly client: WeightsBiasesClient;
9
+
10
+ public readonly runs: RunsApi;
11
+ public readonly events: EventsApi;
12
+ public readonly search: SearchApi;
13
+
14
+ constructor(config: WeightsBiasesConfig) {
15
+ this.client = new WeightsBiasesClient(config);
16
+ this.runs = new RunsApi(this.client);
17
+ this.events = new EventsApi(this.client);
18
+ this.search = new SearchApi(this.client);
19
+ }
20
+
21
+ static fromEnv(): WeightsBiases {
22
+ const apiKey = process.env.WANDB_API_KEY;
23
+ if (!apiKey) {
24
+ throw new Error('WANDB_API_KEY environment variable is required');
25
+ }
26
+ return new WeightsBiases({
27
+ apiKey,
28
+ baseUrl: process.env.WANDB_BASE_URL,
29
+ });
30
+ }
31
+
32
+ getApiKeyPreview(): string {
33
+ return this.client.getApiKeyPreview();
34
+ }
35
+
36
+ getClient(): WeightsBiasesClient {
37
+ return this.client;
38
+ }
39
+
40
+ async rawRequest<T = unknown>(
41
+ path: string,
42
+ options: {
43
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
44
+ params?: Record<string, string | number | boolean | undefined>;
45
+ body?: Record<string, unknown> | unknown[] | string;
46
+ headers?: Record<string, string>;
47
+ } = {},
48
+ ): Promise<T> {
49
+ return this.client.request<T>(path, options);
50
+ }
51
+ }
52
+
53
+ export { WeightsBiasesClient } from './client';
54
+ export { RunsApi } from './runs';
55
+ export { EventsApi } from './events';
56
+ export { SearchApi } from './search';
@@ -0,0 +1,28 @@
1
+ import type { CreateRunParams, RunSummary, RunsListResponse } from '../types';
2
+ import type { WeightsBiasesClient } from './client';
3
+
4
+ export interface ListRunsParams {
5
+ entity?: string;
6
+ project?: string;
7
+ filters?: string;
8
+ order?: string;
9
+ perPage?: number;
10
+ [key: string]: string | number | boolean | undefined;
11
+ }
12
+
13
+ export class RunsApi {
14
+ constructor(private readonly client: WeightsBiasesClient) {}
15
+
16
+ list(params?: ListRunsParams): Promise<RunsListResponse> {
17
+ return this.client.get<RunsListResponse>('/runs', params);
18
+ }
19
+
20
+ get(runId: string): Promise<RunSummary> {
21
+ const encoded = encodeURIComponent(runId);
22
+ return this.client.get<RunSummary>(`/runs/${encoded}`);
23
+ }
24
+
25
+ create(body: CreateRunParams): Promise<RunSummary> {
26
+ return this.client.post<RunSummary>('/runs', body);
27
+ }
28
+ }
@@ -0,0 +1,10 @@
1
+ import type { SearchRequest, SearchResponse } from '../types';
2
+ import type { WeightsBiasesClient } from './client';
3
+
4
+ export class SearchApi {
5
+ constructor(private readonly client: WeightsBiasesClient) {}
6
+
7
+ search(body: SearchRequest): Promise<SearchResponse> {
8
+ return this.client.post<SearchResponse>('/search', body);
9
+ }
10
+ }