@ebowwa/hetzner 0.3.2 → 0.3.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/dist/actions.js +782 -0
- package/dist/bootstrap/index.js +1 -0
- package/dist/client.js +1867 -0
- package/dist/errors.js +236 -0
- package/dist/index.js +40 -38
- package/dist/onboarding/index.js +2 -2
- package/dist/pricing.js +587 -0
- package/dist/schemas.js +475 -0
- package/dist/servers.js +688 -0
- package/dist/ssh-keys.js +474 -0
- package/dist/types.js +101 -0
- package/dist/volumes.js +119 -0
- package/package.json +51 -1
- package/dist/actions.d.ts +0 -355
- package/dist/auth.d.ts +0 -6
- package/dist/bootstrap/cloud-init.d.ts +0 -78
- package/dist/bootstrap/firewall.d.ts +0 -118
- package/dist/bootstrap/genesis.d.ts +0 -82
- package/dist/bootstrap/index.d.ts +0 -29
- package/dist/bootstrap/kernel-hardening.d.ts +0 -69
- package/dist/bootstrap/security-audit.d.ts +0 -45
- package/dist/bootstrap/ssh-hardening.d.ts +0 -67
- package/dist/client.d.ts +0 -62
- package/dist/config.d.ts +0 -4
- package/dist/cpufeatures-mvwrkyaq.node +0 -0
- package/dist/errors.d.ts +0 -170
- package/dist/index.d.ts +0 -21
- package/dist/onboarding/claude.d.ts +0 -37
- package/dist/onboarding/cpufeatures-mvwrkyaq.node +0 -0
- package/dist/onboarding/doppler.d.ts +0 -37
- package/dist/onboarding/git.d.ts +0 -38
- package/dist/onboarding/index.d.ts +0 -19
- package/dist/onboarding/onboarding.d.ts +0 -41
- package/dist/onboarding/sshcrypto-6mayxj08.node +0 -0
- package/dist/onboarding/tailscale.d.ts +0 -38
- package/dist/onboarding/types.d.ts +0 -111
- package/dist/pricing.d.ts +0 -330
- package/dist/schemas.d.ts +0 -6629
- package/dist/server-status.d.ts +0 -25
- package/dist/servers.d.ts +0 -164
- package/dist/ssh-keys.d.ts +0 -35
- package/dist/ssh-setup.d.ts +0 -47
- package/dist/sshcrypto-6mayxj08.node +0 -0
- package/dist/types.d.ts +0 -303
- package/dist/volumes.d.ts +0 -105
package/dist/actions.js
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = import.meta.require;
|
|
19
|
+
|
|
20
|
+
// src/types.ts
|
|
21
|
+
import {
|
|
22
|
+
EnvironmentStatus,
|
|
23
|
+
ActionStatus,
|
|
24
|
+
VolumeStatus
|
|
25
|
+
} from "@ebowwa/codespaces-types/compile";
|
|
26
|
+
var ActionCommand;
|
|
27
|
+
((ActionCommand2) => {
|
|
28
|
+
ActionCommand2["CreateServer"] = "create_server";
|
|
29
|
+
ActionCommand2["DeleteServer"] = "delete_server";
|
|
30
|
+
ActionCommand2["StartServer"] = "start_server";
|
|
31
|
+
ActionCommand2["StopServer"] = "stop_server";
|
|
32
|
+
ActionCommand2["RebootServer"] = "reboot_server";
|
|
33
|
+
ActionCommand2["ResetServer"] = "reset_server";
|
|
34
|
+
ActionCommand2["ShutdownServer"] = "shutdown_server";
|
|
35
|
+
ActionCommand2["Poweroff"] = "poweroff";
|
|
36
|
+
ActionCommand2["ChangeServerType"] = "change_server_type";
|
|
37
|
+
ActionCommand2["RebuildServer"] = "rebuild_server";
|
|
38
|
+
ActionCommand2["EnableBackup"] = "enable_backup";
|
|
39
|
+
ActionCommand2["DisableBackup"] = "disable_backup";
|
|
40
|
+
ActionCommand2["CreateImage"] = "create_image";
|
|
41
|
+
ActionCommand2["ChangeDnsPtr"] = "change_dns_ptr";
|
|
42
|
+
ActionCommand2["AttachToNetwork"] = "attach_to_network";
|
|
43
|
+
ActionCommand2["DetachFromNetwork"] = "detach_from_network";
|
|
44
|
+
ActionCommand2["ChangeAliasIps"] = "change_alias_ips";
|
|
45
|
+
ActionCommand2["EnableRescue"] = "enable_rescue";
|
|
46
|
+
ActionCommand2["DisableRescue"] = "disable_rescue";
|
|
47
|
+
ActionCommand2["ChangeProtection"] = "change_protection";
|
|
48
|
+
ActionCommand2["CreateVolume"] = "create_volume";
|
|
49
|
+
ActionCommand2["DeleteVolume"] = "delete_volume";
|
|
50
|
+
ActionCommand2["AttachVolume"] = "attach_volume";
|
|
51
|
+
ActionCommand2["DetachVolume"] = "detach_volume";
|
|
52
|
+
ActionCommand2["ResizeVolume"] = "resize_volume";
|
|
53
|
+
ActionCommand2["VolumeChangeProtection"] = "volume_change_protection";
|
|
54
|
+
ActionCommand2["AddSubnet"] = "add_subnet";
|
|
55
|
+
ActionCommand2["DeleteSubnet"] = "delete_subnet";
|
|
56
|
+
ActionCommand2["AddRoute"] = "add_route";
|
|
57
|
+
ActionCommand2["DeleteRoute"] = "delete_route";
|
|
58
|
+
ActionCommand2["ChangeIpRange"] = "change_ip_range";
|
|
59
|
+
ActionCommand2["NetworkChangeProtection"] = "network_change_protection";
|
|
60
|
+
ActionCommand2["AssignFloatingIp"] = "assign_floating_ip";
|
|
61
|
+
ActionCommand2["UnassignFloatingIp"] = "unassign_floating_ip";
|
|
62
|
+
ActionCommand2["FloatingIpChangeDnsPtr"] = "floating_ip_change_dns_ptr";
|
|
63
|
+
ActionCommand2["FloatingIpChangeProtection"] = "floating_ip_change_protection";
|
|
64
|
+
ActionCommand2["CreateLoadBalancer"] = "create_load_balancer";
|
|
65
|
+
ActionCommand2["DeleteLoadBalancer"] = "delete_load_balancer";
|
|
66
|
+
ActionCommand2["AddTarget"] = "add_target";
|
|
67
|
+
ActionCommand2["RemoveTarget"] = "remove_target";
|
|
68
|
+
ActionCommand2["AddService"] = "add_service";
|
|
69
|
+
ActionCommand2["UpdateService"] = "update_service";
|
|
70
|
+
ActionCommand2["DeleteService"] = "delete_service";
|
|
71
|
+
ActionCommand2["LoadBalancerAttachToNetwork"] = "load_balancer_attach_to_network";
|
|
72
|
+
ActionCommand2["LoadBalancerDetachFromNetwork"] = "load_balancer_detach_from_network";
|
|
73
|
+
ActionCommand2["ChangeAlgorithm"] = "change_algorithm";
|
|
74
|
+
ActionCommand2["ChangeType"] = "change_type";
|
|
75
|
+
ActionCommand2["LoadBalancerChangeProtection"] = "load_balancer_change_protection";
|
|
76
|
+
ActionCommand2["IssueCertificate"] = "issue_certificate";
|
|
77
|
+
ActionCommand2["RetryCertificate"] = "retry_certificate";
|
|
78
|
+
ActionCommand2["SetFirewallRules"] = "set_firewall_rules";
|
|
79
|
+
ActionCommand2["ApplyFirewall"] = "apply_firewall";
|
|
80
|
+
ActionCommand2["RemoveFirewall"] = "remove_firewall";
|
|
81
|
+
ActionCommand2["FirewallChangeProtection"] = "firewall_change_protection";
|
|
82
|
+
ActionCommand2["ImageChangeProtection"] = "image_change_protection";
|
|
83
|
+
})(ActionCommand ||= {});
|
|
84
|
+
var ResourceType;
|
|
85
|
+
((ResourceType2) => {
|
|
86
|
+
ResourceType2["Server"] = "server";
|
|
87
|
+
ResourceType2["Volume"] = "volume";
|
|
88
|
+
ResourceType2["Network"] = "network";
|
|
89
|
+
ResourceType2["FloatingIp"] = "floating_ip";
|
|
90
|
+
ResourceType2["LoadBalancer"] = "load_balancer";
|
|
91
|
+
ResourceType2["Certificate"] = "certificate";
|
|
92
|
+
ResourceType2["Firewall"] = "firewall";
|
|
93
|
+
ResourceType2["Image"] = "image";
|
|
94
|
+
})(ResourceType ||= {});
|
|
95
|
+
|
|
96
|
+
// src/errors.ts
|
|
97
|
+
var HetznerErrorCode;
|
|
98
|
+
((HetznerErrorCode2) => {
|
|
99
|
+
HetznerErrorCode2["Unauthorized"] = "unauthorized";
|
|
100
|
+
HetznerErrorCode2["InvalidInput"] = "invalid_input";
|
|
101
|
+
HetznerErrorCode2["JSONError"] = "json_error";
|
|
102
|
+
HetznerErrorCode2["Forbidden"] = "forbidden";
|
|
103
|
+
HetznerErrorCode2["NotFound"] = "not_found";
|
|
104
|
+
HetznerErrorCode2["ResourceLocked"] = "locked";
|
|
105
|
+
HetznerErrorCode2["ResourceLimitExceeded"] = "resource_limit_exceeded";
|
|
106
|
+
HetznerErrorCode2["UniquenessError"] = "uniqueness_error";
|
|
107
|
+
HetznerErrorCode2["RateLimitExceeded"] = "rate_limit_exceeded";
|
|
108
|
+
HetznerErrorCode2["Conflict"] = "conflict";
|
|
109
|
+
HetznerErrorCode2["ServiceError"] = "service_error";
|
|
110
|
+
HetznerErrorCode2["ServerNotStopped"] = "server_not_stopped";
|
|
111
|
+
HetznerErrorCode2["ServerAlreadyStopped"] = "server_already_stopped";
|
|
112
|
+
HetznerErrorCode2["InvalidServerType"] = "invalid_server_type";
|
|
113
|
+
HetznerErrorCode2["IpNotOwned"] = "ip_not_owned";
|
|
114
|
+
HetznerErrorCode2["IpAlreadyAssigned"] = "ip_already_assigned";
|
|
115
|
+
HetznerErrorCode2["VolumeAlreadyAttached"] = "volume_already_attached";
|
|
116
|
+
HetznerErrorCode2["VolumeSizeNotMultiple"] = "volume_size_not_multiple";
|
|
117
|
+
HetznerErrorCode2["FirewallInUse"] = "firewall_in_use";
|
|
118
|
+
HetznerErrorCode2["CertificateValidationFailed"] = "certificate_validation_failed";
|
|
119
|
+
HetznerErrorCode2["CertificatePending"] = "certificate_pending";
|
|
120
|
+
})(HetznerErrorCode ||= {});
|
|
121
|
+
|
|
122
|
+
class HetznerAPIError extends Error {
|
|
123
|
+
code;
|
|
124
|
+
details;
|
|
125
|
+
constructor(message, code, details) {
|
|
126
|
+
super(message);
|
|
127
|
+
this.code = code;
|
|
128
|
+
this.details = details;
|
|
129
|
+
this.name = "HetznerAPIError";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
class HetznerUnauthorizedError extends HetznerAPIError {
|
|
134
|
+
constructor(message = "Unauthorized: Invalid API token") {
|
|
135
|
+
super(message, "unauthorized" /* Unauthorized */);
|
|
136
|
+
this.name = "HetznerUnauthorizedError";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class HetznerForbiddenError extends HetznerAPIError {
|
|
141
|
+
constructor(message = "Forbidden: Insufficient permissions") {
|
|
142
|
+
super(message, "forbidden" /* Forbidden */);
|
|
143
|
+
this.name = "HetznerForbiddenError";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
class HetznerNotFoundError extends HetznerAPIError {
|
|
148
|
+
constructor(resource, id) {
|
|
149
|
+
super(`${resource} with ID ${id} not found`, "not_found" /* NotFound */, { resource, id });
|
|
150
|
+
this.name = "HetznerNotFoundError";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class HetznerRateLimitError extends HetznerAPIError {
|
|
155
|
+
rateLimitInfo;
|
|
156
|
+
constructor(message = "Rate limit exceeded", rateLimitInfo) {
|
|
157
|
+
super(message, "rate_limit_exceeded" /* RateLimitExceeded */, rateLimitInfo);
|
|
158
|
+
this.rateLimitInfo = rateLimitInfo;
|
|
159
|
+
this.name = "HetznerRateLimitError";
|
|
160
|
+
}
|
|
161
|
+
get resetInMs() {
|
|
162
|
+
if (!this.rateLimitInfo)
|
|
163
|
+
return 60000;
|
|
164
|
+
return Math.max(0, this.rateLimitInfo.reset * 1000 - Date.now());
|
|
165
|
+
}
|
|
166
|
+
get resetTime() {
|
|
167
|
+
if (!this.rateLimitInfo)
|
|
168
|
+
return "unknown";
|
|
169
|
+
return new Date(this.rateLimitInfo.reset * 1000).toISOString();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
class HetznerResourceLockedError extends HetznerAPIError {
|
|
174
|
+
actionInProgress;
|
|
175
|
+
constructor(resource, id, actionInProgress) {
|
|
176
|
+
super(`${resource} ${id} is locked${actionInProgress ? ` by ${actionInProgress}` : ""}`, "locked" /* ResourceLocked */, { resource, id, actionInProgress });
|
|
177
|
+
this.actionInProgress = actionInProgress;
|
|
178
|
+
this.name = "HetznerResourceLockedError";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
class HetznerResourceLimitError extends HetznerAPIError {
|
|
183
|
+
constructor(resource, limit) {
|
|
184
|
+
super(`Resource limit exceeded: ${resource} (limit: ${limit})`, "resource_limit_exceeded" /* ResourceLimitExceeded */, { resource, limit });
|
|
185
|
+
this.name = "HetznerResourceLimitError";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
class HetznerInvalidInputError extends HetznerAPIError {
|
|
190
|
+
fields;
|
|
191
|
+
constructor(message, fields) {
|
|
192
|
+
super(message, "invalid_input" /* InvalidInput */, { fields });
|
|
193
|
+
this.fields = fields;
|
|
194
|
+
this.name = "HetznerInvalidInputError";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
class HetznerConflictError extends HetznerAPIError {
|
|
199
|
+
constructor(message, details) {
|
|
200
|
+
super(message, "conflict" /* Conflict */, details);
|
|
201
|
+
this.name = "HetznerConflictError";
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
class HetznerServiceError extends HetznerAPIError {
|
|
206
|
+
statusCode;
|
|
207
|
+
constructor(message, statusCode) {
|
|
208
|
+
super(message, "service_error" /* ServiceError */, { statusCode });
|
|
209
|
+
this.statusCode = statusCode;
|
|
210
|
+
this.name = "HetznerServiceError";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
class HetznerActionError extends HetznerAPIError {
|
|
215
|
+
actionError;
|
|
216
|
+
actionId;
|
|
217
|
+
constructor(actionError, actionId) {
|
|
218
|
+
super(`Action ${actionId} failed: ${actionError.code} - ${actionError.message}`, actionError.code, { actionError, actionId });
|
|
219
|
+
this.actionError = actionError;
|
|
220
|
+
this.actionId = actionId;
|
|
221
|
+
this.name = "HetznerActionError";
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
class HetznerTimeoutError extends HetznerAPIError {
|
|
226
|
+
lastProgress;
|
|
227
|
+
constructor(actionId, timeout, lastProgress) {
|
|
228
|
+
super(`Action ${actionId} timed out after ${timeout}ms (last progress: ${lastProgress}%)`, "timeout", { actionId, timeout, lastProgress });
|
|
229
|
+
this.lastProgress = lastProgress;
|
|
230
|
+
this.name = "HetznerTimeoutError";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function createHetznerError(statusCode, body) {
|
|
234
|
+
const error = body.error;
|
|
235
|
+
if (!error) {
|
|
236
|
+
return new HetznerServiceError(`HTTP ${statusCode}: ${JSON.stringify(body)}`, statusCode);
|
|
237
|
+
}
|
|
238
|
+
switch (statusCode) {
|
|
239
|
+
case 401:
|
|
240
|
+
return new HetznerUnauthorizedError(error.message);
|
|
241
|
+
case 403:
|
|
242
|
+
return new HetznerForbiddenError(error.message);
|
|
243
|
+
case 404:
|
|
244
|
+
return new HetznerNotFoundError("resource", "unknown");
|
|
245
|
+
case 429:
|
|
246
|
+
return new HetznerRateLimitError(error.message);
|
|
247
|
+
case 400:
|
|
248
|
+
if (error.code === "locked" /* ResourceLocked */) {
|
|
249
|
+
return new HetznerResourceLockedError("resource", "unknown", error.message);
|
|
250
|
+
}
|
|
251
|
+
if (error.code === "invalid_input" /* InvalidInput */) {
|
|
252
|
+
return new HetznerInvalidInputError(error.message);
|
|
253
|
+
}
|
|
254
|
+
return new HetznerInvalidInputError(error.message);
|
|
255
|
+
case 409:
|
|
256
|
+
return new HetznerConflictError(error.message, error.details);
|
|
257
|
+
default:
|
|
258
|
+
if (statusCode >= 500) {
|
|
259
|
+
return new HetznerServiceError(error.message, statusCode);
|
|
260
|
+
}
|
|
261
|
+
return new HetznerAPIError(error.message, error.code, error.details);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function isRetryableError(error) {
|
|
265
|
+
if (error instanceof HetznerRateLimitError)
|
|
266
|
+
return true;
|
|
267
|
+
if (error instanceof HetznerResourceLockedError)
|
|
268
|
+
return true;
|
|
269
|
+
if (error instanceof HetznerServiceError)
|
|
270
|
+
return true;
|
|
271
|
+
if (error instanceof HetznerConflictError)
|
|
272
|
+
return true;
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
function isRateLimitError(error) {
|
|
276
|
+
return error instanceof HetznerRateLimitError;
|
|
277
|
+
}
|
|
278
|
+
function isResourceLockedError(error) {
|
|
279
|
+
return error instanceof HetznerResourceLockedError;
|
|
280
|
+
}
|
|
281
|
+
function calculateRetryDelay(attempt, baseDelay = 1000, maxDelay = 60000) {
|
|
282
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
283
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
284
|
+
return Math.min(maxDelay, delay + jitter);
|
|
285
|
+
}
|
|
286
|
+
function defaultErrorHandler(error) {
|
|
287
|
+
console.error(`[Hetzner API Error] ${error.name}: ${error.message}`);
|
|
288
|
+
if (error.details) {
|
|
289
|
+
console.error("Details:", error.details);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/actions.ts
|
|
294
|
+
class ActionOperations {
|
|
295
|
+
client;
|
|
296
|
+
constructor(client) {
|
|
297
|
+
this.client = client;
|
|
298
|
+
}
|
|
299
|
+
async get(id) {
|
|
300
|
+
const response = await this.client.request(`/actions/${id}`);
|
|
301
|
+
return response.action;
|
|
302
|
+
}
|
|
303
|
+
async list(options) {
|
|
304
|
+
const params = new URLSearchParams;
|
|
305
|
+
if (options?.status)
|
|
306
|
+
params.append("status", options.status);
|
|
307
|
+
if (options?.sort)
|
|
308
|
+
params.append("sort", options.sort);
|
|
309
|
+
if (options?.page)
|
|
310
|
+
params.append("page", options.page.toString());
|
|
311
|
+
if (options?.per_page)
|
|
312
|
+
params.append("per_page", options.per_page.toString());
|
|
313
|
+
if (options?.server_id)
|
|
314
|
+
params.append("server_id", options.server_id.toString());
|
|
315
|
+
const query = params.toString();
|
|
316
|
+
const response = await this.client.request(`/actions${query ? `?${query}` : ""}`);
|
|
317
|
+
return response.actions;
|
|
318
|
+
}
|
|
319
|
+
async poll(actionId, options = {}) {
|
|
320
|
+
return pollAction(this.client, actionId, options);
|
|
321
|
+
}
|
|
322
|
+
async pollMany(actionIds, options = {}) {
|
|
323
|
+
return pollActions(this.client, actionIds, options);
|
|
324
|
+
}
|
|
325
|
+
async pollManyDetailed(actionIds, options = {}) {
|
|
326
|
+
return pollActionsDetailed(this.client, actionIds, options);
|
|
327
|
+
}
|
|
328
|
+
async waitFor(id, options) {
|
|
329
|
+
return waitForAction(this.client, id, options);
|
|
330
|
+
}
|
|
331
|
+
async waitForMany(ids, options) {
|
|
332
|
+
return waitForMultipleActions(this.client, ids, options);
|
|
333
|
+
}
|
|
334
|
+
async batchCheck(ids) {
|
|
335
|
+
return batchCheckActions(this.client, ids);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
var ACTION_TIMEOUTS = {
|
|
339
|
+
["start_server" /* StartServer */]: 60000,
|
|
340
|
+
["stop_server" /* StopServer */]: 60000,
|
|
341
|
+
["reboot_server" /* RebootServer */]: 120000,
|
|
342
|
+
["poweroff" /* Poweroff */]: 60000,
|
|
343
|
+
["shutdown_server" /* ShutdownServer */]: 60000,
|
|
344
|
+
["reset_server" /* ResetServer */]: 60000,
|
|
345
|
+
["create_server" /* CreateServer */]: 300000,
|
|
346
|
+
["delete_server" /* DeleteServer */]: 180000,
|
|
347
|
+
["change_server_type" /* ChangeServerType */]: 600000,
|
|
348
|
+
["change_dns_ptr" /* ChangeDnsPtr */]: 30000,
|
|
349
|
+
["rebuild_server" /* RebuildServer */]: 900000,
|
|
350
|
+
["create_image" /* CreateImage */]: 1800000,
|
|
351
|
+
["enable_rescue" /* EnableRescue */]: 120000,
|
|
352
|
+
["disable_rescue" /* DisableRescue */]: 60000,
|
|
353
|
+
["create_volume" /* CreateVolume */]: 300000,
|
|
354
|
+
["delete_volume" /* DeleteVolume */]: 180000,
|
|
355
|
+
["attach_volume" /* AttachVolume */]: 120000,
|
|
356
|
+
["detach_volume" /* DetachVolume */]: 60000,
|
|
357
|
+
["resize_volume" /* ResizeVolume */]: 600000,
|
|
358
|
+
["add_subnet" /* AddSubnet */]: 60000,
|
|
359
|
+
["delete_subnet" /* DeleteSubnet */]: 60000,
|
|
360
|
+
["add_route" /* AddRoute */]: 60000,
|
|
361
|
+
["delete_route" /* DeleteRoute */]: 60000,
|
|
362
|
+
["change_ip_range" /* ChangeIpRange */]: 120000,
|
|
363
|
+
["attach_to_network" /* AttachToNetwork */]: 60000,
|
|
364
|
+
["detach_from_network" /* DetachFromNetwork */]: 60000,
|
|
365
|
+
["assign_floating_ip" /* AssignFloatingIp */]: 60000,
|
|
366
|
+
["unassign_floating_ip" /* UnassignFloatingIp */]: 60000,
|
|
367
|
+
["create_load_balancer" /* CreateLoadBalancer */]: 300000,
|
|
368
|
+
["delete_load_balancer" /* DeleteLoadBalancer */]: 180000,
|
|
369
|
+
["add_target" /* AddTarget */]: 60000,
|
|
370
|
+
["remove_target" /* RemoveTarget */]: 60000,
|
|
371
|
+
["add_service" /* AddService */]: 60000,
|
|
372
|
+
["update_service" /* UpdateService */]: 60000,
|
|
373
|
+
["delete_service" /* DeleteService */]: 60000,
|
|
374
|
+
["load_balancer_attach_to_network" /* LoadBalancerAttachToNetwork */]: 60000,
|
|
375
|
+
["load_balancer_detach_from_network" /* LoadBalancerDetachFromNetwork */]: 60000,
|
|
376
|
+
["change_algorithm" /* ChangeAlgorithm */]: 60000,
|
|
377
|
+
["change_type" /* ChangeType */]: 60000,
|
|
378
|
+
["issue_certificate" /* IssueCertificate */]: 600000,
|
|
379
|
+
["retry_certificate" /* RetryCertificate */]: 600000,
|
|
380
|
+
["set_firewall_rules" /* SetFirewallRules */]: 60000,
|
|
381
|
+
["apply_firewall" /* ApplyFirewall */]: 120000,
|
|
382
|
+
["remove_firewall" /* RemoveFirewall */]: 60000,
|
|
383
|
+
["floating_ip_change_dns_ptr" /* FloatingIpChangeDnsPtr */]: 30000,
|
|
384
|
+
["enable_backup" /* EnableBackup */]: 60000,
|
|
385
|
+
["disable_backup" /* DisableBackup */]: 60000,
|
|
386
|
+
["change_protection" /* ChangeProtection */]: 30000,
|
|
387
|
+
["volume_change_protection" /* VolumeChangeProtection */]: 30000,
|
|
388
|
+
["network_change_protection" /* NetworkChangeProtection */]: 30000,
|
|
389
|
+
["floating_ip_change_protection" /* FloatingIpChangeProtection */]: 30000,
|
|
390
|
+
["load_balancer_change_protection" /* LoadBalancerChangeProtection */]: 30000,
|
|
391
|
+
["firewall_change_protection" /* FirewallChangeProtection */]: 30000,
|
|
392
|
+
["image_change_protection" /* ImageChangeProtection */]: 30000,
|
|
393
|
+
["change_alias_ips" /* ChangeAliasIps */]: 60000
|
|
394
|
+
};
|
|
395
|
+
function getActionTimeout(command) {
|
|
396
|
+
return ACTION_TIMEOUTS[command] || 300000;
|
|
397
|
+
}
|
|
398
|
+
var DEFAULT_POLLING_OPTIONS = {
|
|
399
|
+
pollInterval: 2000,
|
|
400
|
+
maxRetries: 60,
|
|
401
|
+
timeout: 300000,
|
|
402
|
+
onProgress: () => {},
|
|
403
|
+
onComplete: () => {},
|
|
404
|
+
onError: () => {},
|
|
405
|
+
onRetry: () => {},
|
|
406
|
+
signal: undefined,
|
|
407
|
+
adaptive: false,
|
|
408
|
+
concurrency: 5
|
|
409
|
+
};
|
|
410
|
+
async function pollAction(client, actionId, options = {}) {
|
|
411
|
+
const opts = { ...DEFAULT_POLLING_OPTIONS, ...options };
|
|
412
|
+
const startTime = Date.now();
|
|
413
|
+
let lastProgress = 0;
|
|
414
|
+
let lastError = null;
|
|
415
|
+
let attempt = 0;
|
|
416
|
+
if (opts.signal?.aborted) {
|
|
417
|
+
throw new Error("Polling aborted before start");
|
|
418
|
+
}
|
|
419
|
+
const abortListener = () => {
|
|
420
|
+
throw new Error("Polling aborted");
|
|
421
|
+
};
|
|
422
|
+
opts.signal?.addEventListener("abort", abortListener);
|
|
423
|
+
try {
|
|
424
|
+
while (attempt < opts.maxRetries) {
|
|
425
|
+
const elapsed = Date.now() - startTime;
|
|
426
|
+
if (elapsed > opts.timeout) {
|
|
427
|
+
throw new HetznerTimeoutError(actionId, opts.timeout, lastProgress);
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
const response = await client.request(`/actions/${actionId}`);
|
|
431
|
+
const action = response.action;
|
|
432
|
+
attempt++;
|
|
433
|
+
if (action.progress !== lastProgress) {
|
|
434
|
+
lastProgress = action.progress;
|
|
435
|
+
opts.onProgress(action);
|
|
436
|
+
}
|
|
437
|
+
if (action.status === "success") {
|
|
438
|
+
opts.onComplete(action);
|
|
439
|
+
return action;
|
|
440
|
+
}
|
|
441
|
+
if (action.status === "error") {
|
|
442
|
+
const error = new HetznerActionError(action.error, actionId);
|
|
443
|
+
opts.onError(error, action);
|
|
444
|
+
throw error;
|
|
445
|
+
}
|
|
446
|
+
let waitTime = opts.pollInterval;
|
|
447
|
+
if (opts.adaptive) {
|
|
448
|
+
waitTime = getAdaptivePollInterval(action.progress);
|
|
449
|
+
}
|
|
450
|
+
await sleep(waitTime);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
if (isRetryableError(error)) {
|
|
453
|
+
const delay = calculateRetryDelay(attempt);
|
|
454
|
+
opts.onRetry(attempt, delay);
|
|
455
|
+
if (opts.signal?.aborted) {
|
|
456
|
+
throw new Error("Polling aborted during retry");
|
|
457
|
+
}
|
|
458
|
+
await sleep(delay);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
throw error;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
throw new HetznerTimeoutError(actionId, opts.timeout, lastProgress);
|
|
465
|
+
} finally {
|
|
466
|
+
opts.signal?.removeEventListener("abort", abortListener);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async function pollActions(client, actionIds, options = {}) {
|
|
470
|
+
const concurrency = options.concurrency ?? DEFAULT_POLLING_OPTIONS.concurrency;
|
|
471
|
+
if (concurrency >= actionIds.length) {
|
|
472
|
+
const promises = actionIds.map((id) => pollAction(client, id, options));
|
|
473
|
+
return Promise.all(promises);
|
|
474
|
+
}
|
|
475
|
+
const results = new Array(actionIds.length);
|
|
476
|
+
let currentIndex = 0;
|
|
477
|
+
const processBatch = async () => {
|
|
478
|
+
while (currentIndex < actionIds.length) {
|
|
479
|
+
const batchStart = currentIndex;
|
|
480
|
+
const batchEnd = Math.min(currentIndex + concurrency, actionIds.length);
|
|
481
|
+
const batchIds = actionIds.slice(batchStart, batchEnd);
|
|
482
|
+
currentIndex = batchEnd;
|
|
483
|
+
const batchResults = await Promise.all(batchIds.map((id, i) => pollAction(client, id, options)));
|
|
484
|
+
batchResults.forEach((result, i) => {
|
|
485
|
+
results[batchStart + i] = result;
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
await processBatch();
|
|
490
|
+
return results;
|
|
491
|
+
}
|
|
492
|
+
async function pollActionsDetailed(client, actionIds, options = {}) {
|
|
493
|
+
const startTime = Date.now();
|
|
494
|
+
const results = new Map;
|
|
495
|
+
let successful = 0;
|
|
496
|
+
let failed = 0;
|
|
497
|
+
const wrappedOptions = {
|
|
498
|
+
...options,
|
|
499
|
+
onComplete: (action) => {
|
|
500
|
+
results.set(action.id, {
|
|
501
|
+
success: true,
|
|
502
|
+
action,
|
|
503
|
+
attempts: 0,
|
|
504
|
+
elapsed: Date.now() - startTime
|
|
505
|
+
});
|
|
506
|
+
successful++;
|
|
507
|
+
options.onComplete?.(action);
|
|
508
|
+
},
|
|
509
|
+
onError: (error, action) => {
|
|
510
|
+
results.set(action.id, {
|
|
511
|
+
success: false,
|
|
512
|
+
action,
|
|
513
|
+
error,
|
|
514
|
+
attempts: 0,
|
|
515
|
+
elapsed: Date.now() - startTime
|
|
516
|
+
});
|
|
517
|
+
failed++;
|
|
518
|
+
options.onError?.(error, action);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
try {
|
|
522
|
+
await pollActions(client, actionIds, wrappedOptions);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
console.warn("Some actions failed during batch polling:", error);
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
results,
|
|
528
|
+
successful,
|
|
529
|
+
failed,
|
|
530
|
+
elapsed: Date.now() - startTime
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
async function waitForAction(client, actionId, options = {}) {
|
|
534
|
+
const opts = { ...DEFAULT_POLLING_OPTIONS, ...options };
|
|
535
|
+
const startTime = Date.now();
|
|
536
|
+
let lastProgress = 0;
|
|
537
|
+
let lastError = null;
|
|
538
|
+
for (let attempt = 0;attempt < opts.maxRetries; attempt++) {
|
|
539
|
+
if (Date.now() - startTime > opts.timeout) {
|
|
540
|
+
throw new HetznerTimeoutError(actionId, opts.timeout, lastProgress);
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
const action = await client.request(`/actions/${actionId}`);
|
|
544
|
+
if (action.action.progress !== lastProgress) {
|
|
545
|
+
lastProgress = action.action.progress;
|
|
546
|
+
opts.onProgress(action.action);
|
|
547
|
+
}
|
|
548
|
+
if (action.action.status === "success") {
|
|
549
|
+
return action.action;
|
|
550
|
+
}
|
|
551
|
+
if (action.action.status === "error") {
|
|
552
|
+
throw new HetznerActionError(action.action.error, actionId);
|
|
553
|
+
}
|
|
554
|
+
await sleep(opts.pollInterval);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
if (isRetryableError(error)) {
|
|
557
|
+
const delay = calculateRetryDelay(attempt);
|
|
558
|
+
console.warn(`Retrying action ${actionId} after ${delay}ms (attempt ${attempt + 1}/${opts.maxRetries})`);
|
|
559
|
+
await sleep(delay);
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
throw new HetznerTimeoutError(actionId, opts.timeout, lastProgress);
|
|
566
|
+
}
|
|
567
|
+
async function waitForMultipleActions(client, actionIds, options = {}) {
|
|
568
|
+
const promises = actionIds.map((id) => waitForAction(client, id, options));
|
|
569
|
+
return Promise.all(promises);
|
|
570
|
+
}
|
|
571
|
+
async function waitForMultipleActionsWithLimit(client, actionIds, concurrency = 5, options = {}) {
|
|
572
|
+
const results = [];
|
|
573
|
+
const chunks = [];
|
|
574
|
+
for (let i = 0;i < actionIds.length; i += concurrency) {
|
|
575
|
+
chunks.push(actionIds.slice(i, i + concurrency));
|
|
576
|
+
}
|
|
577
|
+
for (const chunk of chunks) {
|
|
578
|
+
const chunkResults = await waitForMultipleActions(client, chunk, options);
|
|
579
|
+
results.push(...chunkResults);
|
|
580
|
+
}
|
|
581
|
+
return results;
|
|
582
|
+
}
|
|
583
|
+
async function batchCheckActions(client, actionIds) {
|
|
584
|
+
const MAX_BATCH_SIZE = 50;
|
|
585
|
+
const results = new Map;
|
|
586
|
+
for (let i = 0;i < actionIds.length; i += MAX_BATCH_SIZE) {
|
|
587
|
+
const batch = actionIds.slice(i, i + MAX_BATCH_SIZE);
|
|
588
|
+
const idParams = batch.map((id) => `id=${id}`).join("&");
|
|
589
|
+
const response = await client.request(`/actions?${idParams}`);
|
|
590
|
+
for (const action of response.actions) {
|
|
591
|
+
results.set(action.id, action);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return results;
|
|
595
|
+
}
|
|
596
|
+
function isActionRunning(action) {
|
|
597
|
+
return action.status === "running";
|
|
598
|
+
}
|
|
599
|
+
function isActionSuccess(action) {
|
|
600
|
+
return action.status === "success";
|
|
601
|
+
}
|
|
602
|
+
function isActionError(action) {
|
|
603
|
+
return action.status === "error";
|
|
604
|
+
}
|
|
605
|
+
function formatActionProgress(action) {
|
|
606
|
+
const command = action.command.replace(/_/g, " ");
|
|
607
|
+
const status = action.status.toUpperCase();
|
|
608
|
+
return `${status}: ${command} (${action.progress}%)`;
|
|
609
|
+
}
|
|
610
|
+
function getActionDescription(command) {
|
|
611
|
+
const descriptions = {
|
|
612
|
+
["create_server" /* CreateServer */]: "Creating server",
|
|
613
|
+
["delete_server" /* DeleteServer */]: "Deleting server",
|
|
614
|
+
["start_server" /* StartServer */]: "Starting server",
|
|
615
|
+
["stop_server" /* StopServer */]: "Stopping server",
|
|
616
|
+
["reboot_server" /* RebootServer */]: "Rebooting server",
|
|
617
|
+
["reset_server" /* ResetServer */]: "Resetting server",
|
|
618
|
+
["shutdown_server" /* ShutdownServer */]: "Shutting down server",
|
|
619
|
+
["poweroff" /* Poweroff */]: "Cutting power to server",
|
|
620
|
+
["change_server_type" /* ChangeServerType */]: "Changing server type",
|
|
621
|
+
["rebuild_server" /* RebuildServer */]: "Rebuilding server",
|
|
622
|
+
["enable_backup" /* EnableBackup */]: "Enabling backups",
|
|
623
|
+
["disable_backup" /* DisableBackup */]: "Disabling backups",
|
|
624
|
+
["create_image" /* CreateImage */]: "Creating image",
|
|
625
|
+
["change_dns_ptr" /* ChangeDnsPtr */]: "Changing reverse DNS",
|
|
626
|
+
["attach_to_network" /* AttachToNetwork */]: "Attaching to network",
|
|
627
|
+
["detach_from_network" /* DetachFromNetwork */]: "Detaching from network",
|
|
628
|
+
["change_alias_ips" /* ChangeAliasIps */]: "Changing alias IPs",
|
|
629
|
+
["enable_rescue" /* EnableRescue */]: "Enabling rescue mode",
|
|
630
|
+
["disable_rescue" /* DisableRescue */]: "Disabling rescue mode",
|
|
631
|
+
["change_protection" /* ChangeProtection */]: "Changing protection",
|
|
632
|
+
["create_volume" /* CreateVolume */]: "Creating volume",
|
|
633
|
+
["delete_volume" /* DeleteVolume */]: "Deleting volume",
|
|
634
|
+
["attach_volume" /* AttachVolume */]: "Attaching volume",
|
|
635
|
+
["detach_volume" /* DetachVolume */]: "Detaching volume",
|
|
636
|
+
["resize_volume" /* ResizeVolume */]: "Resizing volume",
|
|
637
|
+
["volume_change_protection" /* VolumeChangeProtection */]: "Changing volume protection",
|
|
638
|
+
["add_subnet" /* AddSubnet */]: "Adding subnet",
|
|
639
|
+
["delete_subnet" /* DeleteSubnet */]: "Deleting subnet",
|
|
640
|
+
["add_route" /* AddRoute */]: "Adding route",
|
|
641
|
+
["delete_route" /* DeleteRoute */]: "Deleting route",
|
|
642
|
+
["change_ip_range" /* ChangeIpRange */]: "Changing IP range",
|
|
643
|
+
["network_change_protection" /* NetworkChangeProtection */]: "Changing network protection",
|
|
644
|
+
["assign_floating_ip" /* AssignFloatingIp */]: "Assigning floating IP",
|
|
645
|
+
["unassign_floating_ip" /* UnassignFloatingIp */]: "Unassigning floating IP",
|
|
646
|
+
["floating_ip_change_dns_ptr" /* FloatingIpChangeDnsPtr */]: "Changing floating IP DNS",
|
|
647
|
+
["floating_ip_change_protection" /* FloatingIpChangeProtection */]: "Changing floating IP protection",
|
|
648
|
+
["create_load_balancer" /* CreateLoadBalancer */]: "Creating load balancer",
|
|
649
|
+
["delete_load_balancer" /* DeleteLoadBalancer */]: "Deleting load balancer",
|
|
650
|
+
["add_target" /* AddTarget */]: "Adding target to load balancer",
|
|
651
|
+
["remove_target" /* RemoveTarget */]: "Removing target from load balancer",
|
|
652
|
+
["add_service" /* AddService */]: "Adding service to load balancer",
|
|
653
|
+
["update_service" /* UpdateService */]: "Updating load balancer service",
|
|
654
|
+
["delete_service" /* DeleteService */]: "Deleting load balancer service",
|
|
655
|
+
["load_balancer_attach_to_network" /* LoadBalancerAttachToNetwork */]: "Attaching load balancer to network",
|
|
656
|
+
["load_balancer_detach_from_network" /* LoadBalancerDetachFromNetwork */]: "Detaching load balancer from network",
|
|
657
|
+
["change_algorithm" /* ChangeAlgorithm */]: "Changing load balancer algorithm",
|
|
658
|
+
["change_type" /* ChangeType */]: "Changing load balancer type",
|
|
659
|
+
["load_balancer_change_protection" /* LoadBalancerChangeProtection */]: "Changing load balancer protection",
|
|
660
|
+
["issue_certificate" /* IssueCertificate */]: "Issuing certificate",
|
|
661
|
+
["retry_certificate" /* RetryCertificate */]: "Retrying certificate",
|
|
662
|
+
["set_firewall_rules" /* SetFirewallRules */]: "Setting firewall rules",
|
|
663
|
+
["apply_firewall" /* ApplyFirewall */]: "Applying firewall",
|
|
664
|
+
["remove_firewall" /* RemoveFirewall */]: "Removing firewall",
|
|
665
|
+
["firewall_change_protection" /* FirewallChangeProtection */]: "Changing firewall protection",
|
|
666
|
+
["image_change_protection" /* ImageChangeProtection */]: "Changing image protection"
|
|
667
|
+
};
|
|
668
|
+
return descriptions[command] || command.replace(/_/g, " ");
|
|
669
|
+
}
|
|
670
|
+
function getPollInterval(command) {
|
|
671
|
+
const intervals = {
|
|
672
|
+
["start_server" /* StartServer */]: 5000,
|
|
673
|
+
["stop_server" /* StopServer */]: 5000,
|
|
674
|
+
["reboot_server" /* RebootServer */]: 1e4,
|
|
675
|
+
["poweroff" /* Poweroff */]: 5000,
|
|
676
|
+
["shutdown_server" /* ShutdownServer */]: 5000,
|
|
677
|
+
["create_server" /* CreateServer */]: 15000,
|
|
678
|
+
["create_volume" /* CreateVolume */]: 1e4,
|
|
679
|
+
["attach_volume" /* AttachVolume */]: 8000,
|
|
680
|
+
["detach_volume" /* DetachVolume */]: 5000,
|
|
681
|
+
["rebuild_server" /* RebuildServer */]: 30000,
|
|
682
|
+
["create_image" /* CreateImage */]: 60000,
|
|
683
|
+
["issue_certificate" /* IssueCertificate */]: 30000
|
|
684
|
+
};
|
|
685
|
+
return intervals[command] || 1e4;
|
|
686
|
+
}
|
|
687
|
+
function getAdaptivePollInterval(progress) {
|
|
688
|
+
if (progress < 10)
|
|
689
|
+
return 2000;
|
|
690
|
+
if (progress < 50)
|
|
691
|
+
return 5000;
|
|
692
|
+
if (progress < 90)
|
|
693
|
+
return 1e4;
|
|
694
|
+
return 15000;
|
|
695
|
+
}
|
|
696
|
+
async function waitForActionAdaptive(client, actionId, command, options = {}) {
|
|
697
|
+
const startTime = Date.now();
|
|
698
|
+
const timeout = options.timeout ?? getActionTimeout(command);
|
|
699
|
+
let lastProgress = -1;
|
|
700
|
+
while (Date.now() - startTime < timeout) {
|
|
701
|
+
const action = await client.request(`/actions/${actionId}`);
|
|
702
|
+
if (action.action.progress !== lastProgress) {
|
|
703
|
+
lastProgress = action.action.progress;
|
|
704
|
+
options.onProgress?.(action.action);
|
|
705
|
+
}
|
|
706
|
+
if (action.action.status === "success") {
|
|
707
|
+
return action.action;
|
|
708
|
+
}
|
|
709
|
+
if (action.action.status === "error") {
|
|
710
|
+
throw new HetznerActionError(action.action.error, actionId);
|
|
711
|
+
}
|
|
712
|
+
const interval = getAdaptivePollInterval(action.action.progress);
|
|
713
|
+
await sleep(interval);
|
|
714
|
+
}
|
|
715
|
+
throw new HetznerTimeoutError(actionId, timeout, lastProgress);
|
|
716
|
+
}
|
|
717
|
+
function parseRateLimitHeaders(headers) {
|
|
718
|
+
const limit = headers.get("RateLimit-Limit");
|
|
719
|
+
const remaining = headers.get("RateLimit-Remaining");
|
|
720
|
+
const reset = headers.get("RateLimit-Reset");
|
|
721
|
+
if (!limit || !remaining || !reset) {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
limit: parseInt(limit, 10),
|
|
726
|
+
remaining: parseInt(remaining, 10),
|
|
727
|
+
reset: parseInt(reset, 10)
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function isRateLimitLow(info, threshold = 100) {
|
|
731
|
+
return info.remaining < threshold;
|
|
732
|
+
}
|
|
733
|
+
function formatRateLimitStatus(info) {
|
|
734
|
+
const resetDate = new Date(info.reset * 1000);
|
|
735
|
+
const remaining = info.remaining;
|
|
736
|
+
const limit = info.limit;
|
|
737
|
+
const percentage = (remaining / limit * 100).toFixed(1);
|
|
738
|
+
return `${remaining}/${limit} (${percentage}%) - resets at ${resetDate.toISOString()}`;
|
|
739
|
+
}
|
|
740
|
+
async function waitForRateLimitReset(info) {
|
|
741
|
+
const resetTime = info.reset * 1000;
|
|
742
|
+
const now = Date.now();
|
|
743
|
+
const waitTime = Math.max(0, resetTime - now);
|
|
744
|
+
if (waitTime > 0) {
|
|
745
|
+
const waitSeconds = Math.ceil(waitTime / 1000);
|
|
746
|
+
console.log(`Rate limit exhausted. Waiting ${waitSeconds}s for reset...`);
|
|
747
|
+
await sleep(waitTime);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function sleep(ms) {
|
|
751
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
752
|
+
}
|
|
753
|
+
function createProgressLogger(prefix = "Action") {
|
|
754
|
+
return (action) => {
|
|
755
|
+
console.log(`[${prefix}] ${getActionDescription(action.command)}: ${action.progress}%`);
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
export {
|
|
759
|
+
waitForRateLimitReset,
|
|
760
|
+
waitForMultipleActionsWithLimit,
|
|
761
|
+
waitForMultipleActions,
|
|
762
|
+
waitForActionAdaptive,
|
|
763
|
+
waitForAction,
|
|
764
|
+
pollActionsDetailed,
|
|
765
|
+
pollActions,
|
|
766
|
+
pollAction,
|
|
767
|
+
parseRateLimitHeaders,
|
|
768
|
+
isRateLimitLow,
|
|
769
|
+
isActionSuccess,
|
|
770
|
+
isActionRunning,
|
|
771
|
+
isActionError,
|
|
772
|
+
getPollInterval,
|
|
773
|
+
getAdaptivePollInterval,
|
|
774
|
+
getActionTimeout,
|
|
775
|
+
getActionDescription,
|
|
776
|
+
formatRateLimitStatus,
|
|
777
|
+
formatActionProgress,
|
|
778
|
+
createProgressLogger,
|
|
779
|
+
batchCheckActions,
|
|
780
|
+
ActionOperations,
|
|
781
|
+
ACTION_TIMEOUTS
|
|
782
|
+
};
|