@hasna/connectors 1.3.45 → 1.3.46
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.
- package/README.md +25 -0
- package/bin/index.js +792 -130
- package/bin/mcp.js +438 -52
- package/bin/serve.js +438 -52
- package/connectors/cloudflare-workers/.env.example +5 -0
- package/connectors/cloudflare-workers/AGENTS.md +11 -0
- package/connectors/cloudflare-workers/CLAUDE.md +11 -0
- package/connectors/cloudflare-workers/README.md +45 -0
- package/connectors/cloudflare-workers/package.json +47 -0
- package/connectors/cloudflare-workers/src/api/client.test.ts +144 -0
- package/connectors/cloudflare-workers/src/api/client.ts +386 -0
- package/connectors/cloudflare-workers/src/api/index.ts +1 -0
- package/connectors/cloudflare-workers/src/cli/index.ts +206 -0
- package/connectors/cloudflare-workers/src/index.ts +16 -0
- package/connectors/cloudflare-workers/src/types/index.ts +53 -0
- package/connectors/cloudflare-workers/src/utils/config.ts +61 -0
- package/connectors/cloudflare-workers/src/utils/output.ts +26 -0
- package/connectors/cloudflare-workers/tsconfig.build.json +4 -0
- package/connectors/cloudflare-workers/tsconfig.json +14 -0
- package/connectors/tidio/.env.example +3 -2
- package/connectors/tidio/CLAUDE.md +21 -96
- package/connectors/tidio/README.md +22 -18
- package/connectors/tidio/package.json +4 -2
- package/connectors/tidio/src/api/client.ts +26 -15
- package/connectors/tidio/src/api/index.ts +185 -128
- package/connectors/tidio/src/api/tidio.test.ts +175 -44
- package/connectors/tidio/src/cli/index.ts +154 -388
- package/connectors/tidio/src/index.ts +5 -3
- package/connectors/tidio/src/types/index.ts +135 -82
- package/connectors/tidio/src/utils/config.ts +90 -44
- package/connectors/wandb/.env.example +7 -0
- package/connectors/wandb/AGENTS.md +43 -0
- package/connectors/wandb/CLAUDE.md +67 -0
- package/connectors/wandb/README.md +65 -0
- package/connectors/wandb/package.json +52 -0
- package/connectors/wandb/src/api/client.test.ts +116 -0
- package/connectors/wandb/src/api/client.ts +83 -0
- package/connectors/wandb/src/api/graphql.ts +9 -0
- package/connectors/wandb/src/api/index.ts +36 -0
- package/connectors/wandb/src/api/projects.ts +36 -0
- package/connectors/wandb/src/api/viewer.ts +20 -0
- package/connectors/wandb/src/cli/index.ts +265 -0
- package/connectors/wandb/src/index.ts +22 -0
- package/connectors/wandb/src/types/index.ts +75 -0
- package/connectors/wandb/src/utils/config.ts +164 -0
- package/connectors/wandb/src/utils/output.ts +119 -0
- package/connectors/wandb/tsconfig.json +16 -0
- package/connectors/weights-biases/.env.example +7 -0
- package/connectors/weights-biases/CLAUDE.md +65 -0
- package/connectors/weights-biases/README.md +62 -0
- package/connectors/weights-biases/package.json +53 -0
- package/connectors/weights-biases/src/api/client.test.ts +46 -0
- package/connectors/weights-biases/src/api/client.ts +119 -0
- package/connectors/weights-biases/src/api/events.ts +18 -0
- package/connectors/weights-biases/src/api/index.ts +56 -0
- package/connectors/weights-biases/src/api/runs.ts +28 -0
- package/connectors/weights-biases/src/api/search.ts +10 -0
- package/connectors/weights-biases/src/cli/index.ts +289 -0
- package/connectors/weights-biases/src/index.ts +19 -0
- package/connectors/weights-biases/src/types/index.ts +92 -0
- package/connectors/weights-biases/src/utils/config.ts +168 -0
- package/connectors/weights-biases/src/utils/output.ts +116 -0
- package/connectors/weights-biases/tsconfig.json +16 -0
- package/connectors/zoominfo/.env.example +4 -0
- package/connectors/zoominfo/AGENTS.md +26 -0
- package/connectors/zoominfo/CLAUDE.md +20 -0
- package/connectors/zoominfo/README.md +69 -0
- package/connectors/zoominfo/package.json +52 -0
- package/connectors/zoominfo/src/api/client.test.ts +140 -0
- package/connectors/zoominfo/src/api/client.ts +228 -0
- package/connectors/zoominfo/src/api/index.ts +1 -0
- package/connectors/zoominfo/src/cli/index.ts +179 -0
- package/connectors/zoominfo/src/index.ts +3 -0
- package/connectors/zoominfo/src/types/index.ts +39 -0
- package/connectors/zoominfo/src/utils/config.ts +61 -0
- package/connectors/zoominfo/src/utils/output.ts +27 -0
- package/connectors/zoominfo/tsconfig.json +20 -0
- package/connectors/zotero/.env.example +7 -0
- package/connectors/zotero/.npmrc.example +2 -0
- package/connectors/zotero/AGENTS.md +40 -0
- package/connectors/zotero/CLAUDE.md +69 -0
- package/connectors/zotero/README.md +91 -0
- package/connectors/zotero/package.json +43 -0
- package/connectors/zotero/src/api/attachments.ts +130 -0
- package/connectors/zotero/src/api/client.test.ts +166 -0
- package/connectors/zotero/src/api/client.ts +169 -0
- package/connectors/zotero/src/api/collections.ts +26 -0
- package/connectors/zotero/src/api/index.ts +45 -0
- package/connectors/zotero/src/api/items.ts +64 -0
- package/connectors/zotero/src/cli/index.ts +386 -0
- package/connectors/zotero/src/index.ts +28 -0
- package/connectors/zotero/src/types/index.ts +129 -0
- package/connectors/zotero/src/utils/config.ts +245 -0
- package/connectors/zotero/src/utils/output.ts +119 -0
- package/connectors/zotero/tsconfig.json +16 -0
- package/connectors/zymbly/.env.example +2 -0
- package/connectors/zymbly/AGENTS.md +11 -0
- package/connectors/zymbly/CLAUDE.md +5 -0
- package/connectors/zymbly/README.md +35 -0
- package/connectors/zymbly/package.json +52 -0
- package/connectors/zymbly/src/api/client.test.ts +110 -0
- package/connectors/zymbly/src/api/client.ts +166 -0
- package/connectors/zymbly/src/api/index.ts +1 -0
- package/connectors/zymbly/src/cli/index.ts +156 -0
- package/connectors/zymbly/src/index.ts +12 -0
- package/connectors/zymbly/src/types/index.ts +34 -0
- package/connectors/zymbly/src/utils/config.ts +52 -0
- package/connectors/zymbly/src/utils/output.ts +17 -0
- package/connectors/zymbly/tsconfig.json +13 -0
- package/dist/index.js +45 -2
- package/dist/lib/compact-output.d.ts +40 -0
- package/dist/mcp/tools/jobs.d.ts +32 -0
- package/dist/mcp/tools/jobs.test.d.ts +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for Claude Code when working with the Weights & Biases connector.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
connect-wandb is a TypeScript CLI and library for the Weights & Biases GraphQL API (`https://api.wandb.ai/graphql`). It provides viewer lookup, project run listing, and ad-hoc GraphQL queries with multi-profile support.
|
|
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
|
+
## Project Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/
|
|
23
|
+
├── api/
|
|
24
|
+
│ ├── client.ts # GraphQL client with Bearer auth
|
|
25
|
+
│ ├── viewer.ts # Viewer query
|
|
26
|
+
│ ├── projects.ts # Project runs query
|
|
27
|
+
│ ├── graphql.ts # Ad-hoc queries
|
|
28
|
+
│ └── index.ts # Wandb facade
|
|
29
|
+
├── cli/index.ts
|
|
30
|
+
├── types/index.ts
|
|
31
|
+
├── utils/config.ts
|
|
32
|
+
└── utils/output.ts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Authentication
|
|
36
|
+
|
|
37
|
+
Bearer Token authentication. Credentials via:
|
|
38
|
+
- `WANDB_API_KEY` environment variable
|
|
39
|
+
- Profile config: `wandb config set-key <key>`
|
|
40
|
+
|
|
41
|
+
## Environment Variables
|
|
42
|
+
|
|
43
|
+
| Variable | Description |
|
|
44
|
+
|----------|-------------|
|
|
45
|
+
| `WANDB_API_KEY` | W&B API key |
|
|
46
|
+
| `WANDB_BASE_URL` | Custom GraphQL endpoint (default: `https://api.wandb.ai/graphql`) |
|
|
47
|
+
|
|
48
|
+
## Multi-Profile Configuration
|
|
49
|
+
|
|
50
|
+
Configuration stored in `~/.hasna/connectors/wandb/`:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
~/.hasna/connectors/wandb/
|
|
54
|
+
├── current_profile
|
|
55
|
+
└── profiles/
|
|
56
|
+
└── default.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## GraphQL Endpoint
|
|
60
|
+
|
|
61
|
+
- Base URL: `https://api.wandb.ai/graphql`
|
|
62
|
+
- Auth: `Authorization: Bearer <api_key>`
|
|
63
|
+
|
|
64
|
+
## Dependencies
|
|
65
|
+
|
|
66
|
+
- commander: CLI framework
|
|
67
|
+
- chalk: Terminal styling
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# connect-wandb
|
|
2
|
+
|
|
3
|
+
Weights & Biases GraphQL API connector — experiment tracking with multi-profile support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install -g @hasna/connect-wandb
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Set your API key
|
|
15
|
+
wandb config set-key YOUR_API_KEY
|
|
16
|
+
|
|
17
|
+
# Or use environment variable
|
|
18
|
+
export WANDB_API_KEY=YOUR_API_KEY
|
|
19
|
+
|
|
20
|
+
# Verify authentication
|
|
21
|
+
wandb viewer
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## CLI Commands
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
wandb config set-key <key> # Set API key
|
|
28
|
+
wandb config show # Show config
|
|
29
|
+
wandb viewer # Get authenticated user
|
|
30
|
+
wandb project-runs --entity <e> --project <p> # List project runs
|
|
31
|
+
wandb graphql --query '<query>' [--variables '<json>']
|
|
32
|
+
wandb raw --body '<json>' # Raw GraphQL POST body
|
|
33
|
+
wandb profile list # List profiles
|
|
34
|
+
wandb profile use <name> # Switch profile
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Profile Management
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
wandb profile create work --api-key xxx --use
|
|
41
|
+
wandb profile create personal --api-key yyy
|
|
42
|
+
wandb -p personal viewer
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Library Usage
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { Wandb } from '@hasna/connect-wandb';
|
|
49
|
+
|
|
50
|
+
const client = new Wandb({ apiKey: process.env.WANDB_API_KEY! });
|
|
51
|
+
|
|
52
|
+
const { viewer } = await client.viewer.get();
|
|
53
|
+
const runs = await client.projects.projectRuns({ entity: 'team', project: 'demo' });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Environment Variables
|
|
57
|
+
|
|
58
|
+
| Variable | Description |
|
|
59
|
+
|----------|-------------|
|
|
60
|
+
| `WANDB_API_KEY` | API key (overrides profile) |
|
|
61
|
+
| `WANDB_BASE_URL` | Override GraphQL base URL |
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
Apache-2.0
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasna/connect-wandb",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Weights & Biases GraphQL API connector - experiment tracking with multi-profile support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wandb": "./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 src/api/client.test.ts"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"wandb",
|
|
25
|
+
"weights-and-biases",
|
|
26
|
+
"ml",
|
|
27
|
+
"experiments",
|
|
28
|
+
"graphql",
|
|
29
|
+
"connector",
|
|
30
|
+
"cli",
|
|
31
|
+
"typescript",
|
|
32
|
+
"bun",
|
|
33
|
+
"hasna"
|
|
34
|
+
],
|
|
35
|
+
"author": "Hasna",
|
|
36
|
+
"license": "Apache-2.0",
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/bun": "latest",
|
|
39
|
+
"typescript": "^5"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"commander": "^12.1.0",
|
|
43
|
+
"chalk": "^5.3.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"bun": ">=1.0.0"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/hasna/connectors.git"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { Wandb, WandbClient } from './index';
|
|
3
|
+
import { WandbApiError } from '../types';
|
|
4
|
+
|
|
5
|
+
const realFetch = globalThis.fetch;
|
|
6
|
+
|
|
7
|
+
interface RecordedRequest {
|
|
8
|
+
url: string;
|
|
9
|
+
method: string;
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
body?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function installFetch(handler: (recorded: RecordedRequest) => unknown) {
|
|
15
|
+
const recorded: RecordedRequest[] = [];
|
|
16
|
+
globalThis.fetch = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
17
|
+
const url = typeof input === 'string' ? input : input.toString();
|
|
18
|
+
const headers: Record<string, string> = {};
|
|
19
|
+
if (init?.headers) {
|
|
20
|
+
const h = init.headers as Record<string, string>;
|
|
21
|
+
Object.assign(headers, h);
|
|
22
|
+
}
|
|
23
|
+
const body = typeof init?.body === 'string' ? init.body : undefined;
|
|
24
|
+
const entry = { url, method: init?.method ?? 'GET', headers, body };
|
|
25
|
+
recorded.push(entry);
|
|
26
|
+
const json = handler(entry);
|
|
27
|
+
return {
|
|
28
|
+
ok: true,
|
|
29
|
+
status: 200,
|
|
30
|
+
statusText: 'OK',
|
|
31
|
+
async json() {
|
|
32
|
+
return json ?? {};
|
|
33
|
+
},
|
|
34
|
+
} as Response;
|
|
35
|
+
}) as typeof fetch;
|
|
36
|
+
return recorded;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
globalThis.fetch = realFetch;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('WandbClient', () => {
|
|
44
|
+
test('requires API key', () => {
|
|
45
|
+
expect(() => new WandbClient({ apiKey: '' })).toThrow('W&B API key is required');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('sends Bearer authorization header', async () => {
|
|
49
|
+
const recorded = installFetch(() => ({
|
|
50
|
+
data: { viewer: { id: '1', username: 'testuser' } },
|
|
51
|
+
}));
|
|
52
|
+
const client = new WandbClient({ apiKey: 'test-key-123' });
|
|
53
|
+
await client.query('query Viewer { viewer { id username } }');
|
|
54
|
+
expect(recorded[0].url).toBe('https://api.wandb.ai/graphql');
|
|
55
|
+
expect(recorded[0].method).toBe('POST');
|
|
56
|
+
expect(recorded[0].headers['Authorization']).toBe('Bearer test-key-123');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('viewer query sends expected GraphQL body', async () => {
|
|
60
|
+
const recorded = installFetch(() => ({
|
|
61
|
+
data: { viewer: { id: 'v1', username: 'alice', name: 'Alice' } },
|
|
62
|
+
}));
|
|
63
|
+
const wandb = new Wandb({ apiKey: 'key' });
|
|
64
|
+
const result = await wandb.viewer.get();
|
|
65
|
+
expect(result.viewer.username).toBe('alice');
|
|
66
|
+
const body = JSON.parse(recorded[0].body!);
|
|
67
|
+
expect(body.query).toContain('viewer');
|
|
68
|
+
expect(body.query).toContain('username');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('projectRuns passes entity and project variables', async () => {
|
|
72
|
+
const recorded = installFetch(() => ({
|
|
73
|
+
data: {
|
|
74
|
+
project: {
|
|
75
|
+
runs: {
|
|
76
|
+
edges: [{ node: { id: 'r1', name: 'run-1', displayName: 'Run 1', state: 'finished' } }],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
}));
|
|
81
|
+
const wandb = new Wandb({ apiKey: 'key' });
|
|
82
|
+
const result = await wandb.projects.projectRuns({ entity: 'team', project: 'demo' });
|
|
83
|
+
expect(result.project?.runs.edges[0].node.name).toBe('run-1');
|
|
84
|
+
const body = JSON.parse(recorded[0].body!);
|
|
85
|
+
expect(body.variables).toEqual({ entity: 'team', project: 'demo', first: 50 });
|
|
86
|
+
expect(body.query).toContain('ProjectRuns');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('throws WandbApiError on GraphQL errors', async () => {
|
|
90
|
+
installFetch(() => ({
|
|
91
|
+
errors: [{ message: 'Not authorized', extensions: { code: 'UNAUTHORIZED' } }],
|
|
92
|
+
}));
|
|
93
|
+
const client = new WandbClient({ apiKey: 'bad-key' });
|
|
94
|
+
await expect(client.query('query { viewer { id } }')).rejects.toThrow('Not authorized');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('throws WandbApiError on HTTP errors', async () => {
|
|
98
|
+
globalThis.fetch = (async () => ({
|
|
99
|
+
ok: false,
|
|
100
|
+
status: 401,
|
|
101
|
+
statusText: 'Unauthorized',
|
|
102
|
+
async json() {
|
|
103
|
+
return {};
|
|
104
|
+
},
|
|
105
|
+
})) as unknown as typeof fetch;
|
|
106
|
+
const client = new WandbClient({ apiKey: 'key' });
|
|
107
|
+
await expect(client.query('query { viewer { id } }')).rejects.toThrow(WandbApiError);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('custom base URL is respected', async () => {
|
|
111
|
+
const recorded = installFetch(() => ({ data: { viewer: { id: '1' } } }));
|
|
112
|
+
const client = new WandbClient({ apiKey: 'key', baseUrl: 'https://custom.example/graphql' });
|
|
113
|
+
await client.query('query { viewer { id } }');
|
|
114
|
+
expect(recorded[0].url).toBe('https://custom.example/graphql');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { WandbConfig, GraphQLResponse } from '../types';
|
|
2
|
+
import { WandbApiError } from '../types';
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_BASE_URL = 'https://api.wandb.ai/graphql';
|
|
5
|
+
|
|
6
|
+
export class WandbClient {
|
|
7
|
+
private readonly apiKey: string;
|
|
8
|
+
private readonly baseUrl: string;
|
|
9
|
+
|
|
10
|
+
constructor(config: WandbConfig) {
|
|
11
|
+
if (!config.apiKey) {
|
|
12
|
+
throw new Error('W&B API key is required');
|
|
13
|
+
}
|
|
14
|
+
this.apiKey = config.apiKey;
|
|
15
|
+
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getBaseUrl(): string {
|
|
19
|
+
return this.baseUrl;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Execute a GraphQL query or mutation against the W&B API.
|
|
24
|
+
*/
|
|
25
|
+
async query<T>(query: string, variables?: Record<string, unknown>): Promise<T> {
|
|
26
|
+
const response = await fetch(this.baseUrl, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({ query, variables }),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new WandbApiError(
|
|
37
|
+
`HTTP ${response.status}: ${response.statusText}`,
|
|
38
|
+
'http_error',
|
|
39
|
+
response.status
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = await response.json() as GraphQLResponse<T>;
|
|
44
|
+
|
|
45
|
+
if (result.errors && result.errors.length > 0) {
|
|
46
|
+
const error = result.errors[0];
|
|
47
|
+
throw new WandbApiError(
|
|
48
|
+
error.message,
|
|
49
|
+
(error.extensions?.code as string) || 'graphql_error'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!result.data) {
|
|
54
|
+
throw new WandbApiError('No data returned', 'no_data');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result.data;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Send a raw JSON body to the GraphQL endpoint (for advanced use).
|
|
62
|
+
*/
|
|
63
|
+
async raw<T>(body: Record<string, unknown>): Promise<T> {
|
|
64
|
+
const response = await fetch(this.baseUrl, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
throw new WandbApiError(
|
|
75
|
+
`HTTP ${response.status}: ${response.statusText}`,
|
|
76
|
+
'http_error',
|
|
77
|
+
response.status
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return await response.json() as T;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WandbClient } from './client';
|
|
2
|
+
|
|
3
|
+
export class GraphqlApi {
|
|
4
|
+
constructor(private readonly client: WandbClient) {}
|
|
5
|
+
|
|
6
|
+
async execute<T>(query: string, variables?: Record<string, unknown>): Promise<T> {
|
|
7
|
+
return this.client.query<T>(query, variables);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { WandbConfig } from '../types';
|
|
2
|
+
import { WandbClient } from './client';
|
|
3
|
+
import { ViewerApi } from './viewer';
|
|
4
|
+
import { ProjectsApi } from './projects';
|
|
5
|
+
import { GraphqlApi } from './graphql';
|
|
6
|
+
|
|
7
|
+
export { WandbClient, DEFAULT_BASE_URL } from './client';
|
|
8
|
+
export { ViewerApi } from './viewer';
|
|
9
|
+
export { ProjectsApi } from './projects';
|
|
10
|
+
export { GraphqlApi } from './graphql';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Main Weights & Biases GraphQL API class.
|
|
14
|
+
*/
|
|
15
|
+
export class Wandb {
|
|
16
|
+
private readonly client: WandbClient;
|
|
17
|
+
|
|
18
|
+
public readonly viewer: ViewerApi;
|
|
19
|
+
public readonly projects: ProjectsApi;
|
|
20
|
+
public readonly graphql: GraphqlApi;
|
|
21
|
+
|
|
22
|
+
constructor(config: WandbConfig) {
|
|
23
|
+
this.client = new WandbClient(config);
|
|
24
|
+
this.viewer = new ViewerApi(this.client);
|
|
25
|
+
this.projects = new ProjectsApi(this.client);
|
|
26
|
+
this.graphql = new GraphqlApi(this.client);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async test() {
|
|
30
|
+
return this.viewer.get();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getClient(): WandbClient {
|
|
34
|
+
return this.client;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { WandbClient } from './client';
|
|
2
|
+
import type { ProjectRunsOptions, ProjectRunsResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
const PROJECT_RUNS_QUERY = `query ProjectRuns($entity: String!, $project: String!, $first: Int, $order: String, $filters: JSONString) {
|
|
5
|
+
project(name: $project, entityName: $entity) {
|
|
6
|
+
runs(first: $first, order: $order, filters: $filters) {
|
|
7
|
+
edges {
|
|
8
|
+
node {
|
|
9
|
+
id
|
|
10
|
+
name
|
|
11
|
+
displayName
|
|
12
|
+
state
|
|
13
|
+
createdAt
|
|
14
|
+
summaryMetrics
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
pageInfo {
|
|
18
|
+
endCursor
|
|
19
|
+
hasNextPage
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}`;
|
|
24
|
+
|
|
25
|
+
export class ProjectsApi {
|
|
26
|
+
constructor(private readonly client: WandbClient) {}
|
|
27
|
+
|
|
28
|
+
async projectRuns(options: ProjectRunsOptions) {
|
|
29
|
+
const { entity, project, first = 50, order, filters } = options;
|
|
30
|
+
const variables: Record<string, unknown> = { entity, project, first };
|
|
31
|
+
if (order) variables.order = order;
|
|
32
|
+
if (filters) variables.filters = JSON.stringify(filters);
|
|
33
|
+
|
|
34
|
+
return this.client.query<ProjectRunsResponse>(PROJECT_RUNS_QUERY, variables);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { WandbClient } from './client';
|
|
2
|
+
import type { ViewerResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
const VIEWER_QUERY = `query Viewer {
|
|
5
|
+
viewer {
|
|
6
|
+
id
|
|
7
|
+
username
|
|
8
|
+
name
|
|
9
|
+
email
|
|
10
|
+
entity
|
|
11
|
+
}
|
|
12
|
+
}`;
|
|
13
|
+
|
|
14
|
+
export class ViewerApi {
|
|
15
|
+
constructor(private readonly client: WandbClient) {}
|
|
16
|
+
|
|
17
|
+
async get() {
|
|
18
|
+
return this.client.query<ViewerResponse>(VIEWER_QUERY);
|
|
19
|
+
}
|
|
20
|
+
}
|