@tinycloud/cli 1.1.0-beta.8 → 1.1.0-beta.9
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 +59 -36
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +353 -165
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +5 -5
- package/skills/tc-cli/REFERENCE.md +3 -1
package/dist/index.js
CHANGED
|
@@ -72,7 +72,7 @@ var init_theme = __esm({
|
|
|
72
72
|
|
|
73
73
|
// src/output/formatter.ts
|
|
74
74
|
import ora from "ora";
|
|
75
|
-
function outputError(code3, message, hint) {
|
|
75
|
+
function outputError(code3, message, hint, meta) {
|
|
76
76
|
if (isInteractive()) {
|
|
77
77
|
process.stderr.write(
|
|
78
78
|
`${theme.error("\u2717")} ${theme.label(code3)}: ${message}
|
|
@@ -89,6 +89,7 @@ function outputError(code3, message, hint) {
|
|
|
89
89
|
error: { code: code3, message }
|
|
90
90
|
};
|
|
91
91
|
if (hint) payload.error.hint = hint;
|
|
92
|
+
if (meta) payload.error.meta = meta;
|
|
92
93
|
process.stderr.write(JSON.stringify(payload, null, 2) + "\n");
|
|
93
94
|
}
|
|
94
95
|
}
|
|
@@ -111,23 +112,23 @@ var init_formatter = __esm({
|
|
|
111
112
|
import { readFileSync, readdirSync } from "fs";
|
|
112
113
|
import { join } from "path";
|
|
113
114
|
import { ProfileDeletedError, ProfileLockTimeoutError } from "@tinycloud/operations/state";
|
|
115
|
+
import { authorizationVerdictOf, parseCapabilityResource, SERVICE_LONG_TO_SHORT, validatedCapabilityOf } from "@tinycloud/sdk-core";
|
|
114
116
|
function setActiveProfileName(name) {
|
|
115
117
|
activeProfileName = name;
|
|
116
118
|
}
|
|
117
119
|
function wrapError(error) {
|
|
120
|
+
if (error instanceof CLIError) return error;
|
|
118
121
|
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return new CLIError(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
);
|
|
122
|
+
const verdict = authorizationVerdictOf(error);
|
|
123
|
+
if (verdict === "unauthenticated") {
|
|
124
|
+
return new CLIError("AUTH_REQUIRED", message, ExitCode.AUTH_REQUIRED);
|
|
125
|
+
}
|
|
126
|
+
if (verdict === "forbidden") {
|
|
127
|
+
return new CLIError("PERMISSION_DENIED", message, ExitCode.PERMISSION_DENIED);
|
|
128
|
+
}
|
|
129
|
+
if (verdict === void 0 && message.includes("Missing private key parameter in JWK")) {
|
|
130
|
+
return missingPrivateKeyError();
|
|
129
131
|
}
|
|
130
|
-
if (error instanceof CLIError) return error;
|
|
131
132
|
if (error instanceof ProfileLockTimeoutError) {
|
|
132
133
|
return new CLIError(
|
|
133
134
|
"PROFILE_LOCK_TIMEOUT",
|
|
@@ -139,13 +140,13 @@ function wrapError(error) {
|
|
|
139
140
|
if (error instanceof ProfileDeletedError) {
|
|
140
141
|
return new CLIError("PROFILE_NOT_FOUND", message);
|
|
141
142
|
}
|
|
142
|
-
if (message.includes("Not signed in") || message.includes("AUTH_EXPIRED") || message.includes("Session expired")) {
|
|
143
|
+
if (verdict === void 0 && (message.includes("Not signed in") || message.includes("AUTH_EXPIRED") || message.includes("Session expired"))) {
|
|
143
144
|
return new CLIError("AUTH_REQUIRED", message, ExitCode.AUTH_REQUIRED);
|
|
144
145
|
}
|
|
145
146
|
if (message.includes("NOT_FOUND") || message.includes("KV_NOT_FOUND")) {
|
|
146
147
|
return new CLIError("NOT_FOUND", message, ExitCode.NOT_FOUND);
|
|
147
148
|
}
|
|
148
|
-
if (message.includes("PERMISSION_DENIED")) {
|
|
149
|
+
if (verdict === void 0 && message.includes("PERMISSION_DENIED")) {
|
|
149
150
|
return new CLIError("PERMISSION_DENIED", message, ExitCode.PERMISSION_DENIED);
|
|
150
151
|
}
|
|
151
152
|
if (message.includes("ECONNREFUSED") || message.includes("ETIMEDOUT") || message.includes("fetch failed")) {
|
|
@@ -153,39 +154,51 @@ function wrapError(error) {
|
|
|
153
154
|
}
|
|
154
155
|
return new CLIError("ERROR", message, ExitCode.ERROR);
|
|
155
156
|
}
|
|
157
|
+
function missingPrivateKeyError() {
|
|
158
|
+
const profileName = activeProfileName ?? process.env.TC_PROFILE ?? DEFAULT_PROFILE;
|
|
159
|
+
return new CLIError(
|
|
160
|
+
"AUTH_REQUIRED",
|
|
161
|
+
`Profile "${profileName}" cannot restore its session because its private key material is missing.`,
|
|
162
|
+
ExitCode.AUTH_REQUIRED,
|
|
163
|
+
{ hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey` }
|
|
164
|
+
);
|
|
165
|
+
}
|
|
156
166
|
function handleError(error) {
|
|
157
167
|
const cliError = wrapError(error);
|
|
158
168
|
const prebuilt = typeof cliError.metadata?.hint === "string" ? cliError.metadata.hint : void 0;
|
|
159
169
|
const hint = prebuilt ?? buildAuthHint(cliError) ?? (cliError.code === "NETWORK_ERROR" ? buildNetworkHint() : void 0);
|
|
160
|
-
|
|
170
|
+
const authMeta = cliError.code === "AUTH_REQUIRED" || cliError.code === "PERMISSION_DENIED" ? cliError.metadata : void 0;
|
|
171
|
+
const meta = {};
|
|
172
|
+
if (cliError.status !== void 0 && authMeta !== void 0) meta.status = cliError.status;
|
|
173
|
+
const capability = validatedCapabilityOf({ meta: authMeta });
|
|
174
|
+
if (capability) {
|
|
175
|
+
meta.resource = capability.resource;
|
|
176
|
+
meta.requiredAction = capability.requiredAction;
|
|
177
|
+
}
|
|
178
|
+
outputError(cliError.code, cliError.message, hint, Object.keys(meta).length ? meta : void 0);
|
|
161
179
|
process.exit(cliError.exitCode);
|
|
162
180
|
}
|
|
163
181
|
function buildAuthHint(error) {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return void 0;
|
|
168
|
-
}
|
|
169
|
-
const spec = capSpecFromAuthMeta(resource, requiredAction);
|
|
182
|
+
const capability = validatedCapabilityOf({ meta: error.metadata });
|
|
183
|
+
if (!capability) return void 0;
|
|
184
|
+
const spec = capSpecFromAuthMeta(capability.resource, capability.requiredAction);
|
|
170
185
|
if (!spec) return void 0;
|
|
171
186
|
return [
|
|
172
187
|
"The active session is missing a TinyCloud capability.",
|
|
173
|
-
`Request it with: tc auth request --cap
|
|
188
|
+
`Request it with: tc auth request --cap '${spec.replaceAll("'", "'\\''")}'`,
|
|
174
189
|
"Then retry the original command."
|
|
175
190
|
].join("\n");
|
|
176
191
|
}
|
|
177
192
|
function capSpecFromAuthMeta(resource, action) {
|
|
178
|
-
const slash =
|
|
179
|
-
if (slash
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
const spaceName = spaceUri.startsWith("tinycloud:") ? spaceUri.slice(spaceUri.lastIndexOf(":") + 1) : spaceUri;
|
|
188
|
-
return `tinycloud.${serviceShort}:${spaceName}:${path}:${actionName}`;
|
|
193
|
+
const slash = action.indexOf("/");
|
|
194
|
+
if (slash < 0) return void 0;
|
|
195
|
+
const longService = action.slice(0, slash);
|
|
196
|
+
const serviceShort = SERVICE_LONG_TO_SHORT[longService];
|
|
197
|
+
if (!serviceShort) return void 0;
|
|
198
|
+
const parsed = parseCapabilityResource(resource, serviceShort);
|
|
199
|
+
if (!parsed) return void 0;
|
|
200
|
+
const spaceName = parsed.space.slice(parsed.space.lastIndexOf(":") + 1);
|
|
201
|
+
return `${longService}:${spaceName}:${parsed.path}:${action.slice(slash + 1)}`;
|
|
189
202
|
}
|
|
190
203
|
function buildNetworkHint() {
|
|
191
204
|
const readHost = (name) => {
|
|
@@ -239,7 +252,9 @@ var init_errors = __esm({
|
|
|
239
252
|
this.exitCode = exitCode;
|
|
240
253
|
this.metadata = metadata;
|
|
241
254
|
this.name = "CLIError";
|
|
255
|
+
if (typeof metadata?.status === "number" && Number.isInteger(metadata.status) && metadata.status >= 400 && metadata.status <= 599) this.status = metadata.status;
|
|
242
256
|
}
|
|
257
|
+
status;
|
|
243
258
|
};
|
|
244
259
|
}
|
|
245
260
|
});
|
|
@@ -15019,7 +15034,6 @@ async function notifyShare(input) {
|
|
|
15019
15034
|
};
|
|
15020
15035
|
}
|
|
15021
15036
|
let attempts = 0;
|
|
15022
|
-
let lastError;
|
|
15023
15037
|
while (attempts < attemptsLimit) {
|
|
15024
15038
|
if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
|
|
15025
15039
|
attempts += 1;
|
|
@@ -15046,10 +15060,19 @@ async function notifyShare(input) {
|
|
|
15046
15060
|
reason: "delivery-window-expired"
|
|
15047
15061
|
};
|
|
15048
15062
|
}
|
|
15049
|
-
|
|
15063
|
+
if (error !== null && typeof error === "object" && "status" in error && (error.status === 401 || error.status === 403)) {
|
|
15064
|
+
return {
|
|
15065
|
+
protocol: "tinycloud-share",
|
|
15066
|
+
version: 1,
|
|
15067
|
+
shareId: input.shareId,
|
|
15068
|
+
state: "partial-failure",
|
|
15069
|
+
idempotencyKey,
|
|
15070
|
+
attempts,
|
|
15071
|
+
retryable: false
|
|
15072
|
+
};
|
|
15073
|
+
}
|
|
15050
15074
|
}
|
|
15051
15075
|
}
|
|
15052
|
-
void lastError;
|
|
15053
15076
|
return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state: "partial-failure", idempotencyKey, attempts, retryable: true };
|
|
15054
15077
|
}
|
|
15055
15078
|
async function defaultIdempotencyKey(shareId, recipient) {
|