@tinycloud/cli 1.1.0-beta.12 → 1.1.0-beta.14
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 +192 -31
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +334 -114
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +5 -5
- package/skills/tc-cli/AUTH.md +6 -4
- package/skills/tc-cli/REFERENCE.md +6 -4
package/dist/legacy-entry.js
CHANGED
|
@@ -59,7 +59,9 @@ var init_constants = __esm({
|
|
|
59
59
|
NOT_FOUND: 4,
|
|
60
60
|
PERMISSION_DENIED: 5,
|
|
61
61
|
NETWORK_ERROR: 6,
|
|
62
|
-
NODE_ERROR: 7
|
|
62
|
+
NODE_ERROR: 7,
|
|
63
|
+
/** A write was refused because the owner's TinyCloud storage is full. */
|
|
64
|
+
STORAGE_FULL: 10
|
|
63
65
|
};
|
|
64
66
|
}
|
|
65
67
|
});
|
|
@@ -163,8 +165,14 @@ import ora from "ora";
|
|
|
163
165
|
function outputJson(data) {
|
|
164
166
|
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
165
167
|
}
|
|
166
|
-
function
|
|
167
|
-
|
|
168
|
+
function warningLine(warnings) {
|
|
169
|
+
const which = warnings.map((warning) => `${warning.grantCid ?? "unidentified"}: ${warning.reason}`).join(", ");
|
|
170
|
+
const subject = warnings.length === 1 ? "A stored grant was" : `${warnings.length} stored grants were`;
|
|
171
|
+
return `${subject} not used (${which}). Approve access again when a command needs it; the old grant expires on its own.`;
|
|
172
|
+
}
|
|
173
|
+
function outputError(code3, message, hint, details = {}) {
|
|
174
|
+
const { meta, warnings = [] } = details;
|
|
175
|
+
if (!shouldOutputJson()) {
|
|
168
176
|
process.stderr.write(
|
|
169
177
|
`${theme.error("\u2717")} ${theme.label(code3)}: ${message}
|
|
170
178
|
`
|
|
@@ -175,20 +183,40 @@ function outputError(code3, message, hint, meta) {
|
|
|
175
183
|
`);
|
|
176
184
|
}
|
|
177
185
|
}
|
|
186
|
+
if (warnings.length > 0) process.stderr.write(` ${theme.warn(warningLine(warnings))}
|
|
187
|
+
`);
|
|
178
188
|
} else {
|
|
179
189
|
const payload = {
|
|
180
190
|
error: { code: code3, message }
|
|
181
191
|
};
|
|
182
192
|
if (hint) payload.error.hint = hint;
|
|
183
193
|
if (meta) payload.error.meta = meta;
|
|
194
|
+
if (warnings.length > 0) payload.error.warnings = warnings;
|
|
184
195
|
process.stderr.write(JSON.stringify(payload, null, 2) + "\n");
|
|
185
196
|
}
|
|
186
197
|
}
|
|
198
|
+
function outputWarnings(warnings) {
|
|
199
|
+
if (warnings.length === 0) return;
|
|
200
|
+
process.stderr.write(shouldOutputJson() ? `${JSON.stringify({ warnings })}
|
|
201
|
+
` : `${theme.warn("!")} ${warningLine(warnings)}
|
|
202
|
+
`);
|
|
203
|
+
}
|
|
204
|
+
function operationWarnings(value) {
|
|
205
|
+
if (!Array.isArray(value)) return [];
|
|
206
|
+
return value.flatMap((warning) => {
|
|
207
|
+
if (warning === null || typeof warning !== "object") return [];
|
|
208
|
+
const { code: code3, reason, grantCid } = warning;
|
|
209
|
+
if (typeof code3 !== "string" || !WARNING_CODE.test(code3) || typeof reason !== "string" || !WARNING_REASON.test(reason)) {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
return [{ code: code3, reason, ...typeof grantCid === "string" && DELEGATION_CID.test(grantCid) ? { grantCid } : {} }];
|
|
213
|
+
});
|
|
214
|
+
}
|
|
187
215
|
function isInteractive() {
|
|
188
216
|
return Boolean(process.stdout.isTTY);
|
|
189
217
|
}
|
|
190
218
|
async function withSpinner(label, fn) {
|
|
191
|
-
if (
|
|
219
|
+
if (shouldOutputJson()) {
|
|
192
220
|
return fn();
|
|
193
221
|
}
|
|
194
222
|
const spinner = ora(label).start();
|
|
@@ -202,7 +230,7 @@ async function withSpinner(label, fn) {
|
|
|
202
230
|
}
|
|
203
231
|
}
|
|
204
232
|
function shouldOutputJson() {
|
|
205
|
-
return !isInteractive() || process.argv.includes("--json");
|
|
233
|
+
return !isInteractive() || (globalThis[JSON_REQUESTED] ?? process.argv.includes("--json"));
|
|
206
234
|
}
|
|
207
235
|
function formatField(label, value) {
|
|
208
236
|
if (value === null || value === void 0) return ` ${theme.label(label + ":")} ${theme.muted("\u2014")}`;
|
|
@@ -251,10 +279,95 @@ function formatTimeAgo(date) {
|
|
|
251
279
|
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
|
|
252
280
|
return `${Math.floor(seconds / 86400)}d ago`;
|
|
253
281
|
}
|
|
282
|
+
var WARNING_CODE, WARNING_REASON, DELEGATION_CID, JSON_REQUESTED;
|
|
254
283
|
var init_formatter = __esm({
|
|
255
284
|
"src/output/formatter.ts"() {
|
|
256
285
|
"use strict";
|
|
257
286
|
init_theme();
|
|
287
|
+
WARNING_CODE = /^[A-Z][A-Z_]{0,63}$/;
|
|
288
|
+
WARNING_REASON = /^[a-z][a-z_]{0,63}$/;
|
|
289
|
+
DELEGATION_CID = /^bafkr4i[a-z2-7]{52}$/;
|
|
290
|
+
JSON_REQUESTED = /* @__PURE__ */ Symbol.for("tinycloud.cli.jsonOutputRequested");
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// src/output/storage.ts
|
|
295
|
+
function storageRejection(error) {
|
|
296
|
+
let node = error;
|
|
297
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH && typeof node === "object" && node !== null; depth += 1) {
|
|
298
|
+
const record = node;
|
|
299
|
+
let code3;
|
|
300
|
+
if (record.code === "STORAGE_QUOTA_EXCEEDED" || record.code === "STORAGE_LIMIT_REACHED") {
|
|
301
|
+
code3 = record.code;
|
|
302
|
+
} else if (record.code !== void 0) {
|
|
303
|
+
return void 0;
|
|
304
|
+
} else if (isStorageStatus(record)) {
|
|
305
|
+
code3 = storageCodeFromText(record.message);
|
|
306
|
+
}
|
|
307
|
+
if (code3) {
|
|
308
|
+
const account = accountTotals(record) ?? accountTotals(record.meta) ?? accountTotals(record.metadata);
|
|
309
|
+
return account ? { code: code3, account } : { code: code3 };
|
|
310
|
+
}
|
|
311
|
+
node = record.cause;
|
|
312
|
+
}
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
function describeStorageRejection(rejection, progress) {
|
|
316
|
+
const reason = rejection.code === "STORAGE_LIMIT_REACHED" ? "This write is larger than the TinyCloud storage you have left" : "TinyCloud storage is full";
|
|
317
|
+
const message = progress ? `${progress}${reason}.` : `${reason}; nothing was written.`;
|
|
318
|
+
const { account } = rejection;
|
|
319
|
+
const totals = account ? `${formatStorageBytes(account.usedBytes)} used of ${formatStorageBytes(account.limitBytes)}${account.plan ? ` (${account.plan} plan)` : ""}. ` : "";
|
|
320
|
+
return {
|
|
321
|
+
message,
|
|
322
|
+
hint: `${totals}Reading still works.
|
|
323
|
+
Free up space or upgrade: ${MANAGE_STORAGE_URL}`
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function formatStorageBytes(bytes) {
|
|
327
|
+
const units = ["KiB", "MiB", "GiB", "TiB"];
|
|
328
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
329
|
+
let value = bytes / 1024;
|
|
330
|
+
let unit = 0;
|
|
331
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
332
|
+
value /= 1024;
|
|
333
|
+
unit += 1;
|
|
334
|
+
}
|
|
335
|
+
return `${value.toFixed(1).replace(/\.0$/, "")} ${units[unit]}`;
|
|
336
|
+
}
|
|
337
|
+
function storageCodeFromText(message) {
|
|
338
|
+
if (typeof message !== "string") return void 0;
|
|
339
|
+
if (message.includes("larger than the TinyCloud storage you have left")) return "STORAGE_LIMIT_REACHED";
|
|
340
|
+
if (message.includes("TinyCloud storage is full") || /storage quota exceeded/i.test(message)) {
|
|
341
|
+
return "STORAGE_QUOTA_EXCEEDED";
|
|
342
|
+
}
|
|
343
|
+
return void 0;
|
|
344
|
+
}
|
|
345
|
+
function isStorageStatus(record) {
|
|
346
|
+
const meta = "meta" in record && typeof record.meta === "object" && record.meta !== null ? record.meta : {};
|
|
347
|
+
const statuses = [
|
|
348
|
+
"status" in record ? record.status : void 0,
|
|
349
|
+
"statusCode" in record ? record.statusCode : void 0,
|
|
350
|
+
"status" in meta ? meta.status : void 0
|
|
351
|
+
];
|
|
352
|
+
return statuses.some((value) => value === 402 || value === 413);
|
|
353
|
+
}
|
|
354
|
+
function accountTotals(meta) {
|
|
355
|
+
if (typeof meta !== "object" || meta === null) return void 0;
|
|
356
|
+
const account = meta.account;
|
|
357
|
+
if (typeof account !== "object" || account === null) return void 0;
|
|
358
|
+
const { usedBytes, limitBytes, plan } = account;
|
|
359
|
+
if (!isByteCount(usedBytes) || !isByteCount(limitBytes) || limitBytes === 0) return void 0;
|
|
360
|
+
return typeof plan === "string" && /^[A-Za-z0-9][A-Za-z0-9 _.-]{0,31}$/.test(plan) ? { usedBytes, limitBytes, plan } : { usedBytes, limitBytes };
|
|
361
|
+
}
|
|
362
|
+
function isByteCount(value) {
|
|
363
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
364
|
+
}
|
|
365
|
+
var MANAGE_STORAGE_URL, MAX_CAUSE_DEPTH;
|
|
366
|
+
var init_storage2 = __esm({
|
|
367
|
+
"src/output/storage.ts"() {
|
|
368
|
+
"use strict";
|
|
369
|
+
MANAGE_STORAGE_URL = "https://account.tinycloud.xyz/billing";
|
|
370
|
+
MAX_CAUSE_DEPTH = 4;
|
|
258
371
|
}
|
|
259
372
|
});
|
|
260
373
|
|
|
@@ -268,6 +381,8 @@ function setActiveProfileName(name) {
|
|
|
268
381
|
}
|
|
269
382
|
function cliErrorFromService(error, message = error.message) {
|
|
270
383
|
if (error instanceof CLIError) return error;
|
|
384
|
+
const storageFull = storageFullError(error);
|
|
385
|
+
if (storageFull) return storageFull;
|
|
271
386
|
const meta = { ...error.meta };
|
|
272
387
|
delete meta.hint;
|
|
273
388
|
const status = error.status ?? error.statusCode;
|
|
@@ -277,15 +392,18 @@ function cliErrorFromService(error, message = error.message) {
|
|
|
277
392
|
return missingPrivateKeyError();
|
|
278
393
|
}
|
|
279
394
|
const missingCapability = validatedCapabilityOf({ ...error, meta }) !== void 0;
|
|
280
|
-
const denied = verdict === "forbidden" || verdict === "unauthenticated" && missingCapability;
|
|
395
|
+
const denied = verdict === "forbidden" || verdict === "unauthenticated" && missingCapability || verdict === void 0 && error.code === "PERMISSION_DENIED";
|
|
396
|
+
const unauthenticated = !denied && (verdict === "unauthenticated" || verdict === void 0 && error.code === "AUTH_REQUIRED");
|
|
281
397
|
return new CLIError(
|
|
282
|
-
denied ? "PERMISSION_DENIED" :
|
|
398
|
+
denied ? "PERMISSION_DENIED" : unauthenticated ? "AUTH_REQUIRED" : error.code,
|
|
283
399
|
message,
|
|
284
|
-
denied ? ExitCode.PERMISSION_DENIED :
|
|
400
|
+
denied ? ExitCode.PERMISSION_DENIED : unauthenticated ? ExitCode.AUTH_REQUIRED : ExitCode.ERROR,
|
|
285
401
|
meta
|
|
286
402
|
);
|
|
287
403
|
}
|
|
288
404
|
function wrapError(error) {
|
|
405
|
+
const storageFull = storageFullError(error);
|
|
406
|
+
if (storageFull) return storageFull;
|
|
289
407
|
if (error instanceof CLIError) return error;
|
|
290
408
|
const message = error instanceof Error ? error.message : String(error);
|
|
291
409
|
const verdict = authorizationVerdictOf(error);
|
|
@@ -332,6 +450,13 @@ function missingPrivateKeyError() {
|
|
|
332
450
|
{ hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey` }
|
|
333
451
|
);
|
|
334
452
|
}
|
|
453
|
+
function storageFullError(error, progress) {
|
|
454
|
+
if (error instanceof CLIError && error.exitCode === ExitCode.STORAGE_FULL) return error;
|
|
455
|
+
const rejection = storageRejection(error);
|
|
456
|
+
if (!rejection) return void 0;
|
|
457
|
+
const { message, hint } = describeStorageRejection(rejection, progress);
|
|
458
|
+
return new CLIError(rejection.code, message, ExitCode.STORAGE_FULL, { hint });
|
|
459
|
+
}
|
|
335
460
|
function handleError(error) {
|
|
336
461
|
const cliError = wrapError(error);
|
|
337
462
|
const prebuilt = typeof cliError.metadata?.hint === "string" ? cliError.metadata.hint : void 0;
|
|
@@ -344,7 +469,10 @@ function handleError(error) {
|
|
|
344
469
|
meta.resource = capability.resource;
|
|
345
470
|
meta.requiredAction = capability.requiredAction;
|
|
346
471
|
}
|
|
347
|
-
outputError(cliError.code, cliError.message, hint,
|
|
472
|
+
outputError(cliError.code, cliError.message, hint, {
|
|
473
|
+
...Object.keys(meta).length ? { meta } : {},
|
|
474
|
+
warnings: operationWarnings(cliError.metadata?.warnings)
|
|
475
|
+
});
|
|
348
476
|
process.exit(cliError.exitCode);
|
|
349
477
|
}
|
|
350
478
|
function buildAuthHint(error) {
|
|
@@ -414,6 +542,7 @@ var init_errors = __esm({
|
|
|
414
542
|
"use strict";
|
|
415
543
|
init_constants();
|
|
416
544
|
init_formatter();
|
|
545
|
+
init_storage2();
|
|
417
546
|
CLIError = class extends Error {
|
|
418
547
|
constructor(code3, message, exitCode = ExitCode.ERROR, metadata) {
|
|
419
548
|
super(message);
|
|
@@ -7055,7 +7184,7 @@ function validatedCapabilityOf2(error) {
|
|
|
7055
7184
|
if (verdict !== "unauthenticated" && verdict !== "forbidden") return void 0;
|
|
7056
7185
|
const seen = /* @__PURE__ */ new Set();
|
|
7057
7186
|
let node = error;
|
|
7058
|
-
for (let depth = 0; depth <
|
|
7187
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH2 && typeof node === "object" && node !== null && !seen.has(node); depth += 1) {
|
|
7059
7188
|
seen.add(node);
|
|
7060
7189
|
const record = node;
|
|
7061
7190
|
const status = errorStatusOf(record);
|
|
@@ -7075,7 +7204,7 @@ function validatedCapabilityOf2(error) {
|
|
|
7075
7204
|
}
|
|
7076
7205
|
return void 0;
|
|
7077
7206
|
}
|
|
7078
|
-
var
|
|
7207
|
+
var MAX_CAUSE_DEPTH2 = 8;
|
|
7079
7208
|
function errorStatusOf(node) {
|
|
7080
7209
|
const meta = node.meta;
|
|
7081
7210
|
const candidates = [
|
|
@@ -7094,7 +7223,7 @@ function authorizationVerdictOf2(error) {
|
|
|
7094
7223
|
const seen = /* @__PURE__ */ new Set();
|
|
7095
7224
|
let sawUnauthorizedCode = false;
|
|
7096
7225
|
let node = error;
|
|
7097
|
-
for (let depth = 0; depth <
|
|
7226
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH2 && typeof node === "object" && node !== null && !seen.has(node); depth += 1) {
|
|
7098
7227
|
seen.add(node);
|
|
7099
7228
|
const record = node;
|
|
7100
7229
|
const status = errorStatusOf(record);
|
|
@@ -13377,6 +13506,10 @@ function canonicalOwnerDid(did, label = "--owner") {
|
|
|
13377
13506
|
}
|
|
13378
13507
|
return `did:pkh:eip155:${match[1]}:${ensureEip55(match[2])}`;
|
|
13379
13508
|
}
|
|
13509
|
+
function canonicalNetworkUrn(path) {
|
|
13510
|
+
const match = /^urn:tinycloud:encryption:(did:pkh:eip155:[1-9]\d*:0x[0-9a-fA-F]{40}):([^:]*)$/.exec(path);
|
|
13511
|
+
return match ? `urn:tinycloud:encryption:${canonicalOwnerDid(match[1])}:${match[2]}` : path;
|
|
13512
|
+
}
|
|
13380
13513
|
function rawEncryptionOwnerMatches(path, ownerDid) {
|
|
13381
13514
|
const match = /^urn:tinycloud:encryption:(did:pkh:eip155:.+):([a-z0-9][a-z0-9-]*)$/.exec(path);
|
|
13382
13515
|
if (!match) return false;
|
|
@@ -13519,13 +13652,14 @@ async function replayAdditionalDelegations(node, profile, options) {
|
|
|
13519
13652
|
for (const stored of entries) {
|
|
13520
13653
|
const kind = storedDelegationKind(stored);
|
|
13521
13654
|
if (kind === "compact" || kind === "signed-login") {
|
|
13522
|
-
const
|
|
13655
|
+
const replay = await replayStoredDelegation(activator, stored, {
|
|
13523
13656
|
host: options.host,
|
|
13524
13657
|
migrated,
|
|
13525
13658
|
resolveSpace
|
|
13526
13659
|
});
|
|
13527
|
-
if (
|
|
13528
|
-
process.stderr.write(
|
|
13660
|
+
if (replay.status === "skipped" && process.env.TC_DEBUG_REPLAY === "1") {
|
|
13661
|
+
process.stderr.write(`[replay] skipping a stored delegation: ${replay.reason}
|
|
13662
|
+
`);
|
|
13529
13663
|
}
|
|
13530
13664
|
continue;
|
|
13531
13665
|
}
|
|
@@ -13555,6 +13689,14 @@ async function replayAdditionalDelegations(node, profile, options) {
|
|
|
13555
13689
|
function storedAdditionalDelegation(delegation, permissions) {
|
|
13556
13690
|
return { delegation, permissions };
|
|
13557
13691
|
}
|
|
13692
|
+
async function cliGrantRecord(node, delegation, permissions, host) {
|
|
13693
|
+
const { bindCliGrant } = await import("@tinycloud/operations/delegation-binding");
|
|
13694
|
+
return bindCliGrant(
|
|
13695
|
+
node,
|
|
13696
|
+
storedAdditionalDelegation(delegation, permissions),
|
|
13697
|
+
host
|
|
13698
|
+
);
|
|
13699
|
+
}
|
|
13558
13700
|
async function appendGrantHistory(profile, entry) {
|
|
13559
13701
|
await ProfileManager.ensureProfileDir(profile);
|
|
13560
13702
|
const line = JSON.stringify({
|
|
@@ -13709,7 +13851,8 @@ function permissionsFromDelegation(delegation) {
|
|
|
13709
13851
|
}
|
|
13710
13852
|
function compactPermission(permission) {
|
|
13711
13853
|
const service = permission.service;
|
|
13712
|
-
const
|
|
13854
|
+
const fullSpace = permission.space ?? (isRawEncryptionPermission(permission) ? ENCRYPTION_MANIFEST_SPACE2 : "");
|
|
13855
|
+
const space = fullSpace.startsWith("tinycloud:") ? fullSpace.slice(fullSpace.lastIndexOf(":") + 1) : fullSpace;
|
|
13713
13856
|
const actions = permission.actions.map((action) => action.startsWith(`${service}/`) ? action.slice(service.length + 1) : action).join(",");
|
|
13714
13857
|
return `${service}:${space}:${permission.path}:${actions}`;
|
|
13715
13858
|
}
|
|
@@ -13805,6 +13948,35 @@ function serviceFromActions(actions) {
|
|
|
13805
13948
|
return first.includes("/") ? first.slice(0, first.indexOf("/")) : "tinycloud.unknown";
|
|
13806
13949
|
}
|
|
13807
13950
|
|
|
13951
|
+
// src/auth/session-expired.ts
|
|
13952
|
+
init_constants();
|
|
13953
|
+
init_errors();
|
|
13954
|
+
var SESSION_EXPIRED_REASON = "session_expired";
|
|
13955
|
+
function signInAgainHint(profileName, posture) {
|
|
13956
|
+
if (posture === "local-owner-key") {
|
|
13957
|
+
return `Sign in again with: tc --profile ${profileName} auth login --method local`;
|
|
13958
|
+
}
|
|
13959
|
+
if (posture === "delegate-session") {
|
|
13960
|
+
return `Have the owner approve a new scoped login: tc --profile ${profileName} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
|
|
13961
|
+
}
|
|
13962
|
+
return `Sign in again with: tc --profile ${profileName} auth login --method openkey`;
|
|
13963
|
+
}
|
|
13964
|
+
function sessionExpiredError(profileName, posture) {
|
|
13965
|
+
return new CLIError(
|
|
13966
|
+
"AUTH_REQUIRED",
|
|
13967
|
+
`The session for profile "${profileName}" has expired or is no longer valid.`,
|
|
13968
|
+
ExitCode.AUTH_REQUIRED,
|
|
13969
|
+
{ hint: signInAgainHint(profileName, posture), reason: SESSION_EXPIRED_REASON }
|
|
13970
|
+
);
|
|
13971
|
+
}
|
|
13972
|
+
function withSignInHint(error, profileName, posture) {
|
|
13973
|
+
if (error.code !== "AUTH_REQUIRED") return error;
|
|
13974
|
+
return new CLIError(error.code, error.message, error.exitCode, {
|
|
13975
|
+
...error.metadata,
|
|
13976
|
+
hint: signInAgainHint(profileName, posture)
|
|
13977
|
+
});
|
|
13978
|
+
}
|
|
13979
|
+
|
|
13808
13980
|
// src/lib/sdk.ts
|
|
13809
13981
|
function jwkHasPrivateParameter(jwk) {
|
|
13810
13982
|
if (!jwk || typeof jwk !== "object") return false;
|
|
@@ -13831,6 +14003,16 @@ function signerJwkForProfile(profileName, sessionJwk, key) {
|
|
|
13831
14003
|
}
|
|
13832
14004
|
);
|
|
13833
14005
|
}
|
|
14006
|
+
async function restoreProfileSession(node, profileName, profile, sessionData) {
|
|
14007
|
+
try {
|
|
14008
|
+
await node.restoreSession(sessionData);
|
|
14009
|
+
} catch (error) {
|
|
14010
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "AUTH_EXPIRED") {
|
|
14011
|
+
throw sessionExpiredError(profileName, profile === null ? void 0 : resolveProfilePosture(profile));
|
|
14012
|
+
}
|
|
14013
|
+
throw error;
|
|
14014
|
+
}
|
|
14015
|
+
}
|
|
13834
14016
|
async function createSDKInstance(ctx, options) {
|
|
13835
14017
|
const profile = options?.privateKey ? await ProfileManager.getProfile(ctx.profile).catch(() => null) : await ProfileManager.getProfile(ctx.profile);
|
|
13836
14018
|
const session = await ProfileManager.getSession(ctx.profile);
|
|
@@ -13850,7 +14032,7 @@ async function createSDKInstance(ctx, options) {
|
|
|
13850
14032
|
});
|
|
13851
14033
|
let restoredOwnSession2 = false;
|
|
13852
14034
|
if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
|
|
13853
|
-
await node2.
|
|
14035
|
+
await restoreProfileSession(node2, ctx.profile, profile, {
|
|
13854
14036
|
delegationHeader: session.delegationHeader,
|
|
13855
14037
|
delegationCid: session.delegationCid,
|
|
13856
14038
|
spaceId: session.spaceId,
|
|
@@ -13880,7 +14062,7 @@ async function createSDKInstance(ctx, options) {
|
|
|
13880
14062
|
if (options?.privateKey) {
|
|
13881
14063
|
await node.signIn();
|
|
13882
14064
|
} else if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
|
|
13883
|
-
await node.
|
|
14065
|
+
await restoreProfileSession(node, ctx.profile, profile, {
|
|
13884
14066
|
delegationHeader: session.delegationHeader,
|
|
13885
14067
|
delegationCid: session.delegationCid,
|
|
13886
14068
|
spaceId: session.spaceId,
|
|
@@ -14462,6 +14644,41 @@ function validateDelegationCallbackPayload(value) {
|
|
|
14462
14644
|
}
|
|
14463
14645
|
return null;
|
|
14464
14646
|
}
|
|
14647
|
+
async function delegationFromInput(input, did, options) {
|
|
14648
|
+
const trimmed = input.trim();
|
|
14649
|
+
let parsed;
|
|
14650
|
+
if (/^[a-z2-7]{4}-[a-z2-7]{4}$/i.test(trimmed)) {
|
|
14651
|
+
if (!options.jwk) throw new Error("A CLI session key is required to retrieve a delegation.");
|
|
14652
|
+
const apiHost = process.env.TC_OPENKEY_API_HOST ?? options.openkeyApiHost ?? (options.openkeyHost && options.openkeyHost !== DEFAULT_OPENKEY_HOST ? options.openkeyHost : DEFAULT_OPENKEY_DEVICE_API_HOST);
|
|
14653
|
+
const response = await fetch(`${apiHost.replace(/\/$/, "")}/api/delegation-codes/${trimmed.toLowerCase()}`, {
|
|
14654
|
+
redirect: "error"
|
|
14655
|
+
});
|
|
14656
|
+
if (!response.ok) {
|
|
14657
|
+
throw new Error(response.status === 404 ? "Delegation code not found or expired. Use the full code shown in OpenKey instead." : `Could not retrieve delegation code (HTTP ${response.status}).`);
|
|
14658
|
+
}
|
|
14659
|
+
const result = await response.json();
|
|
14660
|
+
parsed = result.delegation;
|
|
14661
|
+
const delegation = parsed;
|
|
14662
|
+
const jwk = delegation?.jwk;
|
|
14663
|
+
const localJwk = publicJwkForDelegation(options.jwk);
|
|
14664
|
+
if (typeof delegation?.verificationMethod !== "string" || delegation.verificationMethod.split("#")[0] !== did.split("#")[0] || !jwk || typeof jwk !== "object" || Array.isArray(jwk) || jwk.kty !== "OKP" || jwk.crv !== "Ed25519" || typeof jwk.x !== "string" || jwk.x !== localJwk.x || [...PRIVATE_JWK_FIELDS].some((field) => field in jwk)) {
|
|
14665
|
+
throw new Error("Delegation is bound to a different CLI key.");
|
|
14666
|
+
}
|
|
14667
|
+
} else {
|
|
14668
|
+
try {
|
|
14669
|
+
parsed = JSON.parse(trimmed);
|
|
14670
|
+
} catch {
|
|
14671
|
+
try {
|
|
14672
|
+
parsed = JSON.parse(Buffer.from(trimmed, "base64").toString("utf-8"));
|
|
14673
|
+
} catch {
|
|
14674
|
+
throw new Error("Invalid delegation code. Expected JSON, base64-encoded JSON, or XXXX-XXXX.");
|
|
14675
|
+
}
|
|
14676
|
+
}
|
|
14677
|
+
}
|
|
14678
|
+
const invalid = validateDelegationCallbackPayload(parsed);
|
|
14679
|
+
if (invalid) throw new Error(`Invalid delegation code: ${invalid}`);
|
|
14680
|
+
return parsed;
|
|
14681
|
+
}
|
|
14465
14682
|
function shouldOpenBrowser(options) {
|
|
14466
14683
|
if (options.noPopup) return false;
|
|
14467
14684
|
const env = process.env.TC_AUTH_NO_POPUP ?? process.env.TC_NO_POPUP;
|
|
@@ -14486,21 +14703,6 @@ async function callbackFlow(did, options = {}) {
|
|
|
14486
14703
|
reject(result.error);
|
|
14487
14704
|
}
|
|
14488
14705
|
}
|
|
14489
|
-
function parsePasteInput(input) {
|
|
14490
|
-
const trimmed = input.trim();
|
|
14491
|
-
let parsed;
|
|
14492
|
-
try {
|
|
14493
|
-
parsed = JSON.parse(trimmed);
|
|
14494
|
-
} catch {
|
|
14495
|
-
const decoded = Buffer.from(trimmed, "base64").toString("utf-8");
|
|
14496
|
-
parsed = JSON.parse(decoded);
|
|
14497
|
-
}
|
|
14498
|
-
const invalid = validateDelegationCallbackPayload(parsed);
|
|
14499
|
-
if (invalid) {
|
|
14500
|
-
throw new Error(`Invalid delegation code: ${invalid}`);
|
|
14501
|
-
}
|
|
14502
|
-
return parsed;
|
|
14503
|
-
}
|
|
14504
14706
|
const server = createServer((req, res) => {
|
|
14505
14707
|
if (req.method === "POST" && req.url === "/callback") {
|
|
14506
14708
|
let body = "";
|
|
@@ -14569,18 +14771,21 @@ async function callbackFlow(did, options = {}) {
|
|
|
14569
14771
|
}
|
|
14570
14772
|
if (process.stdin.isTTY) {
|
|
14571
14773
|
console.error(`
|
|
14572
|
-
If the browser can't connect back, paste the delegation code here:`);
|
|
14774
|
+
If the browser can't connect back, enter the short code or paste the full delegation code here:`);
|
|
14573
14775
|
rl = createInterface({
|
|
14574
14776
|
input: process.stdin,
|
|
14575
14777
|
output: process.stderr
|
|
14576
14778
|
});
|
|
14577
|
-
|
|
14578
|
-
|
|
14779
|
+
let resolving = false;
|
|
14780
|
+
rl.on("line", async (input) => {
|
|
14781
|
+
if (settled || resolving) return;
|
|
14782
|
+
resolving = true;
|
|
14579
14783
|
try {
|
|
14580
|
-
|
|
14581
|
-
|
|
14582
|
-
|
|
14583
|
-
|
|
14784
|
+
settle({ data: await delegationFromInput(input, did, options) });
|
|
14785
|
+
} catch (error) {
|
|
14786
|
+
console.error(error instanceof Error ? error.message : "Invalid delegation code. Try again:");
|
|
14787
|
+
} finally {
|
|
14788
|
+
resolving = false;
|
|
14584
14789
|
}
|
|
14585
14790
|
});
|
|
14586
14791
|
}
|
|
@@ -14607,24 +14812,7 @@ Open this URL in a browser to authenticate:
|
|
|
14607
14812
|
if (answered || input.trim() === "") return;
|
|
14608
14813
|
answered = true;
|
|
14609
14814
|
rl.close();
|
|
14610
|
-
|
|
14611
|
-
try {
|
|
14612
|
-
parsed = JSON.parse(input.trim());
|
|
14613
|
-
} catch {
|
|
14614
|
-
try {
|
|
14615
|
-
const decoded = Buffer.from(input.trim(), "base64").toString("utf-8");
|
|
14616
|
-
parsed = JSON.parse(decoded);
|
|
14617
|
-
} catch {
|
|
14618
|
-
reject(new Error("Invalid delegation code. Expected JSON or base64-encoded JSON."));
|
|
14619
|
-
return;
|
|
14620
|
-
}
|
|
14621
|
-
}
|
|
14622
|
-
const invalid = validateDelegationCallbackPayload(parsed);
|
|
14623
|
-
if (invalid) {
|
|
14624
|
-
reject(new Error(`Invalid delegation code: ${invalid}`));
|
|
14625
|
-
return;
|
|
14626
|
-
}
|
|
14627
|
-
resolve4(parsed);
|
|
14815
|
+
void delegationFromInput(input, did, options).then(resolve4, reject);
|
|
14628
14816
|
});
|
|
14629
14817
|
rl.on("close", () => {
|
|
14630
14818
|
if (answered) return;
|
|
@@ -14639,7 +14827,7 @@ Open this URL in a browser to authenticate:
|
|
|
14639
14827
|
}
|
|
14640
14828
|
));
|
|
14641
14829
|
});
|
|
14642
|
-
rl.setPrompt("
|
|
14830
|
+
rl.setPrompt("Enter short code or paste full delegation code: ");
|
|
14643
14831
|
rl.prompt();
|
|
14644
14832
|
});
|
|
14645
14833
|
}
|
|
@@ -14923,7 +15111,13 @@ function validateLoginPermissions(permissions) {
|
|
|
14923
15111
|
}
|
|
14924
15112
|
}
|
|
14925
15113
|
function scopedLoginPermissions(permissions) {
|
|
14926
|
-
|
|
15114
|
+
return withCapabilitiesRead(permissions, permissions.find((p) => !isRawEncryptionPermission(p)).space ?? "");
|
|
15115
|
+
}
|
|
15116
|
+
function grantRequestPermissions(group, anchorSpace) {
|
|
15117
|
+
const request = group.map((p) => isRawEncryptionPermission(p) ? { ...p, space: ENCRYPTION_MANIFEST_SPACE2, path: canonicalNetworkUrn(p.path) } : p);
|
|
15118
|
+
return withCapabilitiesRead(request, request.find((p) => !isRawEncryptionPermission(p))?.space ?? anchorSpace);
|
|
15119
|
+
}
|
|
15120
|
+
function withCapabilitiesRead(permissions, space) {
|
|
14927
15121
|
const hasRead = permissions.some((p) => p.service === "tinycloud.capabilities" && p.path === "" && p.actions.includes(CAPABILITIES_READ) && normalizePkhIdentifier(p.space ?? "") === normalizePkhIdentifier(space));
|
|
14928
15122
|
return hasRead ? permissions : [{ service: "tinycloud.capabilities", space, path: "", actions: [CAPABILITIES_READ] }, ...permissions];
|
|
14929
15123
|
}
|
|
@@ -15810,6 +16004,7 @@ function registerAuthCommand(program) {
|
|
|
15810
16004
|
"Grant requested TinyCloud permissions from `tc auth request --grant`.",
|
|
15811
16005
|
group
|
|
15812
16006
|
);
|
|
16007
|
+
const request = grantRequestPermissions(group, profile.spaceId ?? profile.spaceName);
|
|
15813
16008
|
let delegationData;
|
|
15814
16009
|
if (options.device) {
|
|
15815
16010
|
const approval = await acquireDeviceDelegation({
|
|
@@ -15817,7 +16012,7 @@ function registerAuthCommand(program) {
|
|
|
15817
16012
|
jwk: key,
|
|
15818
16013
|
nodeOrigin: ctx.host,
|
|
15819
16014
|
shareOrigin: DEFAULT_SHARE_ORIGIN,
|
|
15820
|
-
permissions:
|
|
16015
|
+
permissions: request,
|
|
15821
16016
|
expiry: parseRequestedExpiry(expiryOption ?? "7d"),
|
|
15822
16017
|
reason,
|
|
15823
16018
|
expectedOwner: pinnedOwner(profile),
|
|
@@ -15829,17 +16024,17 @@ function registerAuthCommand(program) {
|
|
|
15829
16024
|
delegationData = await startAuthFlow(profile.did, {
|
|
15830
16025
|
jwk: key,
|
|
15831
16026
|
host: ctx.host,
|
|
15832
|
-
permissions:
|
|
16027
|
+
permissions: request,
|
|
15833
16028
|
reason,
|
|
15834
16029
|
openkeyHost,
|
|
15835
16030
|
expiry: expiryCap === void 0 ? void 0 : openKeyExpiryParam(expiryCap),
|
|
15836
16031
|
noPopup: options.popup === false
|
|
15837
16032
|
});
|
|
15838
16033
|
}
|
|
15839
|
-
const delegation = portableFromOpenKeyDelegation(delegationData,
|
|
16034
|
+
const delegation = portableFromOpenKeyDelegation(delegationData, request, ctx.host, proof);
|
|
15840
16035
|
grants.push({ delegation, effective: permissionsFromDelegation(delegation) });
|
|
15841
16036
|
}
|
|
15842
|
-
await activateAndStoreOpenKeyGrants(ctx.profile, node, grants, options.manifest ? "manifest" : "cli");
|
|
16037
|
+
await activateAndStoreOpenKeyGrants(ctx.profile, ctx.host, node, grants, options.manifest ? "manifest" : "cli");
|
|
15843
16038
|
const delegationCids2 = grants.map(({ delegation }) => delegation.cid);
|
|
15844
16039
|
const expiry2 = grants.at(-1)?.delegation.expiry.toISOString();
|
|
15845
16040
|
reportDeclined(declined);
|
|
@@ -15875,8 +16070,7 @@ function registerAuthCommand(program) {
|
|
|
15875
16070
|
for (const delegation of delegations) {
|
|
15876
16071
|
const covering = permissionsFromDelegation(delegation);
|
|
15877
16072
|
localEffective.push(...covering);
|
|
15878
|
-
|
|
15879
|
-
await appendAdditionalDelegation(ctx.profile, stored);
|
|
16073
|
+
await appendAdditionalDelegation(ctx.profile, await cliGrantRecord(node, delegation, covering, ctx.host));
|
|
15880
16074
|
delegationCids.push(delegation.cid);
|
|
15881
16075
|
expiry = delegation.expiry.toISOString();
|
|
15882
16076
|
await appendGrantHistory(ctx.profile, {
|
|
@@ -16298,6 +16492,8 @@ async function importRequestBoundDelegation(ctx, artifact) {
|
|
|
16298
16492
|
{ profile: ctx.profile, host: ctx.host, allowOwnerProfile: true },
|
|
16299
16493
|
artifact
|
|
16300
16494
|
);
|
|
16495
|
+
const warnings = operationWarnings(result.warnings);
|
|
16496
|
+
const metadata = warnings.length === 0 ? void 0 : { warnings };
|
|
16301
16497
|
switch (result.status) {
|
|
16302
16498
|
case "ok": {
|
|
16303
16499
|
const output2 = result.output;
|
|
@@ -16310,22 +16506,25 @@ async function importRequestBoundDelegation(ctx, artifact) {
|
|
|
16310
16506
|
permissions: output2.effectivePermissions,
|
|
16311
16507
|
expiry: output2.expiry
|
|
16312
16508
|
});
|
|
16509
|
+
outputWarnings(warnings);
|
|
16313
16510
|
return;
|
|
16314
16511
|
}
|
|
16315
16512
|
case "authority_required":
|
|
16316
16513
|
throw new CLIError(
|
|
16317
16514
|
"AUTHORITY_REQUIRED",
|
|
16318
16515
|
"The active session requires additional authority before importing this delegation.",
|
|
16319
|
-
ExitCode.PERMISSION_DENIED
|
|
16516
|
+
ExitCode.PERMISSION_DENIED,
|
|
16517
|
+
metadata
|
|
16320
16518
|
);
|
|
16321
16519
|
case "setup_required":
|
|
16322
16520
|
throw new CLIError(
|
|
16323
16521
|
"SETUP_REQUIRED",
|
|
16324
16522
|
"The active profile requires setup before importing this delegation.",
|
|
16325
|
-
ExitCode.ERROR
|
|
16523
|
+
ExitCode.ERROR,
|
|
16524
|
+
metadata
|
|
16326
16525
|
);
|
|
16327
16526
|
case "error":
|
|
16328
|
-
throw cliErrorFromService(result.error);
|
|
16527
|
+
throw withSignInHint(cliErrorFromService({ ...result.error, meta: metadata }), ctx.profile, result.context.posture);
|
|
16329
16528
|
}
|
|
16330
16529
|
}
|
|
16331
16530
|
function isStoredDelegationLike(value) {
|
|
@@ -16350,9 +16549,10 @@ function normalizePortableDelegation(delegation) {
|
|
|
16350
16549
|
}
|
|
16351
16550
|
return { ...delegation, expiry };
|
|
16352
16551
|
}
|
|
16353
|
-
async function activateAndStoreOpenKeyGrants(profileName, node, grants, source) {
|
|
16354
|
-
for (const { delegation } of grants) await node.useRuntimeDelegation(delegation);
|
|
16552
|
+
async function activateAndStoreOpenKeyGrants(profileName, host, node, grants, source) {
|
|
16355
16553
|
if (grants.length === 0) return;
|
|
16554
|
+
const records = await Promise.all(grants.map(({ delegation, effective }) => cliGrantRecord(node, delegation, effective, host)));
|
|
16555
|
+
for (const { delegation } of grants) await node.useRuntimeDelegation(delegation);
|
|
16356
16556
|
await ProfileManager.withLock(profileName, async () => {
|
|
16357
16557
|
for (const { delegation, effective } of grants) {
|
|
16358
16558
|
await appendGrantHistory(profileName, {
|
|
@@ -16362,10 +16562,7 @@ async function activateAndStoreOpenKeyGrants(profileName, node, grants, source)
|
|
|
16362
16562
|
expiry: delegation.expiry.toISOString()
|
|
16363
16563
|
});
|
|
16364
16564
|
}
|
|
16365
|
-
await appendAdditionalDelegations(
|
|
16366
|
-
profileName,
|
|
16367
|
-
grants.map(({ delegation, effective }) => storedAdditionalDelegation(delegation, effective))
|
|
16368
|
-
);
|
|
16565
|
+
await appendAdditionalDelegations(profileName, records);
|
|
16369
16566
|
});
|
|
16370
16567
|
}
|
|
16371
16568
|
async function ensureDelegationAuthority(params) {
|
|
@@ -16388,20 +16585,22 @@ async function ensureDelegationAuthority(params) {
|
|
|
16388
16585
|
expectedOwner: pinnedOwner(params.profile),
|
|
16389
16586
|
expiry: expiryCap
|
|
16390
16587
|
};
|
|
16588
|
+
const anchorSpace = params.anchorSpace ?? params.profile.spaceId ?? params.profile.spaceName;
|
|
16391
16589
|
const grants = [];
|
|
16392
16590
|
for (const group of groupPermissionsBySpace(params.requested)) {
|
|
16591
|
+
const request = grantRequestPermissions(group, anchorSpace);
|
|
16393
16592
|
const delegationData = await acquireOpenKey(params.profile.did, {
|
|
16394
16593
|
jwk: key,
|
|
16395
16594
|
host: params.ctx.host,
|
|
16396
|
-
permissions:
|
|
16595
|
+
permissions: request,
|
|
16397
16596
|
reason: permissionGrantReason(params.reason, group),
|
|
16398
16597
|
openkeyHost,
|
|
16399
16598
|
expiry: expiryCap === void 0 ? void 0 : openKeyExpiryParam(expiryCap)
|
|
16400
16599
|
});
|
|
16401
|
-
const delegation = portableFromOpenKeyDelegation(delegationData,
|
|
16600
|
+
const delegation = portableFromOpenKeyDelegation(delegationData, request, params.ctx.host, proof);
|
|
16402
16601
|
grants.push({ delegation, effective: permissionsFromDelegation(delegation) });
|
|
16403
16602
|
}
|
|
16404
|
-
await activateAndStoreOpenKeyGrants(params.ctx.profile, params.node, grants, "cli");
|
|
16603
|
+
await activateAndStoreOpenKeyGrants(params.ctx.profile, params.ctx.host, params.node, grants, "cli");
|
|
16405
16604
|
return;
|
|
16406
16605
|
}
|
|
16407
16606
|
if (isInteractive()) {
|
|
@@ -16423,7 +16622,7 @@ async function ensureDelegationAuthority(params) {
|
|
|
16423
16622
|
const covering = permissionsFromDelegation(delegation);
|
|
16424
16623
|
await appendAdditionalDelegation(
|
|
16425
16624
|
params.ctx.profile,
|
|
16426
|
-
|
|
16625
|
+
await cliGrantRecord(params.node, delegation, covering, params.ctx.host)
|
|
16427
16626
|
);
|
|
16428
16627
|
await appendGrantHistory(params.ctx.profile, {
|
|
16429
16628
|
addedCaps: covering,
|
|
@@ -16555,7 +16754,8 @@ function portableFromOpenKeyDelegation(data, requested, host, proof) {
|
|
|
16555
16754
|
}
|
|
16556
16755
|
const returnedSpace = data.spaceId;
|
|
16557
16756
|
const effective = session.permissions;
|
|
16558
|
-
const
|
|
16757
|
+
const spaced = effective.filter((permission) => !isRawEncryptionPermission(permission));
|
|
16758
|
+
const primary = spaced.find((permission) => permission.service !== "tinycloud.capabilities") ?? spaced[0] ?? effective[0];
|
|
16559
16759
|
const resources = effective.map((permission) => ({
|
|
16560
16760
|
service: permission.service.slice("tinycloud.".length),
|
|
16561
16761
|
space: isVerifiedRawEncryptionPermission(permission) ? "encryption" : returnedSpace,
|
|
@@ -16575,7 +16775,9 @@ function portableFromOpenKeyDelegation(data, requested, host, proof) {
|
|
|
16575
16775
|
delegateDID: data.verificationMethod,
|
|
16576
16776
|
ownerAddress: ownerParts[4],
|
|
16577
16777
|
chainId: Number(ownerParts[3]),
|
|
16578
|
-
host
|
|
16778
|
+
host,
|
|
16779
|
+
// Verified above; replay rebuilds the CACAO from it before trusting the grant.
|
|
16780
|
+
siweProof: { siwe: data.siwe, signature: data.signature }
|
|
16579
16781
|
};
|
|
16580
16782
|
}
|
|
16581
16783
|
async function rotateAuthKey(profileName, host, options = {}) {
|
|
@@ -17531,15 +17733,6 @@ async function throwKvError(error, spaceUri, profileName) {
|
|
|
17531
17733
|
if (hosted) throw hosted;
|
|
17532
17734
|
throw cliErrorFromService(error);
|
|
17533
17735
|
}
|
|
17534
|
-
function isByteCount(value) {
|
|
17535
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
17536
|
-
}
|
|
17537
|
-
function storageQuotaError(error) {
|
|
17538
|
-
if (error.code !== "STORAGE_QUOTA_EXCEEDED") return void 0;
|
|
17539
|
-
const { usedBytes, limitBytes } = typeof error.meta === "object" && error.meta !== null ? error.meta : {};
|
|
17540
|
-
const sizes = isByteCount(usedBytes) && isByteCount(limitBytes) ? ` (${formatBytes(usedBytes)} used of ${formatBytes(limitBytes)} limit)` : "";
|
|
17541
|
-
return new CLIError("STORAGE_QUOTA_EXCEEDED", `storage quota exceeded${sizes}; nothing was written`, ExitCode.ERROR);
|
|
17542
|
-
}
|
|
17543
17736
|
function assertAddressableKey(key) {
|
|
17544
17737
|
for (let i = 0; i < key.length; i++) {
|
|
17545
17738
|
const code3 = key.charCodeAt(i);
|
|
@@ -17635,8 +17828,6 @@ function registerKvCommand(program) {
|
|
|
17635
17828
|
const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
|
|
17636
17829
|
const result = await withSpinner(`Writing ${key}...`, () => kv2.put(key, putValue));
|
|
17637
17830
|
if (!result.ok) {
|
|
17638
|
-
const quota = storageQuotaError(result.error);
|
|
17639
|
-
if (quota) throw quota;
|
|
17640
17831
|
await throwKvError(result.error, spaceUri, ctx.profile);
|
|
17641
17832
|
}
|
|
17642
17833
|
outputJson({ key, written: true });
|
|
@@ -18311,6 +18502,7 @@ async function runSecretOperation(params) {
|
|
|
18311
18502
|
reason: secretPermissionReason(params.action, params.name),
|
|
18312
18503
|
yes: true,
|
|
18313
18504
|
force: true,
|
|
18505
|
+
anchorSpace: params.space ?? SECRETS_SPACE3,
|
|
18314
18506
|
openKeyAcquisition: params.openKeyAcquisition
|
|
18315
18507
|
})
|
|
18316
18508
|
);
|
|
@@ -18395,7 +18587,26 @@ async function runSecretOperationAttempt(label, operation) {
|
|
|
18395
18587
|
throw error;
|
|
18396
18588
|
}
|
|
18397
18589
|
}
|
|
18590
|
+
function withOperationWarnings(error, warnings) {
|
|
18591
|
+
if (warnings === void 0 || warnings.length === 0) return error;
|
|
18592
|
+
const cliError = wrapError(error);
|
|
18593
|
+
cliError.metadata = { ...cliError.metadata, warnings };
|
|
18594
|
+
return cliError;
|
|
18595
|
+
}
|
|
18398
18596
|
async function invokeCanonicalSecretGet(params) {
|
|
18597
|
+
const warnings = /* @__PURE__ */ new Map();
|
|
18598
|
+
const collect = (result) => {
|
|
18599
|
+
for (const warning of result.warnings ?? []) warnings.set(JSON.stringify(warning), warning);
|
|
18600
|
+
return result;
|
|
18601
|
+
};
|
|
18602
|
+
try {
|
|
18603
|
+
const result = await canonicalSecretGet(params, collect);
|
|
18604
|
+
return warnings.size === 0 ? result : { ...result, warnings: [...warnings.values()] };
|
|
18605
|
+
} catch (error) {
|
|
18606
|
+
throw withOperationWarnings(error, [...warnings.values()]);
|
|
18607
|
+
}
|
|
18608
|
+
}
|
|
18609
|
+
async function canonicalSecretGet(params, collect) {
|
|
18399
18610
|
const auth = authOptions(params.options);
|
|
18400
18611
|
let ownerNode;
|
|
18401
18612
|
if (!auth?.privateKey) {
|
|
@@ -18418,10 +18629,10 @@ async function invokeCanonicalSecretGet(params) {
|
|
|
18418
18629
|
...params.scope === void 0 ? {} : { scope: params.scope },
|
|
18419
18630
|
...params.space === void 0 ? {} : { space: params.space }
|
|
18420
18631
|
};
|
|
18421
|
-
const invoke = () => withSpinner(
|
|
18632
|
+
const invoke = async () => collect(await withSpinner(
|
|
18422
18633
|
params.label,
|
|
18423
18634
|
() => auth?.privateKey ? invokeSecretsGetWithLocalAuthorityRetry(target, input) : invokeOperation2("tinycloud.secrets.get", 1, target, input)
|
|
18424
|
-
);
|
|
18635
|
+
));
|
|
18425
18636
|
let first = await invoke();
|
|
18426
18637
|
if (first.status === "error" && first.error.code === "SESSION_NOT_FOUND" && auth?.privateKey === void 0) {
|
|
18427
18638
|
const profile2 = await ProfileManager.getProfile(params.ctx.profile);
|
|
@@ -18459,6 +18670,7 @@ async function invokeCanonicalSecretGet(params) {
|
|
|
18459
18670
|
reason: secretPermissionReason("get", params.name),
|
|
18460
18671
|
yes: true,
|
|
18461
18672
|
force: true,
|
|
18673
|
+
anchorSpace: params.space ?? SECRETS_SPACE3,
|
|
18462
18674
|
openKeyAcquisition: params.openKeyAcquisition
|
|
18463
18675
|
})
|
|
18464
18676
|
);
|
|
@@ -18470,7 +18682,7 @@ function throwCanonicalSecretGetError(result, name) {
|
|
|
18470
18682
|
throw new CLIError(
|
|
18471
18683
|
"PERMISSION_DENIED",
|
|
18472
18684
|
"Permission denied while reading secret",
|
|
18473
|
-
ExitCode.
|
|
18685
|
+
ExitCode.PERMISSION_DENIED
|
|
18474
18686
|
);
|
|
18475
18687
|
case "setup_required":
|
|
18476
18688
|
throw new CLIError(
|
|
@@ -18492,11 +18704,10 @@ ${result.setup.message}`, setup: result.setup }
|
|
|
18492
18704
|
if (result.error.code === "NODE_UNREACHABLE") {
|
|
18493
18705
|
throw new CLIError("NETWORK_ERROR", result.error.message, ExitCode.NETWORK_ERROR);
|
|
18494
18706
|
}
|
|
18495
|
-
|
|
18496
|
-
result.error.code,
|
|
18497
|
-
|
|
18498
|
-
|
|
18499
|
-
);
|
|
18707
|
+
if (result.error.code === "PERMISSION_HINT_INVALID") {
|
|
18708
|
+
throw new CLIError(result.error.code, result.error.message, ExitCode.PERMISSION_DENIED);
|
|
18709
|
+
}
|
|
18710
|
+
throw withSignInHint(cliErrorFromService(result.error), result.context.profile, result.context.posture);
|
|
18500
18711
|
case "ok":
|
|
18501
18712
|
throw new Error("Expected a failed canonical secret result.");
|
|
18502
18713
|
}
|
|
@@ -19112,19 +19323,26 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
19112
19323
|
openKeyAcquisition
|
|
19113
19324
|
});
|
|
19114
19325
|
if (result.status !== "ok") {
|
|
19115
|
-
|
|
19326
|
+
try {
|
|
19327
|
+
throwCanonicalSecretGetError(result, name);
|
|
19328
|
+
} catch (error) {
|
|
19329
|
+
throw withOperationWarnings(error, result.warnings);
|
|
19330
|
+
}
|
|
19116
19331
|
}
|
|
19117
19332
|
const value = result.output.value;
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
|
|
19121
|
-
|
|
19122
|
-
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19333
|
+
try {
|
|
19334
|
+
if (options.output) {
|
|
19335
|
+
await writeSecretFile(options.output, value);
|
|
19336
|
+
outputJson({ name, written: options.output });
|
|
19337
|
+
} else if (options.raw || options.valueOnly) {
|
|
19338
|
+
process.stdout.write(value);
|
|
19339
|
+
} else {
|
|
19340
|
+
outputJson({ name, value });
|
|
19341
|
+
}
|
|
19342
|
+
} catch (error) {
|
|
19343
|
+
throw withOperationWarnings(error, result.warnings);
|
|
19126
19344
|
}
|
|
19127
|
-
|
|
19345
|
+
outputWarnings(operationWarnings(result.warnings));
|
|
19128
19346
|
} catch (error) {
|
|
19129
19347
|
handleError(error);
|
|
19130
19348
|
}
|
|
@@ -29312,7 +29530,6 @@ var MAX_INLINE_BYTES2 = 256 * 1024;
|
|
|
29312
29530
|
|
|
29313
29531
|
// src/commands/share.ts
|
|
29314
29532
|
import { ProfileLockTimeoutError as ProfileLockTimeoutError3 } from "@tinycloud/operations/state";
|
|
29315
|
-
init_formatter();
|
|
29316
29533
|
init_errors();
|
|
29317
29534
|
|
|
29318
29535
|
// src/share/output.ts
|
|
@@ -29575,9 +29792,8 @@ function shareCliError(error, operation = "publish") {
|
|
|
29575
29792
|
if (failure.kind === "registry-rejected") {
|
|
29576
29793
|
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);
|
|
29577
29794
|
}
|
|
29578
|
-
if (failure.kind === "storage-
|
|
29579
|
-
|
|
29580
|
-
return new CLIError("STORAGE_QUOTA_EXCEEDED", `storage quota exceeded${sizes}; nothing was shared`, 4);
|
|
29795
|
+
if (failure.kind === "storage-full") {
|
|
29796
|
+
return storageFullError(failure);
|
|
29581
29797
|
}
|
|
29582
29798
|
if (failure.kind === "upload-failed") {
|
|
29583
29799
|
return new CLIError("UPLOAD_FAILED", "share source upload failed; nothing was shared", 4);
|
|
@@ -29597,6 +29813,8 @@ function shareCliError(error, operation = "publish") {
|
|
|
29597
29813
|
const code3 = error.code === "fetch-failed" ? "NOT_FOUND" : error.code.replaceAll("-", "_").toUpperCase();
|
|
29598
29814
|
return new CLIError(code3, error.message, exit);
|
|
29599
29815
|
}
|
|
29816
|
+
const storageFull = storageFullError(error);
|
|
29817
|
+
if (storageFull) return storageFull;
|
|
29600
29818
|
const message = error instanceof Error ? error.message : String(error);
|
|
29601
29819
|
const nodeCode = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
29602
29820
|
const known = {
|
|
@@ -30054,6 +30272,8 @@ async function dbHandle(node, dbName, spaceInput, profileName) {
|
|
|
30054
30272
|
return { handle: sql.db(dbName), spaceUri };
|
|
30055
30273
|
}
|
|
30056
30274
|
async function throwSqlError(error, spaceUri, profileName, prefix) {
|
|
30275
|
+
const storageFull = storageFullError(error, prefix);
|
|
30276
|
+
if (storageFull) throw storageFull;
|
|
30057
30277
|
const hosted = await unhostedSpaceError(error, spaceUri, profileName);
|
|
30058
30278
|
if (hosted) throw hosted;
|
|
30059
30279
|
const message = prefix ? `${prefix}${error.message}` : error.message;
|