@tinycloud/cli 1.0.0-beta.17 → 1.0.0-beta.19
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 +64 -1
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +1 -1
- package/skills/tc-cli/AUTH.md +1 -1
- package/skills/tc-cli/REFERENCE.md +4 -2
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",
|
|
@@ -16907,6 +16938,23 @@ async function throwKvError(error, spaceUri, profileName) {
|
|
|
16907
16938
|
if (hosted) throw hosted;
|
|
16908
16939
|
throw new CLIError(error.code, error.message, ExitCode.ERROR);
|
|
16909
16940
|
}
|
|
16941
|
+
function isByteCount(value) {
|
|
16942
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
16943
|
+
}
|
|
16944
|
+
function storageQuotaError(error) {
|
|
16945
|
+
if (error.code !== "STORAGE_QUOTA_EXCEEDED") return void 0;
|
|
16946
|
+
const { usedBytes, limitBytes } = typeof error.meta === "object" && error.meta !== null ? error.meta : {};
|
|
16947
|
+
const sizes = isByteCount(usedBytes) && isByteCount(limitBytes) ? ` (${formatBytes(usedBytes)} used of ${formatBytes(limitBytes)} limit)` : "";
|
|
16948
|
+
return new CLIError("STORAGE_QUOTA_EXCEEDED", `storage quota exceeded${sizes}; nothing was written`, ExitCode.ERROR);
|
|
16949
|
+
}
|
|
16950
|
+
function assertAddressableKey(key) {
|
|
16951
|
+
for (let i = 0; i < key.length; i++) {
|
|
16952
|
+
const code2 = key.charCodeAt(i);
|
|
16953
|
+
if (code2 <= 32 || code2 === 127) {
|
|
16954
|
+
throw new CLIError("USAGE_ERROR", "KV keys cannot contain spaces or control characters; use a URL-safe key", ExitCode.USAGE_ERROR);
|
|
16955
|
+
}
|
|
16956
|
+
}
|
|
16957
|
+
}
|
|
16910
16958
|
async function readStdin2() {
|
|
16911
16959
|
const chunks = [];
|
|
16912
16960
|
for await (const chunk of process.stdin) {
|
|
@@ -16923,6 +16971,7 @@ function registerKvCommand(program) {
|
|
|
16923
16971
|
const kv = program.command("kv").description("Key-value store operations");
|
|
16924
16972
|
kv.command("get <key>").description("Get a value by key").option("--raw", "Output raw value (no JSON wrapping)").option("-o, --output <file>", "Write value to file").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
|
|
16925
16973
|
try {
|
|
16974
|
+
assertAddressableKey(key);
|
|
16926
16975
|
const globalOpts = cmd.optsWithGlobals();
|
|
16927
16976
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
16928
16977
|
const node = await ensureAuthenticated(ctx);
|
|
@@ -16967,6 +17016,7 @@ function registerKvCommand(program) {
|
|
|
16967
17016
|
});
|
|
16968
17017
|
kv.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, value, options, cmd) => {
|
|
16969
17018
|
try {
|
|
17019
|
+
assertAddressableKey(key);
|
|
16970
17020
|
const globalOpts = cmd.optsWithGlobals();
|
|
16971
17021
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
16972
17022
|
const node = await ensureAuthenticated(ctx);
|
|
@@ -16992,6 +17042,8 @@ function registerKvCommand(program) {
|
|
|
16992
17042
|
const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
|
|
16993
17043
|
const result = await withSpinner(`Writing ${key}...`, () => kv2.put(key, putValue));
|
|
16994
17044
|
if (!result.ok) {
|
|
17045
|
+
const quota = storageQuotaError(result.error);
|
|
17046
|
+
if (quota) throw quota;
|
|
16995
17047
|
await throwKvError(result.error, spaceUri, ctx.profile);
|
|
16996
17048
|
}
|
|
16997
17049
|
outputJson({ key, written: true });
|
|
@@ -17001,6 +17053,7 @@ function registerKvCommand(program) {
|
|
|
17001
17053
|
});
|
|
17002
17054
|
kv.command("delete <key>").description("Delete a key").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
|
|
17003
17055
|
try {
|
|
17056
|
+
assertAddressableKey(key);
|
|
17004
17057
|
const globalOpts = cmd.optsWithGlobals();
|
|
17005
17058
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
17006
17059
|
const node = await ensureAuthenticated(ctx);
|
|
@@ -17016,6 +17069,7 @@ function registerKvCommand(program) {
|
|
|
17016
17069
|
});
|
|
17017
17070
|
kv.command("list").description("List keys").option("--prefix <prefix>", "Filter by key prefix").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (options, cmd) => {
|
|
17018
17071
|
try {
|
|
17072
|
+
if (options.prefix) assertAddressableKey(options.prefix);
|
|
17019
17073
|
const globalOpts = cmd.optsWithGlobals();
|
|
17020
17074
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
17021
17075
|
const node = await ensureAuthenticated(ctx);
|
|
@@ -17051,6 +17105,7 @@ function registerKvCommand(program) {
|
|
|
17051
17105
|
});
|
|
17052
17106
|
kv.command("head <key>").description("Get metadata for a key (no body)").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
|
|
17053
17107
|
try {
|
|
17108
|
+
assertAddressableKey(key);
|
|
17054
17109
|
const globalOpts = cmd.optsWithGlobals();
|
|
17055
17110
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
17056
17111
|
const node = await ensureAuthenticated(ctx);
|
|
@@ -27204,6 +27259,7 @@ function parseNativeShareUrl(value) {
|
|
|
27204
27259
|
}
|
|
27205
27260
|
|
|
27206
27261
|
// src/commands/share.ts
|
|
27262
|
+
init_formatter();
|
|
27207
27263
|
init_errors();
|
|
27208
27264
|
|
|
27209
27265
|
// src/share/output.ts
|
|
@@ -27403,7 +27459,7 @@ function shareCliError(error) {
|
|
|
27403
27459
|
}
|
|
27404
27460
|
if (failure.kind === "scope-denied") {
|
|
27405
27461
|
const requiredAction = failure.requiredAction === void 0 ? "" : ` (${failure.requiredAction})`;
|
|
27406
|
-
const renew = localKey ? `renew it with ${loginHint} using the required capability` : `
|
|
27462
|
+
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}`;
|
|
27407
27463
|
return new CLIError("PERMISSION_DENIED", `the session lacks ${failure.capability} authority${requiredAction}; ${renew}`, 5);
|
|
27408
27464
|
}
|
|
27409
27465
|
if (failure.kind === "lifetime-exceeds-session") {
|
|
@@ -27428,6 +27484,13 @@ function shareCliError(error) {
|
|
|
27428
27484
|
if (failure.kind === "registry-rejected") {
|
|
27429
27485
|
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);
|
|
27430
27486
|
}
|
|
27487
|
+
if (failure.kind === "storage-quota-exceeded") {
|
|
27488
|
+
const sizes = failure.usedBytes === void 0 || failure.limitBytes === void 0 ? "" : ` (${formatBytes(failure.usedBytes)} used of ${formatBytes(failure.limitBytes)} limit)`;
|
|
27489
|
+
return new CLIError("STORAGE_QUOTA_EXCEEDED", `storage quota exceeded${sizes}; nothing was shared`, 4);
|
|
27490
|
+
}
|
|
27491
|
+
if (failure.kind === "upload-failed") {
|
|
27492
|
+
return new CLIError("UPLOAD_FAILED", "share source upload failed; nothing was shared", 4);
|
|
27493
|
+
}
|
|
27431
27494
|
}
|
|
27432
27495
|
if (error instanceof ShareNotifyError) {
|
|
27433
27496
|
return new CLIError("INVALID_ARGUMENT", error.message, 2);
|