@tinycloud/cli 0.7.0-beta.1 → 0.7.0-beta.2

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 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(response, options?.raw);
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
- let body;
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
- if (!input) return void 0;
10737
- if (input.startsWith("tinycloud:")) {
10738
- const parsed = parseSpaceUri(input);
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 --space "${input}". Use a short name ([A-Za-z0-9_-]) or a full tinycloud:... URI.`,
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(input)) {
10793
+ if (!/^[A-Za-z0-9_-]+$/.test(effective)) {
10749
10794
  throw new CLIError(
10750
10795
  "INVALID_SPACE",
10751
- `Invalid --space "${input}". Use a short name ([A-Za-z0-9_-]) or a full tinycloud:... URI.`,
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, input);
10803
+ return makePkhSpaceId(address, chainId, effective);
10760
10804
  }
10761
10805
 
10762
10806
  // src/lib/permissions.ts
@@ -12144,7 +12188,11 @@ function registerKvCommand(program2) {
12144
12188
  const ctx = await ProfileManager.resolveContext(globalOpts);
12145
12189
  const node = await ensureAuthenticated(ctx);
12146
12190
  const kv2 = await kvHandle(node, options.space, ctx.profile);
12147
- const result = await withSpinner(`Getting ${key}...`, () => kv2.get(key));
12191
+ const wantBytes = !!options.output || !!options.raw;
12192
+ const result = await withSpinner(
12193
+ `Getting ${key}...`,
12194
+ () => kv2.get(key, wantBytes ? { binary: true } : void 0)
12195
+ );
12148
12196
  if (!result.ok) {
12149
12197
  if (result.error.code === "KV_NOT_FOUND" || result.error.code === "NOT_FOUND") {
12150
12198
  throw new CLIError("NOT_FOUND", `Key "${key}" not found`, ExitCode.NOT_FOUND);
@@ -12154,14 +12202,12 @@ function registerKvCommand(program2) {
12154
12202
  const data = result.data.data;
12155
12203
  const metadata = result.data.headers ?? {};
12156
12204
  if (options.output) {
12157
- const content = typeof data === "string" ? data : JSON.stringify(data);
12158
- await writeFile3(options.output, content);
12205
+ await writeFile3(options.output, data);
12159
12206
  outputJson({ key, written: options.output });
12160
12207
  return;
12161
12208
  }
12162
12209
  if (options.raw) {
12163
- const content = typeof data === "string" ? data : JSON.stringify(data);
12164
- process.stdout.write(content);
12210
+ process.stdout.write(data);
12165
12211
  return;
12166
12212
  }
12167
12213
  if (shouldOutputJson()) {
@@ -12178,7 +12224,7 @@ function registerKvCommand(program2) {
12178
12224
  handleError(error);
12179
12225
  }
12180
12226
  });
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) => {
12227
+ 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
12228
  try {
12183
12229
  const globalOpts = cmd.optsWithGlobals();
12184
12230
  const ctx = await ProfileManager.resolveContext(globalOpts);
@@ -12202,7 +12248,8 @@ function registerKvCommand(program2) {
12202
12248
  putValue = value;
12203
12249
  }
12204
12250
  }
12205
- const result = await withSpinner(`Writing ${key}...`, () => node.kv.put(key, putValue));
12251
+ const kv2 = await kvHandle(node, options.space, ctx.profile);
12252
+ const result = await withSpinner(`Writing ${key}...`, () => kv2.put(key, putValue));
12206
12253
  if (!result.ok) {
12207
12254
  throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
12208
12255
  }
@@ -12211,12 +12258,13 @@ function registerKvCommand(program2) {
12211
12258
  handleError(error);
12212
12259
  }
12213
12260
  });
12214
- kv.command("delete <key>").description("Delete a key").action(async (key, _options, cmd) => {
12261
+ 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
12262
  try {
12216
12263
  const globalOpts = cmd.optsWithGlobals();
12217
12264
  const ctx = await ProfileManager.resolveContext(globalOpts);
12218
12265
  const node = await ensureAuthenticated(ctx);
12219
- const result = await withSpinner(`Deleting ${key}...`, () => node.kv.delete(key));
12266
+ const kv2 = await kvHandle(node, options.space, ctx.profile);
12267
+ const result = await withSpinner(`Deleting ${key}...`, () => kv2.delete(key));
12220
12268
  if (!result.ok) {
12221
12269
  throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
12222
12270
  }
@@ -12315,16 +12363,13 @@ function registerSpaceCommand(program2) {
12315
12363
  handleError(error);
12316
12364
  }
12317
12365
  });
12318
- space.command("create <name>").description("Create a new space").action(async (name, _options, cmd) => {
12366
+ space.command("create <name>").alias("host").description("Create (host) one of your owned spaces by name").action(async (name, _options, cmd) => {
12319
12367
  try {
12320
12368
  const globalOpts = cmd.optsWithGlobals();
12321
12369
  const ctx = await ProfileManager.resolveContext(globalOpts);
12322
12370
  const node = await ensureAuthenticated(ctx);
12323
- const result = await node.spaces.create(name);
12324
- if (!result.ok) {
12325
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
12326
- }
12327
- outputJson({ spaceId: result.data.id, name });
12371
+ const spaceId = await node.hostOwnedSpace(name);
12372
+ outputJson({ spaceId, name, hosted: true });
12328
12373
  } catch (error) {
12329
12374
  handleError(error);
12330
12375
  }
@@ -12776,6 +12821,7 @@ function registerProfileCommand(program2) {
12776
12821
  process.stdout.write(formatField("Posture", posture) + "\n");
12777
12822
  process.stdout.write(formatField("Operator", operatorType) + "\n");
12778
12823
  process.stdout.write(formatField("Space", p.spaceId || null) + "\n");
12824
+ process.stdout.write(formatField("Default Space", p.defaultSpace || null) + "\n");
12779
12825
  process.stdout.write(formatField("Key", hasKey) + "\n");
12780
12826
  process.stdout.write(formatField("Session", hasSession) + "\n");
12781
12827
  process.stdout.write(formatField("Created", p.createdAt) + "\n");
@@ -12796,6 +12842,46 @@ function registerProfileCommand(program2) {
12796
12842
  handleError(error);
12797
12843
  }
12798
12844
  });
12845
+ 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", `
12846
+
12847
+ The default space is a short space NAME (e.g. "applications"), resolved per
12848
+ profile at command time. Precedence for every kv/sql command:
12849
+ explicit --space flag > profile defaultSpace > primary space.
12850
+
12851
+ Examples:
12852
+ $ tc profile set-default-space applications
12853
+ $ tc profile set-default-space applications --profile cli-test
12854
+ $ tc profile set-default-space --unset
12855
+ `).action(async (name, options, cmd) => {
12856
+ try {
12857
+ const globalOpts = cmd.optsWithGlobals();
12858
+ const ctx = await ProfileManager.resolveContext({
12859
+ ...globalOpts,
12860
+ profile: options.profile ?? globalOpts.profile
12861
+ });
12862
+ const profileName = ctx.profile;
12863
+ if (!options.unset && (name === void 0 || name === "")) {
12864
+ throw new CLIError(
12865
+ "USAGE_ERROR",
12866
+ "Provide a space name (e.g. `tc profile set-default-space applications`) or pass --unset.",
12867
+ ExitCode.USAGE_ERROR
12868
+ );
12869
+ }
12870
+ if (!options.unset && !/^[A-Za-z0-9_-]+$/.test(name)) {
12871
+ throw new CLIError(
12872
+ "INVALID_SPACE",
12873
+ `Invalid space name "${name}". Use a short name ([A-Za-z0-9_-]).`,
12874
+ ExitCode.USAGE_ERROR
12875
+ );
12876
+ }
12877
+ const p = await ProfileManager.getProfile(profileName);
12878
+ const defaultSpace = options.unset ? void 0 : name;
12879
+ await ProfileManager.setProfile(profileName, { ...p, defaultSpace });
12880
+ outputJson({ profile: profileName, defaultSpace: defaultSpace ?? null, updated: true });
12881
+ } catch (error) {
12882
+ handleError(error);
12883
+ }
12884
+ });
12799
12885
  profile.command("delete <name>").description("Delete a profile").action(async (name, _options, cmd) => {
12800
12886
  try {
12801
12887
  if (isInteractive()) {