@hasna/connectors 1.3.44 → 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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Tidio Connector
|
|
2
|
-
//
|
|
2
|
+
// Contacts, contact messages, departments, operators, project, tickets, products, and Lyro.
|
|
3
3
|
|
|
4
4
|
export { Tidio, TidioClient } from './api';
|
|
5
5
|
export * from './types';
|
|
6
6
|
|
|
7
7
|
export {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
getClientCredentials,
|
|
9
|
+
setClientCredentials,
|
|
10
10
|
getCurrentProfile,
|
|
11
11
|
setCurrentProfile,
|
|
12
12
|
listProfiles,
|
|
@@ -16,5 +16,7 @@ export {
|
|
|
16
16
|
saveProfile,
|
|
17
17
|
clearConfig,
|
|
18
18
|
getConfigDir,
|
|
19
|
+
getProfilesDir,
|
|
19
20
|
getActiveProfileName,
|
|
21
|
+
validateProfileName,
|
|
20
22
|
} from './utils/config';
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
// Tidio Connector Types
|
|
2
2
|
|
|
3
3
|
export interface TidioConfig {
|
|
4
|
-
|
|
4
|
+
clientId: string;
|
|
5
|
+
clientSecret: string;
|
|
5
6
|
baseUrl?: string;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
export type OutputFormat = 'json' | 'pretty';
|
|
9
10
|
|
|
10
|
-
export type
|
|
11
|
-
export type
|
|
12
|
-
export type
|
|
13
|
-
| 'contact.created'
|
|
14
|
-
| 'contact.updated'
|
|
15
|
-
| 'conversation.created'
|
|
16
|
-
| 'conversation.updated'
|
|
17
|
-
| 'message.created';
|
|
11
|
+
export type TicketStatus = 'open' | 'pending' | 'closed';
|
|
12
|
+
export type TicketPriority = 'low' | 'medium' | 'high' | 'urgent';
|
|
13
|
+
export type LyroDataSourceKind = 'folder' | 'qa';
|
|
18
14
|
|
|
19
15
|
export interface PaginatedMeta {
|
|
20
16
|
cursor?: string;
|
|
@@ -23,7 +19,7 @@ export interface PaginatedMeta {
|
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
export interface PaginatedResponse<T> {
|
|
26
|
-
|
|
22
|
+
resources?: T[];
|
|
27
23
|
meta?: PaginatedMeta;
|
|
28
24
|
[key: string]: unknown;
|
|
29
25
|
}
|
|
@@ -32,40 +28,39 @@ export interface Contact {
|
|
|
32
28
|
id: string;
|
|
33
29
|
email?: string;
|
|
34
30
|
phone?: string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
properties?:
|
|
39
|
-
|
|
40
|
-
consent?: boolean;
|
|
31
|
+
first_name?: string | null;
|
|
32
|
+
last_name?: string | null;
|
|
33
|
+
distinct_id?: string;
|
|
34
|
+
properties?: ContactProperty[];
|
|
35
|
+
email_consent?: 'subscribed' | 'unsubscribed' | null;
|
|
41
36
|
created_at?: string;
|
|
42
37
|
updated_at?: string;
|
|
43
38
|
[key: string]: unknown;
|
|
44
39
|
}
|
|
45
40
|
|
|
46
|
-
export interface
|
|
41
|
+
export interface ContactProperty {
|
|
42
|
+
name: string;
|
|
43
|
+
value: string | number | boolean | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ContactMessage {
|
|
47
47
|
id: string;
|
|
48
|
-
status?: ConversationStatus;
|
|
49
|
-
channel?: string;
|
|
50
48
|
contact_id?: string;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
updated_at?: string;
|
|
49
|
+
message?: string;
|
|
50
|
+
author?: string;
|
|
51
|
+
timestamp?: string;
|
|
55
52
|
[key: string]: unknown;
|
|
56
53
|
}
|
|
57
54
|
|
|
58
|
-
export interface
|
|
59
|
-
|
|
60
|
-
type?: MessageType;
|
|
61
|
-
content?: string;
|
|
62
|
-
media_url?: string;
|
|
63
|
-
private?: boolean;
|
|
64
|
-
operator_id?: string;
|
|
65
|
-
created_at?: string;
|
|
55
|
+
export interface MessageAccepted {
|
|
56
|
+
status?: string;
|
|
66
57
|
[key: string]: unknown;
|
|
67
58
|
}
|
|
68
59
|
|
|
60
|
+
export interface UuidResponse {
|
|
61
|
+
id: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
69
64
|
export interface Operator {
|
|
70
65
|
id: string;
|
|
71
66
|
name?: string;
|
|
@@ -79,32 +74,59 @@ export interface Department {
|
|
|
79
74
|
[key: string]: unknown;
|
|
80
75
|
}
|
|
81
76
|
|
|
82
|
-
export interface
|
|
77
|
+
export interface TicketTag {
|
|
83
78
|
id: string;
|
|
84
79
|
name: string;
|
|
85
|
-
color?: string;
|
|
86
80
|
[key: string]: unknown;
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
export interface
|
|
83
|
+
export interface TicketCustomField {
|
|
90
84
|
id: string;
|
|
91
85
|
name?: string;
|
|
86
|
+
type?: string;
|
|
92
87
|
[key: string]: unknown;
|
|
93
88
|
}
|
|
94
89
|
|
|
95
|
-
export interface
|
|
90
|
+
export interface Ticket {
|
|
96
91
|
id: string;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
status?: TicketStatus;
|
|
93
|
+
priority?: TicketPriority;
|
|
94
|
+
subject?: string;
|
|
95
|
+
contact_id?: string;
|
|
96
|
+
assignee_id?: string | null;
|
|
97
|
+
department_id?: string | null;
|
|
98
|
+
messages?: unknown[];
|
|
99
|
+
created_at?: string;
|
|
100
|
+
updated_at?: string;
|
|
100
101
|
[key: string]: unknown;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
export interface
|
|
104
|
-
id:
|
|
104
|
+
export interface Product {
|
|
105
|
+
id: number;
|
|
106
|
+
title: string;
|
|
105
107
|
url: string;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
image_url?: string;
|
|
109
|
+
description?: string;
|
|
110
|
+
price?: number;
|
|
111
|
+
default_currency?: string;
|
|
112
|
+
vendor?: string;
|
|
113
|
+
product_type?: string;
|
|
114
|
+
sku?: string;
|
|
115
|
+
barcode?: string;
|
|
116
|
+
availability?: string;
|
|
117
|
+
updated_at: string;
|
|
118
|
+
features?: Record<string, string>;
|
|
119
|
+
[key: string]: unknown;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface LyroDataSource {
|
|
123
|
+
id: string;
|
|
124
|
+
kind?: LyroDataSourceKind;
|
|
125
|
+
type?: string;
|
|
126
|
+
title?: string;
|
|
127
|
+
content?: string | null;
|
|
128
|
+
source_url?: string | null;
|
|
129
|
+
parent_id?: string | null;
|
|
108
130
|
[key: string]: unknown;
|
|
109
131
|
}
|
|
110
132
|
|
|
@@ -124,76 +146,106 @@ export interface ListContactsParams {
|
|
|
124
146
|
export interface CreateContactParams {
|
|
125
147
|
email?: string;
|
|
126
148
|
phone?: string;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
properties?:
|
|
131
|
-
|
|
132
|
-
consent?: boolean;
|
|
149
|
+
firstName?: string | null;
|
|
150
|
+
lastName?: string | null;
|
|
151
|
+
distinctId?: string;
|
|
152
|
+
properties?: ContactProperty[];
|
|
153
|
+
emailConsent?: 'subscribed' | 'unsubscribed' | null;
|
|
133
154
|
}
|
|
134
155
|
|
|
135
156
|
export interface UpdateContactParams {
|
|
136
157
|
email?: string;
|
|
137
158
|
phone?: string;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
properties?:
|
|
142
|
-
|
|
143
|
-
consent?: boolean;
|
|
159
|
+
firstName?: string | null;
|
|
160
|
+
lastName?: string | null;
|
|
161
|
+
distinctId?: string;
|
|
162
|
+
properties?: ContactProperty[];
|
|
163
|
+
emailConsent?: 'subscribed' | 'unsubscribed' | null;
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
export interface
|
|
147
|
-
|
|
166
|
+
export interface BatchContactsParams<T extends CreateContactParams | UpdateContactParams> {
|
|
167
|
+
contacts: T[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ListContactMessagesParams {
|
|
148
171
|
cursor?: string;
|
|
149
|
-
status?: ConversationStatus;
|
|
150
|
-
channel?: string;
|
|
151
|
-
updatedAfter?: string;
|
|
152
172
|
}
|
|
153
173
|
|
|
154
|
-
export interface
|
|
174
|
+
export interface SendContactMessageParams {
|
|
175
|
+
message: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface ListTicketsParams {
|
|
155
179
|
limit?: number;
|
|
156
180
|
cursor?: string;
|
|
181
|
+
status?: TicketStatus;
|
|
182
|
+
priority?: TicketPriority;
|
|
157
183
|
}
|
|
158
184
|
|
|
159
|
-
export interface
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
operatorId?: string;
|
|
185
|
+
export interface CreateTicketAsContactParams {
|
|
186
|
+
contactId: string;
|
|
187
|
+
message: string;
|
|
188
|
+
subject?: string;
|
|
189
|
+
priority?: TicketPriority;
|
|
165
190
|
}
|
|
166
191
|
|
|
167
|
-
export interface
|
|
168
|
-
status
|
|
169
|
-
|
|
192
|
+
export interface UpdateTicketParams {
|
|
193
|
+
status?: TicketStatus;
|
|
194
|
+
priority?: TicketPriority;
|
|
195
|
+
assigneeId?: string | null;
|
|
196
|
+
departmentId?: string | null;
|
|
170
197
|
}
|
|
171
198
|
|
|
172
|
-
export interface
|
|
173
|
-
|
|
174
|
-
|
|
199
|
+
export interface ReplyTicketParams {
|
|
200
|
+
message: string;
|
|
201
|
+
authorType?: 'operator' | 'contact';
|
|
175
202
|
}
|
|
176
203
|
|
|
177
|
-
export interface
|
|
178
|
-
|
|
179
|
-
|
|
204
|
+
export interface ListLyroDataSourcesParams {
|
|
205
|
+
cursor?: string;
|
|
206
|
+
limit?: number;
|
|
207
|
+
kind?: LyroDataSourceKind;
|
|
208
|
+
parentId?: string | null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface CreateLyroQaDataSourceParams {
|
|
212
|
+
question: string;
|
|
213
|
+
answer: string;
|
|
214
|
+
parentId?: string | null;
|
|
180
215
|
}
|
|
181
216
|
|
|
182
|
-
export interface
|
|
183
|
-
|
|
217
|
+
export interface UpsertLyroWebsiteDataSourceParams {
|
|
218
|
+
url: string;
|
|
219
|
+
title?: string;
|
|
184
220
|
content: string;
|
|
185
|
-
departmentId?: string;
|
|
186
221
|
}
|
|
187
222
|
|
|
188
|
-
export interface
|
|
223
|
+
export interface ScrapeLyroWebsiteParams {
|
|
189
224
|
url: string;
|
|
190
|
-
|
|
191
|
-
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface LyroTicketMessage {
|
|
228
|
+
created_at: string;
|
|
229
|
+
message_id: string;
|
|
230
|
+
author_type: 'contact';
|
|
231
|
+
message_type: 'public';
|
|
232
|
+
message_content: string;
|
|
233
|
+
attachments?: string[];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export interface AskLyroTicketParams {
|
|
237
|
+
ticketId: string;
|
|
238
|
+
subject: string;
|
|
239
|
+
contactEmail: string;
|
|
240
|
+
contactName: string;
|
|
241
|
+
recipientEmail: string;
|
|
242
|
+
messages: LyroTicketMessage[];
|
|
192
243
|
}
|
|
193
244
|
|
|
194
245
|
export interface TidioErrorResponse {
|
|
195
246
|
message?: string;
|
|
196
247
|
error?: string;
|
|
248
|
+
errors?: Array<{ code?: string; message?: string }>;
|
|
197
249
|
[key: string]: unknown;
|
|
198
250
|
}
|
|
199
251
|
|
|
@@ -211,6 +263,7 @@ export class TidioApiError extends Error {
|
|
|
211
263
|
|
|
212
264
|
export function parseTidioError(data: unknown, statusCode: number): TidioApiError {
|
|
213
265
|
const record = data && typeof data === 'object' ? (data as TidioErrorResponse) : {};
|
|
214
|
-
const
|
|
266
|
+
const nestedMessage = Array.isArray(record.errors) ? record.errors[0]?.message : undefined;
|
|
267
|
+
const message = nestedMessage ?? record.message ?? record.error ?? `Request failed (${statusCode})`;
|
|
215
268
|
return new TidioApiError(String(message), statusCode, record);
|
|
216
269
|
}
|
|
@@ -1,35 +1,73 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
chmodSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from 'fs';
|
|
2
10
|
import { homedir } from 'os';
|
|
3
|
-
import { join } from 'path';
|
|
11
|
+
import { join, resolve, sep } from 'path';
|
|
4
12
|
|
|
5
13
|
const CONNECTOR_NAME = 'connect-tidio';
|
|
6
14
|
const DEFAULT_PROFILE = 'default';
|
|
15
|
+
const PROFILE_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
7
16
|
|
|
8
17
|
export interface ProfileConfig {
|
|
9
|
-
|
|
18
|
+
clientId?: string;
|
|
19
|
+
clientSecret?: string;
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
let profileOverride: string | undefined;
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
function rootConfigDir(): string {
|
|
25
|
+
return process.env.TIDIO_CONFIG_HOME || join(homedir(), '.hasna', 'connectors', CONNECTOR_NAME);
|
|
26
|
+
}
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
function profilesDir(): string {
|
|
29
|
+
return join(rootConfigDir(), 'profiles');
|
|
20
30
|
}
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
function currentProfileFile(): string {
|
|
33
|
+
return join(rootConfigDir(), 'current_profile');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function validateProfileName(profile: string): string {
|
|
37
|
+
if (!PROFILE_NAME_PATTERN.test(profile)) {
|
|
38
|
+
throw new Error('Profile name can only contain letters, numbers, hyphens, and underscores');
|
|
25
39
|
}
|
|
26
|
-
|
|
27
|
-
|
|
40
|
+
return profile;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ensureInsideProfilesDir(path: string): string {
|
|
44
|
+
const root = resolve(profilesDir());
|
|
45
|
+
const candidate = resolve(path);
|
|
46
|
+
if (candidate !== root && !candidate.startsWith(`${root}${sep}`)) {
|
|
47
|
+
throw new Error('Profile path escapes the profiles directory');
|
|
28
48
|
}
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function setProfileOverride(profile: string | undefined): void {
|
|
53
|
+
profileOverride = profile ? validateProfileName(profile) : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function ensureConfigDir(): void {
|
|
57
|
+
mkdirSync(rootConfigDir(), { recursive: true, mode: 0o700 });
|
|
58
|
+
chmodSync(rootConfigDir(), 0o700);
|
|
59
|
+
mkdirSync(profilesDir(), { recursive: true, mode: 0o700 });
|
|
60
|
+
chmodSync(profilesDir(), 0o700);
|
|
29
61
|
}
|
|
30
62
|
|
|
31
63
|
function getProfilePath(profile: string): string {
|
|
32
|
-
|
|
64
|
+
const validProfile = validateProfileName(profile);
|
|
65
|
+
return ensureInsideProfilesDir(join(profilesDir(), `${validProfile}.json`));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function writePrivateFile(path: string, value: string): void {
|
|
69
|
+
writeFileSync(path, value, { mode: 0o600 });
|
|
70
|
+
chmodSync(path, 0o600);
|
|
33
71
|
}
|
|
34
72
|
|
|
35
73
|
export function getCurrentProfile(): string {
|
|
@@ -39,14 +77,14 @@ export function getCurrentProfile(): string {
|
|
|
39
77
|
|
|
40
78
|
ensureConfigDir();
|
|
41
79
|
|
|
42
|
-
if (existsSync(
|
|
80
|
+
if (existsSync(currentProfileFile())) {
|
|
43
81
|
try {
|
|
44
|
-
const profile = readFileSync(
|
|
45
|
-
if (profile && profileExists(profile)) {
|
|
82
|
+
const profile = readFileSync(currentProfileFile(), 'utf-8').trim();
|
|
83
|
+
if (profile && validateProfileName(profile) && profileExists(profile)) {
|
|
46
84
|
return profile;
|
|
47
85
|
}
|
|
48
86
|
} catch {
|
|
49
|
-
// Fall through to default
|
|
87
|
+
// Fall through to default.
|
|
50
88
|
}
|
|
51
89
|
}
|
|
52
90
|
|
|
@@ -54,13 +92,14 @@ export function getCurrentProfile(): string {
|
|
|
54
92
|
}
|
|
55
93
|
|
|
56
94
|
export function setCurrentProfile(profile: string): void {
|
|
95
|
+
const validProfile = validateProfileName(profile);
|
|
57
96
|
ensureConfigDir();
|
|
58
97
|
|
|
59
|
-
if (!profileExists(
|
|
60
|
-
throw new Error(`Profile "${
|
|
98
|
+
if (!profileExists(validProfile) && validProfile !== DEFAULT_PROFILE) {
|
|
99
|
+
throw new Error(`Profile "${validProfile}" does not exist`);
|
|
61
100
|
}
|
|
62
101
|
|
|
63
|
-
|
|
102
|
+
writePrivateFile(currentProfileFile(), validProfile);
|
|
64
103
|
}
|
|
65
104
|
|
|
66
105
|
export function profileExists(profile: string): boolean {
|
|
@@ -70,51 +109,50 @@ export function profileExists(profile: string): boolean {
|
|
|
70
109
|
export function listProfiles(): string[] {
|
|
71
110
|
ensureConfigDir();
|
|
72
111
|
|
|
73
|
-
if (!existsSync(
|
|
112
|
+
if (!existsSync(profilesDir())) {
|
|
74
113
|
return [];
|
|
75
114
|
}
|
|
76
115
|
|
|
77
|
-
return readdirSync(
|
|
78
|
-
.filter(
|
|
79
|
-
.map(
|
|
116
|
+
return readdirSync(profilesDir())
|
|
117
|
+
.filter(file => file.endsWith('.json'))
|
|
118
|
+
.map(file => file.replace(/\.json$/, ''))
|
|
119
|
+
.filter(profile => PROFILE_NAME_PATTERN.test(profile))
|
|
80
120
|
.sort();
|
|
81
121
|
}
|
|
82
122
|
|
|
83
123
|
export function createProfile(profile: string, config: ProfileConfig = {}): boolean {
|
|
124
|
+
const validProfile = validateProfileName(profile);
|
|
84
125
|
ensureConfigDir();
|
|
85
126
|
|
|
86
|
-
if (profileExists(
|
|
127
|
+
if (profileExists(validProfile)) {
|
|
87
128
|
return false;
|
|
88
129
|
}
|
|
89
130
|
|
|
90
|
-
|
|
91
|
-
throw new Error('Profile name can only contain letters, numbers, hyphens, and underscores');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
writeFileSync(getProfilePath(profile), JSON.stringify(config, null, 2));
|
|
131
|
+
writePrivateFile(getProfilePath(validProfile), `${JSON.stringify(config, null, 2)}\n`);
|
|
95
132
|
return true;
|
|
96
133
|
}
|
|
97
134
|
|
|
98
135
|
export function deleteProfile(profile: string): boolean {
|
|
99
|
-
|
|
136
|
+
const validProfile = validateProfileName(profile);
|
|
137
|
+
if (validProfile === DEFAULT_PROFILE) {
|
|
100
138
|
return false;
|
|
101
139
|
}
|
|
102
140
|
|
|
103
|
-
if (!profileExists(
|
|
141
|
+
if (!profileExists(validProfile)) {
|
|
104
142
|
return false;
|
|
105
143
|
}
|
|
106
144
|
|
|
107
|
-
if (getCurrentProfile() ===
|
|
145
|
+
if (getCurrentProfile() === validProfile) {
|
|
108
146
|
setCurrentProfile(DEFAULT_PROFILE);
|
|
109
147
|
}
|
|
110
148
|
|
|
111
|
-
rmSync(getProfilePath(
|
|
149
|
+
rmSync(getProfilePath(validProfile));
|
|
112
150
|
return true;
|
|
113
151
|
}
|
|
114
152
|
|
|
115
153
|
export function loadProfile(profile?: string): ProfileConfig {
|
|
116
154
|
ensureConfigDir();
|
|
117
|
-
const profileName = profile
|
|
155
|
+
const profileName = profile ? validateProfileName(profile) : getCurrentProfile();
|
|
118
156
|
const profilePath = getProfilePath(profileName);
|
|
119
157
|
|
|
120
158
|
if (!existsSync(profilePath)) {
|
|
@@ -122,7 +160,7 @@ export function loadProfile(profile?: string): ProfileConfig {
|
|
|
122
160
|
}
|
|
123
161
|
|
|
124
162
|
try {
|
|
125
|
-
return JSON.parse(readFileSync(profilePath, 'utf-8'));
|
|
163
|
+
return JSON.parse(readFileSync(profilePath, 'utf-8')) as ProfileConfig;
|
|
126
164
|
} catch {
|
|
127
165
|
return {};
|
|
128
166
|
}
|
|
@@ -130,17 +168,21 @@ export function loadProfile(profile?: string): ProfileConfig {
|
|
|
130
168
|
|
|
131
169
|
export function saveProfile(config: ProfileConfig, profile?: string): void {
|
|
132
170
|
ensureConfigDir();
|
|
133
|
-
const profileName = profile
|
|
134
|
-
|
|
171
|
+
const profileName = profile ? validateProfileName(profile) : getCurrentProfile();
|
|
172
|
+
writePrivateFile(getProfilePath(profileName), `${JSON.stringify(config, null, 2)}\n`);
|
|
135
173
|
}
|
|
136
174
|
|
|
137
|
-
export function
|
|
138
|
-
return
|
|
175
|
+
export function getClientCredentials(): ProfileConfig {
|
|
176
|
+
return {
|
|
177
|
+
clientId: process.env.TIDIO_CLIENT_ID || loadProfile().clientId,
|
|
178
|
+
clientSecret: process.env.TIDIO_CLIENT_SECRET || loadProfile().clientSecret,
|
|
179
|
+
};
|
|
139
180
|
}
|
|
140
181
|
|
|
141
|
-
export function
|
|
182
|
+
export function setClientCredentials(clientId: string, clientSecret: string): void {
|
|
142
183
|
const config = loadProfile();
|
|
143
|
-
config.
|
|
184
|
+
config.clientId = clientId;
|
|
185
|
+
config.clientSecret = clientSecret;
|
|
144
186
|
saveProfile(config);
|
|
145
187
|
}
|
|
146
188
|
|
|
@@ -149,7 +191,11 @@ export function clearConfig(): void {
|
|
|
149
191
|
}
|
|
150
192
|
|
|
151
193
|
export function getConfigDir(): string {
|
|
152
|
-
return
|
|
194
|
+
return rootConfigDir();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function getProfilesDir(): string {
|
|
198
|
+
return profilesDir();
|
|
153
199
|
}
|
|
154
200
|
|
|
155
201
|
export function getActiveProfileName(): string {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents working with the Weights & Biases connector.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
connect-wandb is a TypeScript connector for the W&B GraphQL API with multi-profile configuration, Bearer token authentication, and Commander.js CLI.
|
|
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
|
+
Bearer Token authentication via `WANDB_API_KEY` or `wandb config set-key <key>`.
|
|
22
|
+
|
|
23
|
+
## Environment Variables
|
|
24
|
+
|
|
25
|
+
| Variable | Description |
|
|
26
|
+
|----------|-------------|
|
|
27
|
+
| `WANDB_API_KEY` | API key (overrides profile) |
|
|
28
|
+
| `WANDB_BASE_URL` | Override GraphQL base URL |
|
|
29
|
+
|
|
30
|
+
## Data Storage
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
~/.hasna/connectors/wandb/
|
|
34
|
+
├── current_profile
|
|
35
|
+
└── profiles/
|
|
36
|
+
└── default.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Key Patterns
|
|
40
|
+
|
|
41
|
+
- `WandbClient` handles GraphQL POST with Bearer auth and error handling
|
|
42
|
+
- `Wandb` facade exposes `viewer`, `projects`, and `graphql` APIs
|
|
43
|
+
- Run filters use JSON-stringified MongoDB-style syntax per W&B API
|