@tinycloud/cli 1.1.0-beta.11 → 1.1.0-beta.12

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.
@@ -15117,6 +15117,34 @@ async function commitLogin(profileName, snapshot, commit) {
15117
15117
  });
15118
15118
  }
15119
15119
 
15120
+ // src/auth/owner-key.ts
15121
+ init_theme();
15122
+ function openKeyPrimaryFlag(delegation) {
15123
+ return typeof delegation.primary === "boolean" ? delegation.primary : void 0;
15124
+ }
15125
+ function withOwnerKeyPrimary(profile, primary) {
15126
+ const { ownerKeyPrimary: _previous, ...rest } = profile;
15127
+ return primary === void 0 ? rest : { ...rest, ownerKeyPrimary: primary };
15128
+ }
15129
+ function nonPrimaryKeyWarning(ownerDid) {
15130
+ const address = ownerDid.split(":")[4] ?? ownerDid;
15131
+ return `${theme.warn("Warning:")} you signed in with OpenKey key ${address} (${ownerDid}), which is not your account's primary OpenKey key. This key is a separate owner with its own spaces and data, so this profile does not see the data stored under your primary key.
15132
+ To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: tc init --name <profile>
15133
+ `;
15134
+ }
15135
+ function warnIfNotPrimaryKey(ownerDid, primary) {
15136
+ if (primary !== false || ownerDid === void 0) return;
15137
+ process.stderr.write(nonPrimaryKeyWarning(ownerDid));
15138
+ }
15139
+ function ownerLoginPermissions(owner) {
15140
+ const space = ownerSpaceId("default", owner);
15141
+ return [
15142
+ { service: "tinycloud.kv", space, path: "", actions: ["tinycloud.kv/put", "tinycloud.kv/get", "tinycloud.kv/del", "tinycloud.kv/list", "tinycloud.kv/metadata"] },
15143
+ { service: "tinycloud.sql", space, path: "", actions: ["tinycloud.sql/read", "tinycloud.sql/write", "tinycloud.sql/admin"] },
15144
+ { service: "tinycloud.capabilities", space, path: "", actions: ["tinycloud.capabilities/read"] }
15145
+ ];
15146
+ }
15147
+
15120
15148
  // src/auth/device-auth.ts
15121
15149
  var DEVICE_DELEGATION_MAX_SECONDS = 30 * 24 * 60 * 60;
15122
15150
  var DEVICE_APPROVAL_WINDOW_MAX_SECONDS = 60 * 60;
@@ -15321,7 +15349,8 @@ async function verifyApproval(input) {
15321
15349
  spaceId: delegation.spaceId,
15322
15350
  expiresAt: session.expiresAt,
15323
15351
  approved: permissionsFromTuples(signed),
15324
- declined: declinedPermissions(input.requested, session.permissions, ownerDid)
15352
+ declined: declinedPermissions(input.requested, session.permissions, ownerDid),
15353
+ ...openKeyPrimaryFlag(delegation) === void 0 ? {} : { primary: openKeyPrimaryFlag(delegation) }
15325
15354
  };
15326
15355
  }
15327
15356
  function writeApprovalPrompt(prompt) {
@@ -15498,7 +15527,7 @@ async function loginWithDeviceAuthorization(input) {
15498
15527
  openkeyHost: input.openkeyHost ?? resolveDeviceApiHost(existing),
15499
15528
  expectedOwner
15500
15529
  });
15501
- const profile = {
15530
+ const profile = withOwnerKeyPrimary({
15502
15531
  ...existing,
15503
15532
  name: input.profileName,
15504
15533
  host: input.persistHost === true || !existing?.host ? input.nodeOrigin : existing.host,
@@ -15512,7 +15541,7 @@ async function loginWithDeviceAuthorization(input) {
15512
15541
  posture: "owner-openkey",
15513
15542
  operatorType: existing?.operatorType ?? "human",
15514
15543
  authMethod: "openkey"
15515
- };
15544
+ }, result.primary);
15516
15545
  await commitLogin(input.profileName, snapshot, {
15517
15546
  key,
15518
15547
  session: result.session,
@@ -15520,6 +15549,7 @@ async function loginWithDeviceAuthorization(input) {
15520
15549
  // The signed permissions, with their caveats; `approved` is the action summary.
15521
15550
  approved: { scope: result.session.permissions, ownerDid: result.ownerDid, replaceSession: input.replaceSession === true }
15522
15551
  });
15552
+ warnIfNotPrimaryKey(result.ownerDid, result.primary);
15523
15553
  return { profile, result };
15524
15554
  }
15525
15555
 
@@ -15557,7 +15587,7 @@ async function promptAuthMethod() {
15557
15587
  }
15558
15588
  function registerAuthCommand(program) {
15559
15589
  const auth = program.command("auth").description("Authentication management");
15560
- auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Use manual paste mode instead of browser callback").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`tc share publish\``).option("--expiry <duration>", "OpenKey session lifetime, e.g. 1h or 7d (device login: at most 30d, default 30d)").option("--owner <did>", "Require this existing primary DID (did:pkh:eip155:CHAIN:ADDRESS) for scoped login; names the secrets owner for a manifest's `secrets` on a profile with no recorded owner").option("--replace-session", "Scoped or device login: replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").action(async (options, cmd) => {
15590
+ auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Use manual paste mode instead of browser callback").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`tc share publish\``).option("--expiry <duration>", "OpenKey session lifetime, e.g. 1h or 7d (device login: at most 30d, default 30d)").option("--owner <did>", "Sign in as this owner (did:pkh:eip155:CHAIN:ADDRESS): OpenKey preselects that key, e.g. one that is not the account's primary key, and an approval by any other identity is refused. With --manifest, also names the secrets owner for a manifest's `secrets` on a profile with no recorded owner").option("--replace-session", "Scoped or device login: replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").action(async (options, cmd) => {
15561
15591
  try {
15562
15592
  if (options.device && options.paste) {
15563
15593
  throw new CLIError("INVALID_ARGUMENT", "--device and --paste are mutually exclusive.", ExitCode.USAGE_ERROR);
@@ -15566,9 +15596,6 @@ function registerAuthCommand(program) {
15566
15596
  if (scoped && options.method === "local") {
15567
15597
  throw new CLIError("INVALID_ARGUMENT", "--device, --manifest, --expiry and --owner require OpenKey login.", ExitCode.USAGE_ERROR);
15568
15598
  }
15569
- if (options.owner && !options.manifest) {
15570
- throw new CLIError("INVALID_ARGUMENT", "--owner requires --manifest so the signed identity is verified.", ExitCode.USAGE_ERROR);
15571
- }
15572
15599
  if (options.device && !options.manifest) {
15573
15600
  throw new CLIError(
15574
15601
  "MANIFEST_REQUIRED",
@@ -15691,6 +15718,7 @@ function registerAuthCommand(program) {
15691
15718
  did: profile?.did ?? null,
15692
15719
  sessionDid: profile?.sessionDid ?? null,
15693
15720
  ownerDid: profile?.ownerDid ?? null,
15721
+ ownerKeyPrimary: profile?.ownerKeyPrimary ?? null,
15694
15722
  spaceId: profile?.spaceId ?? null,
15695
15723
  host: ctx.host,
15696
15724
  profile: ctx.profile,
@@ -15711,6 +15739,7 @@ function registerAuthCommand(program) {
15711
15739
  process.stdout.write(formatField("DID", profile?.did ?? null) + "\n");
15712
15740
  process.stdout.write(formatField("Session DID", profile?.sessionDid ?? null) + "\n");
15713
15741
  process.stdout.write(formatField("Owner DID", profile?.ownerDid ?? null) + "\n");
15742
+ process.stdout.write(formatField("Primary Key", profile?.ownerKeyPrimary ?? "unknown") + "\n");
15714
15743
  process.stdout.write(formatField("Address", profile?.address ?? null) + "\n");
15715
15744
  process.stdout.write(formatField("Space ID", profile?.spaceId ?? null) + "\n");
15716
15745
  process.stdout.write(formatField("Has Key", hasKey !== null) + "\n");
@@ -16783,7 +16812,9 @@ async function refreshOpenKeySession(profileName, host, options = {}) {
16783
16812
  jwk: key,
16784
16813
  host,
16785
16814
  openkeyHost: resolveOpenKeyHost(profile),
16786
- permissions,
16815
+ // A plain login with --owner names that owner's space, so OpenKey
16816
+ // preselects its key; the signed owner is verified below.
16817
+ permissions: permissions ?? (options.expectedOwner === void 0 ? void 0 : ownerLoginPermissions(expectedOwner)),
16787
16818
  expiry: openKeyExpiry,
16788
16819
  ...permissions ? { reason: "Allow this TinyCloud CLI profile to use the permissions in the requested manifest." } : {}
16789
16820
  });
@@ -16809,7 +16840,8 @@ async function refreshOpenKeySession(profileName, host, options = {}) {
16809
16840
  sanitizedSession = Object.fromEntries(Object.entries(withoutTrustFields(merged)).filter(([name]) => name !== "siwe" && name !== "signature"));
16810
16841
  }
16811
16842
  }
16812
- const updatedProfile = {
16843
+ const ownerKeyPrimary = verifiedOwner === void 0 ? void 0 : openKeyPrimaryFlag(delegationData);
16844
+ const updatedProfile = withOwnerKeyPrimary({
16813
16845
  ...profile,
16814
16846
  host: options.persistHost === true || !profile.host ? host : profile.host,
16815
16847
  sessionDid: profile.sessionDid ?? profile.did,
@@ -16818,13 +16850,14 @@ async function refreshOpenKeySession(profileName, host, options = {}) {
16818
16850
  authMethod: "openkey",
16819
16851
  ...typeof sanitizedSession.spaceId === "string" ? { spaceId: sanitizedSession.spaceId } : {},
16820
16852
  ...verifiedOwner === void 0 ? {} : { ownerDid: verifiedOwner }
16821
- };
16853
+ }, ownerKeyPrimary);
16822
16854
  await commitLogin(profileName, snapshot, {
16823
16855
  key,
16824
16856
  session: sanitizedSession,
16825
16857
  profile: updatedProfile,
16826
16858
  ...permissions && verifiedOwner ? { approved: { scope: sanitizedSession.permissions, ownerDid: verifiedOwner, replaceSession: options.replaceSession === true } } : {}
16827
16859
  });
16860
+ warnIfNotPrimaryKey(verifiedOwner, ownerKeyPrimary);
16828
16861
  return { profile: updatedProfile, delegationData: sanitizedSession, declined, legacyNested };
16829
16862
  }
16830
16863