@tinycloud/cli 1.3.0-beta.36 → 1.3.0-beta.37

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.
@@ -37,7 +37,7 @@ import {
37
37
  tinycloudConfigPath,
38
38
  tinycloudHomePath
39
39
  } from "@tinycloud/operations/state";
40
- var CONFIG_DIR, PROFILES_DIR, CONFIG_FILE, DEFAULT_HOST, DEFAULT_OPENKEY_HOST, DEFAULT_OPENKEY_DEVICE_API_HOST, DEFAULT_SHARE_ORIGIN, DEFAULT_PROFILE, DEFAULT_CHAIN_ID, PROFILE_COMMIT_LOCK_TIMEOUT_MS, ExitCode;
40
+ var CONFIG_DIR, PROFILES_DIR, CONFIG_FILE, DEFAULT_HOST, DEFAULT_OPENKEY_HOST, DEFAULT_OPENKEY_DEVICE_API_HOST, DEFAULT_SHARE_ORIGIN, DEFAULT_PROFILE, DEFAULT_CHAIN_ID, PROFILE_COMMIT_LOCK_TIMEOUT_MS, ExitCode, OPENKEY_SPACE_SETUP_RETRY_HINT;
41
41
  var init_constants = __esm({
42
42
  "src/config/constants.ts"() {
43
43
  "use strict";
@@ -67,6 +67,7 @@ var init_constants = __esm({
67
67
  /** Interrupted by SIGINT/SIGTERM (`CANCELLED`). */
68
68
  CANCELLED: 130
69
69
  };
70
+ OPENKEY_SPACE_SETUP_RETRY_HINT = "Wait a minute and retry; approving again in OpenKey re-runs the setup.";
70
71
  }
71
72
  });
72
73
 
@@ -9604,17 +9605,25 @@ async function isRootAuthority(spaceUri, profileName2) {
9604
9605
  const selfAddr = await resolveLocalAddress(profile, profileName2);
9605
9606
  return selfAddr !== null && selfAddr === ownerAddr;
9606
9607
  }
9608
+ async function primarySpaceUri(profileName2) {
9609
+ const session = await ProfileManager.getSession(profileName2);
9610
+ if (typeof session?.spaceId === "string" && session.spaceId !== "") return session.spaceId;
9611
+ const profile = await ProfileManager.getProfile(profileName2);
9612
+ return typeof profile.spaceId === "string" && profile.spaceId !== "" ? profile.spaceId : void 0;
9613
+ }
9607
9614
  function spaceNameFromUri(spaceUri) {
9608
9615
  return spaceUri.slice(spaceUri.lastIndexOf(":") + 1);
9609
9616
  }
9610
- async function unhostedSpaceError(error, spaceUri, profileName2) {
9611
- if (!spaceUri) return null;
9617
+ async function unhostedSpaceError(error, requestedSpaceUri, profileName2) {
9612
9618
  const status = error.meta?.status;
9613
9619
  const isUnhosted = status === 404 && /space not found/i.test(error.message);
9614
9620
  if (!isUnhosted) return null;
9621
+ const spaceUri = requestedSpaceUri ?? await primarySpaceUri(profileName2);
9622
+ if (!spaceUri) return null;
9615
9623
  const spaceName = spaceNameFromUri(spaceUri);
9616
9624
  const owner = await isRootAuthority(spaceUri, profileName2);
9617
- const hint = owner ? [
9625
+ const openKeyOwner = owner && resolveProfilePosture(await ProfileManager.getProfile(profileName2)) === "owner-openkey";
9626
+ const hint = openKeyOwner ? `OpenKey is still setting up your space. ${OPENKEY_SPACE_SETUP_RETRY_HINT}` : owner ? [
9618
9627
  "You are the owner. Host it once:",
9619
9628
  ` ${CLI_NAME8} space host ${spaceName}`,
9620
9629
  "Then retry."
@@ -11886,6 +11895,132 @@ async function loginWithDeviceAuthorization(input) {
11886
11895
  return { profile, result };
11887
11896
  }
11888
11897
 
11898
+ // src/auth/space-setup.ts
11899
+ init_constants();
11900
+ init_sdk();
11901
+ init_errors();
11902
+ var SPACE_SETUP_TIMEOUT_MS = 15e4;
11903
+ var POLL_DELAYS_MS = [2e3, 3e3, 5e3, 8e3, 13e3, 15e3];
11904
+ var PROGRESS_INTERVAL_MS = 15e3;
11905
+ var SPACE_ERROR_MAX_LENGTH = 500;
11906
+ function spaceSetupReport(delegation) {
11907
+ const rawError = typeof delegation.spaceError === "string" ? delegation.spaceError : void 0;
11908
+ const error = rawError?.replace(/[\u0000-\u001f\u007f-\u009f\s]+/g, " ").trim().slice(0, SPACE_ERROR_MAX_LENGTH);
11909
+ const ready = delegation.spaceReady;
11910
+ if (ready === true) return void 0;
11911
+ const pending = delegation.spacePending === true || ready === null;
11912
+ if (!pending && !error && ready !== false) return void 0;
11913
+ return {
11914
+ ready: ready === false || ready === null ? ready : void 0,
11915
+ pending,
11916
+ ...error ? { error } : {}
11917
+ };
11918
+ }
11919
+ async function awaitSpaceSetup(report, deps) {
11920
+ const write = deps.write ?? ((line) => {
11921
+ process.stderr.write(`${line}
11922
+ `);
11923
+ });
11924
+ const sleep = deps.sleep ?? ((ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2)));
11925
+ const now = deps.now ?? Date.now;
11926
+ const timeoutMs = deps.timeoutMs ?? SPACE_SETUP_TIMEOUT_MS;
11927
+ const started = now();
11928
+ const deadline = started + timeoutMs;
11929
+ const probe = async () => deps.probe().catch(() => "transient");
11930
+ const unreadable = () => write(
11931
+ `Warning: this login cannot read your TinyCloud space to check it${report.error ? `; OpenKey reported: ${report.error}` : report.pending ? "; OpenKey is still setting it up" : ""}. If the first storage command fails with "Space not found", OpenKey is still setting up your space. ${OPENKEY_SPACE_SETUP_RETRY_HINT}`
11932
+ );
11933
+ let result = await probe();
11934
+ if (result === "hosted") {
11935
+ if (!report.pending && report.error) write(`Warning: OpenKey reported a problem finishing your account setup: ${report.error}`);
11936
+ return;
11937
+ }
11938
+ if (result === "unreadable") {
11939
+ unreadable();
11940
+ return;
11941
+ }
11942
+ if (!report.pending) {
11943
+ if (result === "transient") {
11944
+ write(`Warning: could not check whether your TinyCloud space is hosted${report.error ? `; OpenKey reported: ${report.error}` : ""}. ${OPENKEY_SPACE_SETUP_RETRY_HINT}`);
11945
+ return;
11946
+ }
11947
+ if (report.error) {
11948
+ throw new CLIError(
11949
+ "SPACE_SETUP_FAILED",
11950
+ `OpenKey could not finish setting up your space: ${report.error} Your login is saved, but storage commands will fail with "Space not found" until the space is hosted.`,
11951
+ ExitCode.ERROR,
11952
+ { hint: "Approve again in OpenKey to re-run the setup, then retry." }
11953
+ );
11954
+ }
11955
+ write(
11956
+ 'Warning: OpenKey did not set up your TinyCloud space (spaceReady: false), so storage commands will fail with "Space not found". Turn on Auto-Sign in OpenKey and approve again, or host the space from a wallet profile.'
11957
+ );
11958
+ return;
11959
+ }
11960
+ write(
11961
+ "Setting up your space\u2026 (OpenKey is hosting it; this can take a minute or two)" + (report.error ? ` OpenKey reported: ${report.error} It is still retrying.` : "")
11962
+ );
11963
+ let lastProgress = started;
11964
+ for (let attempt = 0; ; attempt++) {
11965
+ const current = now();
11966
+ if (current >= deadline) break;
11967
+ await sleep(Math.min(POLL_DELAYS_MS[Math.min(attempt, POLL_DELAYS_MS.length - 1)], deadline - current));
11968
+ result = await probe();
11969
+ if (result === "hosted") {
11970
+ write("Your space is ready.");
11971
+ return;
11972
+ }
11973
+ if (result === "unreadable") {
11974
+ unreadable();
11975
+ return;
11976
+ }
11977
+ const elapsed = now();
11978
+ if (elapsed < deadline && elapsed - lastProgress >= PROGRESS_INTERVAL_MS) {
11979
+ lastProgress = elapsed;
11980
+ write(`Still setting up your space\u2026 (${Math.round((elapsed - started) / 1e3)}s)`);
11981
+ }
11982
+ }
11983
+ throw new CLIError(
11984
+ "SPACE_SETUP_PENDING",
11985
+ `OpenKey is still setting up your space after ${Math.round(timeoutMs / 1e3)} seconds.${report.error ? ` OpenKey's last report: ${report.error}` : ""} Your login is saved, but storage commands will fail with "Space not found" until the space is hosted.`,
11986
+ ExitCode.ERROR,
11987
+ { hint: OPENKEY_SPACE_SETUP_RETRY_HINT }
11988
+ );
11989
+ }
11990
+ function hasKvAction(permission, action) {
11991
+ return permission.actions.some((entry) => entry === action || entry === `tinycloud.kv/${action}`);
11992
+ }
11993
+ function readTarget(permissions) {
11994
+ const kv = (permissions ?? []).filter((permission) => permission.service === "tinycloud.kv" || permission.service === "kv");
11995
+ const list = kv.find((permission) => hasKvAction(permission, "list"));
11996
+ if (list) return { op: "list", prefix: list.path };
11997
+ const get = kv.find((permission) => hasKvAction(permission, "get"));
11998
+ if (!get) return void 0;
11999
+ return { op: "get", key: get.path === "" || get.path.endsWith("/") ? `${get.path}.tinycloud-space-probe` : get.path };
12000
+ }
12001
+ var UNHOSTED = /space not found/i;
12002
+ function kvSpaceProbe(ctx, permissions, restore2 = restoreProfileSessionNode) {
12003
+ const target = readTarget(permissions);
12004
+ let restored;
12005
+ return async () => {
12006
+ if (!target) return "unreadable";
12007
+ restored ??= await restore2(ctx);
12008
+ if ("unavailable" in restored) return "unreadable";
12009
+ const kv = restored.node.kv;
12010
+ const result = target.op === "list" ? await kv.list({ prefix: target.prefix, limit: 1 }) : await kv.get(target.key);
12011
+ if (result.ok) return "hosted";
12012
+ const status = result.error.meta?.status;
12013
+ if (status === 404 && UNHOSTED.test(result.error.message)) return "unhosted";
12014
+ if (status === 401 || status === 403) return "unreadable";
12015
+ return result.error.code === "KV_NOT_FOUND" || status === 404 ? "hosted" : "transient";
12016
+ };
12017
+ }
12018
+ async function ensureSpaceSetup(ctx, delegation, permissions) {
12019
+ const report = spaceSetupReport(delegation);
12020
+ if (!report) return;
12021
+ await awaitSpaceSetup(report, { probe: kvSpaceProbe(ctx, permissions) });
12022
+ }
12023
+
11889
12024
  // src/commands/auth.ts
11890
12025
  init_raw_encryption();
11891
12026
  init_owner_did();
@@ -13176,6 +13311,7 @@ function registerAuthCommand(program, options) {
13176
13311
  await persistReplicationLogin(ctx.profile, replicationOptions);
13177
13312
  if (replicationOptions.allowSecrets) writeReplicationSecretsWarning();
13178
13313
  reportDeclined(result.declined);
13314
+ await ensureSpaceSetup({ profile: ctx.profile, host: ctx.host }, result.session, result.session.permissions);
13179
13315
  outputJson({
13180
13316
  authenticated: true,
13181
13317
  profile: ctx.profile,
@@ -14518,6 +14654,7 @@ async function handleLocalAuth(profileName2, host, options = {}) {
14518
14654
  async function handleOpenKeyAuth(profileName2, host, options = {}) {
14519
14655
  const { profile, delegationData, declined, legacyNested } = await refreshOpenKeySession(profileName2, host, options);
14520
14656
  reportDeclined(declined, legacyNested);
14657
+ await ensureSpaceSetup({ profile: profileName2, host }, delegationData, delegationData.permissions);
14521
14658
  outputJson({
14522
14659
  authenticated: true,
14523
14660
  profile: profileName2,
@@ -15516,6 +15653,7 @@ function registerInitCommand(program) {
15516
15653
  noPopup: options.popup === false,
15517
15654
  persistHost: true
15518
15655
  });
15656
+ await ensureSpaceSetup({ profile: profileName2, host }, delegationData, delegationData.permissions);
15519
15657
  outputJson({
15520
15658
  profile: profileName2,
15521
15659
  did,