@tinycloud/cli 1.1.0 → 1.2.0-beta.0
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 +118 -90
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +801 -476
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +4 -4
- package/skills/tc-cli/AUTH.md +17 -5
- package/skills/tc-cli/REFERENCE.md +18 -2
package/dist/index.js
CHANGED
|
@@ -72,12 +72,51 @@ var init_theme = __esm({
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
// src/output/warnings.ts
|
|
76
|
+
function warningKey(warning) {
|
|
77
|
+
return JSON.stringify([warning.code, warning.reason, warning.grantCid ?? null]);
|
|
78
|
+
}
|
|
79
|
+
function mergeWarnings(first, second) {
|
|
80
|
+
const merged = [...first];
|
|
81
|
+
const counts = /* @__PURE__ */ new Map();
|
|
82
|
+
for (const warning of first) {
|
|
83
|
+
const key = warningKey(warning);
|
|
84
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
85
|
+
}
|
|
86
|
+
const seen = /* @__PURE__ */ new Map();
|
|
87
|
+
for (const warning of second) {
|
|
88
|
+
const key = warningKey(warning);
|
|
89
|
+
const occurrence = (seen.get(key) ?? 0) + 1;
|
|
90
|
+
seen.set(key, occurrence);
|
|
91
|
+
if (occurrence > (counts.get(key) ?? 0)) merged.push(warning);
|
|
92
|
+
}
|
|
93
|
+
return merged;
|
|
94
|
+
}
|
|
95
|
+
function recordWarnings(warnings) {
|
|
96
|
+
if (warnings.length === 0) return;
|
|
97
|
+
const holder = globalThis;
|
|
98
|
+
holder[PENDING_WARNINGS] = mergeWarnings(holder[PENDING_WARNINGS] ?? [], warnings);
|
|
99
|
+
}
|
|
100
|
+
function drainWarnings() {
|
|
101
|
+
const holder = globalThis;
|
|
102
|
+
const warnings = holder[PENDING_WARNINGS] ?? [];
|
|
103
|
+
delete holder[PENDING_WARNINGS];
|
|
104
|
+
return warnings;
|
|
105
|
+
}
|
|
106
|
+
var PENDING_WARNINGS;
|
|
107
|
+
var init_warnings = __esm({
|
|
108
|
+
"src/output/warnings.ts"() {
|
|
109
|
+
"use strict";
|
|
110
|
+
PENDING_WARNINGS = /* @__PURE__ */ Symbol.for("tinycloud.cli.pendingWarnings.v1");
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
75
114
|
// src/output/formatter.ts
|
|
76
115
|
import ora from "ora";
|
|
77
116
|
function warningLine(warnings) {
|
|
78
117
|
const which = warnings.map((warning) => `${warning.grantCid ?? "unidentified"}: ${warning.reason}`).join(", ");
|
|
79
118
|
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
|
|
119
|
+
return `${subject} not used (${which}). Approve access again when a command needs it; remove the old grant with tc auth grants remove <cid>.`;
|
|
81
120
|
}
|
|
82
121
|
function outputError(code3, message, hint, details = {}) {
|
|
83
122
|
const { meta, warnings = [] } = details;
|
|
@@ -92,7 +131,7 @@ function outputError(code3, message, hint, details = {}) {
|
|
|
92
131
|
`);
|
|
93
132
|
}
|
|
94
133
|
}
|
|
95
|
-
if (warnings.length > 0) process.stderr.write(` ${theme.warn(warningLine(warnings)
|
|
134
|
+
if (warnings.length > 0) process.stderr.write(` ${theme.warn("!")} ${warningLine(warnings)}
|
|
96
135
|
`);
|
|
97
136
|
} else {
|
|
98
137
|
const payload = {
|
|
@@ -104,6 +143,13 @@ function outputError(code3, message, hint, details = {}) {
|
|
|
104
143
|
process.stderr.write(JSON.stringify(payload, null, 2) + "\n");
|
|
105
144
|
}
|
|
106
145
|
}
|
|
146
|
+
function outputWarnings(warnings) {
|
|
147
|
+
const combined = mergeWarnings(drainWarnings(), operationWarnings(warnings));
|
|
148
|
+
if (combined.length === 0) return;
|
|
149
|
+
process.stderr.write(shouldOutputJson() ? `${JSON.stringify({ warnings: combined })}
|
|
150
|
+
` : `${theme.warn("!")} ${warningLine(combined)}
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
107
153
|
function operationWarnings(value) {
|
|
108
154
|
if (!Array.isArray(value)) return [];
|
|
109
155
|
return value.flatMap((warning) => {
|
|
@@ -129,6 +175,7 @@ var init_formatter = __esm({
|
|
|
129
175
|
"src/output/formatter.ts"() {
|
|
130
176
|
"use strict";
|
|
131
177
|
init_theme();
|
|
178
|
+
init_warnings();
|
|
132
179
|
WARNING_CODE = /^[A-Z][A-Z_]{0,63}$/;
|
|
133
180
|
WARNING_REASON = /^[a-z][a-z_]{0,63}$/;
|
|
134
181
|
DELEGATION_CID = /^bafkr4i[a-z2-7]{52}$/;
|
|
@@ -286,6 +333,9 @@ function handleError(error) {
|
|
|
286
333
|
const hint = prebuilt ?? buildAuthHint(cliError) ?? (cliError.code === "NETWORK_ERROR" ? buildNetworkHint() : void 0);
|
|
287
334
|
const authMeta = cliError.code === "AUTH_REQUIRED" || cliError.code === "PERMISSION_DENIED" ? cliError.metadata : void 0;
|
|
288
335
|
const meta = {};
|
|
336
|
+
if (cliError.code === "GRANT_REVOKED_LOCAL_REMOVE_FAILED" && typeof cliError.metadata?.causeCode === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(cliError.metadata.causeCode)) {
|
|
337
|
+
meta.causeCode = cliError.metadata.causeCode;
|
|
338
|
+
}
|
|
289
339
|
if (cliError.status !== void 0 && authMeta !== void 0) meta.status = cliError.status;
|
|
290
340
|
const capability = validatedCapabilityOf({ meta: authMeta });
|
|
291
341
|
if (capability) {
|
|
@@ -294,7 +344,7 @@ function handleError(error) {
|
|
|
294
344
|
}
|
|
295
345
|
outputError(cliError.code, cliError.message, hint, {
|
|
296
346
|
...Object.keys(meta).length ? { meta } : {},
|
|
297
|
-
warnings: operationWarnings(cliError.metadata?.warnings)
|
|
347
|
+
warnings: mergeWarnings(drainWarnings(), operationWarnings(cliError.metadata?.warnings))
|
|
298
348
|
});
|
|
299
349
|
process.exit(cliError.exitCode);
|
|
300
350
|
}
|
|
@@ -366,6 +416,7 @@ var init_errors = __esm({
|
|
|
366
416
|
init_constants();
|
|
367
417
|
init_formatter();
|
|
368
418
|
init_storage();
|
|
419
|
+
init_warnings();
|
|
369
420
|
CLIError = class extends Error {
|
|
370
421
|
constructor(code3, message, exitCode = ExitCode.ERROR, metadata) {
|
|
371
422
|
super(message);
|
|
@@ -5845,59 +5896,14 @@ import {
|
|
|
5845
5896
|
withProfileLock as withProfileLock2,
|
|
5846
5897
|
writeJsonAtomic
|
|
5847
5898
|
} from "@tinycloud/operations/state";
|
|
5848
|
-
async function loadAdditionalDelegations(profile) {
|
|
5849
|
-
return readAdditionalDelegations(profile);
|
|
5850
|
-
}
|
|
5851
5899
|
async function replayAdditionalDelegations(node, profile, options) {
|
|
5852
|
-
const {
|
|
5853
|
-
|
|
5854
|
-
prepareStoredDelegationReplay,
|
|
5855
|
-
replayStoredDelegation,
|
|
5856
|
-
storedDelegationKind
|
|
5857
|
-
} = await import("@tinycloud/operations/delegation-binding");
|
|
5858
|
-
const activator = node;
|
|
5859
|
-
const migrated = await prepareStoredDelegationReplay(profile, activator, {
|
|
5900
|
+
const { operationSpaceResolver, replayProfileDelegations } = await import("@tinycloud/operations/delegation-binding");
|
|
5901
|
+
const { warnings } = await replayProfileDelegations(profile, node, {
|
|
5860
5902
|
host: options.host,
|
|
5861
|
-
migrate: options.migrate
|
|
5903
|
+
migrate: options.migrate,
|
|
5904
|
+
resolveSpace: operationSpaceResolver(node, options.ownerSpace)
|
|
5862
5905
|
});
|
|
5863
|
-
|
|
5864
|
-
const entries = await loadAdditionalDelegations(profile);
|
|
5865
|
-
for (const stored of entries) {
|
|
5866
|
-
const kind = storedDelegationKind(stored);
|
|
5867
|
-
if (kind === "compact" || kind === "signed-login") {
|
|
5868
|
-
const replay = await replayStoredDelegation(activator, stored, {
|
|
5869
|
-
host: options.host,
|
|
5870
|
-
migrated,
|
|
5871
|
-
resolveSpace
|
|
5872
|
-
});
|
|
5873
|
-
if (replay.status === "skipped" && process.env.TC_DEBUG_REPLAY === "1") {
|
|
5874
|
-
process.stderr.write(`[replay] skipping a stored delegation: ${replay.reason}
|
|
5875
|
-
`);
|
|
5876
|
-
}
|
|
5877
|
-
continue;
|
|
5878
|
-
}
|
|
5879
|
-
if (kind === "refused") {
|
|
5880
|
-
if (process.env.TC_DEBUG_REPLAY === "1") {
|
|
5881
|
-
process.stderr.write("[replay] skipping a malformed stored record or one bound to a request it cannot be held to\n");
|
|
5882
|
-
}
|
|
5883
|
-
continue;
|
|
5884
|
-
}
|
|
5885
|
-
const entry = stored;
|
|
5886
|
-
const expiry = entry.delegation.expiry instanceof Date ? entry.delegation.expiry : new Date(entry.delegation.expiry);
|
|
5887
|
-
if (expiry.getTime() <= Date.now()) continue;
|
|
5888
|
-
try {
|
|
5889
|
-
await node.useRuntimeDelegation({
|
|
5890
|
-
...entry.delegation,
|
|
5891
|
-
delegationHeader: { Authorization: entry.delegation.delegationHeader.Authorization },
|
|
5892
|
-
expiry
|
|
5893
|
-
});
|
|
5894
|
-
} catch (err) {
|
|
5895
|
-
if (process.env.TC_DEBUG_REPLAY === "1") {
|
|
5896
|
-
process.stderr.write(`[replay] skipping ${entry.delegation.cid}: ${err.message}
|
|
5897
|
-
`);
|
|
5898
|
-
}
|
|
5899
|
-
}
|
|
5900
|
-
}
|
|
5906
|
+
return warnings;
|
|
5901
5907
|
}
|
|
5902
5908
|
var init_permissions = __esm({
|
|
5903
5909
|
"src/lib/permissions.ts"() {
|
|
@@ -5922,6 +5928,7 @@ __export(sdk_exports, {
|
|
|
5922
5928
|
createSDKInstance: () => createSDKInstance,
|
|
5923
5929
|
ensureAuthenticated: () => ensureAuthenticated,
|
|
5924
5930
|
jwkHasPrivateParameter: () => jwkHasPrivateParameter,
|
|
5931
|
+
restoreProfileSessionNode: () => restoreProfileSessionNode,
|
|
5925
5932
|
selectSignerJwk: () => selectSignerJwk
|
|
5926
5933
|
});
|
|
5927
5934
|
import { TinyCloudNode } from "@tinycloud/node-sdk";
|
|
@@ -5960,6 +5967,44 @@ async function restoreProfileSession(node, profileName, profile, sessionData) {
|
|
|
5960
5967
|
throw error;
|
|
5961
5968
|
}
|
|
5962
5969
|
}
|
|
5970
|
+
function persistedSessionData(profileName, profile, session, key) {
|
|
5971
|
+
return {
|
|
5972
|
+
delegationHeader: session.delegationHeader,
|
|
5973
|
+
delegationCid: session.delegationCid,
|
|
5974
|
+
spaceId: session.spaceId,
|
|
5975
|
+
jwk: signerJwkForProfile(profileName, session.jwk, key),
|
|
5976
|
+
verificationMethod: session.verificationMethod ?? profile.sessionDid ?? profile.did,
|
|
5977
|
+
address: session.address,
|
|
5978
|
+
chainId: session.chainId,
|
|
5979
|
+
siwe: session.siwe,
|
|
5980
|
+
signature: session.signature
|
|
5981
|
+
};
|
|
5982
|
+
}
|
|
5983
|
+
async function restoreProfileSessionNode(ctx) {
|
|
5984
|
+
const profile = await ProfileManager.getProfile(ctx.profile);
|
|
5985
|
+
const session = await ProfileManager.getSession(ctx.profile);
|
|
5986
|
+
if (!session) return { unavailable: "session_missing" };
|
|
5987
|
+
if (typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || session.delegationHeader === null || typeof session.delegationHeader !== "object" || !("Authorization" in session.delegationHeader) || typeof session.delegationHeader.Authorization !== "string") {
|
|
5988
|
+
return { unavailable: "session_invalid" };
|
|
5989
|
+
}
|
|
5990
|
+
if (typeof session.expiresAt === "string" && Date.parse(session.expiresAt) <= Date.now()) {
|
|
5991
|
+
return { unavailable: "session_expired" };
|
|
5992
|
+
}
|
|
5993
|
+
const key = await ProfileManager.getKey(ctx.profile);
|
|
5994
|
+
try {
|
|
5995
|
+
const node = new TinyCloudNode({
|
|
5996
|
+
host: ctx.host,
|
|
5997
|
+
...profile.authMethod === "local" && profile.privateKey ? { privateKey: profile.privateKey } : {}
|
|
5998
|
+
});
|
|
5999
|
+
await restoreProfileSession(node, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
|
|
6000
|
+
return { node };
|
|
6001
|
+
} catch (error) {
|
|
6002
|
+
if (error instanceof CLIError && error.metadata?.reason === "session_expired") {
|
|
6003
|
+
return { unavailable: "session_expired" };
|
|
6004
|
+
}
|
|
6005
|
+
return { unavailable: "session_invalid" };
|
|
6006
|
+
}
|
|
6007
|
+
}
|
|
5963
6008
|
async function createSDKInstance(ctx, options) {
|
|
5964
6009
|
const profile = options?.privateKey ? await ProfileManager.getProfile(ctx.profile).catch(() => null) : await ProfileManager.getProfile(ctx.profile);
|
|
5965
6010
|
const session = await ProfileManager.getSession(ctx.profile);
|
|
@@ -5977,56 +6022,35 @@ async function createSDKInstance(ctx, options) {
|
|
|
5977
6022
|
host: ctx.host,
|
|
5978
6023
|
privateKey: effectivePrivateKey
|
|
5979
6024
|
});
|
|
5980
|
-
|
|
5981
|
-
if (
|
|
5982
|
-
await restoreProfileSession(node2, ctx.profile, profile,
|
|
5983
|
-
delegationHeader: session.delegationHeader,
|
|
5984
|
-
delegationCid: session.delegationCid,
|
|
5985
|
-
spaceId: session.spaceId,
|
|
5986
|
-
jwk: signerJwkForProfile(ctx.profile, session.jwk, key),
|
|
5987
|
-
verificationMethod: session.verificationMethod ?? profile?.sessionDid ?? profile?.did,
|
|
5988
|
-
address: session.address,
|
|
5989
|
-
chainId: session.chainId,
|
|
5990
|
-
siwe: session.siwe,
|
|
5991
|
-
signature: session.signature
|
|
5992
|
-
});
|
|
5993
|
-
restoredOwnSession2 = true;
|
|
6025
|
+
const restoredOwnSession = Boolean(session && session.delegationHeader && session.delegationCid && session.spaceId);
|
|
6026
|
+
if (restoredOwnSession) {
|
|
6027
|
+
await restoreProfileSession(node2, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
|
|
5994
6028
|
} else {
|
|
5995
6029
|
await node2.signIn();
|
|
5996
6030
|
}
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6031
|
+
if (restoredOwnSession) {
|
|
6032
|
+
recordWarnings(await replayAdditionalDelegations(node2, ctx.profile, {
|
|
6033
|
+
host: ctx.host,
|
|
6034
|
+
ownerSpace: profile.spaceId,
|
|
6035
|
+
migrate: options?.privateKey === void 0
|
|
6036
|
+
}));
|
|
6037
|
+
}
|
|
6002
6038
|
return node2;
|
|
6003
6039
|
}
|
|
6004
6040
|
const node = new TinyCloudNode({
|
|
6005
6041
|
host: ctx.host,
|
|
6006
6042
|
privateKey: options?.privateKey
|
|
6007
6043
|
});
|
|
6008
|
-
let restoredOwnSession = false;
|
|
6009
6044
|
if (options?.privateKey) {
|
|
6010
6045
|
await node.signIn();
|
|
6011
6046
|
} else if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
|
|
6012
|
-
await restoreProfileSession(node, ctx.profile, profile,
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
address: session.address,
|
|
6019
|
-
chainId: session.chainId,
|
|
6020
|
-
siwe: session.siwe,
|
|
6021
|
-
signature: session.signature
|
|
6022
|
-
});
|
|
6023
|
-
restoredOwnSession = true;
|
|
6047
|
+
await restoreProfileSession(node, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
|
|
6048
|
+
recordWarnings(await replayAdditionalDelegations(node, ctx.profile, {
|
|
6049
|
+
host: ctx.host,
|
|
6050
|
+
ownerSpace: profile?.spaceId,
|
|
6051
|
+
migrate: true
|
|
6052
|
+
}));
|
|
6024
6053
|
}
|
|
6025
|
-
await replayAdditionalDelegations(node, ctx.profile, {
|
|
6026
|
-
host: ctx.host,
|
|
6027
|
-
ownerSpace: profile?.spaceId,
|
|
6028
|
-
migrate: restoredOwnSession
|
|
6029
|
-
});
|
|
6030
6054
|
return node;
|
|
6031
6055
|
}
|
|
6032
6056
|
async function bootstrapDelegatedSession(ctx, delegation) {
|
|
@@ -6154,6 +6178,7 @@ var init_sdk = __esm({
|
|
|
6154
6178
|
init_constants();
|
|
6155
6179
|
init_permissions();
|
|
6156
6180
|
init_session_expired();
|
|
6181
|
+
init_warnings();
|
|
6157
6182
|
BOOTSTRAP_PROFILE_FIELDS = ["sessionDid", "spaceId"];
|
|
6158
6183
|
}
|
|
6159
6184
|
});
|
|
@@ -18148,6 +18173,9 @@ program.hook("preAction", async (thisCommand) => {
|
|
|
18148
18173
|
}
|
|
18149
18174
|
}
|
|
18150
18175
|
});
|
|
18176
|
+
program.hook("postAction", () => {
|
|
18177
|
+
outputWarnings([]);
|
|
18178
|
+
});
|
|
18151
18179
|
configureShareCommandServices({
|
|
18152
18180
|
targetAdapter: shareAuthority.targetAdapter,
|
|
18153
18181
|
records: shareAuthority.records,
|