@sendmux/cli 1.6.0 → 1.7.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/dist/agent-auth.d.ts.map +1 -1
- package/dist/agent-auth.js +51 -7
- package/dist/operation-runner.d.ts.map +1 -1
- package/dist/operation-runner.js +71 -7
- package/dist/profiles.d.ts.map +1 -1
- package/dist/profiles.js +49 -6
- package/oclif.manifest.json +45 -45
- package/package.json +2 -2
package/dist/agent-auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-auth.d.ts","sourceRoot":"","sources":["../src/agent-auth.ts"],"names":[],"mappings":"AAGA,OAAO,EAKL,KAAK,qBAAqB,EAE1B,KAAK,SAAS,EAGf,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-auth.d.ts","sourceRoot":"","sources":["../src/agent-auth.ts"],"names":[],"mappings":"AAGA,OAAO,EAKL,KAAK,qBAAqB,EAE1B,KAAK,SAAS,EAGf,MAAM,eAAe,CAAC;AAYvB,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAWD,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAyGnG;AAED,wBAAsB,gBAAgB,CAAC,EACrC,SAAS,EACT,KAAK,EACL,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC,CAiChD;AAED,wBAAsB,wBAAwB,CAAC,EAC7C,MAAM,EACN,SAAS,EACT,OAAO,EACP,WAAW,GACZ,EAAE;IACD,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,qBAAqB,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ClB;AA6DD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYxD;AAaD,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,EAC9B,SAAS,SAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
package/dist/agent-auth.js
CHANGED
|
@@ -7,6 +7,8 @@ const SENDING_API_RESOURCE = "https://smtp.sendmux.ai/api/v1";
|
|
|
7
7
|
const TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
8
8
|
const ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
|
|
9
9
|
const READINESS_TIMEOUT_MS = 10 * 60 * 1_000;
|
|
10
|
+
const AUTH_REQUEST_TIMEOUT_MS = 15_000;
|
|
11
|
+
const MAX_AUTH_RESPONSE_BYTES = 256 * 1024;
|
|
10
12
|
const SENDING_TOKEN_SKEW_MS = 60 * 1_000;
|
|
11
13
|
export async function registerAgent(input) {
|
|
12
14
|
const initialConfig = await readCliConfig(input.configDir);
|
|
@@ -155,6 +157,8 @@ export async function resolveAgentSendingToken({ config, configDir, profile, pro
|
|
|
155
157
|
}),
|
|
156
158
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
157
159
|
method: "POST",
|
|
160
|
+
redirect: "error",
|
|
161
|
+
signal: AbortSignal.timeout(AUTH_REQUEST_TIMEOUT_MS),
|
|
158
162
|
});
|
|
159
163
|
const body = await responseJson(response);
|
|
160
164
|
if (!response.ok) {
|
|
@@ -257,22 +261,30 @@ export async function waitForMailbox(profile, timeoutMs = READINESS_TIMEOUT_MS)
|
|
|
257
261
|
const remainingBeforeFetch = deadline - Date.now();
|
|
258
262
|
if (remainingBeforeFetch <= 0)
|
|
259
263
|
throw mailboxReadinessTimeoutError();
|
|
264
|
+
const signal = AbortSignal.timeout(remainingBeforeFetch);
|
|
260
265
|
let response;
|
|
266
|
+
let body;
|
|
261
267
|
try {
|
|
262
268
|
response = await fetch(`${profile.appApiBaseUrl}/mailbox/me`, {
|
|
263
269
|
headers: { Authorization: `Bearer ${profile.accessToken}` },
|
|
264
|
-
|
|
270
|
+
redirect: "error",
|
|
271
|
+
signal,
|
|
265
272
|
});
|
|
273
|
+
if (response.ok) {
|
|
274
|
+
await response.body?.cancel();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
body = await responseJson(response);
|
|
266
278
|
}
|
|
267
279
|
catch (error) {
|
|
268
|
-
if (isAbortError(error) || Date.now() >= deadline)
|
|
280
|
+
if (signal.aborted || isAbortError(error) || Date.now() >= deadline)
|
|
269
281
|
throw mailboxReadinessTimeoutError();
|
|
270
282
|
throw error;
|
|
271
283
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const provisioningUnavailable = response.status === 503 && (
|
|
284
|
+
const errorCode = typeof body.error === "object" && body.error !== null && !Array.isArray(body.error) && "code" in body.error
|
|
285
|
+
? body.error.code
|
|
286
|
+
: body.error;
|
|
287
|
+
const provisioningUnavailable = response.status === 503 && (errorCode === "service_unavailable" || errorCode === "temporarily_unavailable");
|
|
276
288
|
if (!provisioningUnavailable) {
|
|
277
289
|
throw new Error(agentAuthFailureMessage(response.status, body));
|
|
278
290
|
}
|
|
@@ -304,6 +316,8 @@ async function postJson(url, options) {
|
|
|
304
316
|
body: JSON.stringify(options.body),
|
|
305
317
|
headers: { "Content-Type": "application/json", ...options.headers },
|
|
306
318
|
method: "POST",
|
|
319
|
+
redirect: "error",
|
|
320
|
+
signal: AbortSignal.timeout(AUTH_REQUEST_TIMEOUT_MS),
|
|
307
321
|
});
|
|
308
322
|
const body = await responseJson(response);
|
|
309
323
|
if (!options.expectedStatuses.includes(response.status)) {
|
|
@@ -312,7 +326,37 @@ async function postJson(url, options) {
|
|
|
312
326
|
return body;
|
|
313
327
|
}
|
|
314
328
|
async function responseJson(response) {
|
|
315
|
-
|
|
329
|
+
let bytes = 0;
|
|
330
|
+
let body;
|
|
331
|
+
try {
|
|
332
|
+
const reader = response.body?.getReader();
|
|
333
|
+
const chunks = [];
|
|
334
|
+
if (reader) {
|
|
335
|
+
try {
|
|
336
|
+
while (true) {
|
|
337
|
+
const chunk = await reader.read();
|
|
338
|
+
if (chunk.done)
|
|
339
|
+
break;
|
|
340
|
+
bytes += chunk.value.byteLength;
|
|
341
|
+
if (bytes > MAX_AUTH_RESPONSE_BYTES)
|
|
342
|
+
break;
|
|
343
|
+
chunks.push(chunk.value);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
await reader.cancel();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (bytes <= MAX_AUTH_RESPONSE_BYTES) {
|
|
351
|
+
body = JSON.parse(new TextDecoder().decode(Buffer.concat(chunks)));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
body = null;
|
|
356
|
+
}
|
|
357
|
+
if (bytes > MAX_AUTH_RESPONSE_BYTES) {
|
|
358
|
+
throw new Error("Sendmux agent authentication response exceeds the size limit.");
|
|
359
|
+
}
|
|
316
360
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
317
361
|
throw new Error(`Sendmux agent authentication returned HTTP ${response.status} without a JSON object.`);
|
|
318
362
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation-runner.d.ts","sourceRoot":"","sources":["../src/operation-runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"operation-runner.d.ts","sourceRoot":"","sources":["../src/operation-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAuBhE,wBAAsB,eAAe,CACnC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,mBAAmB,EAC9B,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,OAAO,CAAC,CAwElB"}
|
package/dist/operation-runner.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import * as sdk from "@sendmux/sdk";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { closeSync, createReadStream, fsyncSync, openSync, writeSync } from "node:fs";
|
|
2
4
|
import { readFile, stat } from "node:fs/promises";
|
|
3
5
|
import { basename, extname } from "node:path";
|
|
4
6
|
import { parseOperationOptions, } from "./operation-flags.js";
|
|
7
|
+
const MAX_SENDING_ATTACHMENTS = 10;
|
|
8
|
+
// Absolute Sending service ceiling; deployment policy may impose a lower limit.
|
|
9
|
+
const MAX_SENDING_ATTACHMENT_BYTES = 18 * 1024 * 1024;
|
|
5
10
|
const surfaceModules = {
|
|
6
11
|
mailbox: sdk.mailbox,
|
|
7
12
|
management: sdk.management,
|
|
@@ -160,13 +165,18 @@ async function uploadMailboxAttachmentFromFile(command, client, operationOptions
|
|
|
160
165
|
async function withAttachedFiles(command, operation, client, operationOptions, flags) {
|
|
161
166
|
const body = jsonObjectBody(command, operationOptions.body);
|
|
162
167
|
const existingAttachments = attachmentArray(command, body.attachments);
|
|
168
|
+
if (operation.operationId === "sendingSendEmail"
|
|
169
|
+
&& existingAttachments.length + (flags.attach?.length ?? 0) > MAX_SENDING_ATTACHMENTS) {
|
|
170
|
+
command.error(`Sending email supports at most ${MAX_SENDING_ATTACHMENTS} attachments, including existing attachments and files.`, { exit: 2 });
|
|
171
|
+
}
|
|
163
172
|
const files = [];
|
|
173
|
+
const maxBytes = operation.operationId === "sendingSendEmail" ? MAX_SENDING_ATTACHMENT_BYTES : undefined;
|
|
164
174
|
for (const path of flags.attach ?? []) {
|
|
165
|
-
files.push(await readAttachmentFile(command, path, flags["content-type"]));
|
|
175
|
+
files.push(await readAttachmentFile(command, path, flags["content-type"], maxBytes));
|
|
166
176
|
}
|
|
167
177
|
if (operation.operationId === "mailboxSendMessage") {
|
|
168
178
|
const uploaded = [];
|
|
169
|
-
for (const file of files) {
|
|
179
|
+
for (const [ordinal, file] of files.entries()) {
|
|
170
180
|
const uploadResponse = await sdk.mailbox.mailboxUploadAttachment({
|
|
171
181
|
client: client,
|
|
172
182
|
body: blobFor(file),
|
|
@@ -179,8 +189,10 @@ async function withAttachedFiles(command, operation, client, operationOptions, f
|
|
|
179
189
|
},
|
|
180
190
|
});
|
|
181
191
|
const result = envelopeData(uploadResponse, "mailboxUploadAttachment");
|
|
192
|
+
const blobId = stringField(result, "blob_id", "mailboxUploadAttachment");
|
|
193
|
+
journalNestedAttachment({ file, id: blobId, nestedOperationId: "mailboxUploadAttachment", operationId: operation.operationId, ordinal });
|
|
182
194
|
uploaded.push({
|
|
183
|
-
blob_id:
|
|
195
|
+
blob_id: blobId,
|
|
184
196
|
content_type: stringField(result, "content_type", "mailboxUploadAttachment"),
|
|
185
197
|
filename: stringField(result, "filename", "mailboxUploadAttachment"),
|
|
186
198
|
});
|
|
@@ -195,11 +207,19 @@ async function withAttachedFiles(command, operation, client, operationOptions, f
|
|
|
195
207
|
}
|
|
196
208
|
if (operation.operationId === "sendingSendEmail") {
|
|
197
209
|
const uploaded = [];
|
|
198
|
-
|
|
210
|
+
const outerKey = operationOptions.headers?.["Idempotency-Key"];
|
|
211
|
+
const idempotencyKey = typeof outerKey === "string" ? outerKey.trim() : undefined;
|
|
212
|
+
for (const [index, file] of files.entries()) {
|
|
199
213
|
const uploadResponse = await sdk.sending.sendingUploadAttachment({
|
|
200
214
|
client: client,
|
|
201
215
|
body: blobFor(file),
|
|
202
216
|
headers: {
|
|
217
|
+
...(idempotencyKey ? {
|
|
218
|
+
// Match the TS/Python helpers; content must not change the key and evade conflict detection.
|
|
219
|
+
"Idempotency-Key": createHash("sha256")
|
|
220
|
+
.update(`sendmux:sending:attachment:${index}:${idempotencyKey}`)
|
|
221
|
+
.digest("hex"),
|
|
222
|
+
} : {}),
|
|
203
223
|
"Content-Length": file.sizeBytes,
|
|
204
224
|
"Content-Type": file.contentType,
|
|
205
225
|
},
|
|
@@ -209,8 +229,10 @@ async function withAttachedFiles(command, operation, client, operationOptions, f
|
|
|
209
229
|
},
|
|
210
230
|
});
|
|
211
231
|
const result = envelopeData(uploadResponse, "sendingUploadAttachment");
|
|
232
|
+
const attachmentId = stringField(result, "attachment_id", "sendingUploadAttachment");
|
|
233
|
+
journalNestedAttachment({ file, id: attachmentId, nestedOperationId: "sendingUploadAttachment", operationId: operation.operationId, ordinal: index });
|
|
212
234
|
uploaded.push({
|
|
213
|
-
attachment_id:
|
|
235
|
+
attachment_id: attachmentId,
|
|
214
236
|
});
|
|
215
237
|
}
|
|
216
238
|
return {
|
|
@@ -237,7 +259,30 @@ async function withAttachedFiles(command, operation, client, operationOptions, f
|
|
|
237
259
|
},
|
|
238
260
|
};
|
|
239
261
|
}
|
|
240
|
-
|
|
262
|
+
function journalNestedAttachment({ file, id, nestedOperationId, operationId, ordinal }) {
|
|
263
|
+
const path = process.env.SENDMUX_LIVE_E2E_ATTACHMENT_JOURNAL;
|
|
264
|
+
if (!path)
|
|
265
|
+
return;
|
|
266
|
+
const record = {
|
|
267
|
+
adapter: "cli",
|
|
268
|
+
filename: file.filename,
|
|
269
|
+
id,
|
|
270
|
+
operationId,
|
|
271
|
+
nestedOperationId,
|
|
272
|
+
ordinal,
|
|
273
|
+
sha256: createHash("sha256").update(file.bytes).digest("hex"),
|
|
274
|
+
size_bytes: file.sizeBytes,
|
|
275
|
+
};
|
|
276
|
+
const descriptor = openSync(path, "a", 0o600);
|
|
277
|
+
try {
|
|
278
|
+
writeSync(descriptor, `${JSON.stringify(record)}\n`, undefined, "utf8");
|
|
279
|
+
fsyncSync(descriptor);
|
|
280
|
+
}
|
|
281
|
+
finally {
|
|
282
|
+
closeSync(descriptor);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async function readAttachmentFile(command, filePath, contentTypeOverride, maxBytes) {
|
|
241
286
|
const info = await stat(filePath).catch((error) => {
|
|
242
287
|
const message = error instanceof Error ? error.message : String(error);
|
|
243
288
|
command.error(`Could not read attachment file ${filePath}: ${message}`, { exit: 2 });
|
|
@@ -248,7 +293,26 @@ async function readAttachmentFile(command, filePath, contentTypeOverride) {
|
|
|
248
293
|
if (info.size === 0) {
|
|
249
294
|
command.error(`Attachment file is empty: ${filePath}`, { exit: 2 });
|
|
250
295
|
}
|
|
251
|
-
|
|
296
|
+
if (maxBytes !== undefined && info.size > maxBytes) {
|
|
297
|
+
command.error(`Attachment file exceeds ${maxBytes} bytes: ${filePath}`, { exit: 2 });
|
|
298
|
+
}
|
|
299
|
+
let bytes;
|
|
300
|
+
if (maxBytes === undefined) {
|
|
301
|
+
bytes = await readFile(filePath);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
const chunks = [];
|
|
305
|
+
// Include one excess byte so a growing file is rejected instead of truncated and uploaded.
|
|
306
|
+
for await (const chunk of createReadStream(filePath, { end: maxBytes }))
|
|
307
|
+
chunks.push(chunk);
|
|
308
|
+
bytes = Buffer.concat(chunks);
|
|
309
|
+
if (bytes.length > maxBytes) {
|
|
310
|
+
command.error(`Attachment file exceeds ${maxBytes} bytes: ${filePath}`, { exit: 2 });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (bytes.length === 0) {
|
|
314
|
+
command.error(`Attachment file is empty: ${filePath}`, { exit: 2 });
|
|
315
|
+
}
|
|
252
316
|
return {
|
|
253
317
|
bytes,
|
|
254
318
|
contentType: contentTypeOverride ?? inferContentType(filePath),
|
package/dist/profiles.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,UAAU,gBAAgB;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAClE,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC;KACnC,CAAC;IACF,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,0BAA0B,CAAC;AACjF,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,aAAa,CAAC;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,eAAe,GAAG,0BAA0B,GAAG,qBAAqB,CAAC;AACjF,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAE9E,MAAM,WAAW,SAAS;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACtC;
|
|
1
|
+
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"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"}
|
package/dist/profiles.js
CHANGED
|
@@ -5,6 +5,8 @@ const CONFIG_FILE = "config.json";
|
|
|
5
5
|
const CONFIG_LOCK_RETRY_MS = 25;
|
|
6
6
|
const CONFIG_LOCK_STALE_MS = 5_000;
|
|
7
7
|
const CONFIG_LOCK_TIMEOUT_MS = 10_000;
|
|
8
|
+
const CONFIG_REPLACE_TIMEOUT_MS = CONFIG_LOCK_STALE_MS / 2;
|
|
9
|
+
const CONFIG_REPLACE_MAX_WAITS = CONFIG_REPLACE_TIMEOUT_MS / CONFIG_LOCK_RETRY_MS;
|
|
8
10
|
export async function readCliConfig(configDir) {
|
|
9
11
|
const path = configPath(configDir);
|
|
10
12
|
try {
|
|
@@ -25,10 +27,11 @@ export async function readCliConfig(configDir) {
|
|
|
25
27
|
export async function updateCliConfig(configDir, update) {
|
|
26
28
|
await mkdir(configDir, { mode: 0o700, recursive: true });
|
|
27
29
|
const releaseLock = await acquireConfigWriteLock(configDir);
|
|
30
|
+
const replaceDeadline = Date.now() + CONFIG_REPLACE_TIMEOUT_MS;
|
|
28
31
|
try {
|
|
29
32
|
const config = await readCliConfig(configDir);
|
|
30
33
|
const result = update(config);
|
|
31
|
-
await writeCliConfigFile(configDir, config);
|
|
34
|
+
await writeCliConfigFile(configDir, config, replaceDeadline);
|
|
32
35
|
return result;
|
|
33
36
|
}
|
|
34
37
|
finally {
|
|
@@ -86,12 +89,30 @@ export async function clearAgentRegistrationIntent(configDir, profileName) {
|
|
|
86
89
|
export function configPath(configDir) {
|
|
87
90
|
return join(configDir, CONFIG_FILE);
|
|
88
91
|
}
|
|
89
|
-
async function writeCliConfigFile(configDir, config) {
|
|
92
|
+
async function writeCliConfigFile(configDir, config, replaceDeadline) {
|
|
90
93
|
const path = configPath(configDir);
|
|
91
94
|
const tempPath = `${path}.${process.pid}.tmp`;
|
|
92
95
|
await writeFile(tempPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
93
96
|
await chmod(tempPath, 0o600);
|
|
94
|
-
|
|
97
|
+
let waits = 0;
|
|
98
|
+
while (true) {
|
|
99
|
+
try {
|
|
100
|
+
await rename(tempPath, path);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (!isRetryableWindowsProfileFsError(error))
|
|
105
|
+
throw error;
|
|
106
|
+
if (waits >= CONFIG_REPLACE_MAX_WAITS ||
|
|
107
|
+
Date.now() + CONFIG_LOCK_RETRY_MS > replaceDeadline) {
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
waits += 1;
|
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, CONFIG_LOCK_RETRY_MS));
|
|
112
|
+
if (Date.now() >= replaceDeadline)
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
95
116
|
await chmod(path, 0o600);
|
|
96
117
|
}
|
|
97
118
|
async function acquireConfigWriteLock(configDir) {
|
|
@@ -99,7 +120,14 @@ async function acquireConfigWriteLock(configDir) {
|
|
|
99
120
|
}
|
|
100
121
|
async function acquireFileLock(lockPath, timeoutMessage) {
|
|
101
122
|
const deadline = Date.now() + CONFIG_LOCK_TIMEOUT_MS;
|
|
123
|
+
let lastOpenError;
|
|
102
124
|
while (true) {
|
|
125
|
+
if (Date.now() >= deadline) {
|
|
126
|
+
if (lastOpenError && isRetryableWindowsProfileFsError(lastOpenError)) {
|
|
127
|
+
throw lastOpenError;
|
|
128
|
+
}
|
|
129
|
+
throw new Error(timeoutMessage);
|
|
130
|
+
}
|
|
103
131
|
try {
|
|
104
132
|
const handle = await open(lockPath, "wx", 0o600);
|
|
105
133
|
return async () => {
|
|
@@ -118,8 +146,19 @@ async function acquireFileLock(lockPath, timeoutMessage) {
|
|
|
118
146
|
};
|
|
119
147
|
}
|
|
120
148
|
catch (error) {
|
|
121
|
-
if (
|
|
149
|
+
if (isNodeError(error) && error.code === "EEXIST") {
|
|
150
|
+
lastOpenError = error;
|
|
151
|
+
}
|
|
152
|
+
else if (isRetryableWindowsProfileFsError(error)) {
|
|
153
|
+
lastOpenError = error;
|
|
154
|
+
if (Date.now() + CONFIG_LOCK_RETRY_MS > deadline)
|
|
155
|
+
throw error;
|
|
156
|
+
await new Promise((resolve) => setTimeout(resolve, CONFIG_LOCK_RETRY_MS));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
122
160
|
throw error;
|
|
161
|
+
}
|
|
123
162
|
}
|
|
124
163
|
try {
|
|
125
164
|
const lock = await stat(lockPath);
|
|
@@ -141,9 +180,8 @@ async function acquireFileLock(lockPath, timeoutMessage) {
|
|
|
141
180
|
continue;
|
|
142
181
|
throw error;
|
|
143
182
|
}
|
|
144
|
-
if (Date.now()
|
|
183
|
+
if (Date.now() + CONFIG_LOCK_RETRY_MS > deadline)
|
|
145
184
|
throw new Error(timeoutMessage);
|
|
146
|
-
}
|
|
147
185
|
await new Promise((resolve) => setTimeout(resolve, CONFIG_LOCK_RETRY_MS));
|
|
148
186
|
}
|
|
149
187
|
}
|
|
@@ -179,3 +217,8 @@ export function isActiveAgentProfile(profile) {
|
|
|
179
217
|
function isNodeError(error) {
|
|
180
218
|
return Boolean(error && typeof error === "object" && "code" in error);
|
|
181
219
|
}
|
|
220
|
+
function isRetryableWindowsProfileFsError(error) {
|
|
221
|
+
return (process.platform === "win32" &&
|
|
222
|
+
isNodeError(error) &&
|
|
223
|
+
(error.code === "EPERM" || error.code === "EACCES" || error.code === "EBUSY"));
|
|
224
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -18507,10 +18507,10 @@
|
|
|
18507
18507
|
"list.js"
|
|
18508
18508
|
]
|
|
18509
18509
|
},
|
|
18510
|
-
"management:
|
|
18510
|
+
"management:providers:list": {
|
|
18511
18511
|
"aliases": [],
|
|
18512
18512
|
"args": {},
|
|
18513
|
-
"description": "List
|
|
18513
|
+
"description": "List sending accounts",
|
|
18514
18514
|
"flags": {
|
|
18515
18515
|
"json": {
|
|
18516
18516
|
"description": "Format output as json.",
|
|
@@ -18630,7 +18630,7 @@
|
|
|
18630
18630
|
},
|
|
18631
18631
|
"hasDynamicHelp": false,
|
|
18632
18632
|
"hiddenAliases": [],
|
|
18633
|
-
"id": "management:
|
|
18633
|
+
"id": "management:providers:list",
|
|
18634
18634
|
"pluginAlias": "@sendmux/cli",
|
|
18635
18635
|
"pluginName": "@sendmux/cli",
|
|
18636
18636
|
"pluginType": "core",
|
|
@@ -18638,12 +18638,12 @@
|
|
|
18638
18638
|
"enableJsonFlag": true,
|
|
18639
18639
|
"operation": {
|
|
18640
18640
|
"bodyKind": "none",
|
|
18641
|
-
"command": "management:
|
|
18642
|
-
"description": "List
|
|
18641
|
+
"command": "management:providers:list",
|
|
18642
|
+
"description": "List sending accounts",
|
|
18643
18643
|
"headerParams": [],
|
|
18644
18644
|
"method": "get",
|
|
18645
|
-
"operationId": "
|
|
18646
|
-
"path": "/
|
|
18645
|
+
"operationId": "managementListProviders",
|
|
18646
|
+
"path": "/providers",
|
|
18647
18647
|
"pathParams": [],
|
|
18648
18648
|
"queryParams": [
|
|
18649
18649
|
{
|
|
@@ -18654,23 +18654,39 @@
|
|
|
18654
18654
|
}
|
|
18655
18655
|
},
|
|
18656
18656
|
{
|
|
18657
|
-
"name": "
|
|
18657
|
+
"name": "status",
|
|
18658
18658
|
"required": false,
|
|
18659
18659
|
"schema": {
|
|
18660
|
-
"type": "
|
|
18661
|
-
"
|
|
18662
|
-
|
|
18660
|
+
"type": "string",
|
|
18661
|
+
"enum": [
|
|
18662
|
+
"active",
|
|
18663
|
+
"inactive",
|
|
18664
|
+
"error",
|
|
18665
|
+
"pending"
|
|
18666
|
+
]
|
|
18663
18667
|
}
|
|
18664
18668
|
},
|
|
18665
18669
|
{
|
|
18666
|
-
"name": "
|
|
18670
|
+
"name": "type",
|
|
18667
18671
|
"required": false,
|
|
18668
18672
|
"schema": {
|
|
18669
18673
|
"type": "string",
|
|
18670
18674
|
"enum": [
|
|
18671
|
-
"
|
|
18675
|
+
"smtp",
|
|
18676
|
+
"gmail_api",
|
|
18677
|
+
"outlook_api",
|
|
18678
|
+
"amazon_ses"
|
|
18672
18679
|
]
|
|
18673
18680
|
}
|
|
18681
|
+
},
|
|
18682
|
+
{
|
|
18683
|
+
"name": "limit",
|
|
18684
|
+
"required": false,
|
|
18685
|
+
"schema": {
|
|
18686
|
+
"type": "integer",
|
|
18687
|
+
"minimum": 1,
|
|
18688
|
+
"maximum": 100
|
|
18689
|
+
}
|
|
18674
18690
|
}
|
|
18675
18691
|
],
|
|
18676
18692
|
"responseKind": "json",
|
|
@@ -18683,14 +18699,14 @@
|
|
|
18683
18699
|
"dist",
|
|
18684
18700
|
"commands",
|
|
18685
18701
|
"management",
|
|
18686
|
-
"
|
|
18702
|
+
"providers",
|
|
18687
18703
|
"list.js"
|
|
18688
18704
|
]
|
|
18689
18705
|
},
|
|
18690
|
-
"management:
|
|
18706
|
+
"management:mailboxes:list": {
|
|
18691
18707
|
"aliases": [],
|
|
18692
18708
|
"args": {},
|
|
18693
|
-
"description": "List
|
|
18709
|
+
"description": "List mailboxes",
|
|
18694
18710
|
"flags": {
|
|
18695
18711
|
"json": {
|
|
18696
18712
|
"description": "Format output as json.",
|
|
@@ -18810,7 +18826,7 @@
|
|
|
18810
18826
|
},
|
|
18811
18827
|
"hasDynamicHelp": false,
|
|
18812
18828
|
"hiddenAliases": [],
|
|
18813
|
-
"id": "management:
|
|
18829
|
+
"id": "management:mailboxes:list",
|
|
18814
18830
|
"pluginAlias": "@sendmux/cli",
|
|
18815
18831
|
"pluginName": "@sendmux/cli",
|
|
18816
18832
|
"pluginType": "core",
|
|
@@ -18818,12 +18834,12 @@
|
|
|
18818
18834
|
"enableJsonFlag": true,
|
|
18819
18835
|
"operation": {
|
|
18820
18836
|
"bodyKind": "none",
|
|
18821
|
-
"command": "management:
|
|
18822
|
-
"description": "List
|
|
18837
|
+
"command": "management:mailboxes:list",
|
|
18838
|
+
"description": "List mailboxes",
|
|
18823
18839
|
"headerParams": [],
|
|
18824
18840
|
"method": "get",
|
|
18825
|
-
"operationId": "
|
|
18826
|
-
"path": "/
|
|
18841
|
+
"operationId": "managementListMailboxes",
|
|
18842
|
+
"path": "/mailboxes",
|
|
18827
18843
|
"pathParams": [],
|
|
18828
18844
|
"queryParams": [
|
|
18829
18845
|
{
|
|
@@ -18834,39 +18850,23 @@
|
|
|
18834
18850
|
}
|
|
18835
18851
|
},
|
|
18836
18852
|
{
|
|
18837
|
-
"name": "
|
|
18853
|
+
"name": "limit",
|
|
18838
18854
|
"required": false,
|
|
18839
18855
|
"schema": {
|
|
18840
|
-
"type": "
|
|
18841
|
-
"
|
|
18842
|
-
|
|
18843
|
-
"inactive",
|
|
18844
|
-
"error",
|
|
18845
|
-
"pending"
|
|
18846
|
-
]
|
|
18856
|
+
"type": "integer",
|
|
18857
|
+
"minimum": 1,
|
|
18858
|
+
"maximum": 100
|
|
18847
18859
|
}
|
|
18848
18860
|
},
|
|
18849
18861
|
{
|
|
18850
|
-
"name": "
|
|
18862
|
+
"name": "include_deleted",
|
|
18851
18863
|
"required": false,
|
|
18852
18864
|
"schema": {
|
|
18853
18865
|
"type": "string",
|
|
18854
18866
|
"enum": [
|
|
18855
|
-
"
|
|
18856
|
-
"gmail_api",
|
|
18857
|
-
"outlook_api",
|
|
18858
|
-
"amazon_ses"
|
|
18867
|
+
"true"
|
|
18859
18868
|
]
|
|
18860
18869
|
}
|
|
18861
|
-
},
|
|
18862
|
-
{
|
|
18863
|
-
"name": "limit",
|
|
18864
|
-
"required": false,
|
|
18865
|
-
"schema": {
|
|
18866
|
-
"type": "integer",
|
|
18867
|
-
"minimum": 1,
|
|
18868
|
-
"maximum": 100
|
|
18869
|
-
}
|
|
18870
18870
|
}
|
|
18871
18871
|
],
|
|
18872
18872
|
"responseKind": "json",
|
|
@@ -18879,7 +18879,7 @@
|
|
|
18879
18879
|
"dist",
|
|
18880
18880
|
"commands",
|
|
18881
18881
|
"management",
|
|
18882
|
-
"
|
|
18882
|
+
"mailboxes",
|
|
18883
18883
|
"list.js"
|
|
18884
18884
|
]
|
|
18885
18885
|
},
|
|
@@ -19046,5 +19046,5 @@
|
|
|
19046
19046
|
]
|
|
19047
19047
|
}
|
|
19048
19048
|
},
|
|
19049
|
-
"version": "1.
|
|
19049
|
+
"version": "1.7.1"
|
|
19050
19050
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendmux/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@oclif/core": "4.11.4",
|
|
22
|
-
"@sendmux/sdk": "
|
|
22
|
+
"@sendmux/sdk": "2.0.1"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "22.19.19",
|