@archal/cli 0.2.0 → 0.3.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/README.md +24 -11
- package/dist/api-client-D7SCA64V.js +23 -0
- package/dist/api-client-DI7R3H4C.js +21 -0
- package/dist/api-client-EMMBIJU7.js +23 -0
- package/dist/api-client-VYQMFDLN.js +23 -0
- package/dist/api-client-WN45C63M.js +23 -0
- package/dist/api-client-ZOCVG6CC.js +21 -0
- package/dist/api-client-ZUMDL3TP.js +23 -0
- package/dist/chunk-3EH6CG2H.js +561 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-4FTU232H.js +191 -0
- package/dist/chunk-4LM2CKUI.js +561 -0
- package/dist/chunk-A6WOU5RO.js +214 -0
- package/dist/chunk-AXLDC4PC.js +561 -0
- package/dist/chunk-NZEPQ6IZ.js +83 -0
- package/dist/chunk-PGMDLZW5.js +561 -0
- package/dist/chunk-SVGN2AFT.js +148 -0
- package/dist/chunk-UOJHYCMX.js +144 -0
- package/dist/chunk-VYCADG5E.js +189 -0
- package/dist/chunk-WZXES7XO.js +136 -0
- package/dist/chunk-XJOKVFOL.js +561 -0
- package/dist/chunk-XSO7ETSM.js +561 -0
- package/dist/chunk-YDGWON57.js +561 -0
- package/dist/index.js +1900 -647
- package/dist/login-4RNNR4YA.js +7 -0
- package/dist/login-CQ2DRBRU.js +7 -0
- package/dist/login-LOTTPY7G.js +7 -0
- package/dist/login-MBCG3N5P.js +7 -0
- package/dist/login-MP6YLOEA.js +7 -0
- package/dist/login-SGLSVIZZ.js +7 -0
- package/dist/login-TFBKIZ7I.js +7 -0
- package/package.json +4 -5
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// src/api-client.ts
|
|
2
|
+
function normalizeBaseUrl(value, fallback) {
|
|
3
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
4
|
+
const normalized = trimmed.endsWith("/api") ? trimmed.slice(0, -4) : trimmed;
|
|
5
|
+
return normalized.length > 0 ? normalized : fallback;
|
|
6
|
+
}
|
|
7
|
+
var DEFAULT_BASE_URL = "https://archal.ai";
|
|
8
|
+
var AUTH_BASE_URL = normalizeBaseUrl(process.env["ARCHAL_AUTH_URL"] ?? DEFAULT_BASE_URL, DEFAULT_BASE_URL);
|
|
9
|
+
var API_BASE_URL = normalizeBaseUrl(process.env["ARCHAL_API_URL"] ?? AUTH_BASE_URL, AUTH_BASE_URL);
|
|
10
|
+
var REQUEST_TIMEOUT_MS = 8e3;
|
|
11
|
+
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
12
|
+
var RETRYABLE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
13
|
+
"ECONNABORTED",
|
|
14
|
+
"ECONNREFUSED",
|
|
15
|
+
"ECONNRESET",
|
|
16
|
+
"ENETDOWN",
|
|
17
|
+
"ENETUNREACH",
|
|
18
|
+
"ENOTFOUND",
|
|
19
|
+
"ETIMEDOUT"
|
|
20
|
+
]);
|
|
21
|
+
function parseBoundedInt(value, fallback, min, max) {
|
|
22
|
+
const parsed = Number(value);
|
|
23
|
+
if (!Number.isInteger(parsed)) {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
if (parsed < min || parsed > max) {
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
var MAX_RETRIES = parseBoundedInt(process.env["ARCHAL_API_MAX_RETRIES"], 3, 0, 10);
|
|
32
|
+
var RETRY_BASE_DELAY_MS = parseBoundedInt(process.env["ARCHAL_API_RETRY_BASE_MS"], 250, 25, 1e4);
|
|
33
|
+
var RETRY_MAX_DELAY_MS = parseBoundedInt(process.env["ARCHAL_API_RETRY_MAX_MS"], 3e3, RETRY_BASE_DELAY_MS, 2e4);
|
|
34
|
+
function sleep(ms) {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
function retryDelayMs(attempt, retryAfter) {
|
|
38
|
+
if (retryAfter) {
|
|
39
|
+
const retrySeconds = Number(retryAfter);
|
|
40
|
+
if (Number.isFinite(retrySeconds) && retrySeconds > 0) {
|
|
41
|
+
return Math.min(RETRY_MAX_DELAY_MS, Math.floor(retrySeconds * 1e3));
|
|
42
|
+
}
|
|
43
|
+
const retryAtMs = Date.parse(retryAfter);
|
|
44
|
+
if (Number.isFinite(retryAtMs)) {
|
|
45
|
+
const deltaMs = retryAtMs - Date.now();
|
|
46
|
+
if (deltaMs > 0) {
|
|
47
|
+
return Math.min(RETRY_MAX_DELAY_MS, Math.floor(deltaMs));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const exponential = Math.min(
|
|
52
|
+
RETRY_MAX_DELAY_MS,
|
|
53
|
+
RETRY_BASE_DELAY_MS * Math.pow(2, Math.max(0, attempt - 1))
|
|
54
|
+
);
|
|
55
|
+
const jitter = Math.floor(Math.random() * RETRY_BASE_DELAY_MS);
|
|
56
|
+
return Math.min(RETRY_MAX_DELAY_MS, exponential + jitter);
|
|
57
|
+
}
|
|
58
|
+
function normalizeErrorCode(value) {
|
|
59
|
+
if (typeof value !== "string") {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return value.toUpperCase();
|
|
63
|
+
}
|
|
64
|
+
function isRetryableNetworkError(error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
const normalized = message.toLowerCase();
|
|
67
|
+
if (normalized.includes("fetch") || normalized.includes("timeout") || normalized.includes("abort") || normalized.includes("econnrefused") || normalized.includes("enotfound") || normalized.includes("econnreset") || normalized.includes("temporarily unavailable")) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (!error || typeof error !== "object") {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const topLevel = error;
|
|
74
|
+
const directCode = normalizeErrorCode(topLevel.code) ?? normalizeErrorCode(topLevel.errno);
|
|
75
|
+
if (directCode && RETRYABLE_NETWORK_CODES.has(directCode)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (!topLevel.cause || typeof topLevel.cause !== "object") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const cause = topLevel.cause;
|
|
82
|
+
const causeCode = normalizeErrorCode(cause.code) ?? normalizeErrorCode(cause.errno);
|
|
83
|
+
return Boolean(causeCode && RETRYABLE_NETWORK_CODES.has(causeCode));
|
|
84
|
+
}
|
|
85
|
+
function resolveBaseUrl(path) {
|
|
86
|
+
return path.startsWith("/auth/") ? AUTH_BASE_URL : API_BASE_URL;
|
|
87
|
+
}
|
|
88
|
+
async function request(method, path, token, body) {
|
|
89
|
+
const url = `${resolveBaseUrl(path)}${path}`;
|
|
90
|
+
const headers = {
|
|
91
|
+
"content-type": "application/json",
|
|
92
|
+
"user-agent": "archal-cli/0.1.0"
|
|
93
|
+
};
|
|
94
|
+
if (token) {
|
|
95
|
+
headers["authorization"] = `Bearer ${token}`;
|
|
96
|
+
}
|
|
97
|
+
const retriesAllowed = method !== "POST" || path.endsWith("/evidence/finalize");
|
|
98
|
+
const attempts = retriesAllowed ? MAX_RETRIES + 1 : 1;
|
|
99
|
+
let lastError = "request failed";
|
|
100
|
+
let lastOffline = false;
|
|
101
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(url, {
|
|
104
|
+
method,
|
|
105
|
+
headers,
|
|
106
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
107
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
108
|
+
});
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
const text = await response.text().catch(() => "");
|
|
111
|
+
const retryable = retriesAllowed && attempt < attempts && RETRYABLE_STATUS_CODES.has(response.status);
|
|
112
|
+
if (retryable) {
|
|
113
|
+
await sleep(retryDelayMs(attempt, response.headers.get("retry-after")));
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
return { ok: false, error: `HTTP ${response.status}: ${text}`, offline: false };
|
|
117
|
+
}
|
|
118
|
+
if (response.status === 204) {
|
|
119
|
+
return { ok: true, data: void 0 };
|
|
120
|
+
}
|
|
121
|
+
const data = await response.json();
|
|
122
|
+
return { ok: true, data };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const offline = isRetryableNetworkError(error);
|
|
125
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
126
|
+
lastOffline = offline;
|
|
127
|
+
if (retriesAllowed && offline && attempt < attempts) {
|
|
128
|
+
await sleep(retryDelayMs(attempt, null));
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
return { ok: false, error: lastError, offline };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { ok: false, error: lastError, offline: lastOffline };
|
|
135
|
+
}
|
|
136
|
+
function fetchAuthMe(token) {
|
|
137
|
+
return request("GET", "/auth/me", token);
|
|
138
|
+
}
|
|
139
|
+
function fetchTwinsCatalog(token) {
|
|
140
|
+
return request("GET", "/api/twins", token);
|
|
141
|
+
}
|
|
142
|
+
function updateTwinSelection(token, twinIds) {
|
|
143
|
+
return request("POST", "/api/twins/select", token, { twinIds });
|
|
144
|
+
}
|
|
145
|
+
function startSession(token, body) {
|
|
146
|
+
return request("POST", "/api/sessions", token, body);
|
|
147
|
+
}
|
|
148
|
+
function endSession(token, sessionId) {
|
|
149
|
+
return request("DELETE", `/api/sessions/${encodeURIComponent(sessionId)}`, token);
|
|
150
|
+
}
|
|
151
|
+
function getSessionHealth(token, sessionId) {
|
|
152
|
+
return request(
|
|
153
|
+
"GET",
|
|
154
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/health`,
|
|
155
|
+
token
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
function getSessionStatus(token, sessionId) {
|
|
159
|
+
return request(
|
|
160
|
+
"GET",
|
|
161
|
+
`/api/sessions/${encodeURIComponent(sessionId)}`,
|
|
162
|
+
token
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
function finalizeSessionEvidence(token, sessionId, body) {
|
|
166
|
+
return request(
|
|
167
|
+
"POST",
|
|
168
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/evidence/finalize`,
|
|
169
|
+
token,
|
|
170
|
+
body ?? {}
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
function getSessionEvidence(token, sessionId) {
|
|
174
|
+
return request(
|
|
175
|
+
"GET",
|
|
176
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/evidence`,
|
|
177
|
+
token
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export {
|
|
182
|
+
fetchAuthMe,
|
|
183
|
+
fetchTwinsCatalog,
|
|
184
|
+
updateTwinSelection,
|
|
185
|
+
startSession,
|
|
186
|
+
endSession,
|
|
187
|
+
getSessionHealth,
|
|
188
|
+
getSessionStatus,
|
|
189
|
+
finalizeSessionEvidence,
|
|
190
|
+
getSessionEvidence
|
|
191
|
+
};
|