@agent-os-sdk/client 0.9.2 → 0.9.3

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/src/index.ts CHANGED
@@ -96,6 +96,7 @@ export type { SSEEvent, SSEOptions } from "./sse/client.js";
96
96
 
97
97
  export * from "./modules/agents.js";
98
98
  export * from "./modules/auth.js";
99
+ export * from "./modules/chatwoot.js";
99
100
  export * from "./modules/credentials.js";
100
101
  export * from "./modules/me.js";
101
102
  export * from "./modules/members.js";
@@ -7,8 +7,8 @@
7
7
  * - create*, update*, delete* for mutations
8
8
  */
9
9
 
10
- import type { RawClient, APIResponse, components } from "../client/raw.js";
11
- import type { PaginationParams, PaginatedResponse } from "../client/helpers.js";
10
+ import type { PaginatedResponse, PaginationParams } from "../client/helpers.js";
11
+ import type { APIResponse, components, RawClient } from "../client/raw.js";
12
12
 
13
13
  // Type aliases for this module
14
14
 
@@ -31,6 +31,7 @@ export interface Agent {
31
31
  last_run_id?: string | null;
32
32
  last_run_status?: string | null;
33
33
  last_run_at?: string | null;
34
+ metadata?: any | null;
34
35
  }
35
36
 
36
37
  export interface AgentBundle extends AgentBundleSchema { }
@@ -106,6 +107,7 @@ export class AgentsModule {
106
107
  */
107
108
  async create(body: {
108
109
  name: string;
110
+ metadata?: any | null;
109
111
  /** Idempotency key for safe retries. When set, duplicate requests with the same key return 409 Conflict. */
110
112
  idempotency_key?: string;
111
113
  }): Promise<APIResponse<Agent>> {
@@ -115,7 +117,10 @@ export class AgentsModule {
115
117
  }
116
118
 
117
119
  return this.client.POST<Agent>("/v1/api/agents", {
118
- body: { name: body.name },
120
+ body: {
121
+ name: body.name,
122
+ metadata: body.metadata
123
+ },
119
124
  headers,
120
125
  });
121
126
  }
@@ -127,6 +132,7 @@ export class AgentsModule {
127
132
  async update(agentId: string, body: {
128
133
  name?: string;
129
134
  live_bundle_id?: string;
135
+ metadata?: any | null;
130
136
  }): Promise<APIResponse<Agent>> {
131
137
  return this.client.PATCH<Agent>("/v1/api/agents/{id}", {
132
138
  params: { path: { id: agentId } },
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Chatwoot Module
3
+ */
4
+
5
+ import type { APIResponse, RawClient } from "../client/raw.js";
6
+
7
+ export interface ChatwootInboxUrlResponse {
8
+ url: string;
9
+ }
10
+
11
+ export interface ChatwootConfig {
12
+ baseUrl: string;
13
+ accountId: string;
14
+ apiAccessToken: string;
15
+ }
16
+
17
+ export class ChatwootModule {
18
+ constructor(
19
+ private client: RawClient,
20
+ private headers: () => Record<string, string>
21
+ ) { }
22
+
23
+ /**
24
+ * Internal helper to resolve Chatwoot configuration from AgentOS credentials
25
+ */
26
+ private async _getChatwootConfig(credentialId: string): Promise<{ data: ChatwootConfig | undefined; error: any; response?: Response }> {
27
+ const { data: credential, error } = await this.client.GET<any>("/v1/api/credentials/{id}", {
28
+ params: {
29
+ path: { id: credentialId },
30
+ query: { includeValues: true }
31
+ },
32
+ headers: this.headers(),
33
+ });
34
+
35
+ if (error || !credential) {
36
+ return { error: error || { message: "Credential not found", code: "CREDENTIAL_NOT_FOUND" }, data: undefined };
37
+ }
38
+
39
+ // Backend now returns 'values' when includeValues=true
40
+ const values = credential.values || credential.Values || {};
41
+ const publicConfig = credential.publicConfig || credential.PublicConfig || {};
42
+ const data = credential.data || {};
43
+
44
+ const url = publicConfig.url || data.url || values.url || publicConfig.endpoint || data.endpoint || values.endpoint || values.base_url;
45
+ let accountId = publicConfig.account_id || data.account_id || values.account_id || publicConfig.accountId || data.accountId || values.accountId;
46
+ const apiAccessToken = values.api_access_token || data.api_access_token || values.api_key || data.api_key || values.apiKey || data.apiKey || values.api_token;
47
+
48
+ if (!url || !apiAccessToken) {
49
+ return { error: { message: "Invalid Chatwoot credential: missing URL/Endpoint or API Access Token/Key", code: "INVALID_CREDENTIAL" }, data: undefined };
50
+ }
51
+
52
+ const baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
53
+
54
+ // Auto-discover accountId if missing
55
+ if (!accountId) {
56
+ try {
57
+ const profileRes = await fetch(`${baseUrl}/api/v1/profile`, {
58
+ headers: { "api_access_token": apiAccessToken }
59
+ });
60
+ if (profileRes.ok) {
61
+ const profile = await profileRes.json();
62
+ // Use the first available account
63
+ if (profile.accounts && profile.accounts.length > 0) {
64
+ accountId = profile.accounts[0].id;
65
+ }
66
+ }
67
+ } catch (err) {
68
+ console.warn("Failed to auto-discover Chatwoot account ID:", err);
69
+ }
70
+ }
71
+
72
+ if (!accountId) {
73
+ return { error: { message: "Invalid Chatwoot credential: missing Account ID and auto-discovery failed.", code: "ACCOUNT_ID_MISSING" }, data: undefined };
74
+ }
75
+
76
+ return {
77
+ data: {
78
+ baseUrl,
79
+ accountId: String(accountId),
80
+ apiAccessToken
81
+ },
82
+ error: null
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Direct call to Chatwoot API
88
+ */
89
+ private async _chatwootRequest(config: ChatwootConfig, method: string, path: string, body?: any): Promise<APIResponse<any>> {
90
+ const url = `${config.baseUrl}${path}`;
91
+ const headers = {
92
+ "api_access_token": config.apiAccessToken,
93
+ "Content-Type": "application/json",
94
+ };
95
+
96
+ try {
97
+ const response = await fetch(url, {
98
+ method,
99
+ headers,
100
+ body: body ? JSON.stringify(body) : undefined,
101
+ });
102
+
103
+ let data = null;
104
+ const contentType = response.headers.get("content-type");
105
+ if (contentType && contentType.includes("application/json")) {
106
+ try {
107
+ const text = await response.text();
108
+ if (text) data = JSON.parse(text);
109
+ } catch (e) {
110
+ // Ignore JSON parse errors for empty bodies
111
+ }
112
+ }
113
+
114
+ if (!response.ok) {
115
+ return { error: data || { message: `Chatwoot API error: ${response.statusText}`, code: "API_ERROR" }, data: undefined, response };
116
+ }
117
+
118
+ return { data, error: undefined, response };
119
+ } catch (err) {
120
+ return { error: { message: (err as Error).message, code: "UNKNOWN_ERROR" }, data: undefined, response: new Response() };
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Get the inbox URL for a specific credential.
126
+ * Use this to open the Chatwoot inbox directly.
127
+ */
128
+ async getInboxUrl(credentialId: string): Promise<APIResponse<ChatwootInboxUrlResponse>> {
129
+ const { data: config, error } = await this._getChatwootConfig(credentialId);
130
+
131
+ if (error || !config) {
132
+ return { error: error || { message: "Config not found", code: "CONFIG_MISSING" }, data: undefined, response: new Response() };
133
+ }
134
+
135
+ const inboxUrl = `${config.baseUrl}/app/accounts/${config.accountId}/inbox`;
136
+
137
+ return {
138
+ data: { url: inboxUrl },
139
+ error: undefined,
140
+ response: new Response(),
141
+ };
142
+ }
143
+
144
+ /**
145
+ * List all inboxes
146
+ */
147
+ async listInboxes(credentialId: string): Promise<APIResponse<any[]>> {
148
+ const { data: config, error } = await this._getChatwootConfig(credentialId);
149
+ if (error || !config) return { error: error || { message: "Config not found", code: "CONFIG_MISSING" }, data: undefined, response: new Response() };
150
+
151
+ const res = await this._chatwootRequest(config, "GET", `/api/v1/accounts/${config.accountId}/inboxes`);
152
+
153
+ // Chatwoot API returns { payload: [...] }
154
+ if (res.data && Array.isArray(res.data.payload)) {
155
+ return { data: res.data.payload, error: undefined, response: res.response };
156
+ }
157
+
158
+ return res;
159
+ }
160
+
161
+ /**
162
+ * Create a new inbox
163
+ */
164
+ async createInbox(credentialId: string, data: any): Promise<APIResponse<any>> {
165
+ const { data: config, error } = await this._getChatwootConfig(credentialId);
166
+ if (error || !config) return { error: error || { message: "Config not found", code: "CONFIG_MISSING" }, data: undefined, response: new Response() };
167
+
168
+ return this._chatwootRequest(config, "POST", `/api/v1/accounts/${config.accountId}/inboxes`, data);
169
+ }
170
+
171
+ /**
172
+ * Get a specific inbox
173
+ */
174
+ async getInbox(credentialId: string, inboxId: string | number): Promise<APIResponse<any>> {
175
+ const { data: config, error } = await this._getChatwootConfig(credentialId);
176
+ if (error || !config) return { error: error || { message: "Config not found", code: "CONFIG_MISSING" }, data: undefined, response: new Response() };
177
+
178
+ return this._chatwootRequest(config, "GET", `/api/v1/accounts/${config.accountId}/inboxes/${inboxId}`);
179
+ }
180
+
181
+ /**
182
+ * Update an inbox
183
+ */
184
+ async updateInbox(credentialId: string, inboxId: string | number, data: any): Promise<APIResponse<any>> {
185
+ const { data: config, error } = await this._getChatwootConfig(credentialId);
186
+ if (error || !config) return { error: error || { message: "Config not found", code: "CONFIG_MISSING" }, data: undefined, response: new Response() };
187
+
188
+ return this._chatwootRequest(config, "PATCH", `/api/v1/accounts/${config.accountId}/inboxes/${inboxId}`, data);
189
+ }
190
+
191
+ /**
192
+ * Delete an inbox
193
+ */
194
+ async deleteInbox(credentialId: string, inboxId: string | number): Promise<APIResponse<any>> {
195
+ const { data: config, error } = await this._getChatwootConfig(credentialId);
196
+ if (error || !config) return { error: error || { message: "Config not found", code: "CONFIG_MISSING" }, data: undefined, response: new Response() };
197
+
198
+ return this._chatwootRequest(config, "DELETE", `/api/v1/accounts/${config.accountId}/inboxes/${inboxId}`);
199
+ }
200
+
201
+ /**
202
+ * Get inbox metrics
203
+ */
204
+ async getInboxMetrics(credentialId: string, inboxId: string | number): Promise<APIResponse<any>> {
205
+ const { data: config, error } = await this._getChatwootConfig(credentialId);
206
+ if (error || !config) return { error: error || { message: "Config not found", code: "CONFIG_MISSING" }, data: undefined, response: new Response() };
207
+
208
+ return this._chatwootRequest(config, "GET", `/api/v1/accounts/${config.accountId}/inboxes/${inboxId}/metrics`);
209
+ }
210
+
211
+ /**
212
+ * Get the inbox URL for a specific agent.
213
+ */
214
+ async getAgentInboxUrl(agentId: string): Promise<APIResponse<ChatwootInboxUrlResponse>> {
215
+ const { data: triggers, error: triggerError } = await this.client.GET<any>("/v1/api/triggers", {
216
+ params: { query: { agent_id: agentId } },
217
+ headers: this.headers(),
218
+ });
219
+
220
+ if (triggerError || !triggers) {
221
+ return { error: triggerError || { message: "Failed to fetch triggers", code: "TRIGGER_FETCH_FAILED" }, data: undefined, response: new Response() };
222
+ }
223
+
224
+ const chatwootTrigger = triggers.items?.find((t: any) =>
225
+ t.type === "chatwoot" ||
226
+ (t.type === "evolution_whatsapp" && t.config?.chat_platform === "chatwoot") ||
227
+ (t.config?.credential_id && (t.type === "chatwoot" || t.type.includes("whatsapp")))
228
+ );
229
+
230
+ if (!chatwootTrigger) {
231
+ return { error: { message: "No compatible trigger found for this agent", code: "NO_TRIGGER" }, data: undefined, response: new Response() };
232
+ }
233
+
234
+ const credentialId = chatwootTrigger.config?.credential_id;
235
+
236
+ if (!credentialId) {
237
+ return { error: { message: "Trigger configuration missing credential_id", code: "MISSING_CREDENTIAL_ID" }, data: undefined, response: new Response() };
238
+ }
239
+
240
+ return this.getInboxUrl(credentialId);
241
+ }
242
+ }
@@ -68,9 +68,12 @@ export class CredentialsModule {
68
68
  /**
69
69
  * Get a credential by ID.
70
70
  */
71
- async get(credentialId: string): Promise<APIResponse<Credential>> {
71
+ async get(credentialId: string, includeValues: boolean = false): Promise<APIResponse<Credential>> {
72
72
  return this.client.GET<Credential>("/v1/api/credentials/{id}", {
73
- params: { path: { id: credentialId } },
73
+ params: {
74
+ path: { id: credentialId },
75
+ query: { includeValues }
76
+ },
74
77
  headers: this.headers(),
75
78
  });
76
79
  }
@@ -142,4 +145,14 @@ export class CredentialsModule {
142
145
  headers: this.headers(),
143
146
  });
144
147
  }
148
+
149
+ /**
150
+ * List available models for a credential provider.
151
+ */
152
+ async listModels(credentialId: string): Promise<APIResponse<string[]>> {
153
+ return this.client.GET<string[]>("/v1/api/credentials/{id}/models", {
154
+ params: { path: { id: credentialId } },
155
+ headers: this.headers(),
156
+ });
157
+ }
145
158
  }