@tinycloud/cli 1.1.0-beta.7 → 1.1.0-beta.8
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 +42 -13
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +42 -13
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +2 -2
- package/skills/tc-cli/AUTH.md +1 -1
- package/skills/tc-cli/REFERENCE.md +15 -0
package/dist/index.js
CHANGED
|
@@ -110,7 +110,7 @@ var init_formatter = __esm({
|
|
|
110
110
|
// src/output/errors.ts
|
|
111
111
|
import { readFileSync, readdirSync } from "fs";
|
|
112
112
|
import { join } from "path";
|
|
113
|
-
import { ProfileLockTimeoutError } from "@tinycloud/operations/state";
|
|
113
|
+
import { ProfileDeletedError, ProfileLockTimeoutError } from "@tinycloud/operations/state";
|
|
114
114
|
function setActiveProfileName(name) {
|
|
115
115
|
activeProfileName = name;
|
|
116
116
|
}
|
|
@@ -136,6 +136,9 @@ function wrapError(error) {
|
|
|
136
136
|
{ hint: "Wait for the other command to finish and retry. A crashed process's lock is reclaimed automatically after 30 s." }
|
|
137
137
|
);
|
|
138
138
|
}
|
|
139
|
+
if (error instanceof ProfileDeletedError) {
|
|
140
|
+
return new CLIError("PROFILE_NOT_FOUND", message);
|
|
141
|
+
}
|
|
139
142
|
if (message.includes("Not signed in") || message.includes("AUTH_EXPIRED") || message.includes("Session expired")) {
|
|
140
143
|
return new CLIError("AUTH_REQUIRED", message, ExitCode.AUTH_REQUIRED);
|
|
141
144
|
}
|
|
@@ -541,6 +544,8 @@ import { join as join3 } from "path";
|
|
|
541
544
|
import {
|
|
542
545
|
profilePath,
|
|
543
546
|
readSession,
|
|
547
|
+
recordProfileDeletion,
|
|
548
|
+
refuseWriteToDeletedProfile,
|
|
544
549
|
removeSession,
|
|
545
550
|
withProfileLock,
|
|
546
551
|
writeSession
|
|
@@ -655,10 +660,12 @@ var init_profiles = __esm({
|
|
|
655
660
|
* removed while holding the profile lock, so another writer's critical
|
|
656
661
|
* section never sees them vanish midway: session and key first, settings
|
|
657
662
|
* last, so a crash midway never leaves a session or key without its
|
|
658
|
-
* profile.
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
*
|
|
663
|
+
* profile. The deletion is then recorded, so a store write that waited for
|
|
664
|
+
* the lock meanwhile refuses rather than recreating a profile with only a
|
|
665
|
+
* session in it. `.lock` itself is left to the lock's release; the
|
|
666
|
+
* then-empty directory is removed afterwards unless another writer took
|
|
667
|
+
* the lock (or wrote) meanwhile. A profile directory that is a symlink is
|
|
668
|
+
* unlinked, its target left alone.
|
|
662
669
|
* Throws if the name is not one path segment or names the default profile.
|
|
663
670
|
*/
|
|
664
671
|
static async deleteProfile(name) {
|
|
@@ -679,11 +686,12 @@ var init_profiles = __esm({
|
|
|
679
686
|
`Cannot delete the default profile "${name}". Change the default first with \`tc profile default <other>\`.`
|
|
680
687
|
);
|
|
681
688
|
}
|
|
682
|
-
const
|
|
683
|
-
if (error.code === "ENOENT") return
|
|
689
|
+
const kind = await lstat(profileDir).then((stats) => stats.isSymbolicLink() ? "link" : "present", (error) => {
|
|
690
|
+
if (error.code === "ENOENT") return "missing";
|
|
684
691
|
throw error;
|
|
685
692
|
});
|
|
686
|
-
if (
|
|
693
|
+
if (kind === "missing") return;
|
|
694
|
+
if (kind === "link") {
|
|
687
695
|
await rm2(profileDir, { force: true });
|
|
688
696
|
return;
|
|
689
697
|
}
|
|
@@ -693,6 +701,7 @@ var init_profiles = __esm({
|
|
|
693
701
|
if (entry !== ".lock" && entry !== "profile.json") await rm2(join3(profileDir, entry), { recursive: true, force: true });
|
|
694
702
|
}
|
|
695
703
|
await rm2(join3(profileDir, "profile.json"), { force: true });
|
|
704
|
+
await recordProfileDeletion(name);
|
|
696
705
|
});
|
|
697
706
|
await rmdir(profileDir).catch((error) => {
|
|
698
707
|
if (error.code !== "ENOENT" && error.code !== "ENOTEMPTY" && error.code !== "EEXIST") throw error;
|
|
@@ -705,9 +714,14 @@ var init_profiles = __esm({
|
|
|
705
714
|
static async getKey(name) {
|
|
706
715
|
return readJson(join3(PROFILES_DIR, name, "key.json"));
|
|
707
716
|
}
|
|
708
|
-
/**
|
|
717
|
+
/**
|
|
718
|
+
* Saves a JWK key under the profile lock (0600, in an owner-only profile
|
|
719
|
+
* directory). Refused (PROFILE_NOT_FOUND) if the profile was deleted while
|
|
720
|
+
* this waited for the lock, rather than leaving a key-only profile.
|
|
721
|
+
*/
|
|
709
722
|
static async setKey(name, jwk) {
|
|
710
723
|
await _ProfileManager.withLock(name, async () => {
|
|
724
|
+
await refuseWriteToDeletedProfile(name);
|
|
711
725
|
await writeJson(join3(await _ProfileManager.ensureProfileDir(name), "key.json"), jwk);
|
|
712
726
|
});
|
|
713
727
|
}
|
|
@@ -5659,6 +5673,7 @@ import {
|
|
|
5659
5673
|
profileStoreMetadataPath,
|
|
5660
5674
|
readAdditionalDelegations,
|
|
5661
5675
|
readAuthRequests,
|
|
5676
|
+
refuseWriteToDeletedProfile as refuseWriteToDeletedProfile2,
|
|
5662
5677
|
updateProfileStore,
|
|
5663
5678
|
withProfileLock as withProfileLock2,
|
|
5664
5679
|
writeJsonAtomic
|
|
@@ -5885,13 +5900,13 @@ async function bootstrapDelegatedSession(ctx, delegation) {
|
|
|
5885
5900
|
throw error;
|
|
5886
5901
|
});
|
|
5887
5902
|
const session = await ProfileManager.getSession(ctx.profile);
|
|
5888
|
-
if (
|
|
5903
|
+
if (profile === null || BOOTSTRAP_PROFILE_FIELDS.some((field) => profile[field] !== written.profile[field]) || JSON.stringify(session) !== JSON.stringify(written.session)) {
|
|
5889
5904
|
return `Profile "${ctx.profile}" changed while the import was pending (another login, logout or profile update), so its newer state was kept and the provisional session was not rolled back.`;
|
|
5890
5905
|
}
|
|
5891
5906
|
return restoreBeforeBootstrap(ctx.profile, written.previousProfile);
|
|
5892
5907
|
}, { timeoutMs: PROFILE_COMMIT_LOCK_TIMEOUT_MS }).catch((error) => (
|
|
5893
5908
|
// The lock or a read failed: the import's error stays the one reported.
|
|
5894
|
-
`Rolling back the provisional session of profile "${ctx.profile}" could not run (${
|
|
5909
|
+
`Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check \`tc --profile ${ctx.profile} context\`.`
|
|
5895
5910
|
));
|
|
5896
5911
|
throw annotate(cause, note);
|
|
5897
5912
|
};
|
|
@@ -5907,15 +5922,27 @@ async function restoreBeforeBootstrap(profileName, previousProfile) {
|
|
|
5907
5922
|
const failures = [];
|
|
5908
5923
|
for (const write of [
|
|
5909
5924
|
() => ProfileManager.clearSession(profileName),
|
|
5910
|
-
() => ProfileManager.
|
|
5925
|
+
() => ProfileManager.updateProfile(profileName, (current) => {
|
|
5926
|
+
const restored = { ...current };
|
|
5927
|
+
for (const field of BOOTSTRAP_PROFILE_FIELDS) {
|
|
5928
|
+
if (previousProfile[field] === void 0) delete restored[field];
|
|
5929
|
+
else restored[field] = previousProfile[field];
|
|
5930
|
+
}
|
|
5931
|
+
return restored;
|
|
5932
|
+
})
|
|
5911
5933
|
]) {
|
|
5912
5934
|
await write().catch((error) => {
|
|
5913
|
-
failures.push(
|
|
5935
|
+
failures.push(failureName(error));
|
|
5914
5936
|
});
|
|
5915
5937
|
}
|
|
5916
5938
|
if (failures.length === 0) return void 0;
|
|
5917
5939
|
return `Rolling back the provisional session of profile "${profileName}" failed too (${failures.join("; ")}); check \`tc --profile ${profileName} context\`.`;
|
|
5918
5940
|
}
|
|
5941
|
+
function failureName(error) {
|
|
5942
|
+
if (error instanceof SyntaxError) return "a profile file is not valid JSON";
|
|
5943
|
+
if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") return error.code;
|
|
5944
|
+
return error instanceof Error ? error.name : "unknown error";
|
|
5945
|
+
}
|
|
5919
5946
|
function annotate(error, note) {
|
|
5920
5947
|
if (note === void 0) return error;
|
|
5921
5948
|
const cause = wrapError(error);
|
|
@@ -5939,6 +5966,7 @@ async function ensureAuthenticated(ctx, options) {
|
|
|
5939
5966
|
}
|
|
5940
5967
|
return createSDKInstance(ctx, options);
|
|
5941
5968
|
}
|
|
5969
|
+
var BOOTSTRAP_PROFILE_FIELDS;
|
|
5942
5970
|
var init_sdk = __esm({
|
|
5943
5971
|
"src/lib/sdk.ts"() {
|
|
5944
5972
|
"use strict";
|
|
@@ -5947,6 +5975,7 @@ var init_sdk = __esm({
|
|
|
5947
5975
|
init_errors();
|
|
5948
5976
|
init_constants();
|
|
5949
5977
|
init_permissions();
|
|
5978
|
+
BOOTSTRAP_PROFILE_FIELDS = ["sessionDid", "spaceId"];
|
|
5950
5979
|
}
|
|
5951
5980
|
});
|
|
5952
5981
|
|