@tinycloud/cli 1.5.0-beta.19 → 1.5.0-beta.20

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.
@@ -5578,8 +5578,8 @@ function fetched(key, result, request) {
5578
5578
  }
5579
5579
  return { bytes: result.data.data, etag: result.data.headers.etag ?? "" };
5580
5580
  }
5581
- function kvSyncTransport(kv, options = {}) {
5582
- const host = options.host ?? kv.host;
5581
+ function kvSyncTransport(kv2, options = {}) {
5582
+ const host = options.host ?? kv2.host;
5583
5583
  const fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
5584
5584
  return {
5585
5585
  async syncPage({ prefix, cursor, limit, retentionGrant, signal }) {
@@ -5598,7 +5598,7 @@ function kvSyncTransport(kv, options = {}) {
5598
5598
  );
5599
5599
  }
5600
5600
  }
5601
- const result = await kv.changes({
5601
+ const result = await kv2.changes({
5602
5602
  prefix,
5603
5603
  limit,
5604
5604
  ...cursor === void 0 ? {} : { cursor },
@@ -5613,7 +5613,7 @@ function kvSyncTransport(kv, options = {}) {
5613
5613
  if (keys.length === 0) return contents;
5614
5614
  const signal = options2?.signal;
5615
5615
  const ordered = [...keys].sort(compareKeyBytes);
5616
- const batch = await kv.batchGet(ordered, { binary: true, ...signal === void 0 ? {} : { signal } });
5616
+ const batch = await kv2.batchGet(ordered, { binary: true, ...signal === void 0 ? {} : { signal } });
5617
5617
  if (batch.ok) {
5618
5618
  for (const item of batch.data.results) {
5619
5619
  const result = item.result;
@@ -5626,7 +5626,7 @@ function kvSyncTransport(kv, options = {}) {
5626
5626
  for (const key of keys) {
5627
5627
  if (contents.has(key)) continue;
5628
5628
  const request = `Failed to get key ${JSON.stringify(key)}`;
5629
- contents.set(key, fetched(key, await kv.get(key, { binary: true, ...signal === void 0 ? {} : { signal } }), request));
5629
+ contents.set(key, fetched(key, await kv2.get(key, { binary: true, ...signal === void 0 ? {} : { signal } }), request));
5630
5630
  }
5631
5631
  return contents;
5632
5632
  }
@@ -29144,12 +29144,12 @@ async function verifyEnvelopeV3(envelope, options) {
29144
29144
  const nativeProjection2 = sortedCapabilities.map((capability) => capability.kind === "encryption" ? { service: "tinycloud.encryption", space: capability.resource, path: capability.resource, actions: [capability.action] } : { service: "tinycloud.kv", space: capability.resource.slice(0, capability.resource.indexOf("/kv/")), path: capability.resource.split("/kv/")[1], actions: [...capability.actions], caveat: { type: "xyz.tinycloud.resource/selector", kind: capability.selector, value: capability.resource } }).sort((left, right) => canonicalize2(left).localeCompare(canonicalize2(right)));
29145
29145
  const nativeProjectionHashHex = hex(sha2562(new TextEncoder().encode(`${NATIVE_PROJECTION_V1_DOMAIN}${canonicalize2(nativeProjection2)}`)));
29146
29146
  const expectedAttenuation = Object.fromEntries(sortedCapabilities.map((capability) => capability.kind === "encryption" ? [capability.resource, { [capability.action]: [{}] }] : [capability.resource, Object.fromEntries(capability.actions.map((action) => [action, [{ kind: capability.selector, type: "xyz.tinycloud.resource/selector", value: capability.resource }]]))]));
29147
- const kv = policy.capabilityCeiling.find((capability) => capability.kind === "kv");
29147
+ const kv2 = policy.capabilityCeiling.find((capability) => capability.kind === "kv");
29148
29148
  const expectedKvActions = parsed.actions.flatMap((action) => action === "read" ? ["tinycloud.kv/get", "tinycloud.kv/metadata"] : action === "list" ? ["tinycloud.kv/list"] : ["tinycloud.kv/put"]);
29149
29149
  const marker = parsed.contentSource.kvResource.indexOf("/kv/");
29150
29150
  const resourceSpace = marker < 1 ? "" : parsed.contentSource.kvResource.slice(0, marker);
29151
29151
  const resourcePath = marker < 1 ? "" : parsed.contentSource.kvResource.slice(marker + 4);
29152
- if (parsed.shareId !== parsed.contentSource.shareId || resourceSpace !== parsed.target.spaceId || resourcePath !== parsed.resource.path.replace(/\/$/, "") || parsed.resource.kind !== parsed.contentSource.selector || kv?.kind !== "kv" || expectedKvActions.some((action) => !kv.actions.includes(action)) || parsed.encryptionNetwork !== parsed.contentSource.encryptionNetwork || parsed.contentSource.keyVersion <= 0 || policy.expiresAt !== void 0 && Date.parse(parsed.expiry) > Date.parse(policy.expiresAt)) return false;
29152
+ if (parsed.shareId !== parsed.contentSource.shareId || resourceSpace !== parsed.target.spaceId || resourcePath !== parsed.resource.path.replace(/\/$/, "") || parsed.resource.kind !== parsed.contentSource.selector || kv2?.kind !== "kv" || expectedKvActions.some((action) => !kv2.actions.includes(action)) || parsed.encryptionNetwork !== parsed.contentSource.encryptionNetwork || parsed.contentSource.keyVersion <= 0 || policy.expiresAt !== void 0 && Date.parse(parsed.expiry) > Date.parse(policy.expiresAt)) return false;
29153
29153
  const binding = parsed.attestedEnforcerBinding;
29154
29154
  const { signature: bindingSignature, ...unsignedBinding } = binding;
29155
29155
  const expectedBindingDigestHex = hex(sha2562(new TextEncoder().encode(canonicalize2({ enforcerDid: binding.enforcerDid, nodeAudience: binding.nodeAudience }))));
@@ -32525,10 +32525,10 @@ function hasKvAction(permission, action) {
32525
32525
  return permission.actions.some((entry) => entry === action || entry === `tinycloud.kv/${action}`);
32526
32526
  }
32527
32527
  function readTarget(permissions) {
32528
- const kv = (permissions ?? []).filter((permission) => permission.service === "tinycloud.kv" || permission.service === "kv");
32529
- const list = kv.find((permission) => hasKvAction(permission, "list"));
32528
+ const kv2 = (permissions ?? []).filter((permission) => permission.service === "tinycloud.kv" || permission.service === "kv");
32529
+ const list = kv2.find((permission) => hasKvAction(permission, "list"));
32530
32530
  if (list) return { op: "list", prefix: list.path };
32531
- const get = kv.find((permission) => hasKvAction(permission, "get"));
32531
+ const get = kv2.find((permission) => hasKvAction(permission, "get"));
32532
32532
  if (!get) return void 0;
32533
32533
  return { op: "get", key: get.path === "" || get.path.endsWith("/") ? `${get.path}.tinycloud-space-probe` : get.path };
32534
32534
  }
@@ -32540,8 +32540,8 @@ function kvSpaceProbe(ctx, permissions, restore2 = restoreProfileSessionNode) {
32540
32540
  if (!target) return "unreadable";
32541
32541
  restored ??= await restore2(ctx);
32542
32542
  if ("unavailable" in restored) return "unreadable";
32543
- const kv = restored.node.kv;
32544
- const result = target.op === "list" ? await kv.list({ prefix: target.prefix, limit: 1 }) : await kv.get(target.key);
32543
+ const kv2 = restored.node.kv;
32544
+ const result = target.op === "list" ? await kv2.list({ prefix: target.prefix, limit: 1 }) : await kv2.get(target.key);
32545
32545
  if (result.ok) return "hosted";
32546
32546
  const status = result.error.meta?.status;
32547
32547
  if (status === 404 && UNHOSTED.test(result.error.message)) return "unhosted";
@@ -36071,7 +36071,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
36071
36071
  // owner is verified below.
36072
36072
  permissions: permissions ?? ownerLoginPermissions(expectedOwner),
36073
36073
  expiry: openKeyExpiry,
36074
- ...permissions ? { reason: "Allow this TinyCloud CLI profile to use the permissions in the requested manifest." } : {}
36074
+ ...permissions ? { reason: options.reason ?? "Allow this TinyCloud CLI profile to use the permissions in the requested manifest." } : {}
36075
36075
  });
36076
36076
  let sanitizedSession;
36077
36077
  let verifiedOwner;
@@ -37154,7 +37154,7 @@ Examples:
37154
37154
  }
37155
37155
  });
37156
37156
  }
37157
- function appsInstallIo(kv, db) {
37157
+ function appsInstallIo(kv2, db) {
37158
37158
  return {
37159
37159
  async fetchBytes(url) {
37160
37160
  const response = await fetch(url, { redirect: "error" });
@@ -37162,7 +37162,7 @@ function appsInstallIo(kv, db) {
37162
37162
  return new Uint8Array(await response.arrayBuffer());
37163
37163
  },
37164
37164
  async kvGet(key) {
37165
- const result = await kv.get(key, { binary: true });
37165
+ const result = await kv2.get(key, { binary: true });
37166
37166
  if (result.ok) {
37167
37167
  const bytes = result.data.data;
37168
37168
  if (bytes instanceof Uint8Array) return bytes;
@@ -37172,7 +37172,7 @@ function appsInstallIo(kv, db) {
37172
37172
  throw new Error(result.error.code ?? "KV_GET_FAILED");
37173
37173
  },
37174
37174
  async kvCreate(key, bytes) {
37175
- const result = await kv.put(key, bytes, { ifNoneMatch: "*" });
37175
+ const result = await kv2.put(key, bytes, { ifNoneMatch: "*" });
37176
37176
  if (result.ok) return "created";
37177
37177
  if (result.error.code === "KV_PRECONDITION_FAILED") return "exists";
37178
37178
  throw new Error(result.error.code ?? "KV_PUT_FAILED");
@@ -37225,6 +37225,25 @@ init_formatter();
37225
37225
  init_errors();
37226
37226
  init_constants();
37227
37227
  import { CLI_NAME as CLI_NAME25 } from "@tinycloud/operations/cli-name";
37228
+ async function createKeyOnlyProfile(profileName2, host, defaultSpace) {
37229
+ await ProfileManager.ensureConfigDir();
37230
+ const { jwk, did } = await generateKey2();
37231
+ await ProfileManager.setKey(profileName2, jwk);
37232
+ await ProfileManager.setProfile(profileName2, {
37233
+ name: profileName2,
37234
+ host,
37235
+ chainId: DEFAULT_CHAIN_ID,
37236
+ spaceName: "default",
37237
+ did,
37238
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
37239
+ ...defaultSpace ? { defaultSpace } : {}
37240
+ });
37241
+ const config = await ProfileManager.getConfig();
37242
+ if (profileName2 === "default" || !await ProfileManager.profileExists(config.defaultProfile)) {
37243
+ await ProfileManager.setConfig({ ...config, defaultProfile: profileName2 });
37244
+ }
37245
+ return { did };
37246
+ }
37228
37247
  function registerInitCommand(program) {
37229
37248
  program.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) => {
37230
37249
  try {
@@ -37246,25 +37265,7 @@ function registerInitCommand(program) {
37246
37265
  ExitCode.ERROR
37247
37266
  );
37248
37267
  }
37249
- await ProfileManager.ensureConfigDir();
37250
- const { jwk, did } = await withSpinner("Generating key...", async () => {
37251
- return generateKey2();
37252
- });
37253
- await ProfileManager.setKey(profileName2, jwk);
37254
- const profileConfig = {
37255
- name: profileName2,
37256
- host,
37257
- chainId: DEFAULT_CHAIN_ID,
37258
- spaceName: "default",
37259
- did,
37260
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
37261
- ...defaultSpace ? { defaultSpace } : {}
37262
- };
37263
- await ProfileManager.setProfile(profileName2, profileConfig);
37264
- const config = await ProfileManager.getConfig();
37265
- if (profileName2 === "default" || !await ProfileManager.profileExists(config.defaultProfile)) {
37266
- await ProfileManager.setConfig({ ...config, defaultProfile: profileName2 });
37267
- }
37268
+ const { did } = await withSpinner("Generating key...", () => createKeyOnlyProfile(profileName2, host, defaultSpace));
37268
37269
  if (options.keyOnly) {
37269
37270
  outputJson({
37270
37271
  profile: profileName2,
@@ -37539,25 +37540,25 @@ async function readStdin2() {
37539
37540
  }
37540
37541
  async function kvHandle(node, spaceInput, profileName2) {
37541
37542
  const spaceUri = await resolveSpaceUri(spaceInput, profileName2);
37542
- const kv = spaceUri ? node.kvForSpace(spaceUri) : node.kv;
37543
- return { kv, spaceUri };
37543
+ const kv2 = spaceUri ? node.kvForSpace(spaceUri) : node.kv;
37544
+ return { kv: kv2, spaceUri };
37544
37545
  }
37545
37546
  function retryFields(retry) {
37546
37547
  return retry ? { attempts: retry.attempts, backoffMs: retry.backoffMs } : {};
37547
37548
  }
37548
37549
  function registerKvCommand(program) {
37549
- const kv = program.command("kv").description("Key-value store operations");
37550
- kv.command("get <key>").description("Get a value by key").option("--raw", "Output raw value (no JSON wrapping)").option("-o, --output <file>", "Write value to file").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
37550
+ const kv2 = program.command("kv").description("Key-value store operations");
37551
+ kv2.command("get <key>").description("Get a value by key").option("--raw", "Output raw value (no JSON wrapping)").option("-o, --output <file>", "Write value to file").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
37551
37552
  try {
37552
37553
  assertAddressableKey(key);
37553
37554
  const globalOpts = cmd.optsWithGlobals();
37554
37555
  const ctx = await ProfileManager.resolveContext(globalOpts);
37555
37556
  const node = await ensureAuthenticated(ctx);
37556
- const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37557
+ const { kv: kv3, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37557
37558
  const wantBytes = !!options.output || !!options.raw;
37558
37559
  const result = await withSpinner(
37559
37560
  `Getting ${key}...`,
37560
- () => kv2.get(key, wantBytes ? { binary: true } : void 0)
37561
+ () => kv3.get(key, wantBytes ? { binary: true } : void 0)
37561
37562
  );
37562
37563
  if (!result.ok) {
37563
37564
  const hosted = await unhostedSpaceError(result.error, spaceUri, ctx.profile);
@@ -37593,7 +37594,7 @@ function registerKvCommand(program) {
37593
37594
  return handleError(error);
37594
37595
  }
37595
37596
  });
37596
- kv.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").option("--if-none-match <value>", "`--if-none-match '*'`: Write only if the key doesn't exist yet; never overwrite").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, value, options, cmd) => {
37597
+ kv2.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").option("--if-none-match <value>", "`--if-none-match '*'`: Write only if the key doesn't exist yet; never overwrite").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, value, options, cmd) => {
37597
37598
  try {
37598
37599
  assertAddressableKey(key);
37599
37600
  if (options.ifNoneMatch !== void 0 && options.ifNoneMatch !== "*") {
@@ -37621,8 +37622,8 @@ function registerKvCommand(program) {
37621
37622
  putValue = value;
37622
37623
  }
37623
37624
  }
37624
- const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37625
- const result = await withSpinner(`Writing ${key}...`, () => kv2.put(
37625
+ const { kv: kv3, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37626
+ const result = await withSpinner(`Writing ${key}...`, () => kv3.put(
37626
37627
  key,
37627
37628
  putValue,
37628
37629
  options.ifNoneMatch === "*" ? { ifNoneMatch: "*" } : void 0
@@ -37638,14 +37639,14 @@ function registerKvCommand(program) {
37638
37639
  return handleError(error);
37639
37640
  }
37640
37641
  });
37641
- 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) => {
37642
+ kv2.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) => {
37642
37643
  try {
37643
37644
  assertAddressableKey(key);
37644
37645
  const globalOpts = cmd.optsWithGlobals();
37645
37646
  const ctx = await ProfileManager.resolveContext(globalOpts);
37646
37647
  const node = await ensureAuthenticated(ctx);
37647
- const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37648
- const result = await withSpinner(`Deleting ${key}...`, () => kv2.delete(key));
37648
+ const { kv: kv3, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37649
+ const result = await withSpinner(`Deleting ${key}...`, () => kv3.delete(key));
37649
37650
  if (!result.ok) {
37650
37651
  await throwKvError(result.error, spaceUri, ctx.profile);
37651
37652
  }
@@ -37654,7 +37655,7 @@ function registerKvCommand(program) {
37654
37655
  return handleError(error);
37655
37656
  }
37656
37657
  });
37657
- kv.command("list").description("List keys").option("--prefix <prefix>", "Filter by key prefix").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--limit <n>", "Keys per page, 1-1000 (the node's default otherwise)").option("--cursor <cursor>", "Continue from a previous page's nextCursor").option("--all", "Follow nextCursor until the listing is complete").action(async (options, cmd) => {
37658
+ kv2.command("list").description("List keys").option("--prefix <prefix>", "Filter by key prefix").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--limit <n>", "Keys per page, 1-1000 (the node's default otherwise)").option("--cursor <cursor>", "Continue from a previous page's nextCursor").option("--all", "Follow nextCursor until the listing is complete").action(async (options, cmd) => {
37658
37659
  try {
37659
37660
  if (options.prefix) assertAddressableKey(options.prefix);
37660
37661
  const limit = options.limit === void 0 ? void 0 : Number(options.limit);
@@ -37664,13 +37665,13 @@ function registerKvCommand(program) {
37664
37665
  const globalOpts = cmd.optsWithGlobals();
37665
37666
  const ctx = await ProfileManager.resolveContext(globalOpts);
37666
37667
  const node = await ensureAuthenticated(ctx);
37667
- const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37668
+ const { kv: kv3, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37668
37669
  const keyList = [];
37669
37670
  let cursor = options.cursor;
37670
37671
  let truncated;
37671
37672
  const seenCursors = /* @__PURE__ */ new Set();
37672
37673
  for (; ; ) {
37673
- const result = await withSpinner("Listing keys...", () => kv2.list({
37674
+ const result = await withSpinner("Listing keys...", () => kv3.list({
37674
37675
  ...options.prefix ? { prefix: options.prefix } : {},
37675
37676
  ...limit === void 0 ? {} : { limit },
37676
37677
  ...cursor === void 0 ? {} : { cursor }
@@ -37725,14 +37726,14 @@ function registerKvCommand(program) {
37725
37726
  return handleError(error);
37726
37727
  }
37727
37728
  });
37728
- kv.command("head <key>").description("Get metadata for a key (no body)").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
37729
+ kv2.command("head <key>").description("Get metadata for a key (no body)").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
37729
37730
  try {
37730
37731
  assertAddressableKey(key);
37731
37732
  const globalOpts = cmd.optsWithGlobals();
37732
37733
  const ctx = await ProfileManager.resolveContext(globalOpts);
37733
37734
  const node = await ensureAuthenticated(ctx);
37734
- const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37735
- const result = await withSpinner(`Checking ${key}...`, () => kv2.head(key));
37735
+ const { kv: kv3, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37736
+ const result = await withSpinner(`Checking ${key}...`, () => kv3.head(key));
37736
37737
  if (!result.ok) {
37737
37738
  const hosted = await unhostedSpaceError(result.error, spaceUri, ctx.profile);
37738
37739
  if (hosted) throw hosted;
@@ -38134,12 +38135,129 @@ function registerNodeCommand(program) {
38134
38135
  });
38135
38136
  }
38136
38137
 
38138
+ // src/commands/onboard.ts
38139
+ import { CLI_NAME as CLI_NAME29 } from "@tinycloud/operations/cli-name";
38140
+ init_constants();
38141
+ init_profiles();
38142
+ init_types();
38143
+ init_permissions();
38144
+ init_sdk();
38145
+ init_errors();
38146
+ init_formatter();
38147
+ var ONBOARD_EXPIRY = "30d";
38148
+ var kv = (actions) => actions.map((action) => `tinycloud.kv/${action}`);
38149
+ var ONBOARD_STEPS = [
38150
+ {
38151
+ space: "applications",
38152
+ asks: "Read and change your apps' data for 30 days.",
38153
+ permissions: [{ service: "tinycloud.kv", space: "applications", path: "", actions: kv(["get", "list", "put", "del", "metadata"]) }]
38154
+ },
38155
+ {
38156
+ space: "account",
38157
+ asks: "See which spaces and apps you have, read-only, for 30 days.",
38158
+ permissions: [
38159
+ { service: "tinycloud.kv", space: "account", path: "spaces/", actions: kv(["get", "list"]) },
38160
+ { service: "tinycloud.kv", space: "account", path: "applications/", actions: kv(["get", "list"]) }
38161
+ ]
38162
+ }
38163
+ ];
38164
+ var ApprovalNeeded = class extends Error {
38165
+ constructor(approvalUrl) {
38166
+ super("approval needed");
38167
+ this.approvalUrl = approvalUrl;
38168
+ }
38169
+ };
38170
+ function registerOnboardCommand(program) {
38171
+ program.command("onboard").description(`Set up this computer to act for its owner: a profile, the owner's OpenKey sign-in (or new account), and a starting grant for 30 days (read and change the applications space; read which spaces and apps exist). Each run does the next approval: it prints the approval link and exits ${ExitCode.PENDING}; run it again with the code OpenKey shows. Exits 0 when onboarding is complete.`).option("--code <code>", "The code OpenKey showed after the owner approved the last link").action(async (options, cmd) => {
38172
+ try {
38173
+ const globalOpts = cmd.optsWithGlobals();
38174
+ const ctx = await ProfileManager.resolveContext(globalOpts, { storedSession: "unused" });
38175
+ if (!await ProfileManager.profileExists(ctx.profile)) await createKeyOnlyProfile(ctx.profile, ctx.host);
38176
+ let profile = await ProfileManager.getProfile(ctx.profile);
38177
+ if (resolveProfilePosture(profile) !== "owner-openkey") {
38178
+ throw new CLIError(
38179
+ "PROFILE_NOT_ONBOARDABLE",
38180
+ `Profile "${ctx.profile}" doesn't sign in with OpenKey, so onboarding can't use it. Run \`${CLI_NAME29} --profile <new name> onboard\` instead.`,
38181
+ ExitCode.USAGE_ERROR
38182
+ );
38183
+ }
38184
+ let code5 = typeof options.code === "string" && options.code.trim() !== "" ? options.code.trim() : void 0;
38185
+ const acquire = async (did, flow = {}) => {
38186
+ if (code5 === void 0) throw new ApprovalNeeded(buildAuthUrl(did, flow));
38187
+ const pasted = code5;
38188
+ code5 = void 0;
38189
+ return delegationFromInput(pasted, did, flow);
38190
+ };
38191
+ for (const [index, step] of ONBOARD_STEPS.entries()) {
38192
+ const reason = `TinyCloud onboarding, step ${index + 1} of ${ONBOARD_STEPS.length}: ${step.asks}`;
38193
+ try {
38194
+ const session2 = await ProfileManager.getSession(ctx.profile);
38195
+ if (!sessionStateIsUsable(storedSessionState(session2, profile).state)) {
38196
+ const { delegationData } = await refreshOpenKeySession(ctx.profile, ctx.host, {
38197
+ permissions: step.permissions,
38198
+ expiry: ONBOARD_EXPIRY,
38199
+ reason,
38200
+ persistHost: globalOpts.host !== void 0,
38201
+ openKeyAcquisition: acquire
38202
+ });
38203
+ await ensureSpaceSetup({ profile: ctx.profile, host: ctx.host }, delegationData, delegationData.permissions);
38204
+ profile = await ProfileManager.getProfile(ctx.profile);
38205
+ continue;
38206
+ }
38207
+ await ensureDelegationAuthority({
38208
+ ctx,
38209
+ profile,
38210
+ node: await ensureAuthenticated(ctx),
38211
+ requested: await resolvePermissionSpaces(step.permissions, ctx.profile),
38212
+ expiryOption: ONBOARD_EXPIRY,
38213
+ reason,
38214
+ yes: true,
38215
+ openKeyAcquisition: acquire
38216
+ });
38217
+ } catch (error) {
38218
+ if (!(error instanceof ApprovalNeeded)) throw error;
38219
+ const next = `${CLI_NAME29} --profile ${ctx.profile} onboard --code <code>`;
38220
+ output({
38221
+ onboarded: false,
38222
+ profile: ctx.profile,
38223
+ step: index + 1,
38224
+ steps: ONBOARD_STEPS.length,
38225
+ asks: step.asks,
38226
+ approvalUrl: error.approvalUrl,
38227
+ next
38228
+ }, () => [
38229
+ `Step ${index + 1} of ${ONBOARD_STEPS.length}: ${step.asks}`,
38230
+ `Open this link, sign in or create an account, and approve:`,
38231
+ ` ${error.approvalUrl}`,
38232
+ `Then run: ${next}`
38233
+ ].join("\n"));
38234
+ process.exitCode = ExitCode.PENDING;
38235
+ return;
38236
+ }
38237
+ }
38238
+ profile = await ProfileManager.getProfile(ctx.profile);
38239
+ const session = await ProfileManager.getSession(ctx.profile);
38240
+ output({
38241
+ onboarded: true,
38242
+ profile: ctx.profile,
38243
+ ownerDid: profile.ownerDid ?? null,
38244
+ host: ctx.host,
38245
+ granted: ONBOARD_STEPS.map(({ space, asks }) => ({ space, asks })),
38246
+ expiresAt: storedSessionState(session, profile).expiresAt
38247
+ }, () => `Onboarded: profile ${ctx.profile} acts for ${profile.ownerDid ?? "the owner"}.
38248
+ ${ONBOARD_STEPS.map(({ asks }) => ` ${asks}`).join("\n")}`);
38249
+ } catch (error) {
38250
+ return handleError(error);
38251
+ }
38252
+ });
38253
+ }
38254
+
38137
38255
  // src/commands/profile.ts
38138
38256
  init_profiles();
38139
38257
  init_formatter();
38140
38258
  init_errors();
38141
38259
  init_constants();
38142
- import { CLI_NAME as CLI_NAME29 } from "@tinycloud/operations/cli-name";
38260
+ import { CLI_NAME as CLI_NAME30 } from "@tinycloud/operations/cli-name";
38143
38261
  import { createInterface as createInterface5 } from "readline";
38144
38262
  init_theme();
38145
38263
  init_types();
@@ -38285,9 +38403,9 @@ profile at command time. Precedence for every kv/sql command:
38285
38403
  explicit --space flag > profile defaultSpace > primary space.
38286
38404
 
38287
38405
  Examples:
38288
- $ ${CLI_NAME29} profile set-default-space applications
38289
- $ ${CLI_NAME29} profile set-default-space applications --profile cli-test
38290
- $ ${CLI_NAME29} profile set-default-space --unset
38406
+ $ ${CLI_NAME30} profile set-default-space applications
38407
+ $ ${CLI_NAME30} profile set-default-space applications --profile cli-test
38408
+ $ ${CLI_NAME30} profile set-default-space --unset
38291
38409
  `).action(async (name, options, cmd) => {
38292
38410
  try {
38293
38411
  const globalOpts = cmd.optsWithGlobals();
@@ -38299,7 +38417,7 @@ Examples:
38299
38417
  if (!options.unset && (name === void 0 || name === "")) {
38300
38418
  throw new CLIError(
38301
38419
  "USAGE_ERROR",
38302
- `Provide a space name (e.g. \`${CLI_NAME29} profile set-default-space applications\`) or pass --unset.`,
38420
+ `Provide a space name (e.g. \`${CLI_NAME30} profile set-default-space applications\`) or pass --unset.`,
38303
38421
  ExitCode.USAGE_ERROR
38304
38422
  );
38305
38423
  }
@@ -38358,7 +38476,7 @@ function parseOperatorType(raw) {
38358
38476
 
38359
38477
  // src/commands/replica.ts
38360
38478
  init_profile_replicas();
38361
- import { CLI_NAME as CLI_NAME30 } from "@tinycloud/operations/cli-name";
38479
+ import { CLI_NAME as CLI_NAME31 } from "@tinycloud/operations/cli-name";
38362
38480
  import { randomBytes as randomBytes7 } from "crypto";
38363
38481
  import { constants as fsConstants5 } from "fs";
38364
38482
  import { mkdir as mkdir5, open as open11, readdir as readdir6, rename as rename4, unlink as unlink5, lstat as lstat9 } from "fs/promises";
@@ -38499,11 +38617,11 @@ var EXIT_BY_CODE = {
38499
38617
  var READ_FAILURE = {
38500
38618
  absent: { code: "KEY_ABSENT", exit: ExitCode.NOT_FOUND, what: "is not in the replica (the replica covers its whole prefix)" },
38501
38619
  deleted: { code: "KEY_DELETED", exit: ExitCode.NOT_FOUND, what: "was deleted at the source" },
38502
- content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME30} replica sync` },
38620
+ content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME31} replica sync` },
38503
38621
  coverage_incomplete: {
38504
38622
  code: "COVERAGE_INCOMPLETE",
38505
38623
  exit: ExitCode.NOT_FOUND,
38506
- what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME30} replica sync`
38624
+ what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME31} replica sync`
38507
38625
  },
38508
38626
  not_covered: { code: "NOT_COVERED", exit: ExitCode.USAGE_ERROR, what: "is outside the replica's prefix" }
38509
38627
  };
@@ -38621,7 +38739,7 @@ async function existingReplicaName(profile, option) {
38621
38739
  const names = await replicaNames(profile);
38622
38740
  if (names.length === 1) return names[0];
38623
38741
  if (names.length === 0) {
38624
- throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME30} replica sync --prefix <prefix>`);
38742
+ throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME31} replica sync --prefix <prefix>`);
38625
38743
  }
38626
38744
  throw new CLIError("USAGE_ERROR", `Profile "${profile}" has several replicas (${names.join(", ")}); pass --replica <name>.`, ExitCode.USAGE_ERROR);
38627
38745
  }
@@ -38729,9 +38847,9 @@ async function storedGrants(profile) {
38729
38847
  function grantHint(space, prefix) {
38730
38848
  return [
38731
38849
  "A replica needs a device grant with get and sync on its prefix:",
38732
- ` device: ${CLI_NAME30} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
38733
- ` owner: ${CLI_NAME30} auth grant req.json > grant.json`,
38734
- ` device: ${CLI_NAME30} auth import grant.json`
38850
+ ` device: ${CLI_NAME31} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
38851
+ ` owner: ${CLI_NAME31} auth grant req.json > grant.json`,
38852
+ ` device: ${CLI_NAME31} auth import grant.json`
38735
38853
  ].join("\n");
38736
38854
  }
38737
38855
  function resolveSpace(input, prefix, grants) {
@@ -38787,9 +38905,9 @@ async function grantTransports(input) {
38787
38905
  fetch: globalThis.fetch.bind(globalThis),
38788
38906
  hosts: [input.host]
38789
38907
  });
38790
- const kv = new KVService({});
38791
- kv.initialize(context);
38792
- context.registerService("kv", kv);
38908
+ const kv2 = new KVService({});
38909
+ kv2.initialize(context);
38910
+ context.registerService("kv", kv2);
38793
38911
  context.setSession({
38794
38912
  delegationHeader: { Authorization: new TextDecoder().decode(grant.bytes) },
38795
38913
  delegationCid: grant.cid,
@@ -38797,7 +38915,7 @@ async function grantTransports(input) {
38797
38915
  verificationMethod: input.deviceDid,
38798
38916
  jwk: input.jwk
38799
38917
  });
38800
- const client = kv;
38918
+ const client = kv2;
38801
38919
  return kvSyncTransport(client);
38802
38920
  };
38803
38921
  }
@@ -38822,7 +38940,7 @@ function describeStatus(status) {
38822
38940
  formatField("Size", formatBytes(status.bytes)),
38823
38941
  formatField("Grant", status.device.delegationCid),
38824
38942
  formatField("Last error", status.lastError ? `${status.lastError.code}: ${status.lastError.message}` : null),
38825
- formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME30} replica command retries` : null)
38943
+ formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME31} replica command retries` : null)
38826
38944
  ].join("\n");
38827
38945
  }
38828
38946
  async function writeFileAtomic(path, bytes) {
@@ -38986,7 +39104,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
38986
39104
  (options, cmd) => run(async () => {
38987
39105
  const profile = await profileName(cmd);
38988
39106
  if (options.purge) {
38989
- throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${CLI_NAME30} replica reset --replica <name> --purge; flag-owned purge has no equivalent yet (TC-1309).`, ExitCode.USAGE_ERROR);
39107
+ throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${CLI_NAME31} replica reset --replica <name> --purge; flag-owned purge has no equivalent yet (TC-1309).`, ExitCode.USAGE_ERROR);
38990
39108
  }
38991
39109
  if (options.clearPending) {
38992
39110
  throw new CLIError("REPORT_READ_ONLY", "Clearing pending replica writes has no equivalent yet; see TC-1309 for a safe replacement command.", ExitCode.USAGE_ERROR);
@@ -39088,7 +39206,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
39088
39206
  if (differs !== void 0) {
39089
39207
  throw new ReplicaError(
39090
39208
  ReplicaErrorCode.CONFIG_MISMATCH,
39091
- `Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME30} replica reset --replica ${name} --purge first.`
39209
+ `Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME31} replica reset --replica ${name} --purge first.`
39092
39210
  );
39093
39211
  }
39094
39212
  }
@@ -39253,7 +39371,7 @@ init_theme();
39253
39371
  init_errors();
39254
39372
  init_warnings();
39255
39373
  init_constants();
39256
- import { CLI_NAME as CLI_NAME32 } from "@tinycloud/operations/cli-name";
39374
+ import { CLI_NAME as CLI_NAME33 } from "@tinycloud/operations/cli-name";
39257
39375
  import { readFile as readFile14 } from "fs/promises";
39258
39376
  import { join as join17 } from "path";
39259
39377
  import { constants as osConstants, homedir, hostname, userInfo } from "os";
@@ -39534,7 +39652,7 @@ init_safe_text();
39534
39652
  // src/lib/secrets-wait.ts
39535
39653
  init_constants();
39536
39654
  init_errors();
39537
- import { CLI_NAME as CLI_NAME31 } from "@tinycloud/operations/cli-name";
39655
+ import { CLI_NAME as CLI_NAME32 } from "@tinycloud/operations/cli-name";
39538
39656
  var WAIT_BACKOFF_MS = [3e3, 4500, 6750, 10125];
39539
39657
  var WAIT_STEADY_INTERVAL_MS = 15e3;
39540
39658
  var DEFAULT_WAIT_TIMEOUT = "5m";
@@ -39542,7 +39660,7 @@ var MAX_WAIT_TIMEOUT_MS = 9 * 6e4;
39542
39660
  var MAX_CONSECUTIVE_FAILURES = 5;
39543
39661
  var TIMEOUT_RE = /^(0|\d+s|\d+m)$/;
39544
39662
  var RETRYABLE_STATUS_ERRORS = { NODE_UNREACHABLE: true, NODE_ERROR: true };
39545
- var EXPLICIT_KEY_HINT = `${CLI_NAME31} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME31} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
39663
+ var EXPLICIT_KEY_HINT = `${CLI_NAME32} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME32} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
39546
39664
  function abortableSleep(ms2, signal) {
39547
39665
  return new Promise((resolve9) => {
39548
39666
  if (signal.aborted) {
@@ -39578,7 +39696,7 @@ async function runSecretsWait(request, deps) {
39578
39696
  let step = 0;
39579
39697
  const cancelled = () => new CLIError(
39580
39698
  "CANCELLED",
39581
- `Interrupted: ${CLI_NAME31} secrets wait stopped before the secrets were ready.`,
39699
+ `Interrupted: ${CLI_NAME32} secrets wait stopped before the secrets were ready.`,
39582
39700
  ExitCode.CANCELLED,
39583
39701
  progress === void 0 ? {} : { progress }
39584
39702
  );
@@ -39722,13 +39840,13 @@ function statusError(error, profile, warnings) {
39722
39840
  if (error.code === "AUTH_REQUIRED" || error.code === "SESSION_NOT_FOUND") {
39723
39841
  const recovery = recoveryOf(error, "sign_in");
39724
39842
  if (recovery !== void 0) {
39725
- const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME31} auth login, then retry.`;
39843
+ const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME32} auth login, then retry.`;
39726
39844
  return new CLIError("AUTH_REQUIRED", error.message, ExitCode.AUTH_REQUIRED, { hint, recovery, ...extra });
39727
39845
  }
39728
39846
  }
39729
39847
  if (error.code === "PROFILE_MIGRATION_REQUIRED") {
39730
39848
  const recovery = recoveryOf(error, "complete_migration");
39731
- const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME31} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
39849
+ const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME32} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
39732
39850
  return new CLIError("PROFILE_MIGRATION_REQUIRED", error.message, ExitCode.ERROR, {
39733
39851
  hint,
39734
39852
  ...recovery === void 0 ? {} : { recovery },
@@ -39920,11 +40038,11 @@ async function runSecretOperation(params) {
39920
40038
  }
39921
40039
  function secretPermissionReason(action, name) {
39922
40040
  const target = name ? ` secret "${name}"` : " secrets";
39923
- return `Allow \`${CLI_NAME32} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
40041
+ return `Allow \`${CLI_NAME33} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
39924
40042
  }
39925
40043
  function assertOwnerApprovalPossible(profileName2, profile, action, name, missing = []) {
39926
40044
  if (profile.authMethod !== "openkey" || process.stdin.isTTY || process.stderr.isTTY) return;
39927
- const command = `${CLI_NAME32} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
40045
+ const command = `${CLI_NAME33} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
39928
40046
  throw new CLIError(
39929
40047
  "PERMISSION_DENIED",
39930
40048
  action === "get" ? missing.length > 0 && missing.every((permission) => permission.service === "tinycloud.encryption" && Array.isArray(permission.actions) && permission.actions.length > 0 && permission.actions.every((capability) => capability === SECRET_DECRYPT_CAPABILITY)) ? `Profile "${profileName2}" lacks the scoped decrypt authority (${SECRET_DECRYPT_CAPABILITY}) needed to read secret "${name}", and requesting it needs an interactive browser approval.` : `Profile "${profileName2}" lacks a scoped read or decrypt grant needed to read secret "${name}", and requesting it needs an interactive browser approval.` : `Profile "${profileName2}" holds no grant for \`${command}\`, and requesting one needs an interactive browser approval.`,
@@ -39935,7 +40053,7 @@ function assertOwnerApprovalPossible(profileName2, profile, action, name, missin
39935
40053
  );
39936
40054
  }
39937
40055
  function scopedSecretLoginHint(profileName2) {
39938
- return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME32} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
40056
+ return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME33} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
39939
40057
  }
39940
40058
  async function runSecretOperationAttempt(label, operation) {
39941
40059
  try {
@@ -40069,7 +40187,7 @@ ${message}`,
40069
40187
  "AUTH_REQUIRED",
40070
40188
  "Not signed in to TinyCloud.",
40071
40189
  ExitCode.AUTH_REQUIRED,
40072
- { hint: `Sign in with: ${CLI_NAME32} --profile ${result.context.profile} auth login` }
40190
+ { hint: `Sign in with: ${CLI_NAME33} --profile ${result.context.profile} auth login` }
40073
40191
  );
40074
40192
  }
40075
40193
  if (result.error.code === "NODE_UNREACHABLE") {
@@ -40568,7 +40686,7 @@ function argvAfterSeparator(cmd) {
40568
40686
  return void 0;
40569
40687
  }
40570
40688
  function secretRunPlan(cmd, operands, maps) {
40571
- const usage3 = `${CLI_NAME32} secrets run NAME... [--map NAME=ENV_VAR] -- <command> [args...]`;
40689
+ const usage3 = `${CLI_NAME33} secrets run NAME... [--map NAME=ENV_VAR] -- <command> [args...]`;
40572
40690
  const after = argvAfterSeparator(cmd);
40573
40691
  if (after === void 0 || after.length > operands.length) {
40574
40692
  throw new CLIError("USAGE_ERROR", `Put the command after \`--\`: ${usage3}`, ExitCode.USAGE_ERROR);
@@ -40628,7 +40746,7 @@ function exitAsCommand(outcome, program) {
40628
40746
  return process.exit(128 + (osConstants.signals[outcome.signal] ?? 0));
40629
40747
  }
40630
40748
  const reason = outcome.errno === "ENOENT" ? "command not found" : outcome.errno === "EACCES" ? "permission denied" : `cannot run (${outcome.errno ?? "unknown error"})`;
40631
- process.stderr.write(`${CLI_NAME32} secrets run: ${safeInline(program)}: ${reason}
40749
+ process.stderr.write(`${CLI_NAME33} secrets run: ${safeInline(program)}: ${reason}
40632
40750
  `);
40633
40751
  return process.exit(outcome.errno === "ENOENT" ? 127 : 126);
40634
40752
  }
@@ -40693,7 +40811,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
40693
40811
  name: "Encryption network",
40694
40812
  ok: false,
40695
40813
  detail: `${networkName} not found`,
40696
- hint: `${CLI_NAME32} secrets network init ${networkName}`
40814
+ hint: `${CLI_NAME33} secrets network init ${networkName}`
40697
40815
  }
40698
40816
  ];
40699
40817
  let secret;
@@ -40739,7 +40857,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
40739
40857
  name: "Secret access",
40740
40858
  ok: false,
40741
40859
  detail: notFound ? `${resolved.permissionPaths.vault} not found` : result.error.message,
40742
- hint: notFound ? local ? `${CLI_NAME32} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME32} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
40860
+ hint: notFound ? local ? `${CLI_NAME33} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME33} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
40743
40861
  });
40744
40862
  }
40745
40863
  } else {
@@ -40978,7 +41096,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
40978
41096
  if (effectiveLocal(profile, options)) {
40979
41097
  throw new CLIError(
40980
41098
  "LOCAL_OWNER_PROFILE",
40981
- `Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME32} secrets put NAME --stdin instead of requesting them.`,
41099
+ `Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME33} secrets put NAME --stdin instead of requesting them.`,
40982
41100
  ExitCode.USAGE_ERROR
40983
41101
  );
40984
41102
  }
@@ -41101,7 +41219,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
41101
41219
 
41102
41220
  // src/commands/share.ts
41103
41221
  import { Option } from "commander";
41104
- import { CLI_NAME as CLI_NAME33 } from "@tinycloud/operations/cli-name";
41222
+ import { CLI_NAME as CLI_NAME34 } from "@tinycloud/operations/cli-name";
41105
41223
  import { ProfileLockTimeoutError as ProfileLockTimeoutError3 } from "@tinycloud/operations/state";
41106
41224
  init_errors();
41107
41225
 
@@ -42562,7 +42680,7 @@ function shareCliError(error, operation = "publish") {
42562
42680
  "SHARE_HISTORY_UNREADABLE",
42563
42681
  "sender history cannot be read with the current session key",
42564
42682
  1,
42565
- { hint: `Run \`${CLI_NAME33} share history recover\` to move the unreadable file aside. The old file is kept, not deleted.` }
42683
+ { hint: `Run \`${CLI_NAME34} share history recover\` to move the unreadable file aside. The old file is kept, not deleted.` }
42566
42684
  );
42567
42685
  }
42568
42686
  if (error instanceof SharePublishAuthorityError) {
@@ -42570,12 +42688,12 @@ function shareCliError(error, operation = "publish") {
42570
42688
  const profileName2 = "profileName" in failure ? failure.profileName : void 0;
42571
42689
  const localKey = "localKey" in failure && failure.localKey === true;
42572
42690
  const profileHint = profileName2 === void 0 ? "" : `--profile ${profileName2} `;
42573
- const loginHint = localKey ? `\`${CLI_NAME33} ${profileHint}auth login --method local\`` : `\`${CLI_NAME33} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME33} ${profileHint}enable share\`)`;
42691
+ const loginHint = localKey ? `\`${CLI_NAME34} ${profileHint}auth login --method local\`` : `\`${CLI_NAME34} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME34} ${profileHint}enable share\`)`;
42574
42692
  if (failure.kind === "caveated-session") {
42575
42693
  const holder = profileName2 === void 0 ? "this session" : `profile ${profileName2}'s session`;
42576
42694
  return new CLIError(
42577
42695
  "PERMISSION_DENIED",
42578
- `${holder} carries signed restrictions (caveats) on the authority an anyone-with-link share needs, so it cannot create the share link; nothing was shared. Approve Share publishing without restrictions on a new, dedicated profile (any unused name): \`${CLI_NAME33} init --name publisher --key-only && ${CLI_NAME33} --profile publisher enable share\``,
42696
+ `${holder} carries signed restrictions (caveats) on the authority an anyone-with-link share needs, so it cannot create the share link; nothing was shared. Approve Share publishing without restrictions on a new, dedicated profile (any unused name): \`${CLI_NAME34} init --name publisher --key-only && ${CLI_NAME34} --profile publisher enable share\``,
42579
42697
  5
42580
42698
  );
42581
42699
  }
@@ -42676,7 +42794,7 @@ function shareCliError(error, operation = "publish") {
42676
42794
  if (nodeCode === "ENOENT") return new CLIError("INVALID_ARGUMENT", "share input was not found", 2);
42677
42795
  if (nodeCode === "EISDIR") return new CLIError("INVALID_ARGUMENT", "share input must be a Markdown file", 2);
42678
42796
  const mapped = typeof nodeCode === "string" && known[nodeCode] !== void 0 ? known[nodeCode] : known[message];
42679
- const classified = mapped === void 0 ? classifyShareFailure(error, `\`${CLI_NAME33} auth login\``) : void 0;
42797
+ const classified = mapped === void 0 ? classifyShareFailure(error, `\`${CLI_NAME34} auth login\``) : void 0;
42680
42798
  if (classified !== void 0) return Object.assign(new CLIError(classified.code, classified.reason, classified.exitCode), { cause: error });
42681
42799
  if (error instanceof TypeError) return new CLIError("INVALID_ARGUMENT", "share input is invalid", 2);
42682
42800
  return new CLIError(mapped?.code ?? "INVALID_ARGUMENT", mapped ? mapped.code : "share operation failed", mapped?.exit ?? 2);
@@ -43086,7 +43204,7 @@ init_constants();
43086
43204
  init_sdk();
43087
43205
  init_host();
43088
43206
  init_theme();
43089
- import { CLI_NAME as CLI_NAME34 } from "@tinycloud/operations/cli-name";
43207
+ import { CLI_NAME as CLI_NAME35 } from "@tinycloud/operations/cli-name";
43090
43208
  import { ACCOUNT_REGISTRY_SPACE } from "@tinycloud/node-sdk";
43091
43209
  import { randomBytes as randomBytes9 } from "crypto";
43092
43210
  import { mkdir as mkdir7, writeFile as writeFile4 } from "fs/promises";
@@ -43133,7 +43251,7 @@ function registerSpaceCommand(program) {
43133
43251
  } else {
43134
43252
  const profile = await ProfileManager.getProfile(ctx.profile);
43135
43253
  const cap = "tinycloud.kv:account:spaces/:get,list";
43136
- const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${CLI_NAME34} --profile ${ctx.profile} auth request --cap ${cap} --emit` : `Grant it to this session with: ${CLI_NAME34} --profile ${ctx.profile} auth request --grant --cap ${cap}. The default OpenKey owner sign-in covers only the default space, so it includes neither.`;
43254
+ const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${CLI_NAME35} --profile ${ctx.profile} auth request --cap ${cap} --emit` : `Grant it to this session with: ${CLI_NAME35} --profile ${ctx.profile} auth request --grant --cap ${cap}. The default OpenKey owner sign-in covers only the default space, so it includes neither.`;
43137
43255
  throw new CLIError(
43138
43256
  "PERMISSION_DENIED",
43139
43257
  `Listing spaces needs tinycloud.kv/get and tinycloud.kv/list on the account space's spaces/ registry (${accountSpaceId ?? ACCOUNT_REGISTRY_SPACE}), or tinycloud.space/list. This session holds neither.`,
@@ -43170,10 +43288,10 @@ function registerSpaceCommand(program) {
43170
43288
 
43171
43289
  A delegate cannot host a space \u2014 only its owner (root authority) can. This
43172
43290
  emits a tinycloud.host.request artifact naming the space and its owner so you
43173
- can hand it to the owner; they run \`${CLI_NAME34} space host <name>\` and confirm.
43291
+ can hand it to the owner; they run \`${CLI_NAME35} space host <name>\` and confirm.
43174
43292
 
43175
43293
  If you ARE the owner of the resolved space, this refuses and tells you to host
43176
- it directly with \`${CLI_NAME34} space host <name>\` (no request needed).
43294
+ it directly with \`${CLI_NAME35} space host <name>\` (no request needed).
43177
43295
  `).action(async (name, options, cmd) => {
43178
43296
  try {
43179
43297
  const globalOpts = cmd.optsWithGlobals();
@@ -43184,7 +43302,7 @@ it directly with \`${CLI_NAME34} space host <name>\` (no request needed).
43184
43302
  if (await isRootAuthority(spaceId, ctx.profile)) {
43185
43303
  throw new CLIError(
43186
43304
  "ALREADY_ROOT_AUTHORITY",
43187
- `You are the owner of ${spaceId}. Host it directly: ${CLI_NAME34} space host ${spaceName}`,
43305
+ `You are the owner of ${spaceId}. Host it directly: ${CLI_NAME35} space host ${spaceName}`,
43188
43306
  ExitCode.USAGE_ERROR
43189
43307
  );
43190
43308
  }
@@ -43270,7 +43388,7 @@ init_sdk();
43270
43388
  init_space();
43271
43389
  init_host();
43272
43390
  init_theme();
43273
- import { CLI_NAME as CLI_NAME35 } from "@tinycloud/operations/cli-name";
43391
+ import { CLI_NAME as CLI_NAME36 } from "@tinycloud/operations/cli-name";
43274
43392
  import { writeFile as writeFile5 } from "fs/promises";
43275
43393
  import { resolve as resolve8 } from "path";
43276
43394
  async function dbHandle(node, dbName, spaceInput, profileName2) {
@@ -43294,12 +43412,12 @@ database for simple apps, or pass --db to target a named database. Pass
43294
43412
  --space to target a non-primary space (e.g. the manifest "applications" space).
43295
43413
 
43296
43414
  Common workflows:
43297
- $ ${CLI_NAME35} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
43298
- $ ${CLI_NAME35} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
43299
- $ ${CLI_NAME35} sql query "SELECT id, body FROM notes ORDER BY id"
43300
- $ ${CLI_NAME35} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
43301
- $ ${CLI_NAME35} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
43302
- $ ${CLI_NAME35} sql export --db analytics --output analytics.db
43415
+ $ ${CLI_NAME36} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
43416
+ $ ${CLI_NAME36} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
43417
+ $ ${CLI_NAME36} sql query "SELECT id, body FROM notes ORDER BY id"
43418
+ $ ${CLI_NAME36} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
43419
+ $ ${CLI_NAME36} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
43420
+ $ ${CLI_NAME36} sql export --db analytics --output analytics.db
43303
43421
 
43304
43422
  Commands:
43305
43423
  query Read rows with SELECT statements
@@ -43316,10 +43434,10 @@ Tips:
43316
43434
  sql.command("query <sql>").description("Run a read-only SELECT query").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").addHelpText("after", `
43317
43435
 
43318
43436
  Examples:
43319
- $ ${CLI_NAME35} sql query "SELECT * FROM notes ORDER BY id"
43320
- $ ${CLI_NAME35} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
43321
- $ ${CLI_NAME35} sql query "SELECT count(*) AS total FROM events" --db analytics --json
43322
- $ ${CLI_NAME35} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
43437
+ $ ${CLI_NAME36} sql query "SELECT * FROM notes ORDER BY id"
43438
+ $ ${CLI_NAME36} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
43439
+ $ ${CLI_NAME36} sql query "SELECT count(*) AS total FROM events" --db analytics --json
43440
+ $ ${CLI_NAME36} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
43323
43441
 
43324
43442
  Output:
43325
43443
  Human output is formatted as a table. Piped output or --json returns
@@ -43360,11 +43478,11 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
43360
43478
  sql.command("execute <sql>").description("Run a write or schema statement").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").addHelpText("after", `
43361
43479
 
43362
43480
  Examples:
43363
- $ ${CLI_NAME35} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
43364
- $ ${CLI_NAME35} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
43365
- $ ${CLI_NAME35} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
43366
- $ ${CLI_NAME35} sql execute "DROP TABLE old_notes" --db archive
43367
- $ ${CLI_NAME35} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
43481
+ $ ${CLI_NAME36} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
43482
+ $ ${CLI_NAME36} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
43483
+ $ ${CLI_NAME36} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
43484
+ $ ${CLI_NAME36} sql execute "DROP TABLE old_notes" --db archive
43485
+ $ ${CLI_NAME36} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
43368
43486
 
43369
43487
  Output:
43370
43488
  Returns JSON with the changed row count and last inserted row id when available.
@@ -43393,9 +43511,9 @@ Output:
43393
43511
  sql.command("export").description("Export a SQLite database as a binary .db file").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("-o, --output <file>", "Output file path", "export.db").addHelpText("after", `
43394
43512
 
43395
43513
  Examples:
43396
- $ ${CLI_NAME35} sql export
43397
- $ ${CLI_NAME35} sql export --db analytics --output analytics.db
43398
- $ ${CLI_NAME35} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
43514
+ $ ${CLI_NAME36} sql export
43515
+ $ ${CLI_NAME36} sql export --db analytics --output analytics.db
43516
+ $ ${CLI_NAME36} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
43399
43517
 
43400
43518
  Output:
43401
43519
  Writes the database file locally and returns JSON with the path and size.
@@ -43428,19 +43546,19 @@ Output:
43428
43546
  sql.command("copy").description("Copy rows between SQL databases (optionally across spaces)").requiredOption("--from-db <name>", "Source database name").requiredOption("--to-db <name>", "Destination database name").option("--from-space <name|uri>", "Source space (defaults to primary)").option("--to-space <name|uri>", "Destination space (defaults to primary)").option("--table <name...>", "Restrict copy to specific tables (repeat or comma-separated)").option("--dry-run", "Print the plan without writing", false).addHelpText("after", `
43429
43547
 
43430
43548
  Examples:
43431
- $ ${CLI_NAME35} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
43549
+ $ ${CLI_NAME36} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
43432
43550
  --to-db xyz.tinycloud.listen/conversations \\
43433
43551
  --space applications --dry-run
43434
- $ ${CLI_NAME35} sql copy --from-space applications --from-db com.foo/data \\
43552
+ $ ${CLI_NAME36} sql copy --from-space applications --from-db com.foo/data \\
43435
43553
  --to-space applications --to-db com.bar/data \\
43436
43554
  --table conversation --table participant
43437
43555
 
43438
43556
  Notes:
43439
43557
  - Refuses to run when (resolved space, db) is identical for source and destination.
43440
- - Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME35} sql execute\`)
43558
+ - Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME36} sql execute\`)
43441
43559
  to materialize the schema before copying.
43442
43560
  - One row at a time; suitable for small/medium datasets. Large copies should
43443
- use \`${CLI_NAME35} sql export\` + bulk import.
43561
+ use \`${CLI_NAME36} sql export\` + bulk import.
43444
43562
  - Authorization: the active session/delegation must cover sql/read on source
43445
43563
  AND sql/write on destination. Otherwise the relevant operation will fail.
43446
43564
  `).action(async (options, cmd) => {
@@ -43540,7 +43658,7 @@ init_errors();
43540
43658
  init_formatter();
43541
43659
  init_theme();
43542
43660
  init_permissions();
43543
- import { CLI_NAME as CLI_NAME36 } from "@tinycloud/operations/cli-name";
43661
+ import { CLI_NAME as CLI_NAME37 } from "@tinycloud/operations/cli-name";
43544
43662
  import {
43545
43663
  NodeWasmBindings as NodeWasmBindings2
43546
43664
  } from "@tinycloud/node-sdk";
@@ -43814,7 +43932,7 @@ function formatStatus(summary) {
43814
43932
  lines.push(`Default profile: ${theme.value(summary.defaultProfile)}`);
43815
43933
  lines.push("");
43816
43934
  if (summary.profiles.length === 0) {
43817
- lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME36} init`));
43935
+ lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME37} init`));
43818
43936
  return `${lines.join("\n")}
43819
43937
  `;
43820
43938
  }
@@ -44293,6 +44411,7 @@ function registerVarsCommand(program) {
44293
44411
  // src/command-registry.ts
44294
44412
  function registerTinyCloudCommands(program) {
44295
44413
  registerInitCommand(program);
44414
+ registerOnboardCommand(program);
44296
44415
  registerContextCommand(program);
44297
44416
  registerAuthCommand(program);
44298
44417
  registerEnableCommand(program);