@tinycloud/cli 1.0.0-beta.18 → 1.0.0-beta.20
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/index.js +52 -20
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +74 -16
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +5 -5
- package/skills/tc-cli/AUTH.md +1 -1
package/dist/legacy-entry.js
CHANGED
|
@@ -14594,6 +14594,34 @@ function errorDescription(value) {
|
|
|
14594
14594
|
const description = value?.errorDescription ?? value?.error_description;
|
|
14595
14595
|
return typeof description === "string" && description.length > 0 ? description : void 0;
|
|
14596
14596
|
}
|
|
14597
|
+
function retryAfterSeconds(response) {
|
|
14598
|
+
const value = response.headers.get("retry-after");
|
|
14599
|
+
if (!value) return void 0;
|
|
14600
|
+
let seconds;
|
|
14601
|
+
if (/^\d+$/.test(value)) {
|
|
14602
|
+
seconds = Number(value);
|
|
14603
|
+
if (!Number.isSafeInteger(seconds)) return void 0;
|
|
14604
|
+
} else {
|
|
14605
|
+
if (!/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (?:0[1-9]|[12]\d|3[01]) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} (?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d GMT$/.test(value)) {
|
|
14606
|
+
return void 0;
|
|
14607
|
+
}
|
|
14608
|
+
const retryAt = Date.parse(value);
|
|
14609
|
+
if (!Number.isFinite(retryAt) || new Date(retryAt).toUTCString() !== value) return void 0;
|
|
14610
|
+
seconds = Math.ceil((retryAt - Date.now()) / 1e3);
|
|
14611
|
+
}
|
|
14612
|
+
return seconds > 0 && seconds <= 600 ? seconds : void 0;
|
|
14613
|
+
}
|
|
14614
|
+
function deviceAuthRateLimit(response) {
|
|
14615
|
+
const retrySeconds = retryAfterSeconds(response);
|
|
14616
|
+
return new CLIError(
|
|
14617
|
+
"DEVICE_AUTH_RATE_LIMITED",
|
|
14618
|
+
"OpenKey rate limited device sign-in requests from this network",
|
|
14619
|
+
ExitCode.ERROR,
|
|
14620
|
+
{
|
|
14621
|
+
hint: `OpenKey allows 5 device sign-in requests per 10 minutes from this network; wait up to 10 minutes, then retry once. The limit is shared by every agent on this network.${retrySeconds !== void 0 ? ` Retry after ${retrySeconds} seconds.` : ""}`
|
|
14622
|
+
}
|
|
14623
|
+
);
|
|
14624
|
+
}
|
|
14597
14625
|
function publicSessionJwk(value) {
|
|
14598
14626
|
const publicJwk = publicJwkForDelegation(value);
|
|
14599
14627
|
const record = publicJwk;
|
|
@@ -14774,6 +14802,9 @@ async function acquireDeviceDelegation(input) {
|
|
|
14774
14802
|
if (!startResponse.ok) {
|
|
14775
14803
|
const code2 = errorCode(startValue);
|
|
14776
14804
|
const description = errorDescription(startValue);
|
|
14805
|
+
if (startResponse.status === 429 && code2 === "rate_limited") {
|
|
14806
|
+
throw deviceAuthRateLimit(startResponse);
|
|
14807
|
+
}
|
|
14777
14808
|
if (code2 === "invalid_scope") {
|
|
14778
14809
|
throw new CLIError(
|
|
14779
14810
|
"SCOPE_REJECTED",
|
|
@@ -20353,6 +20384,17 @@ var __export2 = (target, all) => {
|
|
|
20353
20384
|
for (var name in all)
|
|
20354
20385
|
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
20355
20386
|
};
|
|
20387
|
+
var UNSAFE_FILENAME_CODE_POINT = /[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;
|
|
20388
|
+
function hasUnsafeFilenameCodePoint(value) {
|
|
20389
|
+
return UNSAFE_FILENAME_CODE_POINT.test(value);
|
|
20390
|
+
}
|
|
20391
|
+
function canonicalShareFilename(value) {
|
|
20392
|
+
const canonical = value.normalize("NFC");
|
|
20393
|
+
if (canonical.length === 0 || canonical === "." || canonical === ".." || canonical.includes("/") || canonical.includes("\\") || hasUnsafeFilenameCodePoint(canonical)) {
|
|
20394
|
+
throw new TypeError("share filename is unsafe");
|
|
20395
|
+
}
|
|
20396
|
+
return canonical;
|
|
20397
|
+
}
|
|
20356
20398
|
var external_exports2 = {};
|
|
20357
20399
|
__export2(external_exports2, {
|
|
20358
20400
|
BRAND: () => BRAND2,
|
|
@@ -26488,8 +26530,17 @@ function normalizeShareTarget(target) {
|
|
|
26488
26530
|
function targetAuthorizationMethod(target) {
|
|
26489
26531
|
return authorizationMethodForTarget(target);
|
|
26490
26532
|
}
|
|
26533
|
+
function publishFilename(value) {
|
|
26534
|
+
try {
|
|
26535
|
+
return canonicalShareFilename(value);
|
|
26536
|
+
} catch {
|
|
26537
|
+
throw new SharePublishError("invalid-argument", hasUnsafeFilenameCodePoint(value) ? "filename contains control or invisible characters" : "filename must be one safe path segment");
|
|
26538
|
+
}
|
|
26539
|
+
}
|
|
26491
26540
|
async function publishTargetShare(input) {
|
|
26492
26541
|
const target = normalizeShareTarget(input.target);
|
|
26542
|
+
const filename = publishFilename(input.filename);
|
|
26543
|
+
const files = input.files?.map((file) => ({ ...file, filename: publishFilename(file.filename) }));
|
|
26493
26544
|
if (input.targetAdapter === void 0) {
|
|
26494
26545
|
if (target.kind === "bearer") throw new SharePublishError("authority-required", "native bearer publication requires an authenticated TinyCloud node");
|
|
26495
26546
|
return {
|
|
@@ -26515,9 +26566,6 @@ async function publishTargetShare(input) {
|
|
|
26515
26566
|
return bytes3;
|
|
26516
26567
|
})();
|
|
26517
26568
|
if (source.byteLength === 0) throw new SharePublishError("invalid-argument", "share content is empty");
|
|
26518
|
-
if (!input.filename || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) {
|
|
26519
|
-
throw new SharePublishError("invalid-argument", "filename must be one safe path segment");
|
|
26520
|
-
}
|
|
26521
26569
|
if (target.kind === "bearer" && input.allowBinary !== true) {
|
|
26522
26570
|
try {
|
|
26523
26571
|
new TextDecoder("utf-8", { fatal: true }).decode(source);
|
|
@@ -26525,10 +26573,10 @@ async function publishTargetShare(input) {
|
|
|
26525
26573
|
throw new SharePublishError("invalid-argument", "Markdown input must be valid UTF-8");
|
|
26526
26574
|
}
|
|
26527
26575
|
}
|
|
26528
|
-
const
|
|
26576
|
+
const publishFiles = files === void 0 || files.length === 0 ? [{ bytes: source }] : files;
|
|
26529
26577
|
const limit = input.maxBytes ?? SHARE_CONTENT_LIMIT;
|
|
26530
26578
|
let totalBytes = 0;
|
|
26531
|
-
for (const file of
|
|
26579
|
+
for (const file of publishFiles) {
|
|
26532
26580
|
totalBytes += file.bytes.byteLength;
|
|
26533
26581
|
if (!Number.isSafeInteger(totalBytes) || totalBytes > limit) {
|
|
26534
26582
|
throw new SharePublishError("max-bytes-exceeded", "share publication exceeds the combined byte limit");
|
|
@@ -26539,8 +26587,8 @@ async function publishTargetShare(input) {
|
|
|
26539
26587
|
if (!Number.isFinite(expiresAt.getTime()) || expiresAt.getTime() <= nowMs2) throw new SharePublishError("invalid-argument", "expiresAt must be a valid future time");
|
|
26540
26588
|
return input.targetAdapter.publish({
|
|
26541
26589
|
source,
|
|
26542
|
-
filename
|
|
26543
|
-
...
|
|
26590
|
+
filename,
|
|
26591
|
+
...files === void 0 ? {} : { files },
|
|
26544
26592
|
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
26545
26593
|
...input.resourceKind === void 0 ? {} : { resourceKind: input.resourceKind },
|
|
26546
26594
|
...input.actions === void 0 ? {} : { actions: input.actions },
|
|
@@ -27271,6 +27319,7 @@ import { constants } from "fs";
|
|
|
27271
27319
|
import { lstat, mkdir as mkdir3, mkdtemp, open as open3, readFile as readFile9, realpath, stat as stat2, link, rename as rename2, rm as rm3, unlink } from "fs/promises";
|
|
27272
27320
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
27273
27321
|
import { basename as basename2, join as join6, resolve as resolve2, sep } from "path";
|
|
27322
|
+
init_errors();
|
|
27274
27323
|
var MAX_SHARE_STDIN_BYTES = 100 * 1024 * 1024;
|
|
27275
27324
|
var MAX_SHARE_URL_BYTES = 64 * 1024;
|
|
27276
27325
|
async function readBoundedStdin(limit = MAX_SHARE_STDIN_BYTES) {
|
|
@@ -27292,21 +27341,28 @@ async function readBoundedUrlStdin() {
|
|
|
27292
27341
|
return value;
|
|
27293
27342
|
}
|
|
27294
27343
|
function safeFilename(value) {
|
|
27295
|
-
|
|
27296
|
-
return value;
|
|
27344
|
+
return shareFilename(value);
|
|
27297
27345
|
}
|
|
27298
27346
|
function shareFilename(value) {
|
|
27299
|
-
|
|
27347
|
+
try {
|
|
27348
|
+
return canonicalShareFilename(value);
|
|
27349
|
+
} catch {
|
|
27350
|
+
throw new CLIError(
|
|
27351
|
+
"UNSAFE_FILENAME",
|
|
27352
|
+
hasUnsafeFilenameCodePoint(value) ? "filename contains control or invisible characters" : "filename must be one safe path segment",
|
|
27353
|
+
8
|
|
27354
|
+
);
|
|
27355
|
+
}
|
|
27356
|
+
}
|
|
27357
|
+
function shareInputFilename(input, name) {
|
|
27358
|
+
return shareFilename(name ?? (input === "-" ? "stdin.md" : basename2(resolve2(input))));
|
|
27300
27359
|
}
|
|
27301
27360
|
async function readShareInput(input, name, limit = MAX_SHARE_STDIN_BYTES) {
|
|
27302
|
-
|
|
27303
|
-
|
|
27304
|
-
return { bytes: bytes2, filename: shareFilename(name ?? "stdin.md") };
|
|
27305
|
-
}
|
|
27361
|
+
const filename = shareInputFilename(input, name);
|
|
27362
|
+
if (input === "-") return { bytes: await readBoundedStdin(limit), filename };
|
|
27306
27363
|
const path = resolve2(input);
|
|
27307
27364
|
const info = await stat2(path);
|
|
27308
27365
|
if (!info.isFile() || info.size > limit) throw new Error("MAX_BYTES_EXCEEDED");
|
|
27309
|
-
const filename = shareFilename(name ?? basename2(path));
|
|
27310
27366
|
const bytes = new Uint8Array(await readFile9(path));
|
|
27311
27367
|
if (bytes.byteLength > limit) throw new Error("MAX_BYTES_EXCEEDED");
|
|
27312
27368
|
return { bytes, filename };
|
|
@@ -27551,7 +27607,9 @@ function registerShareCommand(program) {
|
|
|
27551
27607
|
const json = jsonOutput(options, command);
|
|
27552
27608
|
const maxBytes = byteLimit(options.maxBytes);
|
|
27553
27609
|
if (files.length === 0 || files.includes("-") && files.length > 1) throw new CLIError("INVALID_ARGUMENT", "stdin must be the only publish input", 2);
|
|
27554
|
-
const
|
|
27610
|
+
const name = files.length === 1 ? options.name : void 0;
|
|
27611
|
+
for (const file of files) shareInputFilename(file, name);
|
|
27612
|
+
const inputs = await Promise.all(files.map((file) => readShareInput(file, name, maxBytes)));
|
|
27555
27613
|
assertAggregateInputLimit(inputs, maxBytes);
|
|
27556
27614
|
const target = parseShareTarget(options.to);
|
|
27557
27615
|
const actions = requestedActions(options.action);
|