@tinycloud/cli 1.0.0-beta.19 → 1.0.0-beta.21
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 +69 -20
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +51 -16
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +5 -5
- package/skills/tc-cli/REFERENCE.md +2 -2
package/dist/legacy-entry.js
CHANGED
|
@@ -20384,6 +20384,17 @@ var __export2 = (target, all) => {
|
|
|
20384
20384
|
for (var name in all)
|
|
20385
20385
|
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
20386
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
|
+
}
|
|
20387
20398
|
var external_exports2 = {};
|
|
20388
20399
|
__export2(external_exports2, {
|
|
20389
20400
|
BRAND: () => BRAND2,
|
|
@@ -26519,8 +26530,17 @@ function normalizeShareTarget(target) {
|
|
|
26519
26530
|
function targetAuthorizationMethod(target) {
|
|
26520
26531
|
return authorizationMethodForTarget(target);
|
|
26521
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
|
+
}
|
|
26522
26540
|
async function publishTargetShare(input) {
|
|
26523
26541
|
const target = normalizeShareTarget(input.target);
|
|
26542
|
+
const filename = publishFilename(input.filename);
|
|
26543
|
+
const files = input.files?.map((file) => ({ ...file, filename: publishFilename(file.filename) }));
|
|
26524
26544
|
if (input.targetAdapter === void 0) {
|
|
26525
26545
|
if (target.kind === "bearer") throw new SharePublishError("authority-required", "native bearer publication requires an authenticated TinyCloud node");
|
|
26526
26546
|
return {
|
|
@@ -26546,9 +26566,6 @@ async function publishTargetShare(input) {
|
|
|
26546
26566
|
return bytes3;
|
|
26547
26567
|
})();
|
|
26548
26568
|
if (source.byteLength === 0) throw new SharePublishError("invalid-argument", "share content is empty");
|
|
26549
|
-
if (!input.filename || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) {
|
|
26550
|
-
throw new SharePublishError("invalid-argument", "filename must be one safe path segment");
|
|
26551
|
-
}
|
|
26552
26569
|
if (target.kind === "bearer" && input.allowBinary !== true) {
|
|
26553
26570
|
try {
|
|
26554
26571
|
new TextDecoder("utf-8", { fatal: true }).decode(source);
|
|
@@ -26556,10 +26573,10 @@ async function publishTargetShare(input) {
|
|
|
26556
26573
|
throw new SharePublishError("invalid-argument", "Markdown input must be valid UTF-8");
|
|
26557
26574
|
}
|
|
26558
26575
|
}
|
|
26559
|
-
const
|
|
26576
|
+
const publishFiles = files === void 0 || files.length === 0 ? [{ bytes: source }] : files;
|
|
26560
26577
|
const limit = input.maxBytes ?? SHARE_CONTENT_LIMIT;
|
|
26561
26578
|
let totalBytes = 0;
|
|
26562
|
-
for (const file of
|
|
26579
|
+
for (const file of publishFiles) {
|
|
26563
26580
|
totalBytes += file.bytes.byteLength;
|
|
26564
26581
|
if (!Number.isSafeInteger(totalBytes) || totalBytes > limit) {
|
|
26565
26582
|
throw new SharePublishError("max-bytes-exceeded", "share publication exceeds the combined byte limit");
|
|
@@ -26570,8 +26587,8 @@ async function publishTargetShare(input) {
|
|
|
26570
26587
|
if (!Number.isFinite(expiresAt.getTime()) || expiresAt.getTime() <= nowMs2) throw new SharePublishError("invalid-argument", "expiresAt must be a valid future time");
|
|
26571
26588
|
return input.targetAdapter.publish({
|
|
26572
26589
|
source,
|
|
26573
|
-
filename
|
|
26574
|
-
...
|
|
26590
|
+
filename,
|
|
26591
|
+
...files === void 0 ? {} : { files },
|
|
26575
26592
|
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
26576
26593
|
...input.resourceKind === void 0 ? {} : { resourceKind: input.resourceKind },
|
|
26577
26594
|
...input.actions === void 0 ? {} : { actions: input.actions },
|
|
@@ -27302,6 +27319,7 @@ import { constants } from "fs";
|
|
|
27302
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";
|
|
27303
27320
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
27304
27321
|
import { basename as basename2, join as join6, resolve as resolve2, sep } from "path";
|
|
27322
|
+
init_errors();
|
|
27305
27323
|
var MAX_SHARE_STDIN_BYTES = 100 * 1024 * 1024;
|
|
27306
27324
|
var MAX_SHARE_URL_BYTES = 64 * 1024;
|
|
27307
27325
|
async function readBoundedStdin(limit = MAX_SHARE_STDIN_BYTES) {
|
|
@@ -27323,21 +27341,28 @@ async function readBoundedUrlStdin() {
|
|
|
27323
27341
|
return value;
|
|
27324
27342
|
}
|
|
27325
27343
|
function safeFilename(value) {
|
|
27326
|
-
|
|
27327
|
-
return value;
|
|
27344
|
+
return shareFilename(value);
|
|
27328
27345
|
}
|
|
27329
27346
|
function shareFilename(value) {
|
|
27330
|
-
|
|
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))));
|
|
27331
27359
|
}
|
|
27332
27360
|
async function readShareInput(input, name, limit = MAX_SHARE_STDIN_BYTES) {
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
return { bytes: bytes2, filename: shareFilename(name ?? "stdin.md") };
|
|
27336
|
-
}
|
|
27361
|
+
const filename = shareInputFilename(input, name);
|
|
27362
|
+
if (input === "-") return { bytes: await readBoundedStdin(limit), filename };
|
|
27337
27363
|
const path = resolve2(input);
|
|
27338
27364
|
const info = await stat2(path);
|
|
27339
27365
|
if (!info.isFile() || info.size > limit) throw new Error("MAX_BYTES_EXCEEDED");
|
|
27340
|
-
const filename = shareFilename(name ?? basename2(path));
|
|
27341
27366
|
const bytes = new Uint8Array(await readFile9(path));
|
|
27342
27367
|
if (bytes.byteLength > limit) throw new Error("MAX_BYTES_EXCEEDED");
|
|
27343
27368
|
return { bytes, filename };
|
|
@@ -27454,6 +27479,14 @@ function shareCliError(error) {
|
|
|
27454
27479
|
const localKey = "localKey" in failure && failure.localKey === true;
|
|
27455
27480
|
const profileHint = profileName === void 0 ? "" : `--profile ${profileName} `;
|
|
27456
27481
|
const loginHint = localKey ? `\`tc ${profileHint}auth login --method local\`` : `\`tc ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`tc ${profileHint}enable share\`)`;
|
|
27482
|
+
if (failure.kind === "caveated-session") {
|
|
27483
|
+
const holder = profileName === void 0 ? "this session" : `profile ${profileName}'s session`;
|
|
27484
|
+
return new CLIError(
|
|
27485
|
+
"PERMISSION_DENIED",
|
|
27486
|
+
`${holder} carries signed restrictions (caveats) on the authority an anyone-with-link share needs, so it cannot create the share link; nothing was shared. Approve Share publishing without restrictions on a new, dedicated profile (any unused name): \`tc init --name publisher --key-only && tc --profile publisher enable share\``,
|
|
27487
|
+
5
|
|
27488
|
+
);
|
|
27489
|
+
}
|
|
27457
27490
|
if (failure.kind === "owner-space-unresolved") {
|
|
27458
27491
|
return new CLIError("AUTH_REQUIRED", `a valid signed TinyCloud session is required; run ${loginHint}`, 3);
|
|
27459
27492
|
}
|
|
@@ -27582,7 +27615,9 @@ function registerShareCommand(program) {
|
|
|
27582
27615
|
const json = jsonOutput(options, command);
|
|
27583
27616
|
const maxBytes = byteLimit(options.maxBytes);
|
|
27584
27617
|
if (files.length === 0 || files.includes("-") && files.length > 1) throw new CLIError("INVALID_ARGUMENT", "stdin must be the only publish input", 2);
|
|
27585
|
-
const
|
|
27618
|
+
const name = files.length === 1 ? options.name : void 0;
|
|
27619
|
+
for (const file of files) shareInputFilename(file, name);
|
|
27620
|
+
const inputs = await Promise.all(files.map((file) => readShareInput(file, name, maxBytes)));
|
|
27586
27621
|
assertAggregateInputLimit(inputs, maxBytes);
|
|
27587
27622
|
const target = parseShareTarget(options.to);
|
|
27588
27623
|
const actions = requestedActions(options.action);
|