@kinosec/cli 0.9.0 → 0.10.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/cli.js +1346 -428
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -5383,25 +5383,44 @@ var {
|
|
|
5383
5383
|
// src/lib/errors.ts
|
|
5384
5384
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
5385
5385
|
|
|
5386
|
-
// ../../node_modules/.pnpm/ky@
|
|
5387
|
-
var
|
|
5386
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/KyError.js
|
|
5387
|
+
var KyError = class extends Error {
|
|
5388
|
+
name = "KyError";
|
|
5389
|
+
get isKyError() {
|
|
5390
|
+
return true;
|
|
5391
|
+
}
|
|
5392
|
+
};
|
|
5393
|
+
|
|
5394
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/HTTPError.js
|
|
5395
|
+
var HTTPError = class extends KyError {
|
|
5396
|
+
name = "HTTPError";
|
|
5388
5397
|
response;
|
|
5389
5398
|
request;
|
|
5390
5399
|
options;
|
|
5400
|
+
data;
|
|
5391
5401
|
constructor(response, request, options) {
|
|
5392
5402
|
const code = response.status || response.status === 0 ? response.status : "";
|
|
5393
5403
|
const title = response.statusText ?? "";
|
|
5394
5404
|
const status = `${code} ${title}`.trim();
|
|
5395
5405
|
const reason = status ? `status code ${status}` : "an unknown error";
|
|
5396
5406
|
super(`Request failed with ${reason}: ${request.method} ${request.url}`);
|
|
5397
|
-
this.name = "HTTPError";
|
|
5398
5407
|
this.response = response;
|
|
5399
5408
|
this.request = request;
|
|
5400
5409
|
this.options = options;
|
|
5401
5410
|
}
|
|
5402
5411
|
};
|
|
5403
5412
|
|
|
5404
|
-
// ../../node_modules/.pnpm/ky@
|
|
5413
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/NetworkError.js
|
|
5414
|
+
var NetworkError = class extends KyError {
|
|
5415
|
+
name = "NetworkError";
|
|
5416
|
+
request;
|
|
5417
|
+
constructor(request, options) {
|
|
5418
|
+
super(`Request failed due to a network error: ${request.method} ${request.url}`, options);
|
|
5419
|
+
this.request = request;
|
|
5420
|
+
}
|
|
5421
|
+
};
|
|
5422
|
+
|
|
5423
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/NonError.js
|
|
5405
5424
|
var NonError = class extends Error {
|
|
5406
5425
|
name = "NonError";
|
|
5407
5426
|
value;
|
|
@@ -5420,8 +5439,8 @@ var NonError = class extends Error {
|
|
|
5420
5439
|
}
|
|
5421
5440
|
};
|
|
5422
5441
|
|
|
5423
|
-
// ../../node_modules/.pnpm/ky@
|
|
5424
|
-
var ForceRetryError = class extends
|
|
5442
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/ForceRetryError.js
|
|
5443
|
+
var ForceRetryError = class extends KyError {
|
|
5425
5444
|
name = "ForceRetryError";
|
|
5426
5445
|
customDelay;
|
|
5427
5446
|
code;
|
|
@@ -5435,7 +5454,27 @@ var ForceRetryError = class extends Error {
|
|
|
5435
5454
|
}
|
|
5436
5455
|
};
|
|
5437
5456
|
|
|
5438
|
-
// ../../node_modules/.pnpm/ky@
|
|
5457
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/SchemaValidationError.js
|
|
5458
|
+
var SchemaValidationError = class extends Error {
|
|
5459
|
+
name = "SchemaValidationError";
|
|
5460
|
+
issues;
|
|
5461
|
+
constructor(issues) {
|
|
5462
|
+
super("Response schema validation failed");
|
|
5463
|
+
this.issues = issues;
|
|
5464
|
+
}
|
|
5465
|
+
};
|
|
5466
|
+
|
|
5467
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/TimeoutError.js
|
|
5468
|
+
var TimeoutError = class extends KyError {
|
|
5469
|
+
name = "TimeoutError";
|
|
5470
|
+
request;
|
|
5471
|
+
constructor(request) {
|
|
5472
|
+
super(`Request timed out: ${request.method} ${request.url}`);
|
|
5473
|
+
this.request = request;
|
|
5474
|
+
}
|
|
5475
|
+
};
|
|
5476
|
+
|
|
5477
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/core/constants.js
|
|
5439
5478
|
var supportsRequestStreams = (() => {
|
|
5440
5479
|
let duplexAccessed = false;
|
|
5441
5480
|
let hasContentType = false;
|
|
@@ -5479,7 +5518,7 @@ var responseTypes = {
|
|
|
5479
5518
|
bytes: "*/*"
|
|
5480
5519
|
};
|
|
5481
5520
|
var maxSafeTimeout = 2147483647;
|
|
5482
|
-
var usualFormBoundarySize =
|
|
5521
|
+
var usualFormBoundarySize = 40;
|
|
5483
5522
|
var stop = /* @__PURE__ */ Symbol("stop");
|
|
5484
5523
|
var RetryMarker = class {
|
|
5485
5524
|
options;
|
|
@@ -5493,9 +5532,11 @@ var kyOptionKeys = {
|
|
|
5493
5532
|
parseJson: true,
|
|
5494
5533
|
stringifyJson: true,
|
|
5495
5534
|
searchParams: true,
|
|
5496
|
-
|
|
5535
|
+
baseUrl: true,
|
|
5536
|
+
prefix: true,
|
|
5497
5537
|
retry: true,
|
|
5498
5538
|
timeout: true,
|
|
5539
|
+
totalTimeout: true,
|
|
5499
5540
|
hooks: true,
|
|
5500
5541
|
throwHttpErrors: true,
|
|
5501
5542
|
onDownloadProgress: true,
|
|
@@ -5503,10 +5544,6 @@ var kyOptionKeys = {
|
|
|
5503
5544
|
fetch: true,
|
|
5504
5545
|
context: true
|
|
5505
5546
|
};
|
|
5506
|
-
var vendorSpecificOptions = {
|
|
5507
|
-
next: true
|
|
5508
|
-
// Next.js cache revalidation (revalidate, tags)
|
|
5509
|
-
};
|
|
5510
5547
|
var requestOptionsRegistry = {
|
|
5511
5548
|
method: true,
|
|
5512
5549
|
headers: true,
|
|
@@ -5524,7 +5561,8 @@ var requestOptionsRegistry = {
|
|
|
5524
5561
|
duplex: true
|
|
5525
5562
|
};
|
|
5526
5563
|
|
|
5527
|
-
// ../../node_modules/.pnpm/ky@
|
|
5564
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/body.js
|
|
5565
|
+
var encoder = new TextEncoder();
|
|
5528
5566
|
var getBodySize = (body) => {
|
|
5529
5567
|
if (!body) {
|
|
5530
5568
|
return 0;
|
|
@@ -5533,33 +5571,22 @@ var getBodySize = (body) => {
|
|
|
5533
5571
|
let size = 0;
|
|
5534
5572
|
for (const [key, value] of body) {
|
|
5535
5573
|
size += usualFormBoundarySize;
|
|
5536
|
-
size +=
|
|
5537
|
-
size += typeof value === "string" ?
|
|
5574
|
+
size += encoder.encode(`Content-Disposition: form-data; name="${key}"`).byteLength;
|
|
5575
|
+
size += typeof value === "string" ? encoder.encode(value).byteLength : value.size;
|
|
5538
5576
|
}
|
|
5539
5577
|
return size;
|
|
5540
5578
|
}
|
|
5541
5579
|
if (body instanceof Blob) {
|
|
5542
5580
|
return body.size;
|
|
5543
5581
|
}
|
|
5544
|
-
if (body instanceof ArrayBuffer) {
|
|
5582
|
+
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
|
|
5545
5583
|
return body.byteLength;
|
|
5546
5584
|
}
|
|
5547
5585
|
if (typeof body === "string") {
|
|
5548
|
-
return
|
|
5586
|
+
return encoder.encode(body).byteLength;
|
|
5549
5587
|
}
|
|
5550
5588
|
if (body instanceof URLSearchParams) {
|
|
5551
|
-
return
|
|
5552
|
-
}
|
|
5553
|
-
if ("byteLength" in body) {
|
|
5554
|
-
return body.byteLength;
|
|
5555
|
-
}
|
|
5556
|
-
if (typeof body === "object" && body !== null) {
|
|
5557
|
-
try {
|
|
5558
|
-
const jsonString = JSON.stringify(body);
|
|
5559
|
-
return new TextEncoder().encode(jsonString).length;
|
|
5560
|
-
} catch {
|
|
5561
|
-
return 0;
|
|
5562
|
-
}
|
|
5589
|
+
return encoder.encode(body.toString()).byteLength;
|
|
5563
5590
|
}
|
|
5564
5591
|
return 0;
|
|
5565
5592
|
};
|
|
@@ -5591,19 +5618,16 @@ var streamResponse = (response, onDownloadProgress) => {
|
|
|
5591
5618
|
if (!response.body) {
|
|
5592
5619
|
return response;
|
|
5593
5620
|
}
|
|
5594
|
-
|
|
5595
|
-
return new Response(null, {
|
|
5596
|
-
status: response.status,
|
|
5597
|
-
statusText: response.statusText,
|
|
5598
|
-
headers: response.headers
|
|
5599
|
-
});
|
|
5600
|
-
}
|
|
5601
|
-
const totalBytes = Math.max(0, Number(response.headers.get("content-length")) || 0);
|
|
5602
|
-
return new Response(withProgress(response.body, totalBytes, onDownloadProgress), {
|
|
5621
|
+
const responseInit = {
|
|
5603
5622
|
status: response.status,
|
|
5604
5623
|
statusText: response.statusText,
|
|
5605
5624
|
headers: response.headers
|
|
5606
|
-
}
|
|
5625
|
+
};
|
|
5626
|
+
if (response.status === 204) {
|
|
5627
|
+
return new Response(null, responseInit);
|
|
5628
|
+
}
|
|
5629
|
+
const totalBytes = Math.max(0, Number(response.headers.get("content-length")) || 0);
|
|
5630
|
+
return new Response(withProgress(response.body, totalBytes, onDownloadProgress), responseInit);
|
|
5607
5631
|
};
|
|
5608
5632
|
var streamRequest = (request, onUploadProgress, originalBody) => {
|
|
5609
5633
|
if (!request.body) {
|
|
@@ -5617,10 +5641,18 @@ var streamRequest = (request, onUploadProgress, originalBody) => {
|
|
|
5617
5641
|
});
|
|
5618
5642
|
};
|
|
5619
5643
|
|
|
5620
|
-
// ../../node_modules/.pnpm/ky@
|
|
5644
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/is.js
|
|
5621
5645
|
var isObject = (value) => value !== null && typeof value === "object";
|
|
5622
5646
|
|
|
5623
|
-
// ../../node_modules/.pnpm/ky@
|
|
5647
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/merge.js
|
|
5648
|
+
var replaceSymbol = /* @__PURE__ */ Symbol("replaceOption");
|
|
5649
|
+
var getReplaceState = (value) => isObject(value) && value[replaceSymbol] === true ? {
|
|
5650
|
+
isReplace: true,
|
|
5651
|
+
value: value.value
|
|
5652
|
+
} : {
|
|
5653
|
+
isReplace: false,
|
|
5654
|
+
value
|
|
5655
|
+
};
|
|
5624
5656
|
var validateAndMerge = (...sources) => {
|
|
5625
5657
|
for (const source of sources) {
|
|
5626
5658
|
if ((!isObject(source) || Array.isArray(source)) && source !== void 0) {
|
|
@@ -5642,17 +5674,55 @@ var mergeHeaders = (source1 = {}, source2 = {}) => {
|
|
|
5642
5674
|
}
|
|
5643
5675
|
return result;
|
|
5644
5676
|
};
|
|
5677
|
+
var isPlainObject = (value) => {
|
|
5678
|
+
if (!isObject(value) || Array.isArray(value)) {
|
|
5679
|
+
return false;
|
|
5680
|
+
}
|
|
5681
|
+
const prototype = Object.getPrototypeOf(value);
|
|
5682
|
+
return prototype === Object.prototype || prototype === null;
|
|
5683
|
+
};
|
|
5684
|
+
var cloneShallow = (value) => {
|
|
5685
|
+
if (value instanceof URLSearchParams) {
|
|
5686
|
+
const copy = new URLSearchParams(value);
|
|
5687
|
+
const deleted = value[deletedParametersSymbol];
|
|
5688
|
+
if (deleted) {
|
|
5689
|
+
copy[deletedParametersSymbol] = new Set(deleted);
|
|
5690
|
+
}
|
|
5691
|
+
return copy;
|
|
5692
|
+
}
|
|
5693
|
+
if (value instanceof globalThis.Headers) {
|
|
5694
|
+
return new globalThis.Headers(value);
|
|
5695
|
+
}
|
|
5696
|
+
if (Array.isArray(value)) {
|
|
5697
|
+
return [...value];
|
|
5698
|
+
}
|
|
5699
|
+
if (isPlainObject(value)) {
|
|
5700
|
+
const copy = { ...value };
|
|
5701
|
+
return copy;
|
|
5702
|
+
}
|
|
5703
|
+
return value;
|
|
5704
|
+
};
|
|
5705
|
+
var normalizeHeaderObject = (headers) => Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] !== void 0));
|
|
5706
|
+
var mergeHeaderContainers = (source1, source2) => {
|
|
5707
|
+
if (isPlainObject(source1) && isPlainObject(source2)) {
|
|
5708
|
+
return normalizeHeaderObject({ ...source1, ...source2 });
|
|
5709
|
+
}
|
|
5710
|
+
return mergeHeaders(source1, source2);
|
|
5711
|
+
};
|
|
5645
5712
|
function newHookValue(original, incoming, property) {
|
|
5646
5713
|
return Object.hasOwn(incoming, property) && incoming[property] === void 0 ? [] : deepMerge(original[property] ?? [], incoming[property] ?? []);
|
|
5647
5714
|
}
|
|
5648
5715
|
var mergeHooks = (original = {}, incoming = {}) => ({
|
|
5716
|
+
init: newHookValue(original, incoming, "init"),
|
|
5649
5717
|
beforeRequest: newHookValue(original, incoming, "beforeRequest"),
|
|
5650
5718
|
beforeRetry: newHookValue(original, incoming, "beforeRetry"),
|
|
5651
|
-
|
|
5652
|
-
|
|
5719
|
+
beforeError: newHookValue(original, incoming, "beforeError"),
|
|
5720
|
+
afterResponse: newHookValue(original, incoming, "afterResponse")
|
|
5653
5721
|
});
|
|
5722
|
+
var deletedParametersSymbol = /* @__PURE__ */ Symbol("deletedParameters");
|
|
5654
5723
|
var appendSearchParameters = (target, source) => {
|
|
5655
5724
|
const result = new URLSearchParams();
|
|
5725
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
5656
5726
|
for (const input of [target, source]) {
|
|
5657
5727
|
if (input === void 0) {
|
|
5658
5728
|
continue;
|
|
@@ -5660,6 +5730,14 @@ var appendSearchParameters = (target, source) => {
|
|
|
5660
5730
|
if (input instanceof URLSearchParams) {
|
|
5661
5731
|
for (const [key, value] of input.entries()) {
|
|
5662
5732
|
result.append(key, value);
|
|
5733
|
+
deleted.delete(key);
|
|
5734
|
+
}
|
|
5735
|
+
const inputDeleted = input[deletedParametersSymbol];
|
|
5736
|
+
if (inputDeleted) {
|
|
5737
|
+
for (const key of inputDeleted) {
|
|
5738
|
+
result.delete(key);
|
|
5739
|
+
deleted.add(key);
|
|
5740
|
+
}
|
|
5663
5741
|
}
|
|
5664
5742
|
} else if (Array.isArray(input)) {
|
|
5665
5743
|
for (const pair of input) {
|
|
@@ -5667,20 +5745,29 @@ var appendSearchParameters = (target, source) => {
|
|
|
5667
5745
|
throw new TypeError("Array search parameters must be provided in [[key, value], ...] format");
|
|
5668
5746
|
}
|
|
5669
5747
|
result.append(String(pair[0]), String(pair[1]));
|
|
5748
|
+
deleted.delete(String(pair[0]));
|
|
5670
5749
|
}
|
|
5671
5750
|
} else if (isObject(input)) {
|
|
5672
5751
|
for (const [key, value] of Object.entries(input)) {
|
|
5673
|
-
if (value
|
|
5752
|
+
if (value === void 0) {
|
|
5753
|
+
result.delete(key);
|
|
5754
|
+
deleted.add(key);
|
|
5755
|
+
} else {
|
|
5674
5756
|
result.append(key, String(value));
|
|
5757
|
+
deleted.delete(key);
|
|
5675
5758
|
}
|
|
5676
5759
|
}
|
|
5677
5760
|
} else {
|
|
5678
5761
|
const parameters = new URLSearchParams(input);
|
|
5679
5762
|
for (const [key, value] of parameters.entries()) {
|
|
5680
5763
|
result.append(key, value);
|
|
5764
|
+
deleted.delete(key);
|
|
5681
5765
|
}
|
|
5682
5766
|
}
|
|
5683
5767
|
}
|
|
5768
|
+
if (deleted.size > 0) {
|
|
5769
|
+
result[deletedParametersSymbol] = deleted;
|
|
5770
|
+
}
|
|
5684
5771
|
return result;
|
|
5685
5772
|
};
|
|
5686
5773
|
var deepMerge = (...sources) => {
|
|
@@ -5701,35 +5788,42 @@ var deepMerge = (...sources) => {
|
|
|
5701
5788
|
signals2.push(value);
|
|
5702
5789
|
continue;
|
|
5703
5790
|
}
|
|
5791
|
+
const replaceState = getReplaceState(value);
|
|
5792
|
+
const { isReplace } = replaceState;
|
|
5793
|
+
value = replaceState.value;
|
|
5704
5794
|
if (key === "context") {
|
|
5705
5795
|
if (value !== void 0 && value !== null && (!isObject(value) || Array.isArray(value))) {
|
|
5706
5796
|
throw new TypeError("The `context` option must be an object");
|
|
5707
5797
|
}
|
|
5708
5798
|
returnValue = {
|
|
5709
5799
|
...returnValue,
|
|
5710
|
-
context: value === void 0 || value === null ? {} : { ...returnValue.context, ...value }
|
|
5800
|
+
context: value === void 0 || value === null ? {} : isReplace ? { ...value } : { ...returnValue.context, ...value }
|
|
5711
5801
|
};
|
|
5712
5802
|
continue;
|
|
5713
5803
|
}
|
|
5714
5804
|
if (key === "searchParams") {
|
|
5715
5805
|
if (value === void 0 || value === null) {
|
|
5716
5806
|
searchParameters = void 0;
|
|
5807
|
+
} else if (isReplace) {
|
|
5808
|
+
searchParameters = value;
|
|
5717
5809
|
} else {
|
|
5718
5810
|
searchParameters = searchParameters === void 0 ? value : appendSearchParameters(searchParameters, value);
|
|
5719
5811
|
}
|
|
5720
5812
|
continue;
|
|
5721
5813
|
}
|
|
5722
|
-
if (isObject(value) && key in returnValue) {
|
|
5814
|
+
if (isObject(value) && !isReplace && key in returnValue) {
|
|
5723
5815
|
value = deepMerge(returnValue[key], value);
|
|
5724
5816
|
}
|
|
5725
5817
|
returnValue = { ...returnValue, [key]: value };
|
|
5726
5818
|
}
|
|
5727
5819
|
if (isObject(source.hooks)) {
|
|
5728
|
-
|
|
5820
|
+
const { value: hookValue, isReplace } = getReplaceState(source.hooks);
|
|
5821
|
+
hooks = isReplace ? mergeHooks({}, hookValue) : mergeHooks(hooks, hookValue);
|
|
5729
5822
|
returnValue.hooks = hooks;
|
|
5730
5823
|
}
|
|
5731
5824
|
if (isObject(source.headers)) {
|
|
5732
|
-
|
|
5825
|
+
const { value: headerValue, isReplace } = getReplaceState(source.headers);
|
|
5826
|
+
headers = isReplace ? cloneShallow(headerValue) : mergeHeaderContainers(headers, headerValue);
|
|
5733
5827
|
returnValue.headers = headers;
|
|
5734
5828
|
}
|
|
5735
5829
|
}
|
|
@@ -5749,7 +5843,7 @@ var deepMerge = (...sources) => {
|
|
|
5749
5843
|
return returnValue;
|
|
5750
5844
|
};
|
|
5751
5845
|
|
|
5752
|
-
// ../../node_modules/.pnpm/ky@
|
|
5846
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/normalize.js
|
|
5753
5847
|
var normalizeRequestMethod = (input) => requestMethods.includes(input) ? input.toUpperCase() : input;
|
|
5754
5848
|
var retryMethods = ["get", "put", "head", "delete", "options", "trace"];
|
|
5755
5849
|
var retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
|
|
@@ -5775,28 +5869,20 @@ var normalizeRetryOptions = (retry2 = {}) => {
|
|
|
5775
5869
|
if (retry2.methods && !Array.isArray(retry2.methods)) {
|
|
5776
5870
|
throw new Error("retry.methods must be an array");
|
|
5777
5871
|
}
|
|
5778
|
-
retry2.methods &&= retry2.methods.map((method) => method.toLowerCase());
|
|
5779
5872
|
if (retry2.statusCodes && !Array.isArray(retry2.statusCodes)) {
|
|
5780
5873
|
throw new Error("retry.statusCodes must be an array");
|
|
5781
5874
|
}
|
|
5782
|
-
const normalizedRetry = Object.fromEntries(Object.entries(
|
|
5875
|
+
const normalizedRetry = Object.fromEntries(Object.entries({
|
|
5876
|
+
...retry2,
|
|
5877
|
+
methods: retry2.methods?.map((method) => method.toLowerCase())
|
|
5878
|
+
}).filter(([, value]) => value !== void 0));
|
|
5783
5879
|
return {
|
|
5784
5880
|
...defaultRetryOptions,
|
|
5785
5881
|
...normalizedRetry
|
|
5786
5882
|
};
|
|
5787
5883
|
};
|
|
5788
5884
|
|
|
5789
|
-
// ../../node_modules/.pnpm/ky@
|
|
5790
|
-
var TimeoutError = class extends Error {
|
|
5791
|
-
request;
|
|
5792
|
-
constructor(request) {
|
|
5793
|
-
super(`Request timed out: ${request.method} ${request.url}`);
|
|
5794
|
-
this.name = "TimeoutError";
|
|
5795
|
-
this.request = request;
|
|
5796
|
-
}
|
|
5797
|
-
};
|
|
5798
|
-
|
|
5799
|
-
// ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/timeout.js
|
|
5885
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/timeout.js
|
|
5800
5886
|
async function timeout(request, init, abortController, options) {
|
|
5801
5887
|
return new Promise((resolve, reject) => {
|
|
5802
5888
|
const timeoutId = setTimeout(() => {
|
|
@@ -5811,7 +5897,7 @@ async function timeout(request, init, abortController, options) {
|
|
|
5811
5897
|
});
|
|
5812
5898
|
}
|
|
5813
5899
|
|
|
5814
|
-
// ../../node_modules/.pnpm/ky@
|
|
5900
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/delay.js
|
|
5815
5901
|
async function delay(ms, { signal }) {
|
|
5816
5902
|
return new Promise((resolve, reject) => {
|
|
5817
5903
|
if (signal) {
|
|
@@ -5829,14 +5915,14 @@ async function delay(ms, { signal }) {
|
|
|
5829
5915
|
});
|
|
5830
5916
|
}
|
|
5831
5917
|
|
|
5832
|
-
// ../../node_modules/.pnpm/ky@
|
|
5833
|
-
var findUnknownOptions = (
|
|
5918
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/options.js
|
|
5919
|
+
var findUnknownOptions = (options) => {
|
|
5834
5920
|
const unknownOptions = {};
|
|
5835
5921
|
for (const key in options) {
|
|
5836
5922
|
if (!Object.hasOwn(options, key)) {
|
|
5837
5923
|
continue;
|
|
5838
5924
|
}
|
|
5839
|
-
if (!(key in requestOptionsRegistry) && !(key in kyOptionKeys)
|
|
5925
|
+
if (!(key in requestOptionsRegistry) && !(key in kyOptionKeys)) {
|
|
5840
5926
|
unknownOptions[key] = options[key];
|
|
5841
5927
|
}
|
|
5842
5928
|
}
|
|
@@ -5850,7 +5936,7 @@ var hasSearchParameters = (search) => {
|
|
|
5850
5936
|
return search.length > 0;
|
|
5851
5937
|
}
|
|
5852
5938
|
if (search instanceof URLSearchParams) {
|
|
5853
|
-
return search.size > 0;
|
|
5939
|
+
return search.size > 0 || Boolean(search[deletedParametersSymbol]?.size);
|
|
5854
5940
|
}
|
|
5855
5941
|
if (typeof search === "object") {
|
|
5856
5942
|
return Object.keys(search).length > 0;
|
|
@@ -5861,56 +5947,181 @@ var hasSearchParameters = (search) => {
|
|
|
5861
5947
|
return Boolean(search);
|
|
5862
5948
|
};
|
|
5863
5949
|
|
|
5864
|
-
// ../../node_modules/.pnpm/ky@
|
|
5950
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/is-network-error.js
|
|
5951
|
+
var objectToString = Object.prototype.toString;
|
|
5952
|
+
var isError = (value) => objectToString.call(value) === "[object Error]";
|
|
5953
|
+
var errorMessages = /* @__PURE__ */ new Set([
|
|
5954
|
+
"network error",
|
|
5955
|
+
// Chrome
|
|
5956
|
+
"NetworkError when attempting to fetch resource.",
|
|
5957
|
+
// Firefox
|
|
5958
|
+
"The Internet connection appears to be offline.",
|
|
5959
|
+
// Safari 16
|
|
5960
|
+
"Network request failed",
|
|
5961
|
+
// `cross-fetch`
|
|
5962
|
+
"fetch failed",
|
|
5963
|
+
// Undici (Node.js)
|
|
5964
|
+
"terminated",
|
|
5965
|
+
// Undici (Node.js)
|
|
5966
|
+
" A network error occurred.",
|
|
5967
|
+
// Bun (WebKit) - leading space is intentional
|
|
5968
|
+
"Network connection lost"
|
|
5969
|
+
// Cloudflare Workers (fetch)
|
|
5970
|
+
]);
|
|
5971
|
+
function isRawNetworkError(error2) {
|
|
5972
|
+
const isValid = error2 && isError(error2) && error2.name === "TypeError" && typeof error2.message === "string";
|
|
5973
|
+
if (!isValid) {
|
|
5974
|
+
return false;
|
|
5975
|
+
}
|
|
5976
|
+
const { message, stack } = error2;
|
|
5977
|
+
if (message === "Load failed") {
|
|
5978
|
+
return stack === void 0 || "__sentry_captured__" in error2;
|
|
5979
|
+
}
|
|
5980
|
+
if (message.startsWith("error sending request for url")) {
|
|
5981
|
+
return true;
|
|
5982
|
+
}
|
|
5983
|
+
if (message === "Failed to fetch" || message.startsWith("Failed to fetch (") && message.endsWith(")")) {
|
|
5984
|
+
return true;
|
|
5985
|
+
}
|
|
5986
|
+
return errorMessages.has(message);
|
|
5987
|
+
}
|
|
5988
|
+
|
|
5989
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/type-guards.js
|
|
5990
|
+
var isErrorType = (error2, cls) => error2 instanceof cls || error2?.name === cls.name;
|
|
5865
5991
|
function isHTTPError(error2) {
|
|
5866
|
-
return error2
|
|
5992
|
+
return isErrorType(error2, HTTPError);
|
|
5993
|
+
}
|
|
5994
|
+
function isNetworkError(error2) {
|
|
5995
|
+
return isErrorType(error2, NetworkError);
|
|
5867
5996
|
}
|
|
5868
5997
|
function isTimeoutError(error2) {
|
|
5869
|
-
return error2
|
|
5998
|
+
return isErrorType(error2, TimeoutError);
|
|
5870
5999
|
}
|
|
5871
6000
|
|
|
5872
|
-
// ../../node_modules/.pnpm/ky@
|
|
6001
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/core/Ky.js
|
|
6002
|
+
var maxErrorResponseBodySize = 10 * 1024 * 1024;
|
|
6003
|
+
var prefixUrlRenamedErrorMessage = "The `prefixUrl` option has been renamed `prefix` in v2 and enhanced to allow slashes in input. See also the new `baseUrl` option for improved flexibility with standard URL resolution: https://github.com/sindresorhus/ky#baseurl";
|
|
6004
|
+
var timedOutResponseData = /* @__PURE__ */ Symbol("timedOutResponseData");
|
|
6005
|
+
var createTextDecoder = (contentType) => {
|
|
6006
|
+
const match = /;\s*charset\s*=\s*(?:"([^"]+)"|([^;,\s]+))/i.exec(contentType);
|
|
6007
|
+
const charset = match?.[1] ?? match?.[2];
|
|
6008
|
+
if (charset) {
|
|
6009
|
+
try {
|
|
6010
|
+
return new TextDecoder(charset);
|
|
6011
|
+
} catch {
|
|
6012
|
+
}
|
|
6013
|
+
}
|
|
6014
|
+
return new TextDecoder();
|
|
6015
|
+
};
|
|
6016
|
+
var invalidSchemaMessage = "The `schema` argument must follow the Standard Schema specification";
|
|
6017
|
+
var cloneRetryOptions = (retry2) => {
|
|
6018
|
+
if (typeof retry2 !== "object") {
|
|
6019
|
+
return retry2;
|
|
6020
|
+
}
|
|
6021
|
+
return {
|
|
6022
|
+
...retry2,
|
|
6023
|
+
...retry2.methods && { methods: [...retry2.methods] },
|
|
6024
|
+
...retry2.statusCodes && { statusCodes: [...retry2.statusCodes] },
|
|
6025
|
+
...retry2.afterStatusCodes && { afterStatusCodes: [...retry2.afterStatusCodes] }
|
|
6026
|
+
};
|
|
6027
|
+
};
|
|
6028
|
+
var objectToString2 = Object.prototype.toString;
|
|
6029
|
+
var isRequestInstance = (value) => value instanceof globalThis.Request || objectToString2.call(value) === "[object Request]";
|
|
6030
|
+
var isResponseInstance = (value) => value instanceof globalThis.Response || objectToString2.call(value) === "[object Response]";
|
|
6031
|
+
var cloneSearchParametersForInitHook = (searchParameters) => {
|
|
6032
|
+
if (Array.isArray(searchParameters)) {
|
|
6033
|
+
return searchParameters.map((parameter) => [...parameter]);
|
|
6034
|
+
}
|
|
6035
|
+
return cloneShallow(searchParameters);
|
|
6036
|
+
};
|
|
6037
|
+
function cloneInitHookOptions(options) {
|
|
6038
|
+
const clonedOptions = {
|
|
6039
|
+
...options,
|
|
6040
|
+
json: cloneShallow(options.json),
|
|
6041
|
+
context: cloneShallow(options.context),
|
|
6042
|
+
headers: cloneShallow(options.headers),
|
|
6043
|
+
searchParams: cloneSearchParametersForInitHook(options.searchParams)
|
|
6044
|
+
};
|
|
6045
|
+
if (options.retry !== void 0) {
|
|
6046
|
+
clonedOptions.retry = cloneRetryOptions(options.retry);
|
|
6047
|
+
}
|
|
6048
|
+
return clonedOptions;
|
|
6049
|
+
}
|
|
6050
|
+
var validateJsonWithSchema = async (jsonValue, schema) => {
|
|
6051
|
+
if (typeof schema !== "object" && typeof schema !== "function" || schema === null) {
|
|
6052
|
+
throw new TypeError(invalidSchemaMessage);
|
|
6053
|
+
}
|
|
6054
|
+
const standardSchema = schema["~standard"];
|
|
6055
|
+
if (typeof standardSchema !== "object" || standardSchema === null || typeof standardSchema.validate !== "function") {
|
|
6056
|
+
throw new TypeError(invalidSchemaMessage);
|
|
6057
|
+
}
|
|
6058
|
+
const validationResult = await standardSchema.validate(jsonValue);
|
|
6059
|
+
if (validationResult.issues) {
|
|
6060
|
+
throw new SchemaValidationError(validationResult.issues);
|
|
6061
|
+
}
|
|
6062
|
+
return validationResult.value;
|
|
6063
|
+
};
|
|
5873
6064
|
var Ky = class _Ky {
|
|
5874
6065
|
static create(input, options) {
|
|
5875
|
-
const
|
|
6066
|
+
const initHooks = options.hooks?.init ?? [];
|
|
6067
|
+
const initHookOptions = initHooks.length > 0 ? cloneInitHookOptions(options) : options;
|
|
6068
|
+
for (const hook of initHooks) {
|
|
6069
|
+
hook(initHookOptions);
|
|
6070
|
+
}
|
|
6071
|
+
const ky2 = new _Ky(input, initHookOptions);
|
|
5876
6072
|
const function_ = async () => {
|
|
5877
6073
|
if (typeof ky2.#options.timeout === "number" && ky2.#options.timeout > maxSafeTimeout) {
|
|
5878
6074
|
throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`);
|
|
5879
6075
|
}
|
|
6076
|
+
if (typeof ky2.#options.totalTimeout === "number" && ky2.#options.totalTimeout > maxSafeTimeout) {
|
|
6077
|
+
throw new RangeError(`The \`totalTimeout\` option cannot be greater than ${maxSafeTimeout}`);
|
|
6078
|
+
}
|
|
5880
6079
|
await Promise.resolve();
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
}
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
6080
|
+
const beforeRequestResponse = await ky2.#runBeforeRequestHooks();
|
|
6081
|
+
let response = beforeRequestResponse ?? await ky2.#retry(async () => ky2.#fetch());
|
|
6082
|
+
let responseFromHook = beforeRequestResponse !== void 0 || ky2.#consumeReturnedResponseFromBeforeRetryHook();
|
|
6083
|
+
for (; ; ) {
|
|
6084
|
+
if (response === void 0) {
|
|
6085
|
+
return response;
|
|
6086
|
+
}
|
|
6087
|
+
if (isResponseInstance(response)) {
|
|
6088
|
+
try {
|
|
6089
|
+
response = await ky2.#runAfterResponseHooks(response);
|
|
6090
|
+
} catch (error2) {
|
|
6091
|
+
if (!(error2 instanceof ForceRetryError)) {
|
|
6092
|
+
throw error2;
|
|
6093
|
+
}
|
|
6094
|
+
const retriedResponse = await ky2.#retryFromError(error2, async () => ky2.#fetch());
|
|
6095
|
+
if (retriedResponse === void 0) {
|
|
6096
|
+
return retriedResponse;
|
|
6097
|
+
}
|
|
6098
|
+
response = retriedResponse;
|
|
6099
|
+
responseFromHook = ky2.#consumeReturnedResponseFromBeforeRetryHook();
|
|
6100
|
+
continue;
|
|
6101
|
+
}
|
|
5900
6102
|
}
|
|
5901
|
-
|
|
5902
|
-
|
|
6103
|
+
const currentResponse = response;
|
|
6104
|
+
if (!currentResponse.ok && currentResponse.type !== "opaque" && (typeof ky2.#options.throwHttpErrors === "function" ? ky2.#options.throwHttpErrors(currentResponse.status) : ky2.#options.throwHttpErrors)) {
|
|
6105
|
+
const httpError = new HTTPError(currentResponse, ky2.#getResponseRequest(currentResponse), ky2.#getNormalizedOptions());
|
|
6106
|
+
const errorToThrow = httpError;
|
|
6107
|
+
httpError.data = await ky2.#getResponseData(currentResponse);
|
|
6108
|
+
if (responseFromHook) {
|
|
6109
|
+
throw errorToThrow;
|
|
6110
|
+
}
|
|
6111
|
+
const retriedResponse = await ky2.#retryFromError(httpError, async () => ky2.#fetch());
|
|
6112
|
+
if (retriedResponse === void 0) {
|
|
6113
|
+
return retriedResponse;
|
|
6114
|
+
}
|
|
6115
|
+
response = retriedResponse;
|
|
6116
|
+
responseFromHook = ky2.#consumeReturnedResponseFromBeforeRetryHook();
|
|
6117
|
+
continue;
|
|
5903
6118
|
}
|
|
5904
|
-
|
|
6119
|
+
break;
|
|
5905
6120
|
}
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
let error2 = new HTTPError(response, ky2.request, ky2.#getNormalizedOptions());
|
|
5909
|
-
for (const hook of ky2.#options.hooks.beforeError) {
|
|
5910
|
-
error2 = await hook(error2, { retryCount: ky2.#retryCount });
|
|
5911
|
-
}
|
|
5912
|
-
throw error2;
|
|
6121
|
+
if (!isResponseInstance(response)) {
|
|
6122
|
+
return response;
|
|
5913
6123
|
}
|
|
6124
|
+
ky2.#decorateResponse(response);
|
|
5914
6125
|
if (ky2.#options.onDownloadProgress) {
|
|
5915
6126
|
if (typeof ky2.#options.onDownloadProgress !== "function") {
|
|
5916
6127
|
throw new TypeError("The `onDownloadProgress` option must be a function");
|
|
@@ -5924,32 +6135,56 @@ var Ky = class _Ky {
|
|
|
5924
6135
|
}
|
|
5925
6136
|
return response;
|
|
5926
6137
|
};
|
|
5927
|
-
const result =
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
6138
|
+
const result = (async () => {
|
|
6139
|
+
try {
|
|
6140
|
+
return await function_();
|
|
6141
|
+
} catch (error2) {
|
|
6142
|
+
if (!(error2 instanceof Error)) {
|
|
6143
|
+
throw error2;
|
|
6144
|
+
}
|
|
6145
|
+
if (ky2.#beforeRetryHookErrors.has(error2)) {
|
|
6146
|
+
throw error2;
|
|
6147
|
+
}
|
|
6148
|
+
let processedError = error2;
|
|
6149
|
+
for (const hook of ky2.#options.hooks.beforeError) {
|
|
6150
|
+
const hookResult = await hook({
|
|
6151
|
+
request: ky2.request,
|
|
6152
|
+
options: ky2.#getNormalizedOptions(),
|
|
6153
|
+
error: processedError,
|
|
6154
|
+
retryCount: ky2.#retryCount
|
|
6155
|
+
});
|
|
6156
|
+
if (hookResult instanceof Error) {
|
|
6157
|
+
processedError = hookResult;
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
throw processedError;
|
|
6161
|
+
} finally {
|
|
6162
|
+
const originalRequest = ky2.#originalRequest;
|
|
6163
|
+
ky2.#cancelBody(originalRequest?.body ?? void 0);
|
|
6164
|
+
if (ky2.request !== originalRequest) {
|
|
6165
|
+
ky2.#cancelBody(ky2.request.body ?? void 0);
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
})();
|
|
5932
6169
|
for (const [type, mimeType] of Object.entries(responseTypes)) {
|
|
5933
6170
|
if (type === "bytes" && typeof globalThis.Response?.prototype?.bytes !== "function") {
|
|
5934
6171
|
continue;
|
|
5935
6172
|
}
|
|
5936
|
-
result[type] = async () => {
|
|
6173
|
+
result[type] = async (schema) => {
|
|
5937
6174
|
ky2.request.headers.set("accept", ky2.request.headers.get("accept") || mimeType);
|
|
5938
6175
|
const response = await result;
|
|
5939
|
-
if (type
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
if (
|
|
5945
|
-
return
|
|
5946
|
-
}
|
|
5947
|
-
if (options.parseJson) {
|
|
5948
|
-
return options.parseJson(text);
|
|
6176
|
+
if (type !== "json") {
|
|
6177
|
+
return response[type]();
|
|
6178
|
+
}
|
|
6179
|
+
const text = await response.text();
|
|
6180
|
+
if (text === "") {
|
|
6181
|
+
if (schema !== void 0) {
|
|
6182
|
+
return validateJsonWithSchema(void 0, schema);
|
|
5949
6183
|
}
|
|
5950
6184
|
return JSON.parse(text);
|
|
5951
6185
|
}
|
|
5952
|
-
|
|
6186
|
+
const jsonValue = initHookOptions.parseJson ? await initHookOptions.parseJson(text, { request: ky2.#getResponseRequest(response), response }) : JSON.parse(text);
|
|
6187
|
+
return schema === void 0 ? jsonValue : validateJsonWithSchema(jsonValue, schema);
|
|
5953
6188
|
};
|
|
5954
6189
|
}
|
|
5955
6190
|
return result;
|
|
@@ -5969,44 +6204,55 @@ var Ky = class _Ky {
|
|
|
5969
6204
|
#options;
|
|
5970
6205
|
#originalRequest;
|
|
5971
6206
|
#userProvidedAbortSignal;
|
|
6207
|
+
#beforeRetryHookErrors = /* @__PURE__ */ new WeakSet();
|
|
5972
6208
|
#cachedNormalizedOptions;
|
|
6209
|
+
#startTime;
|
|
6210
|
+
#returnedResponseFromBeforeRetryHook = false;
|
|
6211
|
+
#responseRequests = /* @__PURE__ */ new WeakMap();
|
|
5973
6212
|
// eslint-disable-next-line complexity
|
|
5974
6213
|
constructor(input, options = {}) {
|
|
5975
6214
|
this.#input = input;
|
|
6215
|
+
if (Object.hasOwn(options, "prefixUrl")) {
|
|
6216
|
+
throw new Error(prefixUrlRenamedErrorMessage);
|
|
6217
|
+
}
|
|
5976
6218
|
this.#options = {
|
|
5977
6219
|
...options,
|
|
5978
6220
|
headers: mergeHeaders(this.#input.headers, options.headers),
|
|
5979
|
-
hooks: mergeHooks({
|
|
5980
|
-
beforeRequest: [],
|
|
5981
|
-
beforeRetry: [],
|
|
5982
|
-
beforeError: [],
|
|
5983
|
-
afterResponse: []
|
|
5984
|
-
}, options.hooks),
|
|
6221
|
+
hooks: mergeHooks({}, options.hooks),
|
|
5985
6222
|
method: normalizeRequestMethod(options.method ?? this.#input.method ?? "GET"),
|
|
5986
6223
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
5987
|
-
|
|
6224
|
+
prefix: String(options.prefix || ""),
|
|
5988
6225
|
retry: normalizeRetryOptions(options.retry),
|
|
5989
6226
|
throwHttpErrors: options.throwHttpErrors ?? true,
|
|
5990
6227
|
timeout: options.timeout ?? 1e4,
|
|
6228
|
+
totalTimeout: options.totalTimeout ?? false,
|
|
5991
6229
|
fetch: options.fetch ?? globalThis.fetch.bind(globalThis),
|
|
5992
6230
|
context: options.context ?? {}
|
|
5993
6231
|
};
|
|
5994
6232
|
if (typeof this.#input !== "string" && !(this.#input instanceof URL || this.#input instanceof globalThis.Request)) {
|
|
5995
6233
|
throw new TypeError("`input` must be a string, URL, or Request");
|
|
5996
6234
|
}
|
|
5997
|
-
if (
|
|
5998
|
-
if (this.#
|
|
5999
|
-
|
|
6235
|
+
if (typeof this.#input === "string") {
|
|
6236
|
+
if (this.#options.prefix) {
|
|
6237
|
+
const normalizedPrefix = this.#options.prefix.replace(/\/+$/, "");
|
|
6238
|
+
const normalizedInput = this.#input.replace(/^\/+/, "");
|
|
6239
|
+
this.#input = `${normalizedPrefix}/${normalizedInput}`;
|
|
6000
6240
|
}
|
|
6001
|
-
if (
|
|
6002
|
-
|
|
6241
|
+
if (this.#options.baseUrl) {
|
|
6242
|
+
let absoluteInput;
|
|
6243
|
+
try {
|
|
6244
|
+
absoluteInput = new URL(this.#input);
|
|
6245
|
+
} catch {
|
|
6246
|
+
}
|
|
6247
|
+
if (!absoluteInput) {
|
|
6248
|
+
this.#input = new URL(this.#input, new Request(this.#options.baseUrl).url);
|
|
6249
|
+
}
|
|
6003
6250
|
}
|
|
6004
|
-
this.#input = this.#options.prefixUrl + this.#input;
|
|
6005
6251
|
}
|
|
6006
6252
|
if (supportsAbortController && supportsAbortSignal) {
|
|
6007
6253
|
this.#userProvidedAbortSignal = this.#options.signal ?? this.#input.signal;
|
|
6008
6254
|
this.#abortController = new globalThis.AbortController();
|
|
6009
|
-
this.#options.signal = this.#
|
|
6255
|
+
this.#options.signal = this.#createManagedSignal();
|
|
6010
6256
|
}
|
|
6011
6257
|
if (supportsRequestStreams) {
|
|
6012
6258
|
this.#options.duplex = "half";
|
|
@@ -6021,23 +6267,40 @@ var Ky = class _Ky {
|
|
|
6021
6267
|
}
|
|
6022
6268
|
this.request = new globalThis.Request(this.#input, this.#options);
|
|
6023
6269
|
if (hasSearchParameters(this.#options.searchParams)) {
|
|
6024
|
-
const
|
|
6025
|
-
const
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6270
|
+
const url = new URL(this.request.url);
|
|
6271
|
+
const deleted = this.#options.searchParams?.[deletedParametersSymbol];
|
|
6272
|
+
if (deleted) {
|
|
6273
|
+
for (const key of deleted) {
|
|
6274
|
+
url.searchParams.delete(key);
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
if (typeof this.#options.searchParams === "string") {
|
|
6278
|
+
const stringSearchParameters = this.#options.searchParams.replace(/^\?/, "");
|
|
6279
|
+
if (stringSearchParameters !== "") {
|
|
6280
|
+
url.search = url.search ? `${url.search}&${stringSearchParameters}` : `?${stringSearchParameters}`;
|
|
6281
|
+
}
|
|
6282
|
+
} else {
|
|
6283
|
+
const optionsSearchParameters = new URLSearchParams(_Ky.#normalizeSearchParams(this.#options.searchParams));
|
|
6284
|
+
for (const [key, value] of optionsSearchParameters.entries()) {
|
|
6285
|
+
url.searchParams.append(key, value);
|
|
6286
|
+
}
|
|
6032
6287
|
}
|
|
6033
|
-
if (!
|
|
6034
|
-
|
|
6288
|
+
if (this.#options.searchParams && typeof this.#options.searchParams === "object" && !Array.isArray(this.#options.searchParams) && !(this.#options.searchParams instanceof URLSearchParams)) {
|
|
6289
|
+
for (const [key, value] of Object.entries(this.#options.searchParams)) {
|
|
6290
|
+
if (value === void 0) {
|
|
6291
|
+
url.searchParams.delete(key);
|
|
6292
|
+
}
|
|
6293
|
+
}
|
|
6035
6294
|
}
|
|
6036
|
-
this.request =
|
|
6295
|
+
this.request = new globalThis.Request(url, this.#options);
|
|
6296
|
+
}
|
|
6297
|
+
if (this.#options.onUploadProgress && typeof this.#options.onUploadProgress !== "function") {
|
|
6298
|
+
throw new TypeError("The `onUploadProgress` option must be a function");
|
|
6037
6299
|
}
|
|
6300
|
+
this.#startTime = typeof this.#options.totalTimeout === "number" ? this.#getCurrentTime() : void 0;
|
|
6038
6301
|
}
|
|
6039
6302
|
#calculateDelay() {
|
|
6040
|
-
const retryDelay = this.#options.retry.delay(this.#retryCount);
|
|
6303
|
+
const retryDelay = this.#options.retry.delay(this.#retryCount + 1);
|
|
6041
6304
|
let jitteredDelay = retryDelay;
|
|
6042
6305
|
if (this.#options.retry.jitter === true) {
|
|
6043
6306
|
jitteredDelay = Math.random() * retryDelay;
|
|
@@ -6047,12 +6310,10 @@ var Ky = class _Ky {
|
|
|
6047
6310
|
jitteredDelay = retryDelay;
|
|
6048
6311
|
}
|
|
6049
6312
|
}
|
|
6050
|
-
|
|
6051
|
-
return Math.min(backoffLimit, jitteredDelay);
|
|
6313
|
+
return Math.min(this.#options.retry.backoffLimit, jitteredDelay);
|
|
6052
6314
|
}
|
|
6053
6315
|
async #calculateRetryDelay(error2) {
|
|
6054
|
-
this.#retryCount
|
|
6055
|
-
if (this.#retryCount > this.#options.retry.limit) {
|
|
6316
|
+
if (this.#retryCount >= this.#options.retry.limit) {
|
|
6056
6317
|
throw error2;
|
|
6057
6318
|
}
|
|
6058
6319
|
const errorObject = error2 instanceof Error ? error2 : new NonError(error2);
|
|
@@ -6063,7 +6324,7 @@ var Ky = class _Ky {
|
|
|
6063
6324
|
throw error2;
|
|
6064
6325
|
}
|
|
6065
6326
|
if (this.#options.retry.shouldRetry !== void 0) {
|
|
6066
|
-
const result = await this.#options.retry.shouldRetry({ error: errorObject, retryCount: this.#retryCount });
|
|
6327
|
+
const result = await this.#options.retry.shouldRetry({ error: errorObject, retryCount: this.#retryCount + 1 });
|
|
6067
6328
|
if (result === false) {
|
|
6068
6329
|
throw error2;
|
|
6069
6330
|
}
|
|
@@ -6071,8 +6332,11 @@ var Ky = class _Ky {
|
|
|
6071
6332
|
return this.#calculateDelay();
|
|
6072
6333
|
}
|
|
6073
6334
|
}
|
|
6074
|
-
if (isTimeoutError(error2)
|
|
6075
|
-
|
|
6335
|
+
if (isTimeoutError(error2)) {
|
|
6336
|
+
if (!this.#options.retry.retryOnTimeout) {
|
|
6337
|
+
throw error2;
|
|
6338
|
+
}
|
|
6339
|
+
return this.#calculateDelay();
|
|
6076
6340
|
}
|
|
6077
6341
|
if (isHTTPError(error2)) {
|
|
6078
6342
|
if (!this.#options.retry.statusCodes.includes(error2.response.status)) {
|
|
@@ -6086,21 +6350,138 @@ var Ky = class _Ky {
|
|
|
6086
6350
|
} else if (after >= Date.parse("2024-01-01")) {
|
|
6087
6351
|
after -= Date.now();
|
|
6088
6352
|
}
|
|
6089
|
-
|
|
6090
|
-
|
|
6353
|
+
if (!Number.isFinite(after)) {
|
|
6354
|
+
return Math.min(this.#options.retry.maxRetryAfter, this.#calculateDelay());
|
|
6355
|
+
}
|
|
6356
|
+
after = Math.max(0, after);
|
|
6357
|
+
return Math.min(this.#options.retry.maxRetryAfter, after);
|
|
6091
6358
|
}
|
|
6092
6359
|
if (error2.response.status === 413) {
|
|
6093
6360
|
throw error2;
|
|
6094
6361
|
}
|
|
6362
|
+
return this.#calculateDelay();
|
|
6363
|
+
}
|
|
6364
|
+
if (!isNetworkError(error2)) {
|
|
6365
|
+
throw error2;
|
|
6095
6366
|
}
|
|
6096
6367
|
return this.#calculateDelay();
|
|
6097
6368
|
}
|
|
6098
6369
|
#decorateResponse(response) {
|
|
6370
|
+
const request = this.#getResponseRequest(response);
|
|
6099
6371
|
if (this.#options.parseJson) {
|
|
6100
|
-
response.json = async () =>
|
|
6372
|
+
response.json = async () => {
|
|
6373
|
+
const text = await response.text();
|
|
6374
|
+
if (text === "") {
|
|
6375
|
+
return JSON.parse(text);
|
|
6376
|
+
}
|
|
6377
|
+
return this.#options.parseJson(text, { request, response });
|
|
6378
|
+
};
|
|
6101
6379
|
}
|
|
6102
6380
|
return response;
|
|
6103
6381
|
}
|
|
6382
|
+
async #getResponseData(response) {
|
|
6383
|
+
const text = await this.#readResponseText(response, this.#getErrorDataTimeout());
|
|
6384
|
+
if (text === timedOutResponseData) {
|
|
6385
|
+
this.#throwIfTotalTimeoutExhausted();
|
|
6386
|
+
return void 0;
|
|
6387
|
+
}
|
|
6388
|
+
if (!text) {
|
|
6389
|
+
return void 0;
|
|
6390
|
+
}
|
|
6391
|
+
if (!this.#isJsonContentType(response.headers.get("content-type") ?? "")) {
|
|
6392
|
+
return text;
|
|
6393
|
+
}
|
|
6394
|
+
const data = await this.#parseJson(text, response, this.#getErrorDataTimeout(), this.#getResponseRequest(response));
|
|
6395
|
+
if (data === timedOutResponseData) {
|
|
6396
|
+
this.#throwIfTotalTimeoutExhausted();
|
|
6397
|
+
return void 0;
|
|
6398
|
+
}
|
|
6399
|
+
return data;
|
|
6400
|
+
}
|
|
6401
|
+
#getErrorDataTimeout() {
|
|
6402
|
+
const errorDataTimeout = this.#options.timeout === false ? 1e4 : this.#options.timeout;
|
|
6403
|
+
const remainingTotal = this.#getRemainingTotalTimeout();
|
|
6404
|
+
if (remainingTotal === void 0) {
|
|
6405
|
+
return errorDataTimeout;
|
|
6406
|
+
}
|
|
6407
|
+
if (remainingTotal <= 0) {
|
|
6408
|
+
throw new TimeoutError(this.request);
|
|
6409
|
+
}
|
|
6410
|
+
return Math.min(errorDataTimeout, remainingTotal);
|
|
6411
|
+
}
|
|
6412
|
+
#isJsonContentType(contentType) {
|
|
6413
|
+
const mimeType = (contentType.split(";", 1)[0] ?? "").trim().toLowerCase();
|
|
6414
|
+
return /\/(?:.*[.+-])?json$/.test(mimeType);
|
|
6415
|
+
}
|
|
6416
|
+
async #readResponseText(response, timeoutMs) {
|
|
6417
|
+
const { body } = response;
|
|
6418
|
+
if (!body) {
|
|
6419
|
+
try {
|
|
6420
|
+
return await response.text();
|
|
6421
|
+
} catch {
|
|
6422
|
+
return void 0;
|
|
6423
|
+
}
|
|
6424
|
+
}
|
|
6425
|
+
let reader;
|
|
6426
|
+
try {
|
|
6427
|
+
reader = body.getReader();
|
|
6428
|
+
} catch {
|
|
6429
|
+
return void 0;
|
|
6430
|
+
}
|
|
6431
|
+
const decoder = createTextDecoder(response.headers.get("content-type") ?? "");
|
|
6432
|
+
const chunks = [];
|
|
6433
|
+
let totalBytes = 0;
|
|
6434
|
+
const readAll = (async () => {
|
|
6435
|
+
try {
|
|
6436
|
+
for (; ; ) {
|
|
6437
|
+
const { done, value } = await reader.read();
|
|
6438
|
+
if (done) {
|
|
6439
|
+
break;
|
|
6440
|
+
}
|
|
6441
|
+
totalBytes += value.byteLength;
|
|
6442
|
+
if (totalBytes > maxErrorResponseBodySize) {
|
|
6443
|
+
void reader.cancel().catch(() => void 0);
|
|
6444
|
+
return void 0;
|
|
6445
|
+
}
|
|
6446
|
+
chunks.push(decoder.decode(value, { stream: true }));
|
|
6447
|
+
}
|
|
6448
|
+
} catch {
|
|
6449
|
+
return void 0;
|
|
6450
|
+
}
|
|
6451
|
+
chunks.push(decoder.decode());
|
|
6452
|
+
return chunks.join("");
|
|
6453
|
+
})();
|
|
6454
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
6455
|
+
const timeoutId = setTimeout(() => {
|
|
6456
|
+
resolve(timedOutResponseData);
|
|
6457
|
+
}, timeoutMs);
|
|
6458
|
+
void readAll.finally(() => {
|
|
6459
|
+
clearTimeout(timeoutId);
|
|
6460
|
+
});
|
|
6461
|
+
});
|
|
6462
|
+
const result = await Promise.race([readAll, timeoutPromise]);
|
|
6463
|
+
if (result === timedOutResponseData) {
|
|
6464
|
+
void reader.cancel().catch(() => void 0);
|
|
6465
|
+
}
|
|
6466
|
+
return result;
|
|
6467
|
+
}
|
|
6468
|
+
async #parseJson(text, response, timeoutMs, request) {
|
|
6469
|
+
let timeoutId;
|
|
6470
|
+
try {
|
|
6471
|
+
return await Promise.race([
|
|
6472
|
+
Promise.resolve().then(() => this.#options.parseJson ? this.#options.parseJson(text, { request, response }) : JSON.parse(text)),
|
|
6473
|
+
new Promise((resolve) => {
|
|
6474
|
+
timeoutId = setTimeout(() => {
|
|
6475
|
+
resolve(timedOutResponseData);
|
|
6476
|
+
}, timeoutMs);
|
|
6477
|
+
})
|
|
6478
|
+
]);
|
|
6479
|
+
} catch {
|
|
6480
|
+
return void 0;
|
|
6481
|
+
} finally {
|
|
6482
|
+
clearTimeout(timeoutId);
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6104
6485
|
#cancelBody(body) {
|
|
6105
6486
|
if (!body) {
|
|
6106
6487
|
return;
|
|
@@ -6110,84 +6491,200 @@ var Ky = class _Ky {
|
|
|
6110
6491
|
#cancelResponseBody(response) {
|
|
6111
6492
|
this.#cancelBody(response.body ?? void 0);
|
|
6112
6493
|
}
|
|
6494
|
+
#createManagedSignal() {
|
|
6495
|
+
return this.#userProvidedAbortSignal ? AbortSignal.any([this.#userProvidedAbortSignal, this.#abortController.signal]) : this.#abortController.signal;
|
|
6496
|
+
}
|
|
6497
|
+
#throwIfTotalTimeoutExhausted() {
|
|
6498
|
+
const remaining = this.#getRemainingTotalTimeout();
|
|
6499
|
+
if (remaining !== void 0 && remaining <= 0) {
|
|
6500
|
+
throw new TimeoutError(this.request);
|
|
6501
|
+
}
|
|
6502
|
+
}
|
|
6503
|
+
async #runBeforeRequestHooks() {
|
|
6504
|
+
for (const hook of this.#options.hooks.beforeRequest) {
|
|
6505
|
+
const result = await hook({
|
|
6506
|
+
request: this.request,
|
|
6507
|
+
options: this.#getNormalizedOptions(),
|
|
6508
|
+
retryCount: 0
|
|
6509
|
+
});
|
|
6510
|
+
if (isRequestInstance(result)) {
|
|
6511
|
+
this.#assignRequest(result);
|
|
6512
|
+
} else if (isResponseInstance(result)) {
|
|
6513
|
+
return result;
|
|
6514
|
+
}
|
|
6515
|
+
}
|
|
6516
|
+
return void 0;
|
|
6517
|
+
}
|
|
6518
|
+
async #runAfterResponseHooks(response) {
|
|
6519
|
+
const responseRequest = this.#getResponseRequest(response);
|
|
6520
|
+
for (const hook of this.#options.hooks.afterResponse) {
|
|
6521
|
+
const hookResponse = this.#setResponseRequest(response.clone(), responseRequest);
|
|
6522
|
+
this.#decorateResponse(hookResponse);
|
|
6523
|
+
let modifiedResponse;
|
|
6524
|
+
try {
|
|
6525
|
+
modifiedResponse = await hook({
|
|
6526
|
+
request: this.request,
|
|
6527
|
+
options: this.#getNormalizedOptions(),
|
|
6528
|
+
response: hookResponse,
|
|
6529
|
+
retryCount: this.#retryCount
|
|
6530
|
+
});
|
|
6531
|
+
} catch (error2) {
|
|
6532
|
+
if (hookResponse !== response) {
|
|
6533
|
+
this.#cancelResponseBody(hookResponse);
|
|
6534
|
+
}
|
|
6535
|
+
this.#cancelResponseBody(response);
|
|
6536
|
+
throw error2;
|
|
6537
|
+
}
|
|
6538
|
+
if (modifiedResponse instanceof RetryMarker) {
|
|
6539
|
+
if (hookResponse !== response) {
|
|
6540
|
+
this.#cancelResponseBody(hookResponse);
|
|
6541
|
+
}
|
|
6542
|
+
this.#cancelResponseBody(response);
|
|
6543
|
+
throw new ForceRetryError(modifiedResponse.options);
|
|
6544
|
+
}
|
|
6545
|
+
const nextResponse = isResponseInstance(modifiedResponse) ? this.#setResponseRequest(modifiedResponse, responseRequest) : response;
|
|
6546
|
+
if (hookResponse !== response && hookResponse !== nextResponse && hookResponse.body !== nextResponse.body) {
|
|
6547
|
+
this.#cancelResponseBody(hookResponse);
|
|
6548
|
+
}
|
|
6549
|
+
if (response !== nextResponse && response.body !== nextResponse.body) {
|
|
6550
|
+
this.#cancelResponseBody(response);
|
|
6551
|
+
}
|
|
6552
|
+
response = nextResponse;
|
|
6553
|
+
}
|
|
6554
|
+
return response;
|
|
6555
|
+
}
|
|
6113
6556
|
async #retry(function_) {
|
|
6114
6557
|
try {
|
|
6115
6558
|
return await function_();
|
|
6116
6559
|
} catch (error2) {
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6560
|
+
return this.#retryFromError(error2, function_);
|
|
6561
|
+
}
|
|
6562
|
+
}
|
|
6563
|
+
async #retryFromError(error2, function_) {
|
|
6564
|
+
this.#returnedResponseFromBeforeRetryHook = false;
|
|
6565
|
+
const retryDelay = Math.min(await this.#calculateRetryDelay(error2), maxSafeTimeout);
|
|
6566
|
+
const delayOptions = { signal: this.#userProvidedAbortSignal };
|
|
6567
|
+
const remainingTimeout = this.#getRemainingTotalTimeout();
|
|
6568
|
+
if (remainingTimeout !== void 0) {
|
|
6569
|
+
if (remainingTimeout <= 0) {
|
|
6570
|
+
throw new TimeoutError(this.request);
|
|
6120
6571
|
}
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
this.#assignRequest(managedRequest);
|
|
6572
|
+
if (retryDelay >= remainingTimeout) {
|
|
6573
|
+
await delay(remainingTimeout, delayOptions);
|
|
6574
|
+
throw new TimeoutError(this.request);
|
|
6125
6575
|
}
|
|
6126
|
-
|
|
6127
|
-
|
|
6576
|
+
}
|
|
6577
|
+
await delay(retryDelay, delayOptions);
|
|
6578
|
+
this.#throwIfTotalTimeoutExhausted();
|
|
6579
|
+
if (error2 instanceof ForceRetryError && error2.customRequest) {
|
|
6580
|
+
const customRequest = new globalThis.Request(error2.customRequest, this.#options.signal ? { signal: this.#options.signal } : void 0);
|
|
6581
|
+
this.#assignRequest(customRequest);
|
|
6582
|
+
}
|
|
6583
|
+
for (const hook of this.#options.hooks.beforeRetry) {
|
|
6584
|
+
let hookResult;
|
|
6585
|
+
try {
|
|
6586
|
+
hookResult = await hook({
|
|
6128
6587
|
request: this.request,
|
|
6129
6588
|
options: this.#getNormalizedOptions(),
|
|
6130
6589
|
error: error2,
|
|
6131
|
-
retryCount: this.#retryCount
|
|
6590
|
+
retryCount: this.#retryCount + 1
|
|
6132
6591
|
});
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
}
|
|
6137
|
-
if (hookResult instanceof globalThis.Response) {
|
|
6138
|
-
return hookResult;
|
|
6139
|
-
}
|
|
6140
|
-
if (hookResult === stop) {
|
|
6141
|
-
return;
|
|
6592
|
+
} catch (hookError) {
|
|
6593
|
+
if (hookError instanceof Error && hookError !== error2) {
|
|
6594
|
+
this.#beforeRetryHookErrors.add(hookError);
|
|
6142
6595
|
}
|
|
6596
|
+
throw hookError;
|
|
6597
|
+
}
|
|
6598
|
+
if (isRequestInstance(hookResult)) {
|
|
6599
|
+
this.#assignRequest(hookResult);
|
|
6600
|
+
break;
|
|
6601
|
+
}
|
|
6602
|
+
if (isResponseInstance(hookResult)) {
|
|
6603
|
+
this.#returnedResponseFromBeforeRetryHook = true;
|
|
6604
|
+
this.#retryCount++;
|
|
6605
|
+
return hookResult;
|
|
6606
|
+
}
|
|
6607
|
+
if (hookResult === stop) {
|
|
6608
|
+
return;
|
|
6143
6609
|
}
|
|
6144
|
-
return this.#retry(function_);
|
|
6145
6610
|
}
|
|
6611
|
+
this.#throwIfTotalTimeoutExhausted();
|
|
6612
|
+
this.#retryCount++;
|
|
6613
|
+
return this.#retry(function_);
|
|
6614
|
+
}
|
|
6615
|
+
#consumeReturnedResponseFromBeforeRetryHook() {
|
|
6616
|
+
const value = this.#returnedResponseFromBeforeRetryHook;
|
|
6617
|
+
this.#returnedResponseFromBeforeRetryHook = false;
|
|
6618
|
+
return value;
|
|
6146
6619
|
}
|
|
6147
6620
|
async #fetch() {
|
|
6148
6621
|
if (this.#abortController?.signal.aborted) {
|
|
6149
6622
|
this.#abortController = new globalThis.AbortController();
|
|
6150
|
-
this.#options.signal = this.#
|
|
6623
|
+
this.#options.signal = this.#createManagedSignal();
|
|
6151
6624
|
this.request = new globalThis.Request(this.request, { signal: this.#options.signal });
|
|
6152
6625
|
}
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6626
|
+
const nonRequestOptions = findUnknownOptions(this.#options);
|
|
6627
|
+
const retryRequest = this.#options.retry.limit > 0 ? this.request.clone() : void 0;
|
|
6628
|
+
const request = this.#wrapRequestWithUploadProgress(this.request, this.#options.body ?? void 0);
|
|
6629
|
+
this.#originalRequest = request;
|
|
6630
|
+
if (retryRequest) {
|
|
6631
|
+
this.request = retryRequest;
|
|
6632
|
+
}
|
|
6633
|
+
try {
|
|
6634
|
+
const remainingTotal = this.#getRemainingTotalTimeout();
|
|
6635
|
+
if (remainingTotal !== void 0 && remainingTotal <= 0) {
|
|
6636
|
+
throw new TimeoutError(this.request);
|
|
6637
|
+
}
|
|
6638
|
+
const effectiveTimeout = this.#options.timeout === false ? remainingTotal : remainingTotal === void 0 ? this.#options.timeout : Math.min(this.#options.timeout, remainingTotal);
|
|
6639
|
+
const response = effectiveTimeout === void 0 ? await this.#options.fetch(request, nonRequestOptions) : await timeout(request, nonRequestOptions, this.#abortController, {
|
|
6640
|
+
timeout: effectiveTimeout,
|
|
6641
|
+
fetch: this.#options.fetch
|
|
6642
|
+
});
|
|
6643
|
+
return this.#setResponseRequest(response, request);
|
|
6644
|
+
} catch (error2) {
|
|
6645
|
+
if (isRawNetworkError(error2)) {
|
|
6646
|
+
throw new NetworkError(this.request, { cause: error2 });
|
|
6161
6647
|
}
|
|
6648
|
+
throw error2;
|
|
6162
6649
|
}
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
this
|
|
6166
|
-
|
|
6167
|
-
return this.#options.fetch(this.#originalRequest, nonRequestOptions);
|
|
6650
|
+
}
|
|
6651
|
+
#getRemainingTotalTimeout() {
|
|
6652
|
+
if (this.#startTime === void 0) {
|
|
6653
|
+
return void 0;
|
|
6168
6654
|
}
|
|
6169
|
-
|
|
6655
|
+
const elapsed = this.#getCurrentTime() - this.#startTime;
|
|
6656
|
+
return Math.max(0, this.#options.totalTimeout - elapsed);
|
|
6657
|
+
}
|
|
6658
|
+
#getCurrentTime() {
|
|
6659
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
6170
6660
|
}
|
|
6171
6661
|
#getNormalizedOptions() {
|
|
6172
6662
|
if (!this.#cachedNormalizedOptions) {
|
|
6173
|
-
const { hooks, ...normalizedOptions } = this.#options;
|
|
6663
|
+
const { hooks, json, parseJson, stringifyJson, searchParams, timeout: timeout2, totalTimeout, throwHttpErrors, fetch: fetch2, ...normalizedOptions } = this.#options;
|
|
6174
6664
|
this.#cachedNormalizedOptions = Object.freeze(normalizedOptions);
|
|
6175
6665
|
}
|
|
6176
6666
|
return this.#cachedNormalizedOptions;
|
|
6177
6667
|
}
|
|
6178
6668
|
#assignRequest(request) {
|
|
6179
6669
|
this.#cachedNormalizedOptions = void 0;
|
|
6180
|
-
this.request =
|
|
6670
|
+
this.request = request;
|
|
6671
|
+
}
|
|
6672
|
+
#getResponseRequest(response) {
|
|
6673
|
+
return this.#responseRequests.get(response) ?? this.request;
|
|
6674
|
+
}
|
|
6675
|
+
#setResponseRequest(response, request) {
|
|
6676
|
+
this.#responseRequests.set(response, request);
|
|
6677
|
+
return response;
|
|
6181
6678
|
}
|
|
6182
6679
|
#wrapRequestWithUploadProgress(request, originalBody) {
|
|
6183
|
-
if (!this.#options.onUploadProgress || !request.body) {
|
|
6680
|
+
if (!this.#options.onUploadProgress || !request.body || !supportsRequestStreams) {
|
|
6184
6681
|
return request;
|
|
6185
6682
|
}
|
|
6186
6683
|
return streamRequest(request, this.#options.onUploadProgress, originalBody ?? this.#options.body ?? void 0);
|
|
6187
6684
|
}
|
|
6188
6685
|
};
|
|
6189
6686
|
|
|
6190
|
-
// ../../node_modules/.pnpm/ky@
|
|
6687
|
+
// ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/index.js
|
|
6191
6688
|
var createInstance = (defaults) => {
|
|
6192
6689
|
const ky2 = (input, options) => Ky.create(input, validateAndMerge(defaults, options));
|
|
6193
6690
|
for (const method of requestMethods) {
|
|
@@ -6294,13 +6791,13 @@ var KinoSecClient = class {
|
|
|
6294
6791
|
constructor(config) {
|
|
6295
6792
|
const prefixPathname = new URL(config.apiUrl).pathname.replace(/\/?$/, "/");
|
|
6296
6793
|
this.api = distribution_default.create({
|
|
6297
|
-
|
|
6794
|
+
prefix: config.apiUrl,
|
|
6298
6795
|
headers: {
|
|
6299
6796
|
"user-agent": config.userAgent ?? `kinosec/0.1.0 (${process.platform})`
|
|
6300
6797
|
},
|
|
6301
6798
|
hooks: {
|
|
6302
6799
|
beforeRequest: [
|
|
6303
|
-
(request) => {
|
|
6800
|
+
({ request }) => {
|
|
6304
6801
|
request.headers.set("authorization", `Bearer ${config.token}`);
|
|
6305
6802
|
const url = new URL(request.url);
|
|
6306
6803
|
const path = url.pathname.slice(prefixPathname.length - 1);
|
|
@@ -6313,15 +6810,9 @@ var KinoSecClient = class {
|
|
|
6313
6810
|
}
|
|
6314
6811
|
],
|
|
6315
6812
|
beforeError: [
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
try {
|
|
6320
|
-
body = await response.json();
|
|
6321
|
-
} catch {
|
|
6322
|
-
body = null;
|
|
6323
|
-
}
|
|
6324
|
-
throw parseApiError(body, response.status);
|
|
6813
|
+
({ error: error2 }) => {
|
|
6814
|
+
if (!isHTTPError(error2)) return error2;
|
|
6815
|
+
return parseApiError(error2.data, error2.response.status);
|
|
6325
6816
|
}
|
|
6326
6817
|
]
|
|
6327
6818
|
},
|
|
@@ -6495,6 +6986,117 @@ var AuditResource = class {
|
|
|
6495
6986
|
return this.api.get("v1/audit", { searchParams: buildSearchParams(params) }).json();
|
|
6496
6987
|
}
|
|
6497
6988
|
};
|
|
6989
|
+
var MULTI_LEVEL_TLDS = /* @__PURE__ */ new Set([
|
|
6990
|
+
"co.uk",
|
|
6991
|
+
"org.uk",
|
|
6992
|
+
"ac.uk",
|
|
6993
|
+
"me.uk",
|
|
6994
|
+
"com.au",
|
|
6995
|
+
"net.au",
|
|
6996
|
+
"org.au",
|
|
6997
|
+
"co.nz",
|
|
6998
|
+
"net.nz",
|
|
6999
|
+
"org.nz",
|
|
7000
|
+
"co.jp",
|
|
7001
|
+
"or.jp",
|
|
7002
|
+
"ne.jp",
|
|
7003
|
+
"co.kr",
|
|
7004
|
+
"or.kr",
|
|
7005
|
+
"co.za",
|
|
7006
|
+
"org.za",
|
|
7007
|
+
"web.za",
|
|
7008
|
+
"co.in",
|
|
7009
|
+
"net.in",
|
|
7010
|
+
"org.in",
|
|
7011
|
+
"co.id",
|
|
7012
|
+
"or.id",
|
|
7013
|
+
"com.br",
|
|
7014
|
+
"net.br",
|
|
7015
|
+
"org.br",
|
|
7016
|
+
"com.mx",
|
|
7017
|
+
"org.mx",
|
|
7018
|
+
"com.ar",
|
|
7019
|
+
"org.ar",
|
|
7020
|
+
"com.cn",
|
|
7021
|
+
"net.cn",
|
|
7022
|
+
"org.cn",
|
|
7023
|
+
"com.tw",
|
|
7024
|
+
"org.tw",
|
|
7025
|
+
"com.sg",
|
|
7026
|
+
"org.sg",
|
|
7027
|
+
"com.ph",
|
|
7028
|
+
"org.ph",
|
|
7029
|
+
"com.tr",
|
|
7030
|
+
"org.tr",
|
|
7031
|
+
"com.ng",
|
|
7032
|
+
"org.ng"
|
|
7033
|
+
]);
|
|
7034
|
+
var PUBLIC_HOSTING_SUFFIXES = /* @__PURE__ */ new Set([
|
|
7035
|
+
"github.io",
|
|
7036
|
+
"gitlab.io",
|
|
7037
|
+
"bitbucket.io",
|
|
7038
|
+
"vercel.app",
|
|
7039
|
+
"netlify.app",
|
|
7040
|
+
"netlify.com",
|
|
7041
|
+
"lovable.app",
|
|
7042
|
+
"lovableproject.com",
|
|
7043
|
+
"herokuapp.com",
|
|
7044
|
+
"appspot.com",
|
|
7045
|
+
"firebaseapp.com",
|
|
7046
|
+
"web.app",
|
|
7047
|
+
"azurewebsites.net",
|
|
7048
|
+
"cloudfront.net",
|
|
7049
|
+
"amazonaws.com",
|
|
7050
|
+
"replit.app",
|
|
7051
|
+
"repl.co",
|
|
7052
|
+
"readme.io",
|
|
7053
|
+
"readthedocs.io",
|
|
7054
|
+
"readthedocs.org",
|
|
7055
|
+
"statuspage.io",
|
|
7056
|
+
"uptimerobot.com",
|
|
7057
|
+
"myshopify.com",
|
|
7058
|
+
"webflow.io",
|
|
7059
|
+
"wixsite.com",
|
|
7060
|
+
"squarespace.com",
|
|
7061
|
+
"blogspot.com",
|
|
7062
|
+
"tumblr.com",
|
|
7063
|
+
"substack.com",
|
|
7064
|
+
"medium.com",
|
|
7065
|
+
"slack.com",
|
|
7066
|
+
"notion.site",
|
|
7067
|
+
"codesandbox.io",
|
|
7068
|
+
"stackblitz.io",
|
|
7069
|
+
"glitch.me",
|
|
7070
|
+
"ngrok.app",
|
|
7071
|
+
"ngrok.io",
|
|
7072
|
+
"ngrok-free.app"
|
|
7073
|
+
]);
|
|
7074
|
+
var IPV4_RE = /^\d{1,3}(\.\d{1,3}){3}$/;
|
|
7075
|
+
function normalize(value) {
|
|
7076
|
+
return value.replace(/:\d+$/, "").replace(/\.+$/, "").toLowerCase();
|
|
7077
|
+
}
|
|
7078
|
+
function detectAssetType(value) {
|
|
7079
|
+
const host = normalize(value);
|
|
7080
|
+
if (IPV4_RE.test(host) || host.includes(":")) {
|
|
7081
|
+
return "ip";
|
|
7082
|
+
}
|
|
7083
|
+
const labels = host.split(".");
|
|
7084
|
+
if (labels.length <= 1) return "domain";
|
|
7085
|
+
const tld2 = labels.slice(-2).join(".");
|
|
7086
|
+
const registrableLabels = MULTI_LEVEL_TLDS.has(tld2) ? 3 : 2;
|
|
7087
|
+
return labels.length > registrableLabels ? "subdomain" : "domain";
|
|
7088
|
+
}
|
|
7089
|
+
function deriveParentDomain(value) {
|
|
7090
|
+
const host = normalize(value);
|
|
7091
|
+
if (detectAssetType(host) !== "subdomain") return null;
|
|
7092
|
+
const labels = host.split(".");
|
|
7093
|
+
const tld2 = labels.slice(-2).join(".");
|
|
7094
|
+
const registrableLabels = MULTI_LEVEL_TLDS.has(tld2) ? 3 : 2;
|
|
7095
|
+
return labels.slice(-registrableLabels).join(".");
|
|
7096
|
+
}
|
|
7097
|
+
function isPublicSuffix(value) {
|
|
7098
|
+
return PUBLIC_HOSTING_SUFFIXES.has(normalize(value));
|
|
7099
|
+
}
|
|
6498
7100
|
|
|
6499
7101
|
// src/lib/output.ts
|
|
6500
7102
|
var import_cli_table3 = __toESM(require_cli_table3(), 1);
|
|
@@ -7775,7 +8377,7 @@ Examples:
|
|
|
7775
8377
|
}
|
|
7776
8378
|
|
|
7777
8379
|
// src/commands/pentest.ts
|
|
7778
|
-
var
|
|
8380
|
+
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
7779
8381
|
|
|
7780
8382
|
// src/lib/confirm.ts
|
|
7781
8383
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
@@ -7835,8 +8437,21 @@ ${import_picocolors3.default.dim("use of its services.")}
|
|
|
7835
8437
|
|
|
7836
8438
|
// src/commands/schedule.ts
|
|
7837
8439
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
8440
|
+
|
|
8441
|
+
// src/lib/validate-mode.ts
|
|
8442
|
+
function validateScanMode(mode) {
|
|
8443
|
+
if (mode === "quick") {
|
|
8444
|
+
console.error("Quick mode is temporarily unavailable. Please use --mode full (the default).");
|
|
8445
|
+
process.exit(2);
|
|
8446
|
+
}
|
|
8447
|
+
if (mode !== "full") {
|
|
8448
|
+
console.error(`Invalid mode "${mode}". Must be: full`);
|
|
8449
|
+
process.exit(2);
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
|
|
8453
|
+
// src/commands/schedule.ts
|
|
7838
8454
|
var VALID_PATTERNS = ["once", "daily", "weekly", "biweekly", "monthly"];
|
|
7839
|
-
var VALID_MODES = ["quick", "full"];
|
|
7840
8455
|
var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
7841
8456
|
function extractTime(scheduledAt) {
|
|
7842
8457
|
if (!scheduledAt) return "\u2014";
|
|
@@ -7934,19 +8549,16 @@ Examples:
|
|
|
7934
8549
|
handleError(error2);
|
|
7935
8550
|
}
|
|
7936
8551
|
});
|
|
7937
|
-
schedule.command("create").description("Create a scheduled pentest").requiredOption("--asset <id>", "Asset ID to scan").requiredOption("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode:
|
|
8552
|
+
schedule.command("create").description("Create a scheduled pentest").requiredOption("--asset <id>", "Asset ID to scan").requiredOption("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode (default: full)", "full").option("--time <HH:MM>", "Time of day (24h format)", "09:00").option("--day-of-week <0-6>", "Day of week (0=Sun..6=Sat) for weekly/biweekly").option("--day-of-month <1-31>", "Day of month for monthly").option("--timezone <tz>", "Timezone (e.g. America/New_York)", Intl.DateTimeFormat().resolvedOptions().timeZone).addHelpText("after", `
|
|
7938
8553
|
Examples:
|
|
7939
8554
|
$ kinosec pentest schedule create --asset abc123 --pattern weekly --day-of-week 1 --time 09:00
|
|
7940
|
-
$ kinosec pentest schedule create --asset abc123 --pattern daily --
|
|
8555
|
+
$ kinosec pentest schedule create --asset abc123 --pattern daily --time 14:00
|
|
7941
8556
|
$ kinosec pentest schedule create --asset abc123 --pattern monthly --day-of-month 15 --time 03:00 --timezone UTC`).action(async (options) => {
|
|
7942
8557
|
if (!VALID_PATTERNS.includes(options.pattern)) {
|
|
7943
8558
|
console.error(`Invalid pattern "${options.pattern}". Must be: ${VALID_PATTERNS.join(", ")}`);
|
|
7944
8559
|
process.exit(2);
|
|
7945
8560
|
}
|
|
7946
|
-
|
|
7947
|
-
console.error(`Invalid mode "${options.mode}". Must be: ${VALID_MODES.join(", ")}`);
|
|
7948
|
-
process.exit(2);
|
|
7949
|
-
}
|
|
8561
|
+
validateScanMode(options.mode);
|
|
7950
8562
|
if ((options.pattern === "weekly" || options.pattern === "biweekly") && options.dayOfWeek === void 0) {
|
|
7951
8563
|
console.error(`--day-of-week is required for ${options.pattern} schedules.`);
|
|
7952
8564
|
process.exit(2);
|
|
@@ -7984,14 +8596,17 @@ Manage: kinosec pentest schedule get ${s.id}`);
|
|
|
7984
8596
|
handleError(error2);
|
|
7985
8597
|
}
|
|
7986
8598
|
});
|
|
7987
|
-
schedule.command("update").description("Update a schedule").argument("<id>", "Schedule ID").option("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode
|
|
8599
|
+
schedule.command("update").description("Update a schedule").argument("<id>", "Schedule ID").option("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode").option("--time <HH:MM>", "Time of day (24h format)").option("--day-of-week <0-6>", "Day of week (0=Sun..6=Sat)").option("--day-of-month <1-31>", "Day of month").option("--timezone <tz>", "Timezone").addHelpText("after", `
|
|
7988
8600
|
Examples:
|
|
7989
8601
|
$ kinosec pentest schedule update abc123 --pattern daily --time 14:00
|
|
7990
|
-
$ kinosec pentest schedule update abc123 --
|
|
8602
|
+
$ kinosec pentest schedule update abc123 --time 14:00`).action(async (id, options) => {
|
|
7991
8603
|
const params = {};
|
|
7992
8604
|
if (options.pattern) params.recurrence_pattern = options.pattern;
|
|
7993
8605
|
if (options.name) params.name = options.name;
|
|
7994
|
-
if (options.mode)
|
|
8606
|
+
if (options.mode) {
|
|
8607
|
+
validateScanMode(options.mode);
|
|
8608
|
+
params.scan_mode = options.mode;
|
|
8609
|
+
}
|
|
7995
8610
|
if (options.time) params.time_of_day = options.time;
|
|
7996
8611
|
if (options.dayOfWeek !== void 0) params.day_of_week = parseInt(options.dayOfWeek, 10);
|
|
7997
8612
|
if (options.dayOfMonth !== void 0) params.day_of_month = parseInt(options.dayOfMonth, 10);
|
|
@@ -8088,7 +8703,7 @@ function isUnicodeSupported() {
|
|
|
8088
8703
|
return Boolean(env2.WT_SESSION) || Boolean(env2.TERMINUS_SUBLIME) || env2.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env2.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
8089
8704
|
}
|
|
8090
8705
|
|
|
8091
|
-
// ../../node_modules/.pnpm/ora@9.4.
|
|
8706
|
+
// ../../node_modules/.pnpm/ora@9.4.1/node_modules/ora/index.js
|
|
8092
8707
|
import process8 from "process";
|
|
8093
8708
|
import { stripVTControlCharacters } from "util";
|
|
8094
8709
|
|
|
@@ -10823,7 +11438,7 @@ var StdinDiscarder = class {
|
|
|
10823
11438
|
var stdinDiscarder = new StdinDiscarder();
|
|
10824
11439
|
var stdin_discarder_default = Object.freeze(stdinDiscarder);
|
|
10825
11440
|
|
|
10826
|
-
// ../../node_modules/.pnpm/ora@9.4.
|
|
11441
|
+
// ../../node_modules/.pnpm/ora@9.4.1/node_modules/ora/index.js
|
|
10827
11442
|
var RENDER_DEFERRAL_TIMEOUT = 200;
|
|
10828
11443
|
var SYNCHRONIZED_OUTPUT_ENABLE = "\x1B[?2026h";
|
|
10829
11444
|
var SYNCHRONIZED_OUTPUT_DISABLE = "\x1B[?2026l";
|
|
@@ -11202,7 +11817,7 @@ var Ora = class {
|
|
|
11202
11817
|
return this;
|
|
11203
11818
|
}
|
|
11204
11819
|
start(text) {
|
|
11205
|
-
if (text) {
|
|
11820
|
+
if (text !== void 0) {
|
|
11206
11821
|
this.text = text;
|
|
11207
11822
|
}
|
|
11208
11823
|
if (this.isSilent) {
|
|
@@ -11563,105 +12178,344 @@ function printOnPremInstructions(result) {
|
|
|
11563
12178
|
console.log(` ${sym2.warn} Token expires in 15 minutes.`);
|
|
11564
12179
|
}
|
|
11565
12180
|
}
|
|
11566
|
-
async function waitForScoutConnection(client, profileId, options) {
|
|
11567
|
-
const { timeoutMs, intervalMs, useJson } = options;
|
|
11568
|
-
const tty3 = isTTY();
|
|
11569
|
-
const loopStart = Date.now();
|
|
11570
|
-
let spinner = null;
|
|
11571
|
-
if (!useJson && tty3) {
|
|
11572
|
-
spinner = ora({
|
|
11573
|
-
text: "Waiting for Scout connection...",
|
|
11574
|
-
color: "green",
|
|
11575
|
-
isEnabled: true
|
|
11576
|
-
}).start();
|
|
12181
|
+
async function waitForScoutConnection(client, profileId, options) {
|
|
12182
|
+
const { timeoutMs, intervalMs, useJson } = options;
|
|
12183
|
+
const tty3 = isTTY();
|
|
12184
|
+
const loopStart = Date.now();
|
|
12185
|
+
let spinner = null;
|
|
12186
|
+
if (!useJson && tty3) {
|
|
12187
|
+
spinner = ora({
|
|
12188
|
+
text: "Waiting for Scout connection...",
|
|
12189
|
+
color: "green",
|
|
12190
|
+
isEnabled: true
|
|
12191
|
+
}).start();
|
|
12192
|
+
}
|
|
12193
|
+
const cleanup = () => {
|
|
12194
|
+
if (spinner) spinner.stop();
|
|
12195
|
+
};
|
|
12196
|
+
const onSigint = () => {
|
|
12197
|
+
cleanup();
|
|
12198
|
+
console.log("\nWatch cancelled. Pentest is waiting for Scout connection.");
|
|
12199
|
+
process.exit(0);
|
|
12200
|
+
};
|
|
12201
|
+
process.once("SIGINT", onSigint);
|
|
12202
|
+
while (true) {
|
|
12203
|
+
const status = await client.scans.scoutStatus(profileId);
|
|
12204
|
+
if (useJson) {
|
|
12205
|
+
const elapsed = Math.round((Date.now() - loopStart) / 1e3);
|
|
12206
|
+
process.stdout.write(
|
|
12207
|
+
JSON.stringify({ ...status, elapsed_seconds: elapsed }) + "\n"
|
|
12208
|
+
);
|
|
12209
|
+
}
|
|
12210
|
+
switch (status.status) {
|
|
12211
|
+
case "pending":
|
|
12212
|
+
if (spinner) spinner.text = import_picocolors6.default.green("Waiting for Scout bootstrap...");
|
|
12213
|
+
break;
|
|
12214
|
+
case "bootstrapped":
|
|
12215
|
+
if (spinner) spinner.text = import_picocolors6.default.green("Scout bootstrapped, establishing connection...");
|
|
12216
|
+
break;
|
|
12217
|
+
case "connected": {
|
|
12218
|
+
cleanup();
|
|
12219
|
+
process.removeListener("SIGINT", onSigint);
|
|
12220
|
+
if (!useJson) {
|
|
12221
|
+
const icon = tty3 ? import_picocolors6.default.green(sym2.success) : sym2.success;
|
|
12222
|
+
console.log(`${icon} Scout connected`);
|
|
12223
|
+
if (status.hostname) {
|
|
12224
|
+
console.log(` Hostname: ${status.hostname}`);
|
|
12225
|
+
}
|
|
12226
|
+
if (status.tools.length > 0) {
|
|
12227
|
+
console.log(` Tools: ${status.tools.join(", ")}`);
|
|
12228
|
+
}
|
|
12229
|
+
}
|
|
12230
|
+
return;
|
|
12231
|
+
}
|
|
12232
|
+
case "expired": {
|
|
12233
|
+
cleanup();
|
|
12234
|
+
process.removeListener("SIGINT", onSigint);
|
|
12235
|
+
const icon = tty3 ? import_picocolors6.default.red(sym2.fail) : sym2.fail;
|
|
12236
|
+
console.error(`
|
|
12237
|
+
${icon} Scout bootstrap token expired.`);
|
|
12238
|
+
console.error("Start a new pentest to get a fresh token.");
|
|
12239
|
+
process.exit(1);
|
|
12240
|
+
}
|
|
12241
|
+
case "disconnected": {
|
|
12242
|
+
cleanup();
|
|
12243
|
+
process.removeListener("SIGINT", onSigint);
|
|
12244
|
+
const icon = tty3 ? import_picocolors6.default.red(sym2.fail) : sym2.fail;
|
|
12245
|
+
console.error(`
|
|
12246
|
+
${icon} Scout disconnected unexpectedly.`);
|
|
12247
|
+
console.error(`Check status: kinosec pentest get ${profileId}`);
|
|
12248
|
+
process.exit(1);
|
|
12249
|
+
}
|
|
12250
|
+
}
|
|
12251
|
+
if (Date.now() - loopStart > timeoutMs) {
|
|
12252
|
+
cleanup();
|
|
12253
|
+
process.removeListener("SIGINT", onSigint);
|
|
12254
|
+
const secs = Math.round(timeoutMs / 1e3);
|
|
12255
|
+
console.error(`
|
|
12256
|
+
Timeout: Scout did not connect within ${secs}s.`);
|
|
12257
|
+
console.error("The bootstrap token may have expired. Start a new pentest.");
|
|
12258
|
+
process.exit(3);
|
|
12259
|
+
}
|
|
12260
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
12261
|
+
}
|
|
12262
|
+
}
|
|
12263
|
+
|
|
12264
|
+
// src/lib/scope.ts
|
|
12265
|
+
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
12266
|
+
|
|
12267
|
+
// src/lib/prompt.ts
|
|
12268
|
+
import { createInterface as createInterface2 } from "readline";
|
|
12269
|
+
async function promptMasked(label) {
|
|
12270
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
12271
|
+
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
12272
|
+
process.exit(2);
|
|
12273
|
+
}
|
|
12274
|
+
process.stderr.write(`${label}: `);
|
|
12275
|
+
return new Promise((resolve, reject) => {
|
|
12276
|
+
const chars = [];
|
|
12277
|
+
process.stdin.setRawMode(true);
|
|
12278
|
+
process.stdin.resume();
|
|
12279
|
+
process.stdin.setEncoding("utf-8");
|
|
12280
|
+
const onData = (chunk) => {
|
|
12281
|
+
for (const ch of chunk) {
|
|
12282
|
+
if (ch === "\r" || ch === "\n") {
|
|
12283
|
+
process.stdin.setRawMode(false);
|
|
12284
|
+
process.stdin.pause();
|
|
12285
|
+
process.stdin.removeListener("data", onData);
|
|
12286
|
+
process.stderr.write("\n");
|
|
12287
|
+
resolve(chars.join(""));
|
|
12288
|
+
return;
|
|
12289
|
+
} else if (ch === "") {
|
|
12290
|
+
process.stdin.setRawMode(false);
|
|
12291
|
+
process.stdin.pause();
|
|
12292
|
+
process.stdin.removeListener("data", onData);
|
|
12293
|
+
process.stderr.write("\n");
|
|
12294
|
+
reject(new Error("Cancelled"));
|
|
12295
|
+
return;
|
|
12296
|
+
} else if (ch === "\x7F" || ch === "\b") {
|
|
12297
|
+
if (chars.length > 0) {
|
|
12298
|
+
chars.pop();
|
|
12299
|
+
process.stderr.write("\b \b");
|
|
12300
|
+
}
|
|
12301
|
+
} else if (ch >= " ") {
|
|
12302
|
+
chars.push(ch);
|
|
12303
|
+
process.stderr.write("*");
|
|
12304
|
+
}
|
|
12305
|
+
}
|
|
12306
|
+
};
|
|
12307
|
+
process.stdin.on("data", onData);
|
|
12308
|
+
});
|
|
12309
|
+
}
|
|
12310
|
+
async function promptText(label) {
|
|
12311
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
12312
|
+
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
12313
|
+
process.exit(2);
|
|
12314
|
+
}
|
|
12315
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
12316
|
+
const answer = await new Promise((resolve) => {
|
|
12317
|
+
rl.question(`${label}: `, resolve);
|
|
12318
|
+
});
|
|
12319
|
+
rl.close();
|
|
12320
|
+
return answer;
|
|
12321
|
+
}
|
|
12322
|
+
async function promptSelect(message, options) {
|
|
12323
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
12324
|
+
console.error("Cannot show interactive menu in non-interactive mode. Use flags instead.");
|
|
12325
|
+
process.exit(2);
|
|
12326
|
+
}
|
|
12327
|
+
console.error(`
|
|
12328
|
+
${message}`);
|
|
12329
|
+
for (let i = 0; i < options.length; i++) {
|
|
12330
|
+
console.error(` ${i + 1}. ${options[i].label} \u2014 ${options[i].description}`);
|
|
12331
|
+
}
|
|
12332
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
12333
|
+
const answer = await new Promise((resolve) => {
|
|
12334
|
+
rl.question("> ", resolve);
|
|
12335
|
+
});
|
|
12336
|
+
rl.close();
|
|
12337
|
+
const index = parseInt(answer, 10) - 1;
|
|
12338
|
+
if (isNaN(index) || index < 0 || index >= options.length) {
|
|
12339
|
+
console.error("Invalid selection.");
|
|
12340
|
+
process.exit(2);
|
|
12341
|
+
}
|
|
12342
|
+
return options[index].value;
|
|
12343
|
+
}
|
|
12344
|
+
|
|
12345
|
+
// src/lib/scope.ts
|
|
12346
|
+
var HOSTNAME_RE = /^([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/i;
|
|
12347
|
+
var MAX_SCOPE_HOSTS = 50;
|
|
12348
|
+
function parseScopeHosts(input) {
|
|
12349
|
+
return input.split(",").map((h) => h.trim()).filter(Boolean);
|
|
12350
|
+
}
|
|
12351
|
+
function validateScopeHosts(hosts) {
|
|
12352
|
+
for (const host of hosts) {
|
|
12353
|
+
if (!HOSTNAME_RE.test(host)) {
|
|
12354
|
+
console.error(`Error: Invalid hostname "${host}". Must be a valid domain (e.g., api.example.com).`);
|
|
12355
|
+
process.exit(2);
|
|
12356
|
+
}
|
|
12357
|
+
}
|
|
12358
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12359
|
+
for (const host of hosts) {
|
|
12360
|
+
const lower = host.toLowerCase();
|
|
12361
|
+
if (seen.has(lower)) {
|
|
12362
|
+
console.error(`Error: Duplicate hostname "${host}" in list.`);
|
|
12363
|
+
process.exit(2);
|
|
12364
|
+
}
|
|
12365
|
+
seen.add(lower);
|
|
12366
|
+
}
|
|
12367
|
+
}
|
|
12368
|
+
function validateScopeHostOwnership(hosts, assetValue) {
|
|
12369
|
+
const suffix = `.${assetValue.toLowerCase()}`;
|
|
12370
|
+
for (const host of hosts) {
|
|
12371
|
+
if (!host.toLowerCase().endsWith(suffix)) {
|
|
12372
|
+
console.error(`Error: ${host} is not a subdomain of ${assetValue}.`);
|
|
12373
|
+
process.exit(2);
|
|
12374
|
+
}
|
|
12375
|
+
}
|
|
12376
|
+
}
|
|
12377
|
+
function validateScopeFlags(options) {
|
|
12378
|
+
if (options.include && options.exclude) {
|
|
12379
|
+
console.error("Error: --include and --exclude cannot be used together. Use one or the other.");
|
|
12380
|
+
process.exit(2);
|
|
12381
|
+
}
|
|
12382
|
+
if (options.scopeClear && (options.include || options.exclude)) {
|
|
12383
|
+
console.error("Error: --clear cannot be combined with --include or --exclude.");
|
|
12384
|
+
process.exit(2);
|
|
12385
|
+
}
|
|
12386
|
+
}
|
|
12387
|
+
function validateTotalHostCount(existingCount, newCount) {
|
|
12388
|
+
const total = existingCount + newCount;
|
|
12389
|
+
if (total > MAX_SCOPE_HOSTS) {
|
|
12390
|
+
console.error(`Error: Maximum ${MAX_SCOPE_HOSTS} hosts allowed. You provided ${total}.`);
|
|
12391
|
+
process.exit(2);
|
|
11577
12392
|
}
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
12393
|
+
}
|
|
12394
|
+
function displayScope(asset) {
|
|
12395
|
+
if (!asset.scope_filter || !asset.scope_hosts?.length) {
|
|
12396
|
+
console.error(`Scope: ${isTTY() ? import_picocolors7.default.dim("all subdomains") : "all subdomains"}`);
|
|
12397
|
+
return;
|
|
12398
|
+
}
|
|
12399
|
+
const label = asset.scope_filter === "include" ? "include only" : "exclude";
|
|
12400
|
+
const hosts = asset.scope_hosts.join(", ");
|
|
12401
|
+
console.error(`Scope: ${label} \u2014 ${hosts}`);
|
|
12402
|
+
}
|
|
12403
|
+
async function resolveScopeUpdate(asset, mode, newHosts) {
|
|
12404
|
+
const existingMode = asset.scope_filter;
|
|
12405
|
+
const existingHosts = asset.scope_hosts ?? [];
|
|
12406
|
+
if (!existingMode || existingHosts.length === 0) {
|
|
12407
|
+
validateScopeHosts(newHosts);
|
|
12408
|
+
if (newHosts.length > MAX_SCOPE_HOSTS) {
|
|
12409
|
+
console.error(`Error: Maximum ${MAX_SCOPE_HOSTS} hosts allowed. You provided ${newHosts.length}.`);
|
|
12410
|
+
process.exit(2);
|
|
11594
12411
|
}
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
case "connected": {
|
|
11603
|
-
cleanup();
|
|
11604
|
-
process.removeListener("SIGINT", onSigint);
|
|
11605
|
-
if (!useJson) {
|
|
11606
|
-
const icon = tty3 ? import_picocolors6.default.green(sym2.success) : sym2.success;
|
|
11607
|
-
console.log(`${icon} Scout connected`);
|
|
11608
|
-
if (status.hostname) {
|
|
11609
|
-
console.log(` Hostname: ${status.hostname}`);
|
|
11610
|
-
}
|
|
11611
|
-
if (status.tools.length > 0) {
|
|
11612
|
-
console.log(` Tools: ${status.tools.join(", ")}`);
|
|
11613
|
-
}
|
|
11614
|
-
}
|
|
11615
|
-
return;
|
|
11616
|
-
}
|
|
11617
|
-
case "expired": {
|
|
11618
|
-
cleanup();
|
|
11619
|
-
process.removeListener("SIGINT", onSigint);
|
|
11620
|
-
const icon = tty3 ? import_picocolors6.default.red(sym2.fail) : sym2.fail;
|
|
11621
|
-
console.error(`
|
|
11622
|
-
${icon} Scout bootstrap token expired.`);
|
|
11623
|
-
console.error("Start a new pentest to get a fresh token.");
|
|
11624
|
-
process.exit(1);
|
|
11625
|
-
}
|
|
11626
|
-
case "disconnected": {
|
|
11627
|
-
cleanup();
|
|
11628
|
-
process.removeListener("SIGINT", onSigint);
|
|
11629
|
-
const icon = tty3 ? import_picocolors6.default.red(sym2.fail) : sym2.fail;
|
|
11630
|
-
console.error(`
|
|
11631
|
-
${icon} Scout disconnected unexpectedly.`);
|
|
11632
|
-
console.error(`Check status: kinosec pentest get ${profileId}`);
|
|
11633
|
-
process.exit(1);
|
|
11634
|
-
}
|
|
12412
|
+
return { scope_filter: mode, scope_hosts: newHosts };
|
|
12413
|
+
}
|
|
12414
|
+
if (existingMode !== mode) {
|
|
12415
|
+
console.error(`
|
|
12416
|
+
Existing ${existingMode} scope:`);
|
|
12417
|
+
for (const host of existingHosts) {
|
|
12418
|
+
console.error(` - ${host}`);
|
|
11635
12419
|
}
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
12420
|
+
const answer2 = await promptSelect(
|
|
12421
|
+
`You have an existing ${existingMode} scope. Using --${mode} will replace it. Continue?`,
|
|
12422
|
+
[
|
|
12423
|
+
{ value: "switch", label: `Yes, switch to ${mode}`, description: `Replace ${existingMode} list with new ${mode} list` },
|
|
12424
|
+
{ value: "cancel", label: "Cancel", description: "Keep existing scope" }
|
|
12425
|
+
]
|
|
12426
|
+
);
|
|
12427
|
+
if (answer2 === "cancel") {
|
|
12428
|
+
console.error("Cancelled.");
|
|
12429
|
+
return null;
|
|
11644
12430
|
}
|
|
11645
|
-
|
|
12431
|
+
validateScopeHosts(newHosts);
|
|
12432
|
+
if (newHosts.length > MAX_SCOPE_HOSTS) {
|
|
12433
|
+
console.error(`Error: Maximum ${MAX_SCOPE_HOSTS} hosts allowed. You provided ${newHosts.length}.`);
|
|
12434
|
+
process.exit(2);
|
|
12435
|
+
}
|
|
12436
|
+
return { scope_filter: mode, scope_hosts: newHosts };
|
|
12437
|
+
}
|
|
12438
|
+
console.error(`
|
|
12439
|
+
Existing ${existingMode} scope:`);
|
|
12440
|
+
for (const host of existingHosts) {
|
|
12441
|
+
console.error(` - ${host}`);
|
|
12442
|
+
}
|
|
12443
|
+
const answer = await promptSelect(
|
|
12444
|
+
`You have existing subdomains in your ${mode} list. What would you like to do?`,
|
|
12445
|
+
[
|
|
12446
|
+
{ value: "add", label: "Add to existing list", description: "Append new hosts to the current list" },
|
|
12447
|
+
{ value: "replace", label: "Replace with new list", description: "Remove existing hosts and use only the new ones" }
|
|
12448
|
+
]
|
|
12449
|
+
);
|
|
12450
|
+
if (answer === "add") {
|
|
12451
|
+
validateScopeHosts(newHosts);
|
|
12452
|
+
const existingLower = new Set(existingHosts.map((h) => h.toLowerCase()));
|
|
12453
|
+
const deduped = newHosts.filter((h) => !existingLower.has(h.toLowerCase()));
|
|
12454
|
+
if (deduped.length === 0) {
|
|
12455
|
+
console.error("All provided hosts are already in the list.");
|
|
12456
|
+
return { scope_filter: mode, scope_hosts: existingHosts };
|
|
12457
|
+
}
|
|
12458
|
+
validateScopeHosts(deduped);
|
|
12459
|
+
validateTotalHostCount(existingHosts.length, deduped.length);
|
|
12460
|
+
const merged = [...existingHosts, ...deduped];
|
|
12461
|
+
return { scope_filter: mode, scope_hosts: merged };
|
|
12462
|
+
}
|
|
12463
|
+
validateScopeHosts(newHosts);
|
|
12464
|
+
if (newHosts.length > MAX_SCOPE_HOSTS) {
|
|
12465
|
+
console.error(`Error: Maximum ${MAX_SCOPE_HOSTS} hosts allowed. You provided ${newHosts.length}.`);
|
|
12466
|
+
process.exit(2);
|
|
11646
12467
|
}
|
|
12468
|
+
return { scope_filter: mode, scope_hosts: newHosts };
|
|
11647
12469
|
}
|
|
11648
12470
|
|
|
11649
|
-
// src/
|
|
11650
|
-
function
|
|
11651
|
-
const
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
12471
|
+
// src/lib/parse-target.ts
|
|
12472
|
+
function parseTarget(input) {
|
|
12473
|
+
const warnings = [];
|
|
12474
|
+
let value = input.trim();
|
|
12475
|
+
const protocolMatch = value.match(/^https?:\/\/(.+)/i);
|
|
12476
|
+
if (protocolMatch) {
|
|
12477
|
+
value = protocolMatch[1];
|
|
12478
|
+
}
|
|
12479
|
+
const slashIndex = value.indexOf("/");
|
|
12480
|
+
if (slashIndex !== -1) {
|
|
12481
|
+
value = value.slice(0, slashIndex);
|
|
12482
|
+
}
|
|
12483
|
+
value = value.replace(/\.+$/, "").toLowerCase();
|
|
12484
|
+
if (protocolMatch) {
|
|
12485
|
+
warnings.push(`Protocol stripped from input. Using "${value}".`);
|
|
12486
|
+
}
|
|
12487
|
+
if (/\s/.test(value) || !value.includes(".")) {
|
|
12488
|
+
throw new Error(
|
|
12489
|
+
`Invalid target: "${input}". Provide a domain like example.com or an IP like 192.168.1.1.`
|
|
12490
|
+
);
|
|
12491
|
+
}
|
|
12492
|
+
const assetType = detectAssetType(value);
|
|
12493
|
+
const parentDomain = deriveParentDomain(value);
|
|
12494
|
+
const isPubSuffix = isPublicSuffix(value);
|
|
12495
|
+
if (isPubSuffix) {
|
|
12496
|
+
warnings.push(
|
|
12497
|
+
`${value} is a public hosting suffix. You probably want your specific subdomain (e.g. myapp.${value}).`
|
|
12498
|
+
);
|
|
12499
|
+
}
|
|
12500
|
+
return {
|
|
12501
|
+
hostname: value,
|
|
12502
|
+
assetType,
|
|
12503
|
+
parentDomain,
|
|
12504
|
+
isPublicSuffix: isPubSuffix,
|
|
12505
|
+
warnings
|
|
12506
|
+
};
|
|
11657
12507
|
}
|
|
12508
|
+
|
|
12509
|
+
// src/commands/pentest.ts
|
|
11658
12510
|
function registerPentestCommand(program3) {
|
|
11659
|
-
const pentest = program3.command("pentest").description("Run and manage pentests").option("-t, --target <domain>", "Target domain to pentest").option("--profile <id>", "Pentest profile ID (alternative to --target)").option("--mode <mode>", "Pentest mode
|
|
12511
|
+
const pentest = program3.command("pentest").description("Run and manage pentests").option("-t, --target <domain>", "Target domain to pentest").option("--profile <id>", "Pentest profile ID (alternative to --target)").option("--mode <mode>", "Pentest mode", "full").option("-y, --yes", "Accept authorization terms without prompting").option("--no-watch", "Skip auto-watch after starting the pentest").option("--deployment <type>", "Deployment type: cloud or on-prem", "cloud").option("--include <hosts>", "Include only these subdomains (comma-separated)").option("--exclude <hosts>", "Exclude these subdomains (comma-separated)").option("--scope <action>", "Scope action: clear").addHelpText("after", `
|
|
11660
12512
|
Examples:
|
|
11661
12513
|
$ kinosec pentest -t delikado.org
|
|
11662
|
-
$ kinosec pentest --target delikado.org --mode quick
|
|
11663
12514
|
$ kinosec pentest --profile abc123
|
|
11664
|
-
$ kinosec pentest -t internal.corp --deployment on-prem
|
|
12515
|
+
$ kinosec pentest -t internal.corp --deployment on-prem
|
|
12516
|
+
$ kinosec pentest -t example.com --include api.example.com,app.example.com
|
|
12517
|
+
$ kinosec pentest -t example.com --exclude staging.example.com
|
|
12518
|
+
$ kinosec pentest -t example.com --scope clear`).action(async (options) => {
|
|
11665
12519
|
if (!options.target && !options.profile) {
|
|
11666
12520
|
console.error("Provide --target <domain> or --profile <id>.");
|
|
11667
12521
|
process.exit(2);
|
|
@@ -11673,6 +12527,21 @@ Examples:
|
|
|
11673
12527
|
console.error("Invalid deployment type. Must be: cloud or on-prem.");
|
|
11674
12528
|
process.exit(2);
|
|
11675
12529
|
}
|
|
12530
|
+
validateScanMode(options.mode);
|
|
12531
|
+
const scopeClear = options.scope === "clear";
|
|
12532
|
+
if (options.scope && options.scope !== "clear") {
|
|
12533
|
+
console.error("Invalid --scope value. Must be: clear");
|
|
12534
|
+
process.exit(2);
|
|
12535
|
+
}
|
|
12536
|
+
validateScopeFlags({
|
|
12537
|
+
include: options.include,
|
|
12538
|
+
exclude: options.exclude,
|
|
12539
|
+
scopeClear
|
|
12540
|
+
});
|
|
12541
|
+
if (options.profile && (options.include || options.exclude || scopeClear)) {
|
|
12542
|
+
console.error("Error: Scope flags are only supported with --target, not --profile.");
|
|
12543
|
+
process.exit(2);
|
|
12544
|
+
}
|
|
11676
12545
|
try {
|
|
11677
12546
|
const opts = program3.opts();
|
|
11678
12547
|
const { token, apiUrl } = resolveToken(opts);
|
|
@@ -11684,31 +12553,33 @@ Examples:
|
|
|
11684
12553
|
targetDisplay = detail.domain_or_ip;
|
|
11685
12554
|
params = { scan_profile_id: options.profile };
|
|
11686
12555
|
} else {
|
|
11687
|
-
const
|
|
11688
|
-
|
|
11689
|
-
console.error(`Warning:
|
|
11690
|
-
}
|
|
11691
|
-
if (
|
|
11692
|
-
console.error(
|
|
11693
|
-
|
|
11694
|
-
|
|
12556
|
+
const parsed = parseTarget(options.target);
|
|
12557
|
+
for (const warning2 of parsed.warnings) {
|
|
12558
|
+
console.error(`Warning: ${warning2}`);
|
|
12559
|
+
}
|
|
12560
|
+
if (parsed.isPublicSuffix) {
|
|
12561
|
+
console.error(
|
|
12562
|
+
`
|
|
12563
|
+
${parsed.hostname} is a shared hosting domain. The server will likely reject this.`
|
|
12564
|
+
);
|
|
11695
12565
|
}
|
|
11696
12566
|
let asset;
|
|
11697
12567
|
try {
|
|
11698
|
-
asset = await client.assets.lookup(
|
|
12568
|
+
asset = await client.assets.lookup(parsed.hostname);
|
|
11699
12569
|
} catch (err) {
|
|
11700
12570
|
if (err instanceof ApiError && err.status === 404) {
|
|
11701
12571
|
asset = await client.assets.create({
|
|
11702
|
-
asset_type:
|
|
11703
|
-
asset_value:
|
|
12572
|
+
asset_type: parsed.assetType,
|
|
12573
|
+
asset_value: parsed.hostname,
|
|
12574
|
+
parent_domain: parsed.parentDomain,
|
|
11704
12575
|
verification_method: "dns"
|
|
11705
12576
|
});
|
|
11706
|
-
console.error(`Asset created for ${
|
|
12577
|
+
console.error(`Asset created for ${parsed.hostname}.`);
|
|
11707
12578
|
} else {
|
|
11708
12579
|
throw err;
|
|
11709
12580
|
}
|
|
11710
12581
|
}
|
|
11711
|
-
targetDisplay =
|
|
12582
|
+
targetDisplay = parsed.hostname;
|
|
11712
12583
|
const assetParams = {
|
|
11713
12584
|
asset_id: asset.id,
|
|
11714
12585
|
scan_mode: options.mode
|
|
@@ -11717,6 +12588,40 @@ Examples:
|
|
|
11717
12588
|
assetParams.deployment_type = options.deployment;
|
|
11718
12589
|
}
|
|
11719
12590
|
params = assetParams;
|
|
12591
|
+
if (scopeClear || options.include || options.exclude) {
|
|
12592
|
+
if (asset.asset_type !== "domain") {
|
|
12593
|
+
const hint = parsed.parentDomain ? `
|
|
12594
|
+
Target the parent domain instead:
|
|
12595
|
+
kinosec pentest -t ${parsed.parentDomain} --${options.include ? "include" : "exclude"} ${parsed.hostname}` : "";
|
|
12596
|
+
console.error(
|
|
12597
|
+
`Error: Scope filtering is only available for domain assets. ${parsed.hostname} is a ${asset.asset_type}.${hint}`
|
|
12598
|
+
);
|
|
12599
|
+
process.exit(2);
|
|
12600
|
+
}
|
|
12601
|
+
}
|
|
12602
|
+
if (scopeClear) {
|
|
12603
|
+
await client.assets.update(asset.id, {
|
|
12604
|
+
scope_filter: null,
|
|
12605
|
+
scope_hosts: null
|
|
12606
|
+
});
|
|
12607
|
+
console.error("Scope settings cleared. Scans will now cover all subdomains by default.");
|
|
12608
|
+
asset = await client.assets.get(asset.id);
|
|
12609
|
+
} else if (options.include || options.exclude) {
|
|
12610
|
+
const mode = options.include ? "include" : "exclude";
|
|
12611
|
+
const rawHosts = parseScopeHosts(options.include ?? options.exclude);
|
|
12612
|
+
validateScopeHosts(rawHosts);
|
|
12613
|
+
validateScopeHostOwnership(rawHosts, asset.asset_value);
|
|
12614
|
+
const scopeResult = await resolveScopeUpdate(asset, mode, rawHosts);
|
|
12615
|
+
if (!scopeResult) {
|
|
12616
|
+
process.exit(0);
|
|
12617
|
+
}
|
|
12618
|
+
await client.assets.update(asset.id, {
|
|
12619
|
+
scope_filter: scopeResult.scope_filter,
|
|
12620
|
+
scope_hosts: scopeResult.scope_hosts
|
|
12621
|
+
});
|
|
12622
|
+
asset = await client.assets.get(asset.id);
|
|
12623
|
+
}
|
|
12624
|
+
displayScope(asset);
|
|
11720
12625
|
}
|
|
11721
12626
|
await confirmAuthorization(targetDisplay, Boolean(options.yes));
|
|
11722
12627
|
const result = await client.scans.create(params);
|
|
@@ -11731,7 +12636,7 @@ Examples:
|
|
|
11731
12636
|
if (isOnPrem) {
|
|
11732
12637
|
printOnPremInstructions(result);
|
|
11733
12638
|
if (options.watch && isTTY()) {
|
|
11734
|
-
const divider2 =
|
|
12639
|
+
const divider2 = import_picocolors8.default.dim("\u2500".repeat(37));
|
|
11735
12640
|
console.log(`
|
|
11736
12641
|
${divider2}
|
|
11737
12642
|
`);
|
|
@@ -11758,7 +12663,7 @@ Watch connection: kinosec pentest watch ${result.scan_profile_id}`);
|
|
|
11758
12663
|
Watch progress: kinosec pentest watch ${result.scan_profile_id}`);
|
|
11759
12664
|
return;
|
|
11760
12665
|
}
|
|
11761
|
-
const divider = isTTY() ?
|
|
12666
|
+
const divider = isTTY() ? import_picocolors8.default.dim("\u2500".repeat(37)) : "-".repeat(37);
|
|
11762
12667
|
console.log(`
|
|
11763
12668
|
${divider}
|
|
11764
12669
|
`);
|
|
@@ -11769,6 +12674,9 @@ ${divider}
|
|
|
11769
12674
|
showFindingsSummary: true
|
|
11770
12675
|
});
|
|
11771
12676
|
} catch (error2) {
|
|
12677
|
+
if (error2 instanceof Error && error2.message.startsWith("process.exit(")) {
|
|
12678
|
+
throw error2;
|
|
12679
|
+
}
|
|
11772
12680
|
handleError(error2);
|
|
11773
12681
|
}
|
|
11774
12682
|
});
|
|
@@ -11804,7 +12712,7 @@ Examples:
|
|
|
11804
12712
|
])
|
|
11805
12713
|
);
|
|
11806
12714
|
if (result.next_cursor) {
|
|
11807
|
-
console.log(
|
|
12715
|
+
console.log(import_picocolors8.default.dim(`
|
|
11808
12716
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11809
12717
|
}
|
|
11810
12718
|
} catch (error2) {
|
|
@@ -11896,10 +12804,10 @@ Examples:
|
|
|
11896
12804
|
}
|
|
11897
12805
|
|
|
11898
12806
|
// src/commands/asset.ts
|
|
11899
|
-
var
|
|
12807
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
11900
12808
|
|
|
11901
12809
|
// src/commands/asset-auth.ts
|
|
11902
|
-
var
|
|
12810
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
11903
12811
|
|
|
11904
12812
|
// src/lib/resolve-asset.ts
|
|
11905
12813
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
@@ -11925,84 +12833,6 @@ or use pentest which auto-creates the asset:
|
|
|
11925
12833
|
}
|
|
11926
12834
|
}
|
|
11927
12835
|
|
|
11928
|
-
// src/lib/prompt.ts
|
|
11929
|
-
import { createInterface as createInterface2 } from "readline";
|
|
11930
|
-
async function promptMasked(label) {
|
|
11931
|
-
if (!isTTY() || !process.stdin.isTTY) {
|
|
11932
|
-
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
11933
|
-
process.exit(2);
|
|
11934
|
-
}
|
|
11935
|
-
process.stderr.write(`${label}: `);
|
|
11936
|
-
return new Promise((resolve, reject) => {
|
|
11937
|
-
const chars = [];
|
|
11938
|
-
process.stdin.setRawMode(true);
|
|
11939
|
-
process.stdin.resume();
|
|
11940
|
-
process.stdin.setEncoding("utf-8");
|
|
11941
|
-
const onData = (chunk) => {
|
|
11942
|
-
for (const ch of chunk) {
|
|
11943
|
-
if (ch === "\r" || ch === "\n") {
|
|
11944
|
-
process.stdin.setRawMode(false);
|
|
11945
|
-
process.stdin.pause();
|
|
11946
|
-
process.stdin.removeListener("data", onData);
|
|
11947
|
-
process.stderr.write("\n");
|
|
11948
|
-
resolve(chars.join(""));
|
|
11949
|
-
return;
|
|
11950
|
-
} else if (ch === "") {
|
|
11951
|
-
process.stdin.setRawMode(false);
|
|
11952
|
-
process.stdin.pause();
|
|
11953
|
-
process.stdin.removeListener("data", onData);
|
|
11954
|
-
process.stderr.write("\n");
|
|
11955
|
-
reject(new Error("Cancelled"));
|
|
11956
|
-
return;
|
|
11957
|
-
} else if (ch === "\x7F" || ch === "\b") {
|
|
11958
|
-
if (chars.length > 0) {
|
|
11959
|
-
chars.pop();
|
|
11960
|
-
process.stderr.write("\b \b");
|
|
11961
|
-
}
|
|
11962
|
-
} else if (ch >= " ") {
|
|
11963
|
-
chars.push(ch);
|
|
11964
|
-
process.stderr.write("*");
|
|
11965
|
-
}
|
|
11966
|
-
}
|
|
11967
|
-
};
|
|
11968
|
-
process.stdin.on("data", onData);
|
|
11969
|
-
});
|
|
11970
|
-
}
|
|
11971
|
-
async function promptText(label) {
|
|
11972
|
-
if (!isTTY() || !process.stdin.isTTY) {
|
|
11973
|
-
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
11974
|
-
process.exit(2);
|
|
11975
|
-
}
|
|
11976
|
-
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
11977
|
-
const answer = await new Promise((resolve) => {
|
|
11978
|
-
rl.question(`${label}: `, resolve);
|
|
11979
|
-
});
|
|
11980
|
-
rl.close();
|
|
11981
|
-
return answer;
|
|
11982
|
-
}
|
|
11983
|
-
async function promptSelect(message, options) {
|
|
11984
|
-
if (!isTTY() || !process.stdin.isTTY) {
|
|
11985
|
-
console.error("Cannot show interactive menu in non-interactive mode. Use flags instead.");
|
|
11986
|
-
process.exit(2);
|
|
11987
|
-
}
|
|
11988
|
-
console.error(`
|
|
11989
|
-
${message}`);
|
|
11990
|
-
for (let i = 0; i < options.length; i++) {
|
|
11991
|
-
console.error(` ${i + 1}. ${options[i].label} \u2014 ${options[i].description}`);
|
|
11992
|
-
}
|
|
11993
|
-
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
11994
|
-
const answer = await new Promise((resolve) => {
|
|
11995
|
-
rl.question("> ", resolve);
|
|
11996
|
-
});
|
|
11997
|
-
rl.close();
|
|
11998
|
-
const index = parseInt(answer, 10) - 1;
|
|
11999
|
-
if (isNaN(index) || index < 0 || index >= options.length) {
|
|
12000
|
-
console.error("Invalid selection.");
|
|
12001
|
-
process.exit(2);
|
|
12002
|
-
}
|
|
12003
|
-
return options[index].value;
|
|
12004
|
-
}
|
|
12005
|
-
|
|
12006
12836
|
// src/commands/asset-auth.ts
|
|
12007
12837
|
var VALID_AUTH_TYPES = ["bearer", "basic", "apikey", "session", "cookie"];
|
|
12008
12838
|
var AUTH_TYPE_OPTIONS = [
|
|
@@ -12156,7 +12986,7 @@ Examples:
|
|
|
12156
12986
|
}
|
|
12157
12987
|
if (!creds.has_credentials) {
|
|
12158
12988
|
console.log(`Asset: ${asset.asset_value}`);
|
|
12159
|
-
console.log(`Auth Type: ${
|
|
12989
|
+
console.log(`Auth Type: ${import_picocolors9.default.dim("(none)")}`);
|
|
12160
12990
|
console.log(`Has Credentials: no`);
|
|
12161
12991
|
return;
|
|
12162
12992
|
}
|
|
@@ -12177,9 +13007,9 @@ Examples:
|
|
|
12177
13007
|
const hasAny = ta.has_username || ta.has_cookie || ta.has_bearer_token || ta.has_apikey;
|
|
12178
13008
|
if (hasAny) {
|
|
12179
13009
|
const detail = ta.has_username ? "username" : ta.has_bearer_token ? "bearer token" : ta.has_cookie ? "cookie" : "API key";
|
|
12180
|
-
console.log(`Target Account: ${
|
|
13010
|
+
console.log(`Target Account: ${import_picocolors9.default.green("configured")} (${detail})`);
|
|
12181
13011
|
} else {
|
|
12182
|
-
console.log(`Target Account: ${
|
|
13012
|
+
console.log(`Target Account: ${import_picocolors9.default.dim("not configured")}`);
|
|
12183
13013
|
}
|
|
12184
13014
|
}
|
|
12185
13015
|
} catch (error2) {
|
|
@@ -12320,17 +13150,18 @@ Examples:
|
|
|
12320
13150
|
return;
|
|
12321
13151
|
}
|
|
12322
13152
|
printTable(
|
|
12323
|
-
["ID", "Value", "Type", "Status", "Created"],
|
|
13153
|
+
["ID", "Value", "Type", "Status", "Scope", "Created"],
|
|
12324
13154
|
result.items.map((a) => [
|
|
12325
13155
|
a.id,
|
|
12326
13156
|
truncate(a.asset_value, 40),
|
|
12327
13157
|
a.asset_type,
|
|
12328
13158
|
a.verification_status,
|
|
13159
|
+
a.scope_filter ?? "all",
|
|
12329
13160
|
new Date(a.created_at).toLocaleDateString()
|
|
12330
13161
|
])
|
|
12331
13162
|
);
|
|
12332
13163
|
if (result.next_cursor) {
|
|
12333
|
-
console.log(
|
|
13164
|
+
console.log(import_picocolors10.default.dim(`
|
|
12334
13165
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
12335
13166
|
}
|
|
12336
13167
|
} catch (error2) {
|
|
@@ -12350,18 +13181,24 @@ Examples:
|
|
|
12350
13181
|
printJson(detail);
|
|
12351
13182
|
return;
|
|
12352
13183
|
}
|
|
12353
|
-
|
|
12354
|
-
["
|
|
12355
|
-
[
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
13184
|
+
const rows = [
|
|
13185
|
+
["ID", detail.id],
|
|
13186
|
+
["Value", detail.asset_value],
|
|
13187
|
+
["Type", detail.asset_type],
|
|
13188
|
+
["Status", detail.verification_status],
|
|
13189
|
+
["Parent", detail.parent_domain ?? "(none)"]
|
|
13190
|
+
];
|
|
13191
|
+
if (detail.asset_type === "domain") {
|
|
13192
|
+
rows.push(["Scope", detail.scope_filter ?? "all subdomains"]);
|
|
13193
|
+
if (detail.scope_hosts?.length) {
|
|
13194
|
+
rows.push(["Hosts", detail.scope_hosts.join(", ")]);
|
|
13195
|
+
}
|
|
13196
|
+
}
|
|
13197
|
+
rows.push(
|
|
13198
|
+
["Created", detail.created_at],
|
|
13199
|
+
["Updated", detail.updated_at]
|
|
12364
13200
|
);
|
|
13201
|
+
printTable(["Field", "Value"], rows);
|
|
12365
13202
|
} catch (error2) {
|
|
12366
13203
|
handleError(error2);
|
|
12367
13204
|
}
|
|
@@ -12374,11 +13211,18 @@ Examples:
|
|
|
12374
13211
|
const opts = program3.opts();
|
|
12375
13212
|
const { token, apiUrl } = resolveToken(opts);
|
|
12376
13213
|
const client = new KinoSecClient({ apiUrl, token });
|
|
13214
|
+
const detected = detectAssetType(options.value);
|
|
13215
|
+
if (detected !== options.type) {
|
|
13216
|
+
console.error(
|
|
13217
|
+
`Warning: ${options.value} looks like a ${detected}, not a ${options.type}. Continuing with your specified type.`
|
|
13218
|
+
);
|
|
13219
|
+
}
|
|
13220
|
+
const parentDomain = options.parent ?? (options.type === "subdomain" ? deriveParentDomain(options.value) : null);
|
|
12377
13221
|
const result = await client.assets.create({
|
|
12378
13222
|
asset_type: options.type,
|
|
12379
13223
|
asset_value: options.value,
|
|
12380
13224
|
verification_method: options.verifyMethod,
|
|
12381
|
-
parent_domain:
|
|
13225
|
+
parent_domain: parentDomain
|
|
12382
13226
|
});
|
|
12383
13227
|
if (opts.json || opts.output === "json") {
|
|
12384
13228
|
printJson(result);
|
|
@@ -12438,11 +13282,85 @@ Examples:
|
|
|
12438
13282
|
handleError(error2);
|
|
12439
13283
|
}
|
|
12440
13284
|
});
|
|
13285
|
+
asset.command("scope").description("View or configure subdomain scope for an asset").argument("<id>", "Asset ID or domain").option("--include <hosts>", "Include only these subdomains (comma-separated)").option("--exclude <hosts>", "Exclude these subdomains (comma-separated)").option("--clear", "Clear scope settings").addHelpText("after", `
|
|
13286
|
+
Examples:
|
|
13287
|
+
$ kinosec asset scope abc123
|
|
13288
|
+
$ kinosec asset scope example.com --include api.example.com,app.example.com
|
|
13289
|
+
$ kinosec asset scope abc123 --exclude staging.example.com
|
|
13290
|
+
$ kinosec asset scope abc123 --clear`).action(async (id, options) => {
|
|
13291
|
+
try {
|
|
13292
|
+
validateScopeFlags({
|
|
13293
|
+
include: options.include,
|
|
13294
|
+
exclude: options.exclude,
|
|
13295
|
+
scopeClear: options.clear
|
|
13296
|
+
});
|
|
13297
|
+
const opts = program3.opts();
|
|
13298
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
13299
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
13300
|
+
const asset2 = await resolveAsset(client, id);
|
|
13301
|
+
if (asset2.asset_type !== "domain") {
|
|
13302
|
+
console.error("Error: Scope filtering is only available for domain assets.");
|
|
13303
|
+
process.exit(2);
|
|
13304
|
+
}
|
|
13305
|
+
if (!options.include && !options.exclude && !options.clear) {
|
|
13306
|
+
if (opts.json || opts.output === "json") {
|
|
13307
|
+
printJson({
|
|
13308
|
+
scope_filter: asset2.scope_filter,
|
|
13309
|
+
scope_hosts: asset2.scope_hosts
|
|
13310
|
+
});
|
|
13311
|
+
return;
|
|
13312
|
+
}
|
|
13313
|
+
if (!asset2.scope_filter || !asset2.scope_hosts?.length) {
|
|
13314
|
+
console.log("Scope: all subdomains");
|
|
13315
|
+
} else {
|
|
13316
|
+
console.log(`Scope: ${asset2.scope_filter}`);
|
|
13317
|
+
console.log("Hosts:");
|
|
13318
|
+
for (const host of asset2.scope_hosts) {
|
|
13319
|
+
console.log(` - ${host}`);
|
|
13320
|
+
}
|
|
13321
|
+
}
|
|
13322
|
+
return;
|
|
13323
|
+
}
|
|
13324
|
+
if (options.clear) {
|
|
13325
|
+
await client.assets.update(asset2.id, {
|
|
13326
|
+
scope_filter: null,
|
|
13327
|
+
scope_hosts: null
|
|
13328
|
+
});
|
|
13329
|
+
if (opts.json || opts.output === "json") {
|
|
13330
|
+
printJson({ scope_filter: null, scope_hosts: null });
|
|
13331
|
+
return;
|
|
13332
|
+
}
|
|
13333
|
+
printSuccess("Scope settings cleared. Scans will now cover all subdomains by default.");
|
|
13334
|
+
return;
|
|
13335
|
+
}
|
|
13336
|
+
const mode = options.include ? "include" : "exclude";
|
|
13337
|
+
const rawHosts = parseScopeHosts(options.include ?? options.exclude);
|
|
13338
|
+
validateScopeHosts(rawHosts);
|
|
13339
|
+
validateScopeHostOwnership(rawHosts, asset2.asset_value);
|
|
13340
|
+
const result = await resolveScopeUpdate(asset2, mode, rawHosts);
|
|
13341
|
+
if (!result) return;
|
|
13342
|
+
await client.assets.update(asset2.id, {
|
|
13343
|
+
scope_filter: result.scope_filter,
|
|
13344
|
+
scope_hosts: result.scope_hosts
|
|
13345
|
+
});
|
|
13346
|
+
if (opts.json || opts.output === "json") {
|
|
13347
|
+
printJson(result);
|
|
13348
|
+
return;
|
|
13349
|
+
}
|
|
13350
|
+
printSuccess(`Scope updated: ${result.scope_filter}`);
|
|
13351
|
+
console.log("Hosts:");
|
|
13352
|
+
for (const host of result.scope_hosts) {
|
|
13353
|
+
console.log(` - ${host}`);
|
|
13354
|
+
}
|
|
13355
|
+
} catch (error2) {
|
|
13356
|
+
handleError(error2);
|
|
13357
|
+
}
|
|
13358
|
+
});
|
|
12441
13359
|
registerAssetAuthCommand(asset);
|
|
12442
13360
|
}
|
|
12443
13361
|
|
|
12444
13362
|
// src/commands/finding.ts
|
|
12445
|
-
var
|
|
13363
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
12446
13364
|
function formatPoc(poc) {
|
|
12447
13365
|
if (poc == null) return null;
|
|
12448
13366
|
if (typeof poc === "string") return poc.trim() || null;
|
|
@@ -12483,7 +13401,7 @@ Examples:
|
|
|
12483
13401
|
])
|
|
12484
13402
|
);
|
|
12485
13403
|
if (result.next_cursor) {
|
|
12486
|
-
console.log(
|
|
13404
|
+
console.log(import_picocolors11.default.dim(`
|
|
12487
13405
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
12488
13406
|
}
|
|
12489
13407
|
} catch (error2) {
|
|
@@ -12571,7 +13489,7 @@ Examples:
|
|
|
12571
13489
|
}
|
|
12572
13490
|
|
|
12573
13491
|
// src/commands/report.ts
|
|
12574
|
-
var
|
|
13492
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
12575
13493
|
function renderMarkdown(data) {
|
|
12576
13494
|
const { scan, findings } = data;
|
|
12577
13495
|
const lines = [];
|
|
@@ -12648,7 +13566,7 @@ Examples:
|
|
|
12648
13566
|
])
|
|
12649
13567
|
);
|
|
12650
13568
|
if (result.next_cursor) {
|
|
12651
|
-
console.log(
|
|
13569
|
+
console.log(import_picocolors12.default.dim(`
|
|
12652
13570
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
12653
13571
|
}
|
|
12654
13572
|
} catch (error2) {
|
|
@@ -12719,7 +13637,7 @@ Examples:
|
|
|
12719
13637
|
}
|
|
12720
13638
|
|
|
12721
13639
|
// src/commands/token.ts
|
|
12722
|
-
var
|
|
13640
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
12723
13641
|
function registerTokenCommand(program3) {
|
|
12724
13642
|
const token = program3.command("token").description("Manage API tokens");
|
|
12725
13643
|
token.command("list").description("List API tokens").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
|
|
@@ -12754,7 +13672,7 @@ Examples:
|
|
|
12754
13672
|
])
|
|
12755
13673
|
);
|
|
12756
13674
|
if (result.next_cursor) {
|
|
12757
|
-
console.log(
|
|
13675
|
+
console.log(import_picocolors13.default.dim(`
|
|
12758
13676
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
12759
13677
|
}
|
|
12760
13678
|
} catch (error2) {
|
|
@@ -12781,7 +13699,7 @@ Examples:
|
|
|
12781
13699
|
}
|
|
12782
13700
|
printSuccess(`Token created: ${result.name}`);
|
|
12783
13701
|
console.log("");
|
|
12784
|
-
console.log(
|
|
13702
|
+
console.log(import_picocolors13.default.bold(" Token: ") + result.token);
|
|
12785
13703
|
console.log("");
|
|
12786
13704
|
printWarning("Save this token \u2014 it will not be shown again.");
|
|
12787
13705
|
console.log(` ID: ${result.id}`);
|
|
@@ -12814,7 +13732,7 @@ Examples:
|
|
|
12814
13732
|
|
|
12815
13733
|
// src/cli.ts
|
|
12816
13734
|
var program2 = new Command();
|
|
12817
|
-
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.
|
|
13735
|
+
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.10.1", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
|
|
12818
13736
|
registerAuthCommand(program2);
|
|
12819
13737
|
registerMeCommand(program2);
|
|
12820
13738
|
registerCreditsCommand(program2);
|
|
@@ -12823,7 +13741,7 @@ registerAssetCommand(program2);
|
|
|
12823
13741
|
registerFindingCommand(program2);
|
|
12824
13742
|
registerReportCommand(program2);
|
|
12825
13743
|
registerTokenCommand(program2);
|
|
12826
|
-
program2.parseAsync(process.argv).then(() => checkForUpdate("0.
|
|
13744
|
+
program2.parseAsync(process.argv).then(() => checkForUpdate("0.10.1")).catch(handleError);
|
|
12827
13745
|
/*! Bundled license information:
|
|
12828
13746
|
|
|
12829
13747
|
ky/distribution/index.js:
|