agental 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -536
- package/bin/agental.js +1 -1
- package/dist/cli.js +20 -20
- package/package.json +8 -26
- package/dist/client.d.ts +0 -321
- package/dist/client.js +0 -451
- package/dist/errors.d.ts +0 -62
- package/dist/errors.js +0 -65
- package/dist/http.d.ts +0 -69
- package/dist/http.js +0 -246
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -5
- package/dist/openapi.d.ts +0 -17070
- package/dist/openapi.js +0 -5
- package/dist/sandbox.d.ts +0 -605
- package/dist/sandbox.js +0 -846
- package/dist/template.d.ts +0 -183
- package/dist/template.js +0 -261
- package/dist/types.d.ts +0 -198
- package/dist/types.js +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.js +0 -2
- package/dist/webhooks.d.ts +0 -6
- package/dist/webhooks.js +0 -16
package/dist/http.js
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { AgentalError, ApiError, ConnectionError, ProxyError } from "./errors.js";
|
|
2
|
-
import { VERSION } from "./version.js";
|
|
3
|
-
export const DEFAULT_BASE_URL = "https://agental.io";
|
|
4
|
-
/** How long a sandbox_migrating is waited out: a move of a running sandbox takes a minute or two. */
|
|
5
|
-
const MIGRATING_TRIES = 18;
|
|
6
|
-
const runtime = () => globalThis.process;
|
|
7
|
-
function environment(name) {
|
|
8
|
-
const value = runtime()?.env?.[name];
|
|
9
|
-
return value?.trim() || undefined;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* What every request says it is, where the runtime lets it: agental-typescript/0.1.0. A
|
|
13
|
-
* browser sends its own User-Agent, and asked for another it would ask the API first whether
|
|
14
|
-
* it may, so in a browser none is set.
|
|
15
|
-
*/
|
|
16
|
-
function userAgent() {
|
|
17
|
-
const inBrowser = typeof globalThis.window !== "undefined" || typeof globalThis.importScripts === "function";
|
|
18
|
-
return inBrowser ? {} : { "User-Agent": `agental-typescript/${VERSION}` };
|
|
19
|
-
}
|
|
20
|
-
const sameApi = (left, right) => left.replace(/\/+$/, "") === right.replace(/\/+$/, "");
|
|
21
|
-
/**
|
|
22
|
-
* The key and the API the agental command signed in with on this computer, kept in
|
|
23
|
-
* ~/.config/agental/credentials.json (or under XDG_CONFIG_HOME). Nothing where there is no
|
|
24
|
-
* such file, or no file system, as in a browser.
|
|
25
|
-
*/
|
|
26
|
-
function signedIn() {
|
|
27
|
-
const fs = runtime()?.getBuiltinModule?.("node:fs");
|
|
28
|
-
const os = runtime()?.getBuiltinModule?.("node:os");
|
|
29
|
-
const path = runtime()?.getBuiltinModule?.("node:path");
|
|
30
|
-
if (!fs || !os || !path) {
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
const base = environment("XDG_CONFIG_HOME") ?? path.join(environment("HOME") ?? os.homedir(), ".config");
|
|
35
|
-
const saved = JSON.parse(fs.readFileSync(path.join(base, "agental", "credentials.json"), "utf8"));
|
|
36
|
-
return typeof saved["apiUrl"] === "string" && typeof saved["apiKey"] === "string" ? { apiUrl: saved["apiUrl"], apiKey: saved["apiKey"] } : undefined;
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
export function sleep(ms, signal) {
|
|
43
|
-
return new Promise((resolve, reject) => {
|
|
44
|
-
if (signal?.aborted) {
|
|
45
|
-
reject(signal.reason);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const timer = setTimeout(() => {
|
|
49
|
-
signal?.removeEventListener("abort", stopped);
|
|
50
|
-
resolve();
|
|
51
|
-
}, ms);
|
|
52
|
-
const stopped = () => {
|
|
53
|
-
clearTimeout(timer);
|
|
54
|
-
reject(signal?.reason);
|
|
55
|
-
};
|
|
56
|
-
signal?.addEventListener("abort", stopped, { once: true });
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
/** Sends requests to the API, and sends again the ones that are safe to. */
|
|
60
|
-
export class Http {
|
|
61
|
-
pollIntervalMs;
|
|
62
|
-
apiKey;
|
|
63
|
-
baseUrl;
|
|
64
|
-
fetch;
|
|
65
|
-
maxRetries;
|
|
66
|
-
retryDelayMs;
|
|
67
|
-
constructor(options = {}) {
|
|
68
|
-
let apiKey = options.apiKey ?? environment("AGENTAL_API_KEY");
|
|
69
|
-
let baseUrl = options.baseUrl ?? environment("AGENTAL_API_URL");
|
|
70
|
-
if (!apiKey) {
|
|
71
|
-
// The agental command's sign-in, for the API it was made to and no other.
|
|
72
|
-
const saved = signedIn();
|
|
73
|
-
if (saved && (baseUrl === undefined || sameApi(baseUrl, saved.apiUrl))) {
|
|
74
|
-
apiKey = saved.apiKey;
|
|
75
|
-
baseUrl = saved.apiUrl;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (!apiKey) {
|
|
79
|
-
throw new AgentalError("No API key. Pass apiKey, or set AGENTAL_API_KEY, or sign in on this computer with npx agental@latest. Keys are made in the console, under API keys.");
|
|
80
|
-
}
|
|
81
|
-
this.apiKey = apiKey;
|
|
82
|
-
this.baseUrl = (baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
83
|
-
this.fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
84
|
-
this.maxRetries = options.maxRetries ?? 3;
|
|
85
|
-
this.retryDelayMs = options.retryDelayMs ?? 1000;
|
|
86
|
-
this.pollIntervalMs = options.pollIntervalMs ?? 2000;
|
|
87
|
-
}
|
|
88
|
-
/** The answer to a request that worked. One that did not is thrown, after it was sent again if that was safe. */
|
|
89
|
-
async send(call) {
|
|
90
|
-
const query = new URLSearchParams();
|
|
91
|
-
for (const [name, value] of Object.entries(call.query ?? {})) {
|
|
92
|
-
if (typeof value === "string") {
|
|
93
|
-
query.set(name, value);
|
|
94
|
-
}
|
|
95
|
-
else if (value !== undefined) {
|
|
96
|
-
for (const one of value) {
|
|
97
|
-
query.append(name, one);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
const url = `${this.baseUrl}${call.path}${query.size > 0 ? `?${query}` : ""}`;
|
|
102
|
-
const init = {
|
|
103
|
-
method: call.method,
|
|
104
|
-
headers: {
|
|
105
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
106
|
-
Accept: "application/json",
|
|
107
|
-
...userAgent(),
|
|
108
|
-
...(call.body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
109
|
-
...call.headers,
|
|
110
|
-
},
|
|
111
|
-
...(call.body === undefined ? {} : { body: JSON.stringify(call.body) }),
|
|
112
|
-
...(call.signal ? { signal: call.signal } : {}),
|
|
113
|
-
};
|
|
114
|
-
for (let attempt = 0;; attempt += 1) {
|
|
115
|
-
let failure;
|
|
116
|
-
try {
|
|
117
|
-
const response = await this.fetch(url, init);
|
|
118
|
-
if (response.ok) {
|
|
119
|
-
return response;
|
|
120
|
-
}
|
|
121
|
-
failure = await errorOf(response);
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
if (call.signal?.aborted) {
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
failure = new ConnectionError(`Could not reach ${this.baseUrl}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
128
|
-
}
|
|
129
|
-
const wait = this.waitBefore(failure, attempt, call.replay === true);
|
|
130
|
-
if (wait === null) {
|
|
131
|
-
throw failure;
|
|
132
|
-
}
|
|
133
|
-
await sleep(wait, call.signal);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/** The JSON a request was answered with, and the status and headers it came with. */
|
|
137
|
-
async json(call) {
|
|
138
|
-
const response = await this.send(call);
|
|
139
|
-
return { status: response.status, body: (await response.json()), headers: response.headers };
|
|
140
|
-
}
|
|
141
|
-
/** The server-sent events a request is answered with, each as it arrives. */
|
|
142
|
-
async *events(call) {
|
|
143
|
-
const response = await this.send({ ...call, headers: { ...call.headers, Accept: "text/event-stream" } });
|
|
144
|
-
if (!response.body) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const reader = response.body.getReader();
|
|
148
|
-
const decoder = new TextDecoder();
|
|
149
|
-
let buffered = "";
|
|
150
|
-
try {
|
|
151
|
-
for (;;) {
|
|
152
|
-
const { value, done } = await reader.read();
|
|
153
|
-
if (done) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
buffered = (buffered + decoder.decode(value, { stream: true })).replaceAll("\r\n", "\n");
|
|
157
|
-
// A blank line ends an event.
|
|
158
|
-
for (let end = buffered.indexOf("\n\n"); end >= 0; end = buffered.indexOf("\n\n")) {
|
|
159
|
-
const lines = buffered.slice(0, end).split("\n");
|
|
160
|
-
buffered = buffered.slice(end + 2);
|
|
161
|
-
const field = (name) => lines
|
|
162
|
-
.filter((line) => line.startsWith(`${name}:`))
|
|
163
|
-
.map((line) => line.slice(name.length + 1).replace(/^ /, ""))
|
|
164
|
-
.join("\n");
|
|
165
|
-
const event = field("event");
|
|
166
|
-
if (event) {
|
|
167
|
-
yield { event, data: parsed(field("data")) };
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
finally {
|
|
173
|
-
await reader.cancel().catch(() => undefined);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* How long to wait before a failed request is sent again, or null when it is not to be.
|
|
178
|
-
* The waits are the ones the API asks for: a request cut off by a move is sent again at
|
|
179
|
-
* once, a move under way is waited out, and a proxy or a failing host is given ten times
|
|
180
|
-
* the shortest wait. Only a request that may be sent twice is sent again, except one the
|
|
181
|
-
* API turned away as it restarted: nothing was done with that one, a command neither.
|
|
182
|
-
*/
|
|
183
|
-
waitBefore(failure, attempt, replay) {
|
|
184
|
-
if (failure instanceof ApiError && failure.code === "restarting") {
|
|
185
|
-
return attempt < this.maxRetries ? this.retryDelayMs : null;
|
|
186
|
-
}
|
|
187
|
-
if (!replay) {
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
if (failure instanceof ApiError) {
|
|
191
|
-
if (failure.code === "sandbox_migrating") {
|
|
192
|
-
return attempt < MIGRATING_TRIES ? this.retryDelayMs * 10 : null;
|
|
193
|
-
}
|
|
194
|
-
if (failure.code === "sandbox_moved") {
|
|
195
|
-
return attempt < this.maxRetries ? this.retryDelayMs : null;
|
|
196
|
-
}
|
|
197
|
-
if (failure.code === "host_error" || failure.code === "internal_error") {
|
|
198
|
-
return attempt < 1 ? this.retryDelayMs * 10 : null;
|
|
199
|
-
}
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
if (attempt >= this.maxRetries) {
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
if (failure instanceof ProxyError) {
|
|
206
|
-
return failure.status >= 500 ? this.retryDelayMs * 10 : null;
|
|
207
|
-
}
|
|
208
|
-
return failure instanceof ConnectionError ? this.retryDelayMs * 2 ** attempt : null;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
function parsed(text) {
|
|
212
|
-
try {
|
|
213
|
-
return JSON.parse(text);
|
|
214
|
-
}
|
|
215
|
-
catch {
|
|
216
|
-
return text;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
/** The API's own error when the answer has a code and a message in it, and a proxy's when it has not. */
|
|
220
|
-
async function errorOf(response) {
|
|
221
|
-
const body = parsed(await response.text().catch(() => ""));
|
|
222
|
-
const error = body && typeof body === "object" ? body.error : null;
|
|
223
|
-
if (error && typeof error === "object") {
|
|
224
|
-
const { code, message, regionsWithRoom, feeMicros, retryAfterSeconds } = error;
|
|
225
|
-
if (typeof code === "string" && typeof message === "string") {
|
|
226
|
-
const regions = Array.isArray(regionsWithRoom) ? regionsWithRoom.filter((one) => typeof one === "string") : undefined;
|
|
227
|
-
const burst = {
|
|
228
|
-
...(typeof feeMicros === "number" ? { feeMicros } : {}),
|
|
229
|
-
...(typeof retryAfterSeconds === "number" || retryAfterSeconds === null ? { retryAfterSeconds } : {}),
|
|
230
|
-
};
|
|
231
|
-
return new ApiError(response.status, code, message, regions, burst);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return new ProxyError(response.status);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* A tag as a part of a path: lowercased and trimmed as the API would, and refused here when it
|
|
238
|
-
* is not one, so that no word, "..", say, can make the path another.
|
|
239
|
-
*/
|
|
240
|
-
export function tagSegment(tag) {
|
|
241
|
-
const word = tag.trim().toLowerCase();
|
|
242
|
-
if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(word)) {
|
|
243
|
-
throw new AgentalError(`A tag is lowercase letters, digits and hyphens, 1 to 63 characters, not starting or ending with a hyphen, not ${JSON.stringify(tag)}.`);
|
|
244
|
-
}
|
|
245
|
-
return word;
|
|
246
|
-
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export { Agental } from "./client.js";
|
|
2
|
-
export { AgentalError, ApiError, ConnectionError, ProxyError, TimeoutError } from "./errors.js";
|
|
3
|
-
export type { BurstRefusalCode } from "./errors.js";
|
|
4
|
-
export type { AgentalOptions } from "./http.js";
|
|
5
|
-
export { Browser, Commands, Desktop, Email, Files, Sandbox, Schedules, Ssh } from "./sandbox.js";
|
|
6
|
-
export { Secrets, Template, Templates } from "./template.js";
|
|
7
|
-
export type { TemplateChanges } from "./template.js";
|
|
8
|
-
export type { BurstOptions, CommandLine, CreateOptions, DesktopButton, ExecEvent, ExecOptions, SandboxChanges, ScreenshotOptions, SendEmailOptions, StartOptions, StopOptions, WaitOptions } from "./sandbox.js";
|
|
9
|
-
export type { AccountEmail, ApiKey, ApiKeyAction, ApiKeyPreset, ApiKeyUsage, Billing, BrowserState, Burst, BurstFee, BurstQuote, Catalog, Region, AutoReload, Checkout, Command, CreateSchedule, CreateApiKey, CreatedApiKey, DesktopLink, DesktopScreenshot, DesktopState, EmailMessage, EmailMessageList, EmailMessageSummary, ExecResult, HiddenVariable, HostRoute, Invite, Me, Member, Organization, MetricsAtMoment, MetricsReading, NetworkRule, Operation, PlainVariable, Plan, Policy, Prices, RunningOverTime, SandboxAutoStop, SandboxEmail, SandboxInfo, SandboxMetrics, SandboxNetwork, SandboxNetworkDetail, SandboxOwnNetwork, SandboxStatus, SandboxTemplate, SandboxUsage, Schedule, ScheduleRun, SecretVariable, SendEmail, SetAutoStop, Size, Snapshot, SshKey, SshState, UpdateSchedule, Team, TeamRole, Telemetry, DataRetention, AccountExport, TelemetryTest, RunsOnRepository, RunsOnRepositories, TemplateApplied, TemplateInfo, TemplateNetwork, TemplateRepo, TemplateSandbox, TemplateStart, TemplateSsh, Usage, Webhook, WebhookDelivery, } from "./types.js";
|
|
10
|
-
export { verifyWebhook } from "./webhooks.js";
|
package/dist/index.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { Agental } from "./client.js";
|
|
2
|
-
export { AgentalError, ApiError, ConnectionError, ProxyError, TimeoutError } from "./errors.js";
|
|
3
|
-
export { Browser, Commands, Desktop, Email, Files, Sandbox, Schedules, Ssh } from "./sandbox.js";
|
|
4
|
-
export { Secrets, Template, Templates } from "./template.js";
|
|
5
|
-
export { verifyWebhook } from "./webhooks.js";
|