@m8tes/sdk 0.1.0-alpha.1
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/CHANGELOG.md +100 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/chunk-UFQQNUFE.js +1083 -0
- package/dist/chunk-UFQQNUFE.js.map +1 -0
- package/dist/fixtures.cjs +297 -0
- package/dist/fixtures.cjs.map +1 -0
- package/dist/fixtures.d.cts +81 -0
- package/dist/fixtures.d.ts +81 -0
- package/dist/fixtures.js +295 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.cjs +1783 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +937 -0
- package/dist/index.d.ts +937 -0
- package/dist/index.js +672 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/index.cjs +1109 -0
- package/dist/protocol/index.cjs.map +1 -0
- package/dist/protocol/index.d.cts +576 -0
- package/dist/protocol/index.d.ts +576 -0
- package/dist/protocol/index.js +3 -0
- package/dist/protocol/index.js.map +1 -0
- package/package.json +64 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import { createNormalizer, createSseDecoder, initialConversationState, accumulate, RunFailedError, APIError, parseRetryAfter, parseErrorEnvelope, errorClassForStatus } from './chunk-UFQQNUFE.js';
|
|
2
|
+
export { APIError, AuthenticationError, BillingError, ConflictError, M8tesApiError, NotFoundError, PROTOCOL_VERSION, PermissionDeniedError, RateLimitError, RunFailedError, RunNotStreamingError, TERMINAL_EVENT_TYPES, ValidationError, accumulate, createAccumulator, createNormalizer, createSseDecoder, errorClassForStatus, errorFromResponse, initialConversationState, isTerminalEvent, parseErrorEnvelope, parseRetryAfter, parseSse, splitConcatenatedJson } from './chunk-UFQQNUFE.js';
|
|
3
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
4
|
+
|
|
5
|
+
// src/http.ts
|
|
6
|
+
var DEFAULT_BASE_URL = "https://api.m8tes.ai/api/v2";
|
|
7
|
+
var MAX_ATTEMPTS = 3;
|
|
8
|
+
var INITIAL_BACKOFF_MS = 500;
|
|
9
|
+
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
10
|
+
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
11
|
+
function backoff(ms, signal) {
|
|
12
|
+
if (signal?.aborted) return Promise.resolve();
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const timer = setTimeout(done, ms);
|
|
15
|
+
function done() {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
signal?.removeEventListener("abort", done);
|
|
18
|
+
resolve();
|
|
19
|
+
}
|
|
20
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function looksLikeHtml(res, text) {
|
|
24
|
+
if ((res.headers.get("content-type") ?? "").includes("text/html")) return true;
|
|
25
|
+
const head = text.trimStart().slice(0, 9).toLowerCase();
|
|
26
|
+
return head.startsWith("<!doctype") || head.startsWith("<html");
|
|
27
|
+
}
|
|
28
|
+
function diagnose(res, text, body, url) {
|
|
29
|
+
if (looksLikeHtml(res, text)) {
|
|
30
|
+
return `Received an HTML page instead of an API response (HTTP ${res.status}) from ${url}. This usually means the host does not serve the m8tes API; check your baseUrl (the hosted API is ${DEFAULT_BASE_URL}).`;
|
|
31
|
+
}
|
|
32
|
+
const isBareNotFound = res.status === 404 && body !== null && typeof body === "object" && !("error" in body) && body.detail === "Not Found";
|
|
33
|
+
if (isBareNotFound) {
|
|
34
|
+
return `HTTP 404 from ${url} with no API error envelope \u2014 the path matched no route. Check your baseUrl includes the /api/v2 prefix (the hosted API is ${DEFAULT_BASE_URL}).`;
|
|
35
|
+
}
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
function createHttp(options = {}) {
|
|
39
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"@m8tes/sdk is server-only: it holds your secret m8_ API key, which anyone could read out of a browser bundle. To render an agent in the browser use @m8tes/react, which talks to a server proxy that keeps the key on your server. See https://www.m8tes.ai/docs/embed-a-ui"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const apiKey = options.apiKey ?? globalThis.process?.env?.["M8TES_API_KEY"];
|
|
45
|
+
if (!apiKey) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"@m8tes/sdk: no API key. Pass `new M8tes({ apiKey })` or set M8TES_API_KEY. Create a key at https://m8tes.ai/developer."
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
51
|
+
const timeout = options.timeout ?? 3e5;
|
|
52
|
+
const maybeFetch = options.fetch ?? globalThis.fetch;
|
|
53
|
+
if (!maybeFetch) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
"@m8tes/sdk: no global fetch. Use Node 18+ or pass `fetch` in the client options."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const fetchImpl = maybeFetch;
|
|
59
|
+
async function toError(res, url, errOpts) {
|
|
60
|
+
const text = await res.text().catch(() => "");
|
|
61
|
+
let body;
|
|
62
|
+
try {
|
|
63
|
+
body = JSON.parse(text);
|
|
64
|
+
} catch {
|
|
65
|
+
body = void 0;
|
|
66
|
+
}
|
|
67
|
+
const parsed = parseErrorEnvelope(res.status, body, res.headers);
|
|
68
|
+
const message = diagnose(res, text, body, url) ?? (body ? parsed.message : text || parsed.message);
|
|
69
|
+
return new (errorClassForStatus(res.status, errOpts))(message, parsed.fields);
|
|
70
|
+
}
|
|
71
|
+
async function attempt(method, url, opts) {
|
|
72
|
+
const headers = {
|
|
73
|
+
authorization: `Bearer ${apiKey}`,
|
|
74
|
+
...options.headers,
|
|
75
|
+
...opts.headers
|
|
76
|
+
};
|
|
77
|
+
const init = { method, headers };
|
|
78
|
+
if (opts.body !== void 0) {
|
|
79
|
+
headers["content-type"] = "application/json";
|
|
80
|
+
init.body = JSON.stringify(opts.body);
|
|
81
|
+
}
|
|
82
|
+
const timer = AbortSignal.timeout(timeout);
|
|
83
|
+
init.signal = opts.signal ? AbortSignal.any([opts.signal, timer]) : timer;
|
|
84
|
+
return fetchImpl(url, init);
|
|
85
|
+
}
|
|
86
|
+
async function send(method, path, opts, errOpts = {}) {
|
|
87
|
+
const url = `${baseUrl}${path}${opts.query ?? ""}`;
|
|
88
|
+
const idempotent = IDEMPOTENT_METHODS.has(method.toUpperCase());
|
|
89
|
+
let lastNetworkError;
|
|
90
|
+
for (let i = 0; i < MAX_ATTEMPTS; i++) {
|
|
91
|
+
const isLast = i === MAX_ATTEMPTS - 1;
|
|
92
|
+
let res;
|
|
93
|
+
try {
|
|
94
|
+
res = await attempt(method, url, opts);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
if (opts.signal?.aborted) throw e;
|
|
97
|
+
lastNetworkError = e;
|
|
98
|
+
if (!idempotent || isLast) {
|
|
99
|
+
throw new APIError(e instanceof Error ? e.message : String(e), {
|
|
100
|
+
type: "api_error",
|
|
101
|
+
code: 0,
|
|
102
|
+
status: 0
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
await backoff(INITIAL_BACKOFF_MS * 2 ** i, opts.signal);
|
|
106
|
+
if (opts.signal?.aborted) throw e;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (res.ok) return res;
|
|
110
|
+
if (!RETRYABLE_STATUS.has(res.status) || !idempotent || isLast) {
|
|
111
|
+
throw await toError(res, url, errOpts);
|
|
112
|
+
}
|
|
113
|
+
const retryAfter = res.status === 429 ? parseRetryAfter(res.headers.get("retry-after")) : void 0;
|
|
114
|
+
await res.text().catch(() => "");
|
|
115
|
+
await backoff(retryAfter !== void 0 ? retryAfter * 1e3 : INITIAL_BACKOFF_MS * 2 ** i, opts.signal);
|
|
116
|
+
if (opts.signal?.aborted) throw await toError(res, url, errOpts);
|
|
117
|
+
}
|
|
118
|
+
throw new APIError(
|
|
119
|
+
lastNetworkError instanceof Error ? lastNetworkError.message : "Max retries exceeded",
|
|
120
|
+
{ type: "api_error", code: 0, status: 0 }
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
baseUrl,
|
|
125
|
+
async request(method, path, opts = {}) {
|
|
126
|
+
const res = await send(method, path, opts);
|
|
127
|
+
if (res.status === 204) return void 0;
|
|
128
|
+
const text = await res.text();
|
|
129
|
+
return text ? JSON.parse(text) : void 0;
|
|
130
|
+
},
|
|
131
|
+
raw(method, path, opts = {}) {
|
|
132
|
+
return send(method, path, opts);
|
|
133
|
+
},
|
|
134
|
+
async *stream(method, path, opts = {}) {
|
|
135
|
+
const res = await send(method, path, opts, { conflictIsNotStreaming: true });
|
|
136
|
+
if (!res.body) return;
|
|
137
|
+
const normalizer = opts.normalizer ?? createNormalizer();
|
|
138
|
+
const decoder = createSseDecoder({ onMalformed: options.onMalformed });
|
|
139
|
+
const reader = res.body.getReader();
|
|
140
|
+
const td = new TextDecoder();
|
|
141
|
+
try {
|
|
142
|
+
for (; ; ) {
|
|
143
|
+
const { done, value } = await reader.read();
|
|
144
|
+
if (done) break;
|
|
145
|
+
for (const frame of decoder.push(td.decode(value, { stream: true }))) {
|
|
146
|
+
yield* normalizer.push(frame);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const frame of decoder.flush()) yield* normalizer.push(frame);
|
|
150
|
+
} finally {
|
|
151
|
+
try {
|
|
152
|
+
await reader.cancel();
|
|
153
|
+
} catch {
|
|
154
|
+
try {
|
|
155
|
+
reader.releaseLock();
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/pagination.ts
|
|
165
|
+
var Page = class {
|
|
166
|
+
data;
|
|
167
|
+
hasMore;
|
|
168
|
+
/** Fetches the next page given a cursor. Absent on a terminal page. */
|
|
169
|
+
fetchNext;
|
|
170
|
+
constructor(data, hasMore, fetchNext) {
|
|
171
|
+
this.data = data;
|
|
172
|
+
this.hasMore = hasMore;
|
|
173
|
+
this.fetchNext = fetchNext;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Auto-paging: yields every item across every page.
|
|
177
|
+
*
|
|
178
|
+
* Stops if the cursor ever fails to advance. A server that returns the same
|
|
179
|
+
* page again with `has_more: true` — a caching layer, a replica lagging, a bug
|
|
180
|
+
* — would otherwise spin forever, re-yielding the same rows and never
|
|
181
|
+
* returning. Terminating is the only safe response: the caller gets the items
|
|
182
|
+
* it did see rather than a hung process.
|
|
183
|
+
*/
|
|
184
|
+
async *[Symbol.asyncIterator]() {
|
|
185
|
+
let page = this;
|
|
186
|
+
const seen = /* @__PURE__ */ new Set();
|
|
187
|
+
for (; ; ) {
|
|
188
|
+
yield* page.data;
|
|
189
|
+
const last = page.data.at(-1);
|
|
190
|
+
if (!page.hasMore || !last || !page.fetchNext) return;
|
|
191
|
+
const cursor = typeof last.id === "number" || typeof last.id === "string" ? last.id : typeof last.name === "string" ? last.name : void 0;
|
|
192
|
+
if (cursor === void 0 || seen.has(cursor)) return;
|
|
193
|
+
seen.add(cursor);
|
|
194
|
+
page = await page.fetchNext(cursor);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Every item across every page, collected. Prefer iteration for large sets. */
|
|
198
|
+
async all() {
|
|
199
|
+
const out = [];
|
|
200
|
+
for await (const item of this) out.push(item);
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// src/params.ts
|
|
206
|
+
function toBody(params) {
|
|
207
|
+
const out = {};
|
|
208
|
+
for (const [k, v] of Object.entries(params)) {
|
|
209
|
+
if (v !== void 0) out[k] = v;
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
function toQuery(params) {
|
|
214
|
+
const q = new URLSearchParams();
|
|
215
|
+
for (const [k, v] of Object.entries(params)) {
|
|
216
|
+
if (v === void 0 || v === null) continue;
|
|
217
|
+
q.set(k, String(v));
|
|
218
|
+
}
|
|
219
|
+
const s = q.toString();
|
|
220
|
+
return s ? `?${s}` : "";
|
|
221
|
+
}
|
|
222
|
+
function resolveAgentId(teammateId, agentId) {
|
|
223
|
+
if (agentId !== void 0 && teammateId !== void 0 && agentId !== teammateId) {
|
|
224
|
+
throw new Error("Pass agent_id or teammate_id, not both");
|
|
225
|
+
}
|
|
226
|
+
return teammateId ?? agentId;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/resources/agents.ts
|
|
230
|
+
function createAgentsResource(http) {
|
|
231
|
+
return {
|
|
232
|
+
create(params = {}) {
|
|
233
|
+
return http.request("POST", "/agents/", { body: toBody({ ...params }) });
|
|
234
|
+
},
|
|
235
|
+
async list(params = {}) {
|
|
236
|
+
const fetchPage = async (p) => {
|
|
237
|
+
const res = await http.request("GET", "/agents/", {
|
|
238
|
+
query: toQuery(p)
|
|
239
|
+
});
|
|
240
|
+
return new Page(
|
|
241
|
+
res?.data ?? [],
|
|
242
|
+
res?.has_more ?? false,
|
|
243
|
+
(starting_after) => fetchPage({ ...p, starting_after })
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
return fetchPage({ ...params });
|
|
247
|
+
},
|
|
248
|
+
get(agentId, params = {}) {
|
|
249
|
+
return http.request("GET", `/agents/${agentId}`, { query: toQuery(params) });
|
|
250
|
+
},
|
|
251
|
+
update(agentId, params) {
|
|
252
|
+
const { user_id, ...patch } = params;
|
|
253
|
+
return http.request("PATCH", `/agents/${agentId}`, {
|
|
254
|
+
body: toBody(patch),
|
|
255
|
+
query: toQuery({ user_id })
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
async delete(agentId, params = {}) {
|
|
259
|
+
await http.request("DELETE", `/agents/${agentId}`, { query: toQuery(params) });
|
|
260
|
+
},
|
|
261
|
+
enableWebhook(agentId) {
|
|
262
|
+
return http.request("POST", `/agents/${agentId}/webhook`, { body: {} });
|
|
263
|
+
},
|
|
264
|
+
async disableWebhook(agentId) {
|
|
265
|
+
await http.request("DELETE", `/agents/${agentId}/webhook`);
|
|
266
|
+
},
|
|
267
|
+
enableEmailInbox(agentId) {
|
|
268
|
+
return http.request("POST", `/agents/${agentId}/email-inbox`, { body: {} });
|
|
269
|
+
},
|
|
270
|
+
async disableEmailInbox(agentId) {
|
|
271
|
+
await http.request("DELETE", `/agents/${agentId}/email-inbox`);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/resources/apps.ts
|
|
277
|
+
function createAppsResource(http) {
|
|
278
|
+
const slug = (name) => encodeURIComponent(name);
|
|
279
|
+
return {
|
|
280
|
+
async list(params = {}) {
|
|
281
|
+
const res = await http.request("GET", "/apps/", {
|
|
282
|
+
query: toQuery({ user_id: params.user_id })
|
|
283
|
+
});
|
|
284
|
+
return new Page(res?.data ?? [], res?.has_more ?? false);
|
|
285
|
+
},
|
|
286
|
+
async isConnected(appName, params = {}) {
|
|
287
|
+
const { data } = await this.list(params);
|
|
288
|
+
return data.find((a) => a.name === appName)?.connected ?? false;
|
|
289
|
+
},
|
|
290
|
+
connectOauth(appName, params) {
|
|
291
|
+
return http.request("POST", `/apps/${slug(appName)}/connect`, {
|
|
292
|
+
body: toBody({ ...params })
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
connectApiKey(appName, params) {
|
|
296
|
+
return http.request("POST", `/apps/${slug(appName)}/connect/api-key`, {
|
|
297
|
+
body: toBody({ ...params })
|
|
298
|
+
});
|
|
299
|
+
},
|
|
300
|
+
connectComplete(appName, params) {
|
|
301
|
+
return http.request("POST", `/apps/${slug(appName)}/connect/complete`, {
|
|
302
|
+
body: toBody({ ...params })
|
|
303
|
+
});
|
|
304
|
+
},
|
|
305
|
+
async disconnect(appName, params = {}) {
|
|
306
|
+
await http.request("DELETE", `/apps/${slug(appName)}/connections`, { query: toQuery(params) });
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/streaming.ts
|
|
312
|
+
var RunStream = class {
|
|
313
|
+
source;
|
|
314
|
+
raiseOnError;
|
|
315
|
+
state = initialConversationState;
|
|
316
|
+
textChunks = [];
|
|
317
|
+
errorMessages = [];
|
|
318
|
+
runIdValue = null;
|
|
319
|
+
consumed = false;
|
|
320
|
+
constructor(source, options = {}) {
|
|
321
|
+
this.source = source;
|
|
322
|
+
this.raiseOnError = options.raiseOnError ?? false;
|
|
323
|
+
}
|
|
324
|
+
async *[Symbol.asyncIterator]() {
|
|
325
|
+
if (this.consumed) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
"RunStream has already been consumed. A stream is single-pass \u2014 iterate once, then read .text / .state / .errors, or call runs.get(runId) to re-read the result."
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
this.consumed = true;
|
|
331
|
+
try {
|
|
332
|
+
for await (const event of this.source) {
|
|
333
|
+
this.state = accumulate(this.state, event);
|
|
334
|
+
if (event.type === "text-delta") this.textChunks.push(event.delta);
|
|
335
|
+
if (event.type === "run-start" && event.runId !== null) this.runIdValue = event.runId;
|
|
336
|
+
if (event.type === "run-error") this.errorMessages.push(event.message || event.error);
|
|
337
|
+
yield event;
|
|
338
|
+
}
|
|
339
|
+
} finally {
|
|
340
|
+
await this.source.return(void 0).catch(() => void 0);
|
|
341
|
+
}
|
|
342
|
+
if (this.raiseOnError && this.errorMessages.length > 0) {
|
|
343
|
+
throw new RunFailedError(`Run failed: ${this.errorMessages.join("; ")}`, {
|
|
344
|
+
type: "run_failed",
|
|
345
|
+
code: 0,
|
|
346
|
+
status: 0,
|
|
347
|
+
details: { errors: this.errorMessages }
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/** Yield only the assistant's text, in order. The 90% case for a server. */
|
|
352
|
+
async *iterText() {
|
|
353
|
+
for await (const event of this) {
|
|
354
|
+
if (event.type === "text-delta") yield event.delta;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/** Drain the stream and return the full assistant text. */
|
|
358
|
+
async text() {
|
|
359
|
+
for await (const _ of this) {
|
|
360
|
+
}
|
|
361
|
+
return this.textChunks.join("");
|
|
362
|
+
}
|
|
363
|
+
/** Accumulated text so far (complete once iteration finishes). */
|
|
364
|
+
get output() {
|
|
365
|
+
return this.textChunks.join("");
|
|
366
|
+
}
|
|
367
|
+
/** Run id, available as soon as the first `run-start` event arrives. */
|
|
368
|
+
get runId() {
|
|
369
|
+
return this.runIdValue;
|
|
370
|
+
}
|
|
371
|
+
/** Error messages the run emitted. Check this, or pass `raiseOnError`. */
|
|
372
|
+
get errors() {
|
|
373
|
+
return [...this.errorMessages];
|
|
374
|
+
}
|
|
375
|
+
get hasErrors() {
|
|
376
|
+
return this.errorMessages.length > 0;
|
|
377
|
+
}
|
|
378
|
+
/** Full normalized conversation: messages, tool calls, notices, status. */
|
|
379
|
+
get conversation() {
|
|
380
|
+
return this.state;
|
|
381
|
+
}
|
|
382
|
+
/** Close the underlying response without draining it. */
|
|
383
|
+
async close() {
|
|
384
|
+
await this.source.return(void 0).catch(() => void 0);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// src/resources/runs.ts
|
|
389
|
+
function items(payload) {
|
|
390
|
+
return Array.isArray(payload) ? payload : payload?.data ?? [];
|
|
391
|
+
}
|
|
392
|
+
function createRunsResource(http) {
|
|
393
|
+
const createBody = (p, stream) => {
|
|
394
|
+
const { agent_id, teammate_id, ...rest } = p;
|
|
395
|
+
return toBody({ ...rest, teammate_id: resolveAgentId(teammate_id, agent_id), stream });
|
|
396
|
+
};
|
|
397
|
+
return {
|
|
398
|
+
create(params, options) {
|
|
399
|
+
return new RunStream(http.stream("POST", "/runs", { body: createBody(params, true) }), options);
|
|
400
|
+
},
|
|
401
|
+
createAsync(params) {
|
|
402
|
+
return http.request("POST", "/runs", { body: createBody(params, false) });
|
|
403
|
+
},
|
|
404
|
+
stream(runId, options) {
|
|
405
|
+
return new RunStream(http.stream("GET", `/runs/${runId}/stream`), options);
|
|
406
|
+
},
|
|
407
|
+
reply(runId, message, options) {
|
|
408
|
+
return new RunStream(http.stream("POST", `/runs/${runId}/reply`, { body: { message } }), options);
|
|
409
|
+
},
|
|
410
|
+
get(runId) {
|
|
411
|
+
return http.request("GET", `/runs/${runId}`);
|
|
412
|
+
},
|
|
413
|
+
async list(params = {}) {
|
|
414
|
+
const { agent_id, teammate_id, ...rest } = params;
|
|
415
|
+
const q = toBody({ ...rest, teammate_id: resolveAgentId(teammate_id, agent_id) });
|
|
416
|
+
const fetchPage = async (p) => {
|
|
417
|
+
const res = await http.request("GET", "/runs", {
|
|
418
|
+
query: toQuery(p)
|
|
419
|
+
});
|
|
420
|
+
return new Page(
|
|
421
|
+
res?.data ?? [],
|
|
422
|
+
res?.has_more ?? false,
|
|
423
|
+
(starting_after) => fetchPage({ ...p, starting_after })
|
|
424
|
+
);
|
|
425
|
+
};
|
|
426
|
+
return fetchPage(q);
|
|
427
|
+
},
|
|
428
|
+
cancel(runId) {
|
|
429
|
+
return http.request("POST", `/runs/${runId}/cancel`, { body: {} });
|
|
430
|
+
},
|
|
431
|
+
approve(runId, params) {
|
|
432
|
+
return http.request("POST", `/runs/${runId}/approve`, {
|
|
433
|
+
body: { remember: false, ...params }
|
|
434
|
+
});
|
|
435
|
+
},
|
|
436
|
+
answer(runId, params) {
|
|
437
|
+
return http.request("POST", `/runs/${runId}/answer`, {
|
|
438
|
+
body: { answers: params.answers }
|
|
439
|
+
});
|
|
440
|
+
},
|
|
441
|
+
async permissions(runId) {
|
|
442
|
+
return items(await http.request("GET", `/runs/${runId}/permissions`));
|
|
443
|
+
},
|
|
444
|
+
outcome(runId) {
|
|
445
|
+
return http.request("GET", `/runs/${runId}/outcome`);
|
|
446
|
+
},
|
|
447
|
+
async files(runId) {
|
|
448
|
+
return items(await http.request("GET", `/runs/${runId}/files`));
|
|
449
|
+
},
|
|
450
|
+
async downloadFile(runId, filename) {
|
|
451
|
+
const res = await http.raw("GET", `/runs/${runId}/files/${encodeURIComponent(filename)}/download`, {
|
|
452
|
+
headers: { accept: "application/octet-stream" }
|
|
453
|
+
});
|
|
454
|
+
return res.arrayBuffer();
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/resources/settings.ts
|
|
460
|
+
function createSettingsResource(http) {
|
|
461
|
+
return {
|
|
462
|
+
get() {
|
|
463
|
+
return http.request("GET", "/settings/");
|
|
464
|
+
},
|
|
465
|
+
// `null` must survive (it CLEARS a cap); only `undefined` is dropped.
|
|
466
|
+
update(params) {
|
|
467
|
+
return http.request("PATCH", "/settings/", { body: toBody({ ...params }) });
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/resources/tasks.ts
|
|
473
|
+
function createTasksResource(http) {
|
|
474
|
+
const triggers = {
|
|
475
|
+
create(taskId, params) {
|
|
476
|
+
return http.request("POST", `/tasks/${taskId}/triggers/`, {
|
|
477
|
+
body: toBody({ timezone: "UTC", ...params })
|
|
478
|
+
});
|
|
479
|
+
},
|
|
480
|
+
async list(taskId) {
|
|
481
|
+
const res = await http.request(
|
|
482
|
+
"GET",
|
|
483
|
+
`/tasks/${taskId}/triggers/`
|
|
484
|
+
);
|
|
485
|
+
return Array.isArray(res) ? res : res?.data ?? [];
|
|
486
|
+
},
|
|
487
|
+
update(taskId, triggerId, params) {
|
|
488
|
+
return http.request("PATCH", `/tasks/${taskId}/triggers/${triggerId}`, {
|
|
489
|
+
body: toBody({ ...params })
|
|
490
|
+
});
|
|
491
|
+
},
|
|
492
|
+
async delete(taskId, triggerId) {
|
|
493
|
+
await http.request("DELETE", `/tasks/${taskId}/triggers/${triggerId}`);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
return {
|
|
497
|
+
triggers,
|
|
498
|
+
create(params) {
|
|
499
|
+
const { agent_id, teammate_id, ...rest } = params;
|
|
500
|
+
return http.request("POST", "/tasks/", {
|
|
501
|
+
body: toBody({ ...rest, teammate_id: resolveAgentId(teammate_id, agent_id) })
|
|
502
|
+
});
|
|
503
|
+
},
|
|
504
|
+
async list(params = {}) {
|
|
505
|
+
const { agent_id, teammate_id, ...rest } = params;
|
|
506
|
+
const q = toBody({ ...rest, teammate_id: resolveAgentId(teammate_id, agent_id) });
|
|
507
|
+
const fetchPage = async (p) => {
|
|
508
|
+
const res = await http.request("GET", "/tasks/", {
|
|
509
|
+
query: toQuery(p)
|
|
510
|
+
});
|
|
511
|
+
return new Page(
|
|
512
|
+
res?.data ?? [],
|
|
513
|
+
res?.has_more ?? false,
|
|
514
|
+
(starting_after) => fetchPage({ ...p, starting_after })
|
|
515
|
+
);
|
|
516
|
+
};
|
|
517
|
+
return fetchPage(q);
|
|
518
|
+
},
|
|
519
|
+
get(taskId, params = {}) {
|
|
520
|
+
return http.request("GET", `/tasks/${taskId}`, { query: toQuery(params) });
|
|
521
|
+
},
|
|
522
|
+
update(taskId, params) {
|
|
523
|
+
const { user_id, ...patch } = params;
|
|
524
|
+
return http.request("PATCH", `/tasks/${taskId}`, {
|
|
525
|
+
body: toBody(patch),
|
|
526
|
+
query: toQuery({ user_id })
|
|
527
|
+
});
|
|
528
|
+
},
|
|
529
|
+
async delete(taskId, params = {}) {
|
|
530
|
+
await http.request("DELETE", `/tasks/${taskId}`, { query: toQuery(params) });
|
|
531
|
+
},
|
|
532
|
+
run(taskId, params = {}, options) {
|
|
533
|
+
return new RunStream(
|
|
534
|
+
http.stream("POST", `/tasks/${taskId}/runs`, { body: toBody({ ...params, stream: true }) }),
|
|
535
|
+
options
|
|
536
|
+
);
|
|
537
|
+
},
|
|
538
|
+
runAsync(taskId, params = {}) {
|
|
539
|
+
return http.request("POST", `/tasks/${taskId}/runs`, {
|
|
540
|
+
body: toBody({ ...params, stream: false })
|
|
541
|
+
});
|
|
542
|
+
},
|
|
543
|
+
enableWebhook(taskId) {
|
|
544
|
+
return http.request("POST", `/tasks/${taskId}/webhook`, { body: {} });
|
|
545
|
+
},
|
|
546
|
+
async disableWebhook(taskId) {
|
|
547
|
+
await http.request("DELETE", `/tasks/${taskId}/webhook`);
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/resources/users.ts
|
|
553
|
+
function pager(http, path) {
|
|
554
|
+
const fetchPage = async (p) => {
|
|
555
|
+
const res = await http.request("GET", path, {
|
|
556
|
+
query: toQuery(p)
|
|
557
|
+
});
|
|
558
|
+
return new Page(
|
|
559
|
+
res?.data ?? [],
|
|
560
|
+
res?.has_more ?? false,
|
|
561
|
+
(starting_after) => fetchPage({ ...p, starting_after })
|
|
562
|
+
);
|
|
563
|
+
};
|
|
564
|
+
return fetchPage;
|
|
565
|
+
}
|
|
566
|
+
function createUsersResource(http) {
|
|
567
|
+
return {
|
|
568
|
+
create(params) {
|
|
569
|
+
return http.request("POST", "/users/", { body: toBody({ ...params }) });
|
|
570
|
+
},
|
|
571
|
+
list(params = {}) {
|
|
572
|
+
return pager(http, "/users/")({ ...params });
|
|
573
|
+
},
|
|
574
|
+
get(userId) {
|
|
575
|
+
return http.request("GET", `/users/${encodeURIComponent(userId)}`);
|
|
576
|
+
},
|
|
577
|
+
update(userId, params) {
|
|
578
|
+
return http.request("PATCH", `/users/${encodeURIComponent(userId)}`, {
|
|
579
|
+
body: toBody({ ...params })
|
|
580
|
+
});
|
|
581
|
+
},
|
|
582
|
+
async delete(userId) {
|
|
583
|
+
await http.request("DELETE", `/users/${encodeURIComponent(userId)}`);
|
|
584
|
+
},
|
|
585
|
+
usage(params = {}) {
|
|
586
|
+
return pager(http, "/usage/end-users")({ ...params });
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function verifySignature(body, headers, secret, options = {}) {
|
|
591
|
+
const get = (name) => {
|
|
592
|
+
if (typeof headers.get === "function") {
|
|
593
|
+
return headers.get(name) ?? void 0;
|
|
594
|
+
}
|
|
595
|
+
const lower = Object.fromEntries(
|
|
596
|
+
Object.entries(headers).map(([k, v]) => [
|
|
597
|
+
k.toLowerCase(),
|
|
598
|
+
Array.isArray(v) ? v[0] : v
|
|
599
|
+
])
|
|
600
|
+
);
|
|
601
|
+
return lower[name];
|
|
602
|
+
};
|
|
603
|
+
const webhookId = get("webhook-id");
|
|
604
|
+
const timestamp = get("webhook-timestamp");
|
|
605
|
+
const signature = get("webhook-signature");
|
|
606
|
+
if (!webhookId || !timestamp || !signature) return false;
|
|
607
|
+
if (options.toleranceSeconds !== void 0) {
|
|
608
|
+
const ts = Number.parseInt(timestamp, 10);
|
|
609
|
+
if (!Number.isFinite(ts)) return false;
|
|
610
|
+
const now = options.now ? options.now() : Math.floor(Date.now() / 1e3);
|
|
611
|
+
if (Math.abs(now - ts) > options.toleranceSeconds) return false;
|
|
612
|
+
}
|
|
613
|
+
const raw = typeof body === "string" ? body : new TextDecoder().decode(body);
|
|
614
|
+
const expected = `v1=${createHmac("sha256", secret).update(`${webhookId}.${timestamp}.${raw}`).digest("hex")}`;
|
|
615
|
+
const a = Buffer.from(expected);
|
|
616
|
+
const b = Buffer.from(signature);
|
|
617
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
618
|
+
}
|
|
619
|
+
function createWebhooksResource(http) {
|
|
620
|
+
return {
|
|
621
|
+
verifySignature,
|
|
622
|
+
create(params) {
|
|
623
|
+
return http.request("POST", "/webhooks/", { body: toBody({ ...params }) });
|
|
624
|
+
},
|
|
625
|
+
list(params = {}) {
|
|
626
|
+
return pager(http, "/webhooks/")({ ...params });
|
|
627
|
+
},
|
|
628
|
+
get(webhookId) {
|
|
629
|
+
return http.request("GET", `/webhooks/${webhookId}`);
|
|
630
|
+
},
|
|
631
|
+
update(webhookId, params) {
|
|
632
|
+
return http.request("PATCH", `/webhooks/${webhookId}`, { body: toBody({ ...params }) });
|
|
633
|
+
},
|
|
634
|
+
async delete(webhookId) {
|
|
635
|
+
await http.request("DELETE", `/webhooks/${webhookId}`);
|
|
636
|
+
},
|
|
637
|
+
listDeliveries(webhookId, params = {}) {
|
|
638
|
+
return pager(http, `/webhooks/${webhookId}/deliveries`)({ ...params });
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/index.ts
|
|
644
|
+
var M8TES_SDK_VERSION = "0.1.0-alpha.1";
|
|
645
|
+
var M8tes = class {
|
|
646
|
+
runs;
|
|
647
|
+
agents;
|
|
648
|
+
/** Permanent alias for `agents` — the DB model and older docs say "teammate". */
|
|
649
|
+
teammates;
|
|
650
|
+
tasks;
|
|
651
|
+
users;
|
|
652
|
+
apps;
|
|
653
|
+
webhooks;
|
|
654
|
+
settings;
|
|
655
|
+
/** The underlying transport. Use it to call an endpoint this version does not wrap yet. */
|
|
656
|
+
http;
|
|
657
|
+
constructor(options = {}) {
|
|
658
|
+
this.http = createHttp(options);
|
|
659
|
+
this.runs = createRunsResource(this.http);
|
|
660
|
+
this.agents = createAgentsResource(this.http);
|
|
661
|
+
this.teammates = this.agents;
|
|
662
|
+
this.tasks = createTasksResource(this.http);
|
|
663
|
+
this.users = createUsersResource(this.http);
|
|
664
|
+
this.apps = createAppsResource(this.http);
|
|
665
|
+
this.webhooks = createWebhooksResource(this.http);
|
|
666
|
+
this.settings = createSettingsResource(this.http);
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
export { DEFAULT_BASE_URL, M8TES_SDK_VERSION, M8tes, Page, RunStream, createHttp, verifySignature };
|
|
671
|
+
//# sourceMappingURL=index.js.map
|
|
672
|
+
//# sourceMappingURL=index.js.map
|