@tinycloud/cli 1.1.0-beta.13 → 1.1.0-beta.15
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 +90 -11
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +435 -96
- 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 +1 -1
package/dist/index.js
CHANGED
|
@@ -74,8 +74,14 @@ var init_theme = __esm({
|
|
|
74
74
|
|
|
75
75
|
// src/output/formatter.ts
|
|
76
76
|
import ora from "ora";
|
|
77
|
-
function
|
|
78
|
-
|
|
77
|
+
function warningLine(warnings) {
|
|
78
|
+
const which = warnings.map((warning) => `${warning.grantCid ?? "unidentified"}: ${warning.reason}`).join(", ");
|
|
79
|
+
const subject = warnings.length === 1 ? "A stored grant was" : `${warnings.length} stored grants were`;
|
|
80
|
+
return `${subject} not used (${which}). Approve access again when a command needs it; the old grant expires on its own.`;
|
|
81
|
+
}
|
|
82
|
+
function outputError(code3, message, hint, details = {}) {
|
|
83
|
+
const { meta, warnings = [] } = details;
|
|
84
|
+
if (!shouldOutputJson()) {
|
|
79
85
|
process.stderr.write(
|
|
80
86
|
`${theme.error("\u2717")} ${theme.label(code3)}: ${message}
|
|
81
87
|
`
|
|
@@ -86,22 +92,47 @@ function outputError(code3, message, hint, meta) {
|
|
|
86
92
|
`);
|
|
87
93
|
}
|
|
88
94
|
}
|
|
95
|
+
if (warnings.length > 0) process.stderr.write(` ${theme.warn(warningLine(warnings))}
|
|
96
|
+
`);
|
|
89
97
|
} else {
|
|
90
98
|
const payload = {
|
|
91
99
|
error: { code: code3, message }
|
|
92
100
|
};
|
|
93
101
|
if (hint) payload.error.hint = hint;
|
|
94
102
|
if (meta) payload.error.meta = meta;
|
|
103
|
+
if (warnings.length > 0) payload.error.warnings = warnings;
|
|
95
104
|
process.stderr.write(JSON.stringify(payload, null, 2) + "\n");
|
|
96
105
|
}
|
|
97
106
|
}
|
|
107
|
+
function operationWarnings(value) {
|
|
108
|
+
if (!Array.isArray(value)) return [];
|
|
109
|
+
return value.flatMap((warning) => {
|
|
110
|
+
if (warning === null || typeof warning !== "object") return [];
|
|
111
|
+
const { code: code3, reason, grantCid } = warning;
|
|
112
|
+
if (typeof code3 !== "string" || !WARNING_CODE.test(code3) || typeof reason !== "string" || !WARNING_REASON.test(reason)) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
return [{ code: code3, reason, ...typeof grantCid === "string" && DELEGATION_CID.test(grantCid) ? { grantCid } : {} }];
|
|
116
|
+
});
|
|
117
|
+
}
|
|
98
118
|
function isInteractive() {
|
|
99
119
|
return Boolean(process.stdout.isTTY);
|
|
100
120
|
}
|
|
121
|
+
function setJsonOutputRequested(requested) {
|
|
122
|
+
globalThis[JSON_REQUESTED] = requested;
|
|
123
|
+
}
|
|
124
|
+
function shouldOutputJson() {
|
|
125
|
+
return !isInteractive() || (globalThis[JSON_REQUESTED] ?? process.argv.includes("--json"));
|
|
126
|
+
}
|
|
127
|
+
var WARNING_CODE, WARNING_REASON, DELEGATION_CID, JSON_REQUESTED;
|
|
101
128
|
var init_formatter = __esm({
|
|
102
129
|
"src/output/formatter.ts"() {
|
|
103
130
|
"use strict";
|
|
104
131
|
init_theme();
|
|
132
|
+
WARNING_CODE = /^[A-Z][A-Z_]{0,63}$/;
|
|
133
|
+
WARNING_REASON = /^[a-z][a-z_]{0,63}$/;
|
|
134
|
+
DELEGATION_CID = /^bafkr4i[a-z2-7]{52}$/;
|
|
135
|
+
JSON_REQUESTED = /* @__PURE__ */ Symbol.for("tinycloud.cli.jsonOutputRequested");
|
|
105
136
|
}
|
|
106
137
|
});
|
|
107
138
|
|
|
@@ -261,7 +292,10 @@ function handleError(error) {
|
|
|
261
292
|
meta.resource = capability.resource;
|
|
262
293
|
meta.requiredAction = capability.requiredAction;
|
|
263
294
|
}
|
|
264
|
-
outputError(cliError.code, cliError.message, hint,
|
|
295
|
+
outputError(cliError.code, cliError.message, hint, {
|
|
296
|
+
...Object.keys(meta).length ? { meta } : {},
|
|
297
|
+
warnings: operationWarnings(cliError.metadata?.warnings)
|
|
298
|
+
});
|
|
265
299
|
process.exit(cliError.exitCode);
|
|
266
300
|
}
|
|
267
301
|
function buildAuthHint(error) {
|
|
@@ -5738,6 +5772,37 @@ var init_zod = __esm({
|
|
|
5738
5772
|
}
|
|
5739
5773
|
});
|
|
5740
5774
|
|
|
5775
|
+
// src/auth/session-expired.ts
|
|
5776
|
+
function signInAgainHint(profileName, posture) {
|
|
5777
|
+
if (posture === "local-owner-key") {
|
|
5778
|
+
return `Sign in again with: tc --profile ${profileName} auth login --method local`;
|
|
5779
|
+
}
|
|
5780
|
+
if (posture === "delegate-session") {
|
|
5781
|
+
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.`;
|
|
5782
|
+
}
|
|
5783
|
+
return `Sign in again with: tc --profile ${profileName} auth login --method openkey`;
|
|
5784
|
+
}
|
|
5785
|
+
function sessionExpiredError(profileName, posture) {
|
|
5786
|
+
return new CLIError(
|
|
5787
|
+
"AUTH_REQUIRED",
|
|
5788
|
+
`The session for profile "${profileName}" has expired or is no longer valid.`,
|
|
5789
|
+
ExitCode.AUTH_REQUIRED,
|
|
5790
|
+
{ hint: signInAgainHint(profileName, posture), reason: SESSION_EXPIRED_REASON }
|
|
5791
|
+
);
|
|
5792
|
+
}
|
|
5793
|
+
function isSessionExpiredError(error) {
|
|
5794
|
+
return error instanceof CLIError && error.metadata?.reason === SESSION_EXPIRED_REASON;
|
|
5795
|
+
}
|
|
5796
|
+
var SESSION_EXPIRED_REASON;
|
|
5797
|
+
var init_session_expired = __esm({
|
|
5798
|
+
"src/auth/session-expired.ts"() {
|
|
5799
|
+
"use strict";
|
|
5800
|
+
init_constants();
|
|
5801
|
+
init_errors();
|
|
5802
|
+
SESSION_EXPIRED_REASON = "session_expired";
|
|
5803
|
+
}
|
|
5804
|
+
});
|
|
5805
|
+
|
|
5741
5806
|
// src/lib/raw-encryption.ts
|
|
5742
5807
|
var init_raw_encryption = __esm({
|
|
5743
5808
|
"src/lib/raw-encryption.ts"() {
|
|
@@ -5800,13 +5865,14 @@ async function replayAdditionalDelegations(node, profile, options) {
|
|
|
5800
5865
|
for (const stored of entries) {
|
|
5801
5866
|
const kind = storedDelegationKind(stored);
|
|
5802
5867
|
if (kind === "compact" || kind === "signed-login") {
|
|
5803
|
-
const
|
|
5868
|
+
const replay = await replayStoredDelegation(activator, stored, {
|
|
5804
5869
|
host: options.host,
|
|
5805
5870
|
migrated,
|
|
5806
5871
|
resolveSpace
|
|
5807
5872
|
});
|
|
5808
|
-
if (
|
|
5809
|
-
process.stderr.write(
|
|
5873
|
+
if (replay.status === "skipped" && process.env.TC_DEBUG_REPLAY === "1") {
|
|
5874
|
+
process.stderr.write(`[replay] skipping a stored delegation: ${replay.reason}
|
|
5875
|
+
`);
|
|
5810
5876
|
}
|
|
5811
5877
|
continue;
|
|
5812
5878
|
}
|
|
@@ -5884,6 +5950,16 @@ function signerJwkForProfile(profileName, sessionJwk, key) {
|
|
|
5884
5950
|
}
|
|
5885
5951
|
);
|
|
5886
5952
|
}
|
|
5953
|
+
async function restoreProfileSession(node, profileName, profile, sessionData) {
|
|
5954
|
+
try {
|
|
5955
|
+
await node.restoreSession(sessionData);
|
|
5956
|
+
} catch (error) {
|
|
5957
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "AUTH_EXPIRED") {
|
|
5958
|
+
throw sessionExpiredError(profileName, profile === null ? void 0 : resolveProfilePosture(profile));
|
|
5959
|
+
}
|
|
5960
|
+
throw error;
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5887
5963
|
async function createSDKInstance(ctx, options) {
|
|
5888
5964
|
const profile = options?.privateKey ? await ProfileManager.getProfile(ctx.profile).catch(() => null) : await ProfileManager.getProfile(ctx.profile);
|
|
5889
5965
|
const session = await ProfileManager.getSession(ctx.profile);
|
|
@@ -5903,7 +5979,7 @@ async function createSDKInstance(ctx, options) {
|
|
|
5903
5979
|
});
|
|
5904
5980
|
let restoredOwnSession2 = false;
|
|
5905
5981
|
if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
|
|
5906
|
-
await node2.
|
|
5982
|
+
await restoreProfileSession(node2, ctx.profile, profile, {
|
|
5907
5983
|
delegationHeader: session.delegationHeader,
|
|
5908
5984
|
delegationCid: session.delegationCid,
|
|
5909
5985
|
spaceId: session.spaceId,
|
|
@@ -5933,7 +6009,7 @@ async function createSDKInstance(ctx, options) {
|
|
|
5933
6009
|
if (options?.privateKey) {
|
|
5934
6010
|
await node.signIn();
|
|
5935
6011
|
} else if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
|
|
5936
|
-
await node.
|
|
6012
|
+
await restoreProfileSession(node, ctx.profile, profile, {
|
|
5937
6013
|
delegationHeader: session.delegationHeader,
|
|
5938
6014
|
delegationCid: session.delegationCid,
|
|
5939
6015
|
spaceId: session.spaceId,
|
|
@@ -6077,6 +6153,7 @@ var init_sdk = __esm({
|
|
|
6077
6153
|
init_errors();
|
|
6078
6154
|
init_constants();
|
|
6079
6155
|
init_permissions();
|
|
6156
|
+
init_session_expired();
|
|
6080
6157
|
BOOTSTRAP_PROFILE_FIELDS = ["sessionDid", "spaceId"];
|
|
6081
6158
|
}
|
|
6082
6159
|
});
|
|
@@ -6175,7 +6252,7 @@ function formatBannerLine(version2) {
|
|
|
6175
6252
|
}
|
|
6176
6253
|
function emitBanner(version2) {
|
|
6177
6254
|
if (bannerEmitted) return;
|
|
6178
|
-
if (
|
|
6255
|
+
if (shouldOutputJson()) return;
|
|
6179
6256
|
if (process.env.TC_HIDE_BANNER === "1") return;
|
|
6180
6257
|
bannerEmitted = true;
|
|
6181
6258
|
process.stderr.write(formatBannerLine(version2) + "\n\n");
|
|
@@ -17370,6 +17447,7 @@ function registerShareCommand(program2) {
|
|
|
17370
17447
|
init_storage();
|
|
17371
17448
|
init_profiles();
|
|
17372
17449
|
init_constants();
|
|
17450
|
+
init_session_expired();
|
|
17373
17451
|
import { readFile as readFile4 } from "fs/promises";
|
|
17374
17452
|
import { join as join6 } from "path";
|
|
17375
17453
|
import { createHash } from "crypto";
|
|
@@ -17682,7 +17760,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
17682
17760
|
} catch (error) {
|
|
17683
17761
|
const profileConfig = await ProfileManager.getProfile(profile).catch(() => void 0);
|
|
17684
17762
|
const code3 = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
17685
|
-
if (error instanceof InvalidRestoredSessionError || code3 === "AUTH_EXPIRED") {
|
|
17763
|
+
if (error instanceof InvalidRestoredSessionError || code3 === "AUTH_EXPIRED" || isSessionExpiredError(error)) {
|
|
17686
17764
|
throw new SharePublishAuthorityError({
|
|
17687
17765
|
kind: "owner-space-unresolved",
|
|
17688
17766
|
localKey: profileConfig?.authMethod === "local",
|
|
@@ -18044,6 +18122,7 @@ function selectedShareProfile() {
|
|
|
18044
18122
|
program.name("tc").description("TinyCloud CLI \u2014 self-sovereign storage from the terminal").version(version).option("-p, --profile <name>", "Profile to use").option("-H, --host <url>", "TinyCloud node URL").option("-v, --verbose", "Enable verbose output").option("--no-cache", "Disable caching").option("-q, --quiet", "Suppress non-essential output").option("--json", "Force JSON output");
|
|
18045
18123
|
program.hook("preAction", async (thisCommand) => {
|
|
18046
18124
|
const opts = thisCommand.optsWithGlobals();
|
|
18125
|
+
setJsonOutputRequested(opts.json === true);
|
|
18047
18126
|
const parentName = thisCommand.parent?.name();
|
|
18048
18127
|
const isShareCommand = parentName === "share" || thisCommand.name() === "share";
|
|
18049
18128
|
if (!opts.quiet && !isShareCommand) {
|
|
@@ -18052,7 +18131,7 @@ program.hook("preAction", async (thisCommand) => {
|
|
|
18052
18131
|
const commandName = thisCommand.name();
|
|
18053
18132
|
const fullCommand = parentName && parentName !== "tc" ? `${parentName} ${commandName}` : commandName;
|
|
18054
18133
|
const skipGuard = ["tc", "init", "doctor", "completion", "help", "upgrade", "status"].includes(commandName) || fullCommand === "profile create";
|
|
18055
|
-
if (!skipGuard && !opts.quiet &&
|
|
18134
|
+
if (!skipGuard && !opts.quiet && !shouldOutputJson()) {
|
|
18056
18135
|
try {
|
|
18057
18136
|
const config = await ProfileManager.getConfig();
|
|
18058
18137
|
const profileName = opts.profile || config.defaultProfile;
|