@tinycloud/cli 0.7.0-beta.1 → 0.7.0-beta.3
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 +132 -37
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -973,11 +973,19 @@ Open this URL in a browser to authenticate:
|
|
|
973
973
|
|
|
974
974
|
// src/commands/init.ts
|
|
975
975
|
function registerInitCommand(program2) {
|
|
976
|
-
program2.command("init").description("Initialize a new TinyCloud profile").option("--name <profile>", "Profile name", "default").option("--key-only", "Only generate key, skip authentication").option("--host <url>", "TinyCloud node URL").option("--paste", "Use manual paste mode for authentication").option("--no-popup", "Print the OpenKey URL without opening a browser").action(async (options, cmd) => {
|
|
976
|
+
program2.command("init").description("Initialize a new TinyCloud profile").option("--name <profile>", "Profile name", "default").option("--key-only", "Only generate key, skip authentication").option("--host <url>", "TinyCloud node URL").option("--paste", "Use manual paste mode for authentication").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--default-space <name>", "Default space used when --space is omitted (e.g. applications)").action(async (options, cmd) => {
|
|
977
977
|
try {
|
|
978
978
|
const globalOpts = cmd.optsWithGlobals();
|
|
979
979
|
const profileName = options.name;
|
|
980
980
|
const host = options.host ?? globalOpts.host ?? DEFAULT_HOST;
|
|
981
|
+
const defaultSpace = options.defaultSpace;
|
|
982
|
+
if (defaultSpace !== void 0 && !/^[A-Za-z0-9_-]+$/.test(defaultSpace)) {
|
|
983
|
+
throw new CLIError(
|
|
984
|
+
"INVALID_SPACE",
|
|
985
|
+
`Invalid --default-space "${defaultSpace}". Use a short name ([A-Za-z0-9_-]).`,
|
|
986
|
+
ExitCode.USAGE_ERROR
|
|
987
|
+
);
|
|
988
|
+
}
|
|
981
989
|
if (await ProfileManager.profileExists(profileName)) {
|
|
982
990
|
throw new CLIError(
|
|
983
991
|
"PROFILE_EXISTS",
|
|
@@ -996,7 +1004,8 @@ function registerInitCommand(program2) {
|
|
|
996
1004
|
chainId: DEFAULT_CHAIN_ID,
|
|
997
1005
|
spaceName: "default",
|
|
998
1006
|
did,
|
|
999
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1007
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1008
|
+
...defaultSpace ? { defaultSpace } : {}
|
|
1000
1009
|
};
|
|
1001
1010
|
await ProfileManager.setProfile(profileName, profileConfig);
|
|
1002
1011
|
const config = await ProfileManager.getConfig();
|
|
@@ -5781,6 +5790,38 @@ var KVService = class extends BaseService {
|
|
|
5781
5790
|
signal: this.combineSignals(signal)
|
|
5782
5791
|
});
|
|
5783
5792
|
}
|
|
5793
|
+
/**
|
|
5794
|
+
* Serialize a single put value into a fetch body.
|
|
5795
|
+
*
|
|
5796
|
+
* Binary values (Blob/ArrayBuffer/typed-array, incl. Node Buffer) are sent as
|
|
5797
|
+
* raw bytes (as a Blob) so they round-trip byte-identically — without this a
|
|
5798
|
+
* Buffer would be JSON.stringify'd into `{"type":"Buffer","data":[...]}`.
|
|
5799
|
+
* Strings are returned unchanged (preserving prior behavior); other values are
|
|
5800
|
+
* JSON-encoded. `contentType` overrides the inferred type for binary values.
|
|
5801
|
+
*/
|
|
5802
|
+
serializePutValue(value, contentType) {
|
|
5803
|
+
if (value instanceof Blob) {
|
|
5804
|
+
if (!contentType || value.type === contentType) {
|
|
5805
|
+
return value;
|
|
5806
|
+
}
|
|
5807
|
+
return new Blob([value], { type: contentType });
|
|
5808
|
+
}
|
|
5809
|
+
if (value instanceof ArrayBuffer) {
|
|
5810
|
+
return new Blob([value], {
|
|
5811
|
+
type: contentType ?? "application/octet-stream"
|
|
5812
|
+
});
|
|
5813
|
+
}
|
|
5814
|
+
if (ArrayBuffer.isView(value)) {
|
|
5815
|
+
const view = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
5816
|
+
return new Blob([view], {
|
|
5817
|
+
type: contentType ?? "application/octet-stream"
|
|
5818
|
+
});
|
|
5819
|
+
}
|
|
5820
|
+
if (typeof value === "string") {
|
|
5821
|
+
return contentType ? new Blob([value], { type: contentType }) : value;
|
|
5822
|
+
}
|
|
5823
|
+
return JSON.stringify(value);
|
|
5824
|
+
}
|
|
5784
5825
|
serializeBatchPutValue(item) {
|
|
5785
5826
|
const contentType = item.contentType;
|
|
5786
5827
|
if (item.value instanceof Blob) {
|
|
@@ -5850,10 +5891,13 @@ var KVService = class extends BaseService {
|
|
|
5850
5891
|
* @param raw - Whether to return raw text
|
|
5851
5892
|
* @returns Parsed data
|
|
5852
5893
|
*/
|
|
5853
|
-
async parseResponse(response, raw = false) {
|
|
5894
|
+
async parseResponse(response, raw = false, binary = false) {
|
|
5854
5895
|
if (!response.ok) {
|
|
5855
5896
|
return void 0;
|
|
5856
5897
|
}
|
|
5898
|
+
if (binary) {
|
|
5899
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
5900
|
+
}
|
|
5857
5901
|
if (raw) {
|
|
5858
5902
|
return await response.text();
|
|
5859
5903
|
}
|
|
@@ -5960,7 +6004,11 @@ var KVService = class extends BaseService {
|
|
|
5960
6004
|
)
|
|
5961
6005
|
);
|
|
5962
6006
|
}
|
|
5963
|
-
const data = await this.parseResponse(
|
|
6007
|
+
const data = await this.parseResponse(
|
|
6008
|
+
response,
|
|
6009
|
+
options?.raw,
|
|
6010
|
+
options?.binary
|
|
6011
|
+
);
|
|
5964
6012
|
return ok({
|
|
5965
6013
|
data,
|
|
5966
6014
|
headers: this.createResponseHeaders(response.headers)
|
|
@@ -5979,12 +6027,7 @@ var KVService = class extends BaseService {
|
|
|
5979
6027
|
return err(authRequiredError("kv"));
|
|
5980
6028
|
}
|
|
5981
6029
|
const path = this.getFullPath(key, options?.prefix);
|
|
5982
|
-
|
|
5983
|
-
if (typeof value === "string") {
|
|
5984
|
-
body = value;
|
|
5985
|
-
} else {
|
|
5986
|
-
body = JSON.stringify(value);
|
|
5987
|
-
}
|
|
6030
|
+
const body = this.serializePutValue(value, options?.contentType);
|
|
5988
6031
|
try {
|
|
5989
6032
|
const response = await this.invokeOperation(
|
|
5990
6033
|
path,
|
|
@@ -10733,30 +10776,31 @@ function resolveChainId(profile, session) {
|
|
|
10733
10776
|
return profile.chainId;
|
|
10734
10777
|
}
|
|
10735
10778
|
async function resolveSpaceUri(input, profileName) {
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10779
|
+
const profile = await ProfileManager.getProfile(profileName);
|
|
10780
|
+
const effective = input || profile.defaultSpace;
|
|
10781
|
+
if (!effective) return void 0;
|
|
10782
|
+
if (effective.startsWith("tinycloud:")) {
|
|
10783
|
+
const parsed = parseSpaceUri(effective);
|
|
10739
10784
|
if (!parsed) {
|
|
10740
10785
|
throw new CLIError(
|
|
10741
10786
|
"INVALID_SPACE",
|
|
10742
|
-
`Invalid
|
|
10787
|
+
`Invalid space "${effective}". Use a short name ([A-Za-z0-9_-]) or a full tinycloud:... URI.`,
|
|
10743
10788
|
ExitCode.USAGE_ERROR
|
|
10744
10789
|
);
|
|
10745
10790
|
}
|
|
10746
10791
|
return buildSpaceUri(parsed.owner, parsed.name);
|
|
10747
10792
|
}
|
|
10748
|
-
if (!/^[A-Za-z0-9_-]+$/.test(
|
|
10793
|
+
if (!/^[A-Za-z0-9_-]+$/.test(effective)) {
|
|
10749
10794
|
throw new CLIError(
|
|
10750
10795
|
"INVALID_SPACE",
|
|
10751
|
-
`Invalid
|
|
10796
|
+
`Invalid space "${effective}". Use a short name ([A-Za-z0-9_-]) or a full tinycloud:... URI.`,
|
|
10752
10797
|
ExitCode.USAGE_ERROR
|
|
10753
10798
|
);
|
|
10754
10799
|
}
|
|
10755
|
-
const profile = await ProfileManager.getProfile(profileName);
|
|
10756
10800
|
const session = await ProfileManager.getSession(profileName);
|
|
10757
10801
|
const address = resolveAddress(profile, session);
|
|
10758
10802
|
const chainId = resolveChainId(profile, session);
|
|
10759
|
-
return makePkhSpaceId(address, chainId,
|
|
10803
|
+
return makePkhSpaceId(address, chainId, effective);
|
|
10760
10804
|
}
|
|
10761
10805
|
|
|
10762
10806
|
// src/lib/permissions.ts
|
|
@@ -11874,9 +11918,10 @@ function groupPermissionsBySpace(permissions) {
|
|
|
11874
11918
|
rawEntries.push(permission);
|
|
11875
11919
|
continue;
|
|
11876
11920
|
}
|
|
11877
|
-
const
|
|
11921
|
+
const key = normalizeSpaceForCompare(permission.space);
|
|
11922
|
+
const group = groups.get(key) ?? [];
|
|
11878
11923
|
group.push(permission);
|
|
11879
|
-
groups.set(
|
|
11924
|
+
groups.set(key, group);
|
|
11880
11925
|
}
|
|
11881
11926
|
const grouped = Array.from(groups.values());
|
|
11882
11927
|
if (grouped.length === 0) {
|
|
@@ -11888,8 +11933,16 @@ function groupPermissionsBySpace(permissions) {
|
|
|
11888
11933
|
function isRawPermission(permission) {
|
|
11889
11934
|
return permission.service === "tinycloud.encryption" && permission.path.startsWith("urn:tinycloud:encryption:");
|
|
11890
11935
|
}
|
|
11936
|
+
function normalizeSpaceForCompare(space) {
|
|
11937
|
+
return space.replace(
|
|
11938
|
+
/(eip155:\d+:)(0x[0-9a-fA-F]{40})/,
|
|
11939
|
+
(_match, prefix, addr) => prefix + addr.toLowerCase()
|
|
11940
|
+
);
|
|
11941
|
+
}
|
|
11891
11942
|
function returnedSpaceMatchesExpected(returnedSpace, expectedSpace) {
|
|
11892
|
-
if (returnedSpace === expectedSpace)
|
|
11943
|
+
if (normalizeSpaceForCompare(returnedSpace) === normalizeSpaceForCompare(expectedSpace)) {
|
|
11944
|
+
return true;
|
|
11945
|
+
}
|
|
11893
11946
|
if (!returnedSpace.startsWith("tinycloud:")) return false;
|
|
11894
11947
|
const returnedName = returnedSpace.slice(returnedSpace.lastIndexOf(":") + 1);
|
|
11895
11948
|
return returnedName === expectedSpace;
|
|
@@ -11898,7 +11951,7 @@ function portableFromOpenKeyDelegation(data, permissions, host) {
|
|
|
11898
11951
|
const primary = permissions.find((permission) => !isRawPermission(permission)) ?? permissions[0];
|
|
11899
11952
|
const returnedSpace = String(data.spaceId ?? primary.space ?? "encryption");
|
|
11900
11953
|
const expectedSpaces = new Set(
|
|
11901
|
-
permissions.filter((permission) => !isRawPermission(permission)).map((permission) => permission.space)
|
|
11954
|
+
permissions.filter((permission) => !isRawPermission(permission)).map((permission) => normalizeSpaceForCompare(permission.space))
|
|
11902
11955
|
);
|
|
11903
11956
|
const matchesExpectedSpace = expectedSpaces.size === 1 && returnedSpaceMatchesExpected(returnedSpace, Array.from(expectedSpaces)[0]);
|
|
11904
11957
|
if (expectedSpaces.size > 0 && !matchesExpectedSpace) {
|
|
@@ -12144,7 +12197,11 @@ function registerKvCommand(program2) {
|
|
|
12144
12197
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
12145
12198
|
const node = await ensureAuthenticated(ctx);
|
|
12146
12199
|
const kv2 = await kvHandle(node, options.space, ctx.profile);
|
|
12147
|
-
const
|
|
12200
|
+
const wantBytes = !!options.output || !!options.raw;
|
|
12201
|
+
const result = await withSpinner(
|
|
12202
|
+
`Getting ${key}...`,
|
|
12203
|
+
() => kv2.get(key, wantBytes ? { binary: true } : void 0)
|
|
12204
|
+
);
|
|
12148
12205
|
if (!result.ok) {
|
|
12149
12206
|
if (result.error.code === "KV_NOT_FOUND" || result.error.code === "NOT_FOUND") {
|
|
12150
12207
|
throw new CLIError("NOT_FOUND", `Key "${key}" not found`, ExitCode.NOT_FOUND);
|
|
@@ -12154,14 +12211,12 @@ function registerKvCommand(program2) {
|
|
|
12154
12211
|
const data = result.data.data;
|
|
12155
12212
|
const metadata = result.data.headers ?? {};
|
|
12156
12213
|
if (options.output) {
|
|
12157
|
-
|
|
12158
|
-
await writeFile3(options.output, content);
|
|
12214
|
+
await writeFile3(options.output, data);
|
|
12159
12215
|
outputJson({ key, written: options.output });
|
|
12160
12216
|
return;
|
|
12161
12217
|
}
|
|
12162
12218
|
if (options.raw) {
|
|
12163
|
-
|
|
12164
|
-
process.stdout.write(content);
|
|
12219
|
+
process.stdout.write(data);
|
|
12165
12220
|
return;
|
|
12166
12221
|
}
|
|
12167
12222
|
if (shouldOutputJson()) {
|
|
@@ -12178,7 +12233,7 @@ function registerKvCommand(program2) {
|
|
|
12178
12233
|
handleError(error);
|
|
12179
12234
|
}
|
|
12180
12235
|
});
|
|
12181
|
-
kv.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").action(async (key, value, options, cmd) => {
|
|
12236
|
+
kv.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, value, options, cmd) => {
|
|
12182
12237
|
try {
|
|
12183
12238
|
const globalOpts = cmd.optsWithGlobals();
|
|
12184
12239
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
@@ -12202,7 +12257,8 @@ function registerKvCommand(program2) {
|
|
|
12202
12257
|
putValue = value;
|
|
12203
12258
|
}
|
|
12204
12259
|
}
|
|
12205
|
-
const
|
|
12260
|
+
const kv2 = await kvHandle(node, options.space, ctx.profile);
|
|
12261
|
+
const result = await withSpinner(`Writing ${key}...`, () => kv2.put(key, putValue));
|
|
12206
12262
|
if (!result.ok) {
|
|
12207
12263
|
throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
|
|
12208
12264
|
}
|
|
@@ -12211,12 +12267,13 @@ function registerKvCommand(program2) {
|
|
|
12211
12267
|
handleError(error);
|
|
12212
12268
|
}
|
|
12213
12269
|
});
|
|
12214
|
-
kv.command("delete <key>").description("Delete a key").action(async (key,
|
|
12270
|
+
kv.command("delete <key>").description("Delete a key").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
|
|
12215
12271
|
try {
|
|
12216
12272
|
const globalOpts = cmd.optsWithGlobals();
|
|
12217
12273
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
12218
12274
|
const node = await ensureAuthenticated(ctx);
|
|
12219
|
-
const
|
|
12275
|
+
const kv2 = await kvHandle(node, options.space, ctx.profile);
|
|
12276
|
+
const result = await withSpinner(`Deleting ${key}...`, () => kv2.delete(key));
|
|
12220
12277
|
if (!result.ok) {
|
|
12221
12278
|
throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
|
|
12222
12279
|
}
|
|
@@ -12315,16 +12372,13 @@ function registerSpaceCommand(program2) {
|
|
|
12315
12372
|
handleError(error);
|
|
12316
12373
|
}
|
|
12317
12374
|
});
|
|
12318
|
-
space.command("create <name>").description("Create
|
|
12375
|
+
space.command("create <name>").alias("host").description("Create (host) one of your owned spaces by name").action(async (name, _options, cmd) => {
|
|
12319
12376
|
try {
|
|
12320
12377
|
const globalOpts = cmd.optsWithGlobals();
|
|
12321
12378
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
12322
12379
|
const node = await ensureAuthenticated(ctx);
|
|
12323
|
-
const
|
|
12324
|
-
|
|
12325
|
-
throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
|
|
12326
|
-
}
|
|
12327
|
-
outputJson({ spaceId: result.data.id, name });
|
|
12380
|
+
const spaceId = await node.hostOwnedSpace(name);
|
|
12381
|
+
outputJson({ spaceId, name, hosted: true });
|
|
12328
12382
|
} catch (error) {
|
|
12329
12383
|
handleError(error);
|
|
12330
12384
|
}
|
|
@@ -12776,6 +12830,7 @@ function registerProfileCommand(program2) {
|
|
|
12776
12830
|
process.stdout.write(formatField("Posture", posture) + "\n");
|
|
12777
12831
|
process.stdout.write(formatField("Operator", operatorType) + "\n");
|
|
12778
12832
|
process.stdout.write(formatField("Space", p.spaceId || null) + "\n");
|
|
12833
|
+
process.stdout.write(formatField("Default Space", p.defaultSpace || null) + "\n");
|
|
12779
12834
|
process.stdout.write(formatField("Key", hasKey) + "\n");
|
|
12780
12835
|
process.stdout.write(formatField("Session", hasSession) + "\n");
|
|
12781
12836
|
process.stdout.write(formatField("Created", p.createdAt) + "\n");
|
|
@@ -12796,6 +12851,46 @@ function registerProfileCommand(program2) {
|
|
|
12796
12851
|
handleError(error);
|
|
12797
12852
|
}
|
|
12798
12853
|
});
|
|
12854
|
+
profile.command("set-default-space [name]").description("Set (or clear) the default space used when --space is omitted").option("--profile <name>", "Profile to modify (defaults to the active profile)").option("--unset", "Clear the default space so commands fall back to the primary space").addHelpText("after", `
|
|
12855
|
+
|
|
12856
|
+
The default space is a short space NAME (e.g. "applications"), resolved per
|
|
12857
|
+
profile at command time. Precedence for every kv/sql command:
|
|
12858
|
+
explicit --space flag > profile defaultSpace > primary space.
|
|
12859
|
+
|
|
12860
|
+
Examples:
|
|
12861
|
+
$ tc profile set-default-space applications
|
|
12862
|
+
$ tc profile set-default-space applications --profile cli-test
|
|
12863
|
+
$ tc profile set-default-space --unset
|
|
12864
|
+
`).action(async (name, options, cmd) => {
|
|
12865
|
+
try {
|
|
12866
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
12867
|
+
const ctx = await ProfileManager.resolveContext({
|
|
12868
|
+
...globalOpts,
|
|
12869
|
+
profile: options.profile ?? globalOpts.profile
|
|
12870
|
+
});
|
|
12871
|
+
const profileName = ctx.profile;
|
|
12872
|
+
if (!options.unset && (name === void 0 || name === "")) {
|
|
12873
|
+
throw new CLIError(
|
|
12874
|
+
"USAGE_ERROR",
|
|
12875
|
+
"Provide a space name (e.g. `tc profile set-default-space applications`) or pass --unset.",
|
|
12876
|
+
ExitCode.USAGE_ERROR
|
|
12877
|
+
);
|
|
12878
|
+
}
|
|
12879
|
+
if (!options.unset && !/^[A-Za-z0-9_-]+$/.test(name)) {
|
|
12880
|
+
throw new CLIError(
|
|
12881
|
+
"INVALID_SPACE",
|
|
12882
|
+
`Invalid space name "${name}". Use a short name ([A-Za-z0-9_-]).`,
|
|
12883
|
+
ExitCode.USAGE_ERROR
|
|
12884
|
+
);
|
|
12885
|
+
}
|
|
12886
|
+
const p = await ProfileManager.getProfile(profileName);
|
|
12887
|
+
const defaultSpace = options.unset ? void 0 : name;
|
|
12888
|
+
await ProfileManager.setProfile(profileName, { ...p, defaultSpace });
|
|
12889
|
+
outputJson({ profile: profileName, defaultSpace: defaultSpace ?? null, updated: true });
|
|
12890
|
+
} catch (error) {
|
|
12891
|
+
handleError(error);
|
|
12892
|
+
}
|
|
12893
|
+
});
|
|
12799
12894
|
profile.command("delete <name>").description("Delete a profile").action(async (name, _options, cmd) => {
|
|
12800
12895
|
try {
|
|
12801
12896
|
if (isInteractive()) {
|