@tinycloud/cli 1.0.0-beta.17 → 1.0.0-beta.18
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 +37 -5
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +33 -1
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +1 -1
- package/skills/tc-cli/REFERENCE.md +4 -2
package/dist/index.js
CHANGED
|
@@ -94,6 +94,11 @@ function outputError(code3, message, hint) {
|
|
|
94
94
|
function isInteractive() {
|
|
95
95
|
return Boolean(process.stdout.isTTY);
|
|
96
96
|
}
|
|
97
|
+
function formatBytes(bytes) {
|
|
98
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
99
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
100
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
101
|
+
}
|
|
97
102
|
var init_formatter = __esm({
|
|
98
103
|
"src/output/formatter.ts"() {
|
|
99
104
|
"use strict";
|
|
@@ -15051,6 +15056,7 @@ function parseDuration(input) {
|
|
|
15051
15056
|
}
|
|
15052
15057
|
|
|
15053
15058
|
// src/commands/share.ts
|
|
15059
|
+
init_formatter();
|
|
15054
15060
|
init_errors();
|
|
15055
15061
|
|
|
15056
15062
|
// src/share/output.ts
|
|
@@ -15253,7 +15259,7 @@ function shareCliError(error) {
|
|
|
15253
15259
|
}
|
|
15254
15260
|
if (failure.kind === "scope-denied") {
|
|
15255
15261
|
const requiredAction = failure.requiredAction === void 0 ? "" : ` (${failure.requiredAction})`;
|
|
15256
|
-
const renew = localKey ? `renew it with ${loginHint} using the required capability` : `
|
|
15262
|
+
const renew = localKey ? `renew it with ${loginHint} using the required capability` : `verify the session includes the builtin:share-publishing scope; if it does not, request it with ${loginHint}`;
|
|
15257
15263
|
return new CLIError("PERMISSION_DENIED", `the session lacks ${failure.capability} authority${requiredAction}; ${renew}`, 5);
|
|
15258
15264
|
}
|
|
15259
15265
|
if (failure.kind === "lifetime-exceeds-session") {
|
|
@@ -15278,6 +15284,13 @@ function shareCliError(error) {
|
|
|
15278
15284
|
if (failure.kind === "registry-rejected") {
|
|
15279
15285
|
return new CLIError("REGISTRY_REJECTED", "the TinyCloud location registry rejected this session's location record, so nothing was shared; retrying will not help. Log in again, and report the problem if it persists", 6);
|
|
15280
15286
|
}
|
|
15287
|
+
if (failure.kind === "storage-quota-exceeded") {
|
|
15288
|
+
const sizes = failure.usedBytes === void 0 || failure.limitBytes === void 0 ? "" : ` (${formatBytes(failure.usedBytes)} used of ${formatBytes(failure.limitBytes)} limit)`;
|
|
15289
|
+
return new CLIError("STORAGE_QUOTA_EXCEEDED", `storage quota exceeded${sizes}; nothing was shared`, 4);
|
|
15290
|
+
}
|
|
15291
|
+
if (failure.kind === "upload-failed") {
|
|
15292
|
+
return new CLIError("UPLOAD_FAILED", "share source upload failed; nothing was shared", 4);
|
|
15293
|
+
}
|
|
15281
15294
|
}
|
|
15282
15295
|
if (error instanceof ShareNotifyError) {
|
|
15283
15296
|
return new CLIError("INVALID_ARGUMENT", error.message, 2);
|
|
@@ -16897,7 +16910,26 @@ function requiredKvAction(meta) {
|
|
|
16897
16910
|
const action = meta.requiredAction;
|
|
16898
16911
|
return action === "tinycloud.kv/put" || action === "tinycloud.kv/get" ? action : void 0;
|
|
16899
16912
|
}
|
|
16913
|
+
function isByteCount(value) {
|
|
16914
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
16915
|
+
}
|
|
16916
|
+
function throwKvUploadFailure(error) {
|
|
16917
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "STORAGE_QUOTA_EXCEEDED") {
|
|
16918
|
+
const meta = "meta" in error && typeof error.meta === "object" && error.meta !== null ? error.meta : {};
|
|
16919
|
+
const { usedBytes, limitBytes } = meta;
|
|
16920
|
+
throw new SharePublishAuthorityError(isByteCount(usedBytes) && isByteCount(limitBytes) ? { kind: "storage-quota-exceeded", usedBytes, limitBytes } : { kind: "storage-quota-exceeded" });
|
|
16921
|
+
}
|
|
16922
|
+
throw new SharePublishAuthorityError({ kind: "upload-failed" });
|
|
16923
|
+
}
|
|
16900
16924
|
var DEFAULT_SHARE_ORIGIN = "https://share.tinycloud.xyz";
|
|
16925
|
+
var URI_SAFE_FILENAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
16926
|
+
function safeStorageFilename(filename) {
|
|
16927
|
+
if (URI_SAFE_FILENAME.test(filename) && !filename.includes("..")) return filename;
|
|
16928
|
+
const dot = filename.lastIndexOf(".");
|
|
16929
|
+
const extension = dot >= 0 && /^[A-Za-z0-9]{1,16}$/.test(filename.slice(dot + 1)) ? filename.slice(dot + 1) : "";
|
|
16930
|
+
const stem = (extension === "" ? filename : filename.slice(0, dot)).normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(extension === "" ? /[^A-Za-z0-9_-]+/g : /[^A-Za-z0-9._-]+/g, "-").replace(/\.{2,}/g, ".").replace(/-{2,}/g, "-").slice(0, 100).replace(/^[^A-Za-z0-9]+|[-.]+$/g, "");
|
|
16931
|
+
return `${stem === "" ? "share" : stem}${extension === "" ? "" : `.${extension}`}`;
|
|
16932
|
+
}
|
|
16901
16933
|
function createEncryptedSessionHistory() {
|
|
16902
16934
|
const records = /* @__PURE__ */ new Map();
|
|
16903
16935
|
let keyPromise;
|
|
@@ -17129,7 +17161,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
17129
17161
|
if (targetInput.target.kind === "bearer") {
|
|
17130
17162
|
if (resourceKind !== "exact" || files.length !== 1) throw new Error("native bearer publication requires one exact source file");
|
|
17131
17163
|
const file2 = files[0];
|
|
17132
|
-
const resourcePath2 = `xyz.tinycloud.share/shares/${shareId}/${targetInput.filename}`;
|
|
17164
|
+
const resourcePath2 = `xyz.tinycloud.share/shares/${shareId}/${safeStorageFilename(targetInput.filename)}`;
|
|
17133
17165
|
const written = await node.kvForSpace(ownerSpaceId).put(resourcePath2, file2.bytes.slice(), {
|
|
17134
17166
|
contentType: targetInput.mediaType ?? file2.mediaType ?? "application/octet-stream"
|
|
17135
17167
|
});
|
|
@@ -17145,7 +17177,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
17145
17177
|
profileName: activeProfileName2
|
|
17146
17178
|
});
|
|
17147
17179
|
}
|
|
17148
|
-
|
|
17180
|
+
throwKvUploadFailure(written.error);
|
|
17149
17181
|
}
|
|
17150
17182
|
let native;
|
|
17151
17183
|
try {
|
|
@@ -17193,7 +17225,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
17193
17225
|
}
|
|
17194
17226
|
if (resourceKind !== "exact" || files.length !== 1) throw new Error("addressed publication requires a single exact source file");
|
|
17195
17227
|
const file = files[0];
|
|
17196
|
-
const resourcePath = `shares/${shareId}/${targetInput.filename}`;
|
|
17228
|
+
const resourcePath = `shares/${shareId}/${safeStorageFilename(targetInput.filename)}`;
|
|
17197
17229
|
const byteLength = file.bytes.byteLength;
|
|
17198
17230
|
if (!Number.isSafeInteger(byteLength) || byteLength > 100 * 1024 * 1024) throw new Error("addressed publication exceeds the combined byte limit");
|
|
17199
17231
|
const mediaType = targetInput.mediaType ?? file.mediaType ?? "application/octet-stream";
|
|
@@ -17228,7 +17260,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
17228
17260
|
profileName: activeProfileName2
|
|
17229
17261
|
});
|
|
17230
17262
|
}
|
|
17231
|
-
|
|
17263
|
+
throwKvUploadFailure(stored.error);
|
|
17232
17264
|
}
|
|
17233
17265
|
const contentSource = {
|
|
17234
17266
|
shareId,
|