@tinycloud/cli 1.2.0-beta.1 → 1.2.0-beta.10

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
@@ -8,6 +8,114 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/lib/replication-registry.ts
12
+ function stopForSignal(signal) {
13
+ void closeReplication().finally(() => {
14
+ process.exit(signal === "SIGINT" ? 130 : 143);
15
+ });
16
+ }
17
+ function installSignalHandlers() {
18
+ if (state.installedSignalHandlers) return;
19
+ state.installedSignalHandlers = true;
20
+ state.sigintHandler = () => stopForSignal("SIGINT");
21
+ state.sigtermHandler = () => stopForSignal("SIGTERM");
22
+ process.once("SIGINT", state.sigintHandler);
23
+ process.once("SIGTERM", state.sigtermHandler);
24
+ }
25
+ function removeSignalHandlers() {
26
+ if (!state.installedSignalHandlers) return;
27
+ state.installedSignalHandlers = false;
28
+ if (state.sigintHandler) process.removeListener("SIGINT", state.sigintHandler);
29
+ if (state.sigtermHandler) process.removeListener("SIGTERM", state.sigtermHandler);
30
+ state.sigintHandler = void 0;
31
+ state.sigtermHandler = void 0;
32
+ }
33
+ function registerReplication(profile, control) {
34
+ state.controls.set(profile, control);
35
+ state.allControls.add(control);
36
+ state.generation += 1;
37
+ installSignalHandlers();
38
+ }
39
+ function replicationForProfile(profile) {
40
+ return state.controls.get(profile);
41
+ }
42
+ function registeredReplications() {
43
+ return state.controls;
44
+ }
45
+ function closeReplication(schedule = scheduleTimeout) {
46
+ if (state.closing) return state.closing;
47
+ removeSignalHandlers();
48
+ let resolveClosing;
49
+ let rejectClosing;
50
+ const closing = new Promise((resolve2, reject) => {
51
+ resolveClosing = resolve2;
52
+ rejectClosing = reject;
53
+ });
54
+ state.closing = closing;
55
+ void (async () => {
56
+ let timeout;
57
+ const deadline = new Promise((resolve2) => {
58
+ timeout = schedule(() => resolve2(true), CLOSE_TIMEOUT_MS);
59
+ });
60
+ let timedOut = false;
61
+ const abandonQueuedControls = () => {
62
+ for (const control of state.allControls) {
63
+ void Promise.resolve().then(() => control.close()).catch(() => void 0);
64
+ }
65
+ state.controls.clear();
66
+ state.allControls.clear();
67
+ };
68
+ try {
69
+ while (!timedOut) {
70
+ while (state.allControls.size > 0) {
71
+ const generation = state.generation;
72
+ const pending = [...state.allControls];
73
+ state.controls.clear();
74
+ state.allControls.clear();
75
+ const drained = Promise.allSettled(pending.map((control) => Promise.resolve().then(() => control.close()))).then(() => false);
76
+ timedOut ||= await Promise.race([drained, deadline]);
77
+ if (timedOut) {
78
+ abandonQueuedControls();
79
+ break;
80
+ }
81
+ if (state.generation === generation && state.allControls.size === 0) break;
82
+ }
83
+ if (timedOut) break;
84
+ const turn = new Promise((resolve2) => setImmediate(() => resolve2(false)));
85
+ timedOut ||= await Promise.race([turn, deadline]);
86
+ if (timedOut) abandonQueuedControls();
87
+ else if (state.allControls.size === 0) break;
88
+ }
89
+ state.closing = void 0;
90
+ if (state.allControls.size === 0) removeSignalHandlers();
91
+ resolveClosing(timedOut);
92
+ } catch (error) {
93
+ state.closing = void 0;
94
+ if (state.allControls.size === 0) removeSignalHandlers();
95
+ rejectClosing(error);
96
+ } finally {
97
+ clearTimeout(timeout);
98
+ }
99
+ })();
100
+ return closing;
101
+ }
102
+ var registryKey, globalState, state, CLOSE_TIMEOUT_MS, scheduleTimeout;
103
+ var init_replication_registry = __esm({
104
+ "src/lib/replication-registry.ts"() {
105
+ "use strict";
106
+ registryKey = /* @__PURE__ */ Symbol.for("@tinycloud/cli/replication-registry");
107
+ globalState = globalThis;
108
+ state = globalState[registryKey] ??= {
109
+ controls: /* @__PURE__ */ new Map(),
110
+ allControls: /* @__PURE__ */ new Set(),
111
+ installedSignalHandlers: false,
112
+ generation: 0
113
+ };
114
+ CLOSE_TIMEOUT_MS = 3e3;
115
+ scheduleTimeout = (callback, delayMs) => setTimeout(callback, delayMs);
116
+ }
117
+ });
118
+
11
119
  // src/config/constants.ts
12
120
  import {
13
121
  profilesPath,
@@ -33,8 +141,12 @@ var init_constants = __esm({
33
141
  PERMISSION_DENIED: 5,
34
142
  NETWORK_ERROR: 6,
35
143
  NODE_ERROR: 7,
144
+ /** `tc secrets wait` only: the deadline passed while a secret was still missing. */
145
+ PENDING: 8,
36
146
  /** A write was refused because the owner's TinyCloud storage is full. */
37
- STORAGE_FULL: 10
147
+ STORAGE_FULL: 10,
148
+ /** Interrupted by SIGINT/SIGTERM (`CANCELLED`). */
149
+ CANCELLED: 130
38
150
  };
39
151
  }
40
152
  });
@@ -119,7 +231,7 @@ function warningLine(warnings) {
119
231
  return `${subject} not used (${which}). Approve access again when a command needs it; remove the old grant with tc auth grants remove <cid>.`;
120
232
  }
121
233
  function outputError(code3, message, hint, details = {}) {
122
- const { meta, warnings = [] } = details;
234
+ const { meta, warnings = [], setup, recovery, progress } = details;
123
235
  if (!shouldOutputJson()) {
124
236
  process.stderr.write(
125
237
  `${theme.error("\u2717")} ${theme.label(code3)}: ${message}
@@ -138,6 +250,9 @@ function outputError(code3, message, hint, details = {}) {
138
250
  error: { code: code3, message }
139
251
  };
140
252
  if (hint) payload.error.hint = hint;
253
+ if (recovery) payload.error.recovery = recovery;
254
+ if (setup) payload.error.setup = setup;
255
+ if (progress) payload.error.progress = progress;
141
256
  if (meta) payload.error.meta = meta;
142
257
  if (warnings.length > 0) payload.error.warnings = warnings;
143
258
  process.stderr.write(JSON.stringify(payload, null, 2) + "\n");
@@ -267,6 +382,13 @@ var init_storage = __esm({
267
382
  import { readFileSync, readdirSync } from "fs";
268
383
  import { join } from "path";
269
384
  import { ProfileDeletedError, ProfileLockTimeoutError } from "@tinycloud/operations/state";
385
+ import { isSafeRecovery } from "@tinycloud/operations/recovery";
386
+ import {
387
+ buildSecretRequestUrl,
388
+ isSafeRequestAccessRecovery,
389
+ isValidSecretName,
390
+ SECRET_REQUEST_MAX_NAMES
391
+ } from "@tinycloud/operations/secret-request";
270
392
  import { authorizationVerdictOf, parseCapabilityResource, SERVICE_LONG_TO_SHORT, validatedCapabilityOf } from "@tinycloud/sdk-core";
271
393
  function setActiveProfileName(name) {
272
394
  activeProfileName = name;
@@ -342,11 +464,64 @@ function handleError(error) {
342
464
  meta.resource = capability.resource;
343
465
  meta.requiredAction = capability.requiredAction;
344
466
  }
467
+ const deniedNames = cliError.metadata?.deniedNames;
468
+ const recovery = safeCliRecovery(cliError.metadata?.recovery, {
469
+ deniedNames: Array.isArray(deniedNames) && deniedNames.every((name) => typeof name === "string") ? deniedNames : []
470
+ });
471
+ const setup = cliError.code === "NOT_FOUND" || cliError.code === "PERMISSION_DENIED" ? safeCliSetup(cliError.metadata?.setup) : void 0;
472
+ const progress = cliError.code === "NETWORK_ERROR" || cliError.code === "CANCELLED" ? safeCliProgress(cliError.metadata?.progress) : void 0;
345
473
  outputError(cliError.code, cliError.message, hint, {
474
+ ...recovery ? { recovery } : {},
475
+ ...setup ? { setup } : {},
476
+ ...progress ? { progress } : {},
346
477
  ...Object.keys(meta).length ? { meta } : {},
347
478
  warnings: mergeWarnings(drainWarnings(), operationWarnings(cliError.metadata?.warnings))
348
479
  });
349
- process.exit(cliError.exitCode);
480
+ if (registeredReplications().size === 0) return process.exit(cliError.exitCode);
481
+ return closeReplication().then(() => process.exit(cliError.exitCode));
482
+ }
483
+ function safeCliRecovery(value, context) {
484
+ if (typeof value !== "object" || value === null || Array.isArray(value) || !("action" in value)) return void 0;
485
+ const action = value.action;
486
+ switch (action) {
487
+ case "sign_in":
488
+ case "complete_migration":
489
+ return isSafeRecovery(value, action) ? { ...value } : void 0;
490
+ case "request_access":
491
+ return isSafeRequestAccessRecovery(value, context.deniedNames) ? { ...value, names: [...value.names] } : void 0;
492
+ default:
493
+ return void 0;
494
+ }
495
+ }
496
+ function safeCliSetup(value) {
497
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
498
+ const setup = { ...value };
499
+ if (!Object.keys(setup).every((key) => SETUP_KEYS[key] === true)) return void 0;
500
+ const { url, names, scope, owner, action } = setup;
501
+ if (action !== "add_secret" || typeof url !== "string" || typeof owner !== "string") return void 0;
502
+ if (!Array.isArray(names) || !names.every((name) => typeof name === "string")) return void 0;
503
+ if (scope !== void 0 && typeof scope !== "string") return void 0;
504
+ try {
505
+ const link2 = buildSecretRequestUrl({ owner, names, ...scope !== void 0 ? { scope } : {} });
506
+ if (link2.url !== url || link2.owner !== owner || link2.names.length !== names.length) return void 0;
507
+ } catch {
508
+ return void 0;
509
+ }
510
+ return { url, names: [...names], ...scope !== void 0 ? { scope } : {}, owner, action };
511
+ }
512
+ function safeNameList(value) {
513
+ if (!Array.isArray(value) || value.length > SECRET_REQUEST_MAX_NAMES) return void 0;
514
+ if (!value.every((name) => typeof name === "string" && isValidSecretName(name))) return void 0;
515
+ if (new Set(value).size !== value.length) return void 0;
516
+ return [...value];
517
+ }
518
+ function safeCliProgress(value) {
519
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
520
+ if (!("present" in value) || !("pending" in value) || Object.keys(value).length !== 2) return void 0;
521
+ const present = safeNameList(value.present);
522
+ const pending = safeNameList(value.pending);
523
+ if (!present || !pending || present.some((name) => pending.includes(name))) return void 0;
524
+ return { present, pending };
350
525
  }
351
526
  function buildAuthHint(error) {
352
527
  const capability = validatedCapabilityOf({ meta: error.metadata });
@@ -409,10 +584,11 @@ function buildNetworkHint() {
409
584
  lines.push(`Or override per-command with --host or TC_HOST.`);
410
585
  return lines.join("\n");
411
586
  }
412
- var activeProfileName, CLIError;
587
+ var activeProfileName, CLIError, SETUP_KEYS;
413
588
  var init_errors = __esm({
414
589
  "src/output/errors.ts"() {
415
590
  "use strict";
591
+ init_replication_registry();
416
592
  init_constants();
417
593
  init_formatter();
418
594
  init_storage();
@@ -428,6 +604,7 @@ var init_errors = __esm({
428
604
  }
429
605
  status;
430
606
  };
607
+ SETUP_KEYS = { url: true, names: true, scope: true, owner: true, action: true };
431
608
  }
432
609
  });
433
610
 
@@ -900,11 +1077,11 @@ var init_md = __esm({
900
1077
  if (len % 4)
901
1078
  throw new Error("_sha2: outputLen should be aligned to 32bit");
902
1079
  const outLen = len / 4;
903
- const state = this.get();
904
- if (outLen > state.length)
1080
+ const state2 = this.get();
1081
+ if (outLen > state2.length)
905
1082
  throw new Error("_sha2: outputLen bigger than state");
906
1083
  for (let i = 0; i < outLen; i++)
907
- oview.setUint32(4 * i, state[i], isLE2);
1084
+ oview.setUint32(4 * i, state2[i], isLE2);
908
1085
  }
909
1086
  digest() {
910
1087
  const { buffer, outputLen } = this;
@@ -5464,6 +5641,8 @@ import {
5464
5641
  buildPermissionRequestArtifact,
5465
5642
  isPermissionRequestArtifact
5466
5643
  } from "@tinycloud/operations/artifacts";
5644
+ import { isLocalOwnerProfile } from "@tinycloud/operations/profile";
5645
+ import { resolveSecretsOwner } from "@tinycloud/operations/secret-request";
5467
5646
  import {
5468
5647
  additionalDelegationsPath as sharedAdditionalDelegationsPath,
5469
5648
  authRequestsPath as sharedAuthRequestsPath,
@@ -5501,14 +5680,17 @@ var init_permissions = __esm({
5501
5680
  });
5502
5681
 
5503
5682
  // src/auth/session-expired.ts
5683
+ import { signInRecovery } from "@tinycloud/operations/recovery";
5504
5684
  function signInAgainHint(profileName, posture) {
5505
- if (posture === "local-owner-key") {
5506
- return `Sign in again with: tc --profile ${profileName} auth login --method local`;
5507
- }
5508
- if (posture === "delegate-session") {
5509
- return `Have the owner approve a new scoped login: tc --profile ${profileName} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
5685
+ const delegate = posture === "delegate-session";
5686
+ const { command } = signInRecovery(
5687
+ profileName,
5688
+ delegate ? "delegate-session" : posture === "local-owner-key" ? "local-owner-key" : "owner-openkey"
5689
+ );
5690
+ if (command === void 0) {
5691
+ return delegate ? `Have the owner approve a new scoped login for profile ${JSON.stringify(profileName)} with tc auth login --method openkey --paste --manifest <manifest.json>.` : `Sign in again to profile ${JSON.stringify(profileName)} with tc auth login.`;
5510
5692
  }
5511
- return `Sign in again with: tc --profile ${profileName} auth login --method openkey`;
5693
+ return delegate ? `Have the owner approve a new scoped login: ${command}. Pass the owner's code on stdin, newline-terminated.` : `Sign in again with: ${command}`;
5512
5694
  }
5513
5695
  function sessionExpiredError(profileName, posture) {
5514
5696
  return new CLIError(
@@ -5543,1019 +5725,643 @@ var init_delegation_expiry = __esm({
5543
5725
  }
5544
5726
  });
5545
5727
 
5546
- // src/lib/sdk.ts
5547
- var sdk_exports = {};
5548
- __export(sdk_exports, {
5549
- bootstrapDelegatedSession: () => bootstrapDelegatedSession,
5550
- createSDKInstance: () => createSDKInstance,
5551
- ensureAuthenticated: () => ensureAuthenticated,
5552
- jwkHasPrivateParameter: () => jwkHasPrivateParameter,
5553
- restoreProfileSessionNode: () => restoreProfileSessionNode,
5554
- selectSignerJwk: () => selectSignerJwk,
5555
- storedSessionVerifiesLocally: () => storedSessionVerifiesLocally
5556
- });
5557
- import { TinyCloudNode, signedCompactUcanExpiry } from "@tinycloud/node-sdk";
5558
- function jwkHasPrivateParameter(jwk) {
5559
- if (!jwk || typeof jwk !== "object") return false;
5560
- const d = jwk.d;
5561
- return typeof d === "string" && d.length > 0;
5562
- }
5563
- function selectSignerJwk(sessionJwk, key) {
5564
- if (jwkHasPrivateParameter(sessionJwk)) {
5565
- return sessionJwk;
5566
- }
5567
- return key ?? void 0;
5568
- }
5569
- function signerJwkForProfile(profileName, sessionJwk, key) {
5570
- const jwk = selectSignerJwk(sessionJwk, key);
5571
- if (jwkHasPrivateParameter(jwk)) {
5572
- return jwk;
5573
- }
5574
- throw new CLIError(
5575
- "AUTH_REQUIRED",
5576
- `Profile "${profileName}" cannot restore its session because its private key material is missing.`,
5577
- ExitCode.AUTH_REQUIRED,
5578
- {
5579
- hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey`
5580
- }
5581
- );
5582
- }
5583
- function signedLifetimePassed(delegation) {
5584
- const authorization = delegation.delegationHeader?.Authorization;
5585
- const expiry = typeof authorization === "string" ? signedCompactUcanExpiry(authorization) : void 0;
5586
- return expiry !== void 0 && expiry.getTime() <= Date.now();
5587
- }
5588
- async function restoreProfileSession(node, profileName, profile, sessionData) {
5728
+ // src/lib/replication-log.ts
5729
+ import { appendFileSync, chmodSync, mkdirSync, renameSync, statSync, writeFileSync } from "fs";
5730
+ import { dirname as dirname2, join as join4 } from "path";
5731
+ function appendReplicationEvent(root, event) {
5732
+ mkdirSync(root, { recursive: true, mode: DIR_MODE });
5733
+ chmodSync(root, DIR_MODE);
5734
+ const path = join4(root, "events.jsonl");
5735
+ let size = 0;
5589
5736
  try {
5590
- await node.restoreSession(sessionData);
5737
+ size = statSync(path).size;
5591
5738
  } catch (error) {
5592
- if (typeof error === "object" && error !== null && "code" in error && error.code === "AUTH_EXPIRED") {
5593
- throw sessionExpiredError(profileName, profile === null ? void 0 : resolveProfilePosture(profile));
5594
- }
5595
- throw error;
5739
+ if (error.code !== "ENOENT") throw error;
5596
5740
  }
5597
- }
5598
- function persistedSessionData(profileName, profile, session, key) {
5599
- return {
5600
- delegationHeader: session.delegationHeader,
5601
- delegationCid: session.delegationCid,
5602
- spaceId: session.spaceId,
5603
- jwk: signerJwkForProfile(profileName, session.jwk, key),
5604
- verificationMethod: session.verificationMethod ?? profile.sessionDid ?? profile.did,
5605
- address: session.address,
5606
- chainId: session.chainId,
5607
- siwe: session.siwe,
5608
- signature: session.signature
5741
+ const line = `${JSON.stringify(event)}
5742
+ `;
5743
+ if (size + Buffer.byteLength(line) > MAX_LOG_BYTES) {
5744
+ const rotated = join4(root, "events.jsonl.1");
5745
+ try {
5746
+ renameSync(path, rotated);
5747
+ } catch (error) {
5748
+ if (error.code !== "ENOENT") throw error;
5749
+ }
5750
+ writeFileSync(path, line, { mode: LOG_MODE });
5751
+ } else {
5752
+ appendFileSync(path, line, { mode: LOG_MODE });
5753
+ }
5754
+ chmodSync(path, LOG_MODE);
5755
+ }
5756
+ function replicationLogPath(profileRoot) {
5757
+ return join4(profileRoot, "replication", "events.jsonl");
5758
+ }
5759
+ function createReplicationEventSink(profileRoot, options) {
5760
+ const pinnedNotices = /* @__PURE__ */ new Set();
5761
+ const missingGrantWarnings = /* @__PURE__ */ new Set();
5762
+ return (event) => {
5763
+ appendProfileReplicationEvent(profileRoot, event);
5764
+ if (event.type === "replication.state" && event.state === "grant_missing") {
5765
+ const prefix = event.replica ?? event.space ?? "configured prefix";
5766
+ const key = `${event.space ?? ""}\0${event.replica ?? ""}`;
5767
+ if (!missingGrantWarnings.has(key)) {
5768
+ missingGrantWarnings.add(key);
5769
+ process.stderr.write(`[replication] Warning: no usable kv/get+sync grant for ${prefix}; reads use the network (${event.code ?? "grant_missing"}).
5770
+ `);
5771
+ }
5772
+ }
5773
+ if (options.debug) {
5774
+ if (event.type === "replication.read") {
5775
+ const detail = event.source === "replica" ? "replica hit" : `${event.source} ${event.reason}`;
5776
+ const syncStatus = event.syncedBeforeRead === void 0 ? "" : ` syncedBeforeRead:${event.syncedBeforeRead}`;
5777
+ const syncError = event.syncError === void 0 ? "" : ` syncError:${event.syncError}`;
5778
+ process.stderr.write(`[replication] ${event.op} ${event.key} \u2190 ${detail} ${event.latencyMs}ms${syncStatus}${syncError}${event.stalenessMs === null ? "" : ` (synced ${Math.round(event.stalenessMs / 1e3)}s ago)`}
5779
+ `);
5780
+ } else if (event.type === "replication.write") {
5781
+ process.stderr.write(`[replication] ${event.op} ${event.keys.join(",")} ${event.outcome}${event.code ? ` ${event.code}` : ""}
5782
+ `);
5783
+ } else if (event.type === "replication.sync") {
5784
+ const pending = event.pendingCleared === void 0 ? "" : ` pendingCleared:${event.pendingCleared}`;
5785
+ process.stderr.write(`[replication] sync ${event.replica} ${event.outcome}${event.code ? ` ${event.code}` : ""}${pending}
5786
+ `);
5787
+ }
5788
+ }
5789
+ if (event.type === "replication.read" && (event.pendingState === "in_flight" || event.pendingState === "ambiguous") && !options.quiet && !pinnedNotices.has(event.key)) {
5790
+ pinnedNotices.add(event.key);
5791
+ process.stderr.write(`[replication] ${event.key} reads from the network: an earlier write's outcome is unknown (${event.pendingState}${event.code ? `, ${event.code}` : ""}). Review: tc replica report
5792
+ `);
5793
+ }
5609
5794
  };
5610
5795
  }
5611
- async function restoreProfileSessionNode(ctx) {
5612
- const profile = await ProfileManager.getProfile(ctx.profile);
5613
- const session = await ProfileManager.getSession(ctx.profile);
5614
- if (!session) return { unavailable: "session_missing" };
5615
- if (typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || session.delegationHeader === null || typeof session.delegationHeader !== "object" || !("Authorization" in session.delegationHeader) || typeof session.delegationHeader.Authorization !== "string") {
5616
- return { unavailable: "session_invalid" };
5617
- }
5618
- if (typeof session.expiresAt === "string" && Date.parse(session.expiresAt) <= Date.now()) {
5619
- return { unavailable: "session_expired" };
5620
- }
5621
- const key = await ProfileManager.getKey(ctx.profile);
5622
- try {
5623
- const node = new TinyCloudNode({
5624
- host: ctx.host,
5625
- ...profile.authMethod === "local" && profile.privateKey ? { privateKey: profile.privateKey } : {}
5626
- });
5627
- await restoreProfileSession(node, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
5628
- return { node };
5629
- } catch (error) {
5630
- if (error instanceof CLIError && error.metadata?.reason === "session_expired") {
5631
- return { unavailable: "session_expired" };
5632
- }
5633
- return { unavailable: "session_invalid" };
5634
- }
5796
+ function appendProfileReplicationEvent(profileRoot, event) {
5797
+ appendReplicationEvent(dirname2(replicationLogPath(profileRoot)), event);
5635
5798
  }
5636
- async function storedSessionVerifiesLocally(profileName) {
5637
- const profile = await ProfileManager.getProfile(profileName).catch(() => null);
5638
- if (profile === null) return true;
5639
- const local = profile.authMethod === "local" && typeof profile.privateKey === "string";
5640
- try {
5641
- const session = await ProfileManager.getSession(profileName);
5642
- if (!session || typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || session.delegationHeader === null || typeof session.delegationHeader !== "object" || !("Authorization" in session.delegationHeader) || typeof session.delegationHeader.Authorization !== "string") return true;
5643
- const key = await ProfileManager.getKey(profileName);
5644
- const node = new TinyCloudNode({
5645
- host: profile.host ?? DEFAULT_HOST,
5646
- ...local ? { privateKey: profile.privateKey } : {}
5647
- });
5648
- await restoreProfileSession(node, profileName, profile, persistedSessionData(profileName, profile, session, key));
5649
- return true;
5650
- } catch {
5651
- return false;
5799
+ var MAX_LOG_BYTES, LOG_MODE, DIR_MODE;
5800
+ var init_replication_log = __esm({
5801
+ "src/lib/replication-log.ts"() {
5802
+ "use strict";
5803
+ MAX_LOG_BYTES = 5 * 1024 * 1024;
5804
+ LOG_MODE = 384;
5805
+ DIR_MODE = 448;
5652
5806
  }
5807
+ });
5808
+
5809
+ // ../../node_modules/@noble/hashes/esm/_blake.js
5810
+ function G1s(a, b, c, d, x) {
5811
+ a = a + b + x | 0;
5812
+ d = rotr(d ^ a, 16);
5813
+ c = c + d | 0;
5814
+ b = rotr(b ^ c, 12);
5815
+ return { a, b, c, d };
5653
5816
  }
5654
- async function createSDKInstance(ctx, options) {
5655
- const profile = options?.privateKey ? await ProfileManager.getProfile(ctx.profile).catch(() => null) : await ProfileManager.getProfile(ctx.profile);
5656
- const session = await ProfileManager.getSession(ctx.profile);
5657
- const key = await ProfileManager.getKey(ctx.profile);
5658
- const effectivePrivateKey = options?.privateKey ?? profile?.privateKey;
5659
- if (!key && !effectivePrivateKey && !(profile?.authMethod === "openkey" && session !== null)) {
5660
- throw new CLIError(
5661
- "AUTH_REQUIRED",
5662
- `No key found for profile "${ctx.profile}". Run \`tc --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`,
5663
- ExitCode.AUTH_REQUIRED
5664
- );
5665
- }
5666
- if (profile?.authMethod === "local" && effectivePrivateKey) {
5667
- const node2 = new TinyCloudNode({
5668
- host: ctx.host,
5669
- privateKey: effectivePrivateKey
5670
- });
5671
- const restoredOwnSession = Boolean(session && session.delegationHeader && session.delegationCid && session.spaceId);
5672
- if (restoredOwnSession) {
5673
- await restoreProfileSession(node2, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
5674
- } else {
5675
- await node2.signIn();
5676
- }
5677
- if (restoredOwnSession) {
5678
- recordWarnings(await replayAdditionalDelegations(node2, ctx.profile, {
5679
- host: ctx.host,
5680
- ownerSpace: profile.spaceId,
5681
- migrate: options?.privateKey === void 0
5682
- }));
5683
- }
5684
- return node2;
5817
+ function G2s(a, b, c, d, x) {
5818
+ a = a + b + x | 0;
5819
+ d = rotr(d ^ a, 8);
5820
+ c = c + d | 0;
5821
+ b = rotr(b ^ c, 7);
5822
+ return { a, b, c, d };
5823
+ }
5824
+ var init_blake = __esm({
5825
+ "../../node_modules/@noble/hashes/esm/_blake.js"() {
5826
+ "use strict";
5827
+ init_utils();
5685
5828
  }
5686
- const node = new TinyCloudNode({
5687
- host: ctx.host,
5688
- privateKey: options?.privateKey
5689
- });
5690
- if (options?.privateKey) {
5691
- await node.signIn();
5692
- } else if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
5693
- await restoreProfileSession(node, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
5694
- recordWarnings(await replayAdditionalDelegations(node, ctx.profile, {
5695
- host: ctx.host,
5696
- ownerSpace: profile?.spaceId,
5697
- migrate: true
5698
- }));
5829
+ });
5830
+
5831
+ // ../../node_modules/@noble/hashes/esm/blake2.js
5832
+ function compress(s, offset, msg, rounds, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) {
5833
+ let j = 0;
5834
+ for (let i = 0; i < rounds; i++) {
5835
+ ({ a: v0, b: v4, c: v8, d: v12 } = G1s(v0, v4, v8, v12, msg[offset + s[j++]]));
5836
+ ({ a: v0, b: v4, c: v8, d: v12 } = G2s(v0, v4, v8, v12, msg[offset + s[j++]]));
5837
+ ({ a: v1, b: v5, c: v9, d: v13 } = G1s(v1, v5, v9, v13, msg[offset + s[j++]]));
5838
+ ({ a: v1, b: v5, c: v9, d: v13 } = G2s(v1, v5, v9, v13, msg[offset + s[j++]]));
5839
+ ({ a: v2, b: v6, c: v10, d: v14 } = G1s(v2, v6, v10, v14, msg[offset + s[j++]]));
5840
+ ({ a: v2, b: v6, c: v10, d: v14 } = G2s(v2, v6, v10, v14, msg[offset + s[j++]]));
5841
+ ({ a: v3, b: v7, c: v11, d: v15 } = G1s(v3, v7, v11, v15, msg[offset + s[j++]]));
5842
+ ({ a: v3, b: v7, c: v11, d: v15 } = G2s(v3, v7, v11, v15, msg[offset + s[j++]]));
5843
+ ({ a: v0, b: v5, c: v10, d: v15 } = G1s(v0, v5, v10, v15, msg[offset + s[j++]]));
5844
+ ({ a: v0, b: v5, c: v10, d: v15 } = G2s(v0, v5, v10, v15, msg[offset + s[j++]]));
5845
+ ({ a: v1, b: v6, c: v11, d: v12 } = G1s(v1, v6, v11, v12, msg[offset + s[j++]]));
5846
+ ({ a: v1, b: v6, c: v11, d: v12 } = G2s(v1, v6, v11, v12, msg[offset + s[j++]]));
5847
+ ({ a: v2, b: v7, c: v8, d: v13 } = G1s(v2, v7, v8, v13, msg[offset + s[j++]]));
5848
+ ({ a: v2, b: v7, c: v8, d: v13 } = G2s(v2, v7, v8, v13, msg[offset + s[j++]]));
5849
+ ({ a: v3, b: v4, c: v9, d: v14 } = G1s(v3, v4, v9, v14, msg[offset + s[j++]]));
5850
+ ({ a: v3, b: v4, c: v9, d: v14 } = G2s(v3, v4, v9, v14, msg[offset + s[j++]]));
5699
5851
  }
5700
- return node;
5852
+ return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 };
5701
5853
  }
5702
- async function bootstrapDelegatedSession(ctx, delegation) {
5703
- const written = await ProfileManager.withLock(ctx.profile, async () => {
5704
- const previousProfile = await ProfileManager.getProfile(ctx.profile);
5705
- if (resolveProfilePosture(previousProfile) !== "delegate-session") {
5706
- throw new CLIError(
5707
- "AUTH_REQUIRED",
5708
- `Profile "${ctx.profile}" is not a delegate-session profile.`,
5709
- ExitCode.AUTH_REQUIRED
5710
- );
5711
- }
5712
- const sessionDid = previousProfile.sessionDid ?? previousProfile.did;
5713
- if (delegation.delegateDID.split("#", 1)[0] !== sessionDid.split("#", 1)[0]) {
5714
- throw new CLIError(
5715
- "DELEGATION_AUDIENCE_MISMATCH",
5716
- `Delegation targets ${delegation.delegateDID}, but profile "${ctx.profile}" uses ${sessionDid}.`,
5717
- ExitCode.PERMISSION_DENIED
5718
- );
5719
- }
5720
- if (await ProfileManager.getSession(ctx.profile) !== null) {
5721
- throw new CLIError(
5722
- "PROFILE_CHANGED_DURING_IMPORT",
5723
- `Profile "${ctx.profile}" gained a session (another login or import) after this import checked it. Nothing was saved; run the import again.`,
5724
- ExitCode.ERROR
5725
- );
5726
- }
5727
- if (signedLifetimePassed(delegation)) throw delegationExpiredError();
5728
- const jwk = signerJwkForProfile(ctx.profile, void 0, await ProfileManager.getKey(ctx.profile));
5729
- const session = {
5730
- delegationHeader: delegation.delegationHeader,
5731
- delegationCid: delegation.cid,
5732
- spaceId: delegation.spaceId,
5733
- jwk,
5734
- verificationMethod: sessionDid
5854
+ var BLAKE2;
5855
+ var init_blake2 = __esm({
5856
+ "../../node_modules/@noble/hashes/esm/blake2.js"() {
5857
+ "use strict";
5858
+ init_blake();
5859
+ init_utils();
5860
+ BLAKE2 = class extends Hash {
5861
+ constructor(blockLen, outputLen) {
5862
+ super();
5863
+ this.finished = false;
5864
+ this.destroyed = false;
5865
+ this.length = 0;
5866
+ this.pos = 0;
5867
+ anumber(blockLen);
5868
+ anumber(outputLen);
5869
+ this.blockLen = blockLen;
5870
+ this.outputLen = outputLen;
5871
+ this.buffer = new Uint8Array(blockLen);
5872
+ this.buffer32 = u32(this.buffer);
5873
+ }
5874
+ update(data) {
5875
+ aexists(this);
5876
+ data = toBytes(data);
5877
+ abytes(data);
5878
+ const { blockLen, buffer, buffer32 } = this;
5879
+ const len = data.length;
5880
+ const offset = data.byteOffset;
5881
+ const buf = data.buffer;
5882
+ for (let pos = 0; pos < len; ) {
5883
+ if (this.pos === blockLen) {
5884
+ swap32IfBE(buffer32);
5885
+ this.compress(buffer32, 0, false);
5886
+ swap32IfBE(buffer32);
5887
+ this.pos = 0;
5888
+ }
5889
+ const take = Math.min(blockLen - this.pos, len - pos);
5890
+ const dataOffset = offset + pos;
5891
+ if (take === blockLen && !(dataOffset % 4) && pos + take < len) {
5892
+ const data32 = new Uint32Array(buf, dataOffset, Math.floor((len - pos) / 4));
5893
+ swap32IfBE(data32);
5894
+ for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {
5895
+ this.length += blockLen;
5896
+ this.compress(data32, pos32, false);
5897
+ }
5898
+ swap32IfBE(data32);
5899
+ continue;
5900
+ }
5901
+ buffer.set(data.subarray(pos, pos + take), this.pos);
5902
+ this.pos += take;
5903
+ this.length += take;
5904
+ pos += take;
5905
+ }
5906
+ return this;
5907
+ }
5908
+ digestInto(out) {
5909
+ aexists(this);
5910
+ aoutput(out, this);
5911
+ const { pos, buffer32 } = this;
5912
+ this.finished = true;
5913
+ clean(this.buffer.subarray(pos));
5914
+ swap32IfBE(buffer32);
5915
+ this.compress(buffer32, 0, true);
5916
+ swap32IfBE(buffer32);
5917
+ const out32 = u32(out);
5918
+ this.get().forEach((v, i) => out32[i] = swap8IfBE(v));
5919
+ }
5920
+ digest() {
5921
+ const { buffer, outputLen } = this;
5922
+ this.digestInto(buffer);
5923
+ const res = buffer.slice(0, outputLen);
5924
+ this.destroy();
5925
+ return res;
5926
+ }
5927
+ _cloneInto(to) {
5928
+ const { buffer, length: length4, finished, destroyed, outputLen, pos } = this;
5929
+ to || (to = new this.constructor({ dkLen: outputLen }));
5930
+ to.set(...this.get());
5931
+ to.buffer.set(buffer);
5932
+ to.destroyed = destroyed;
5933
+ to.finished = finished;
5934
+ to.length = length4;
5935
+ to.pos = pos;
5936
+ to.outputLen = outputLen;
5937
+ return to;
5938
+ }
5939
+ clone() {
5940
+ return this._cloneInto();
5941
+ }
5735
5942
  };
5736
- const profile = { ...previousProfile, sessionDid, spaceId: delegation.spaceId };
5737
- try {
5738
- await ProfileManager.setSession(ctx.profile, session);
5739
- await ProfileManager.setProfile(ctx.profile, profile);
5740
- } catch (error) {
5741
- throw annotate(error, await restoreBeforeBootstrap(ctx.profile, previousProfile));
5742
- }
5743
- return { previousProfile, profile, session };
5744
- });
5745
- const abandon = async (cause) => {
5746
- const reported = cause instanceof CLIError && cause.code === "AUTH_REQUIRED" && signedLifetimePassed(delegation) ? delegationExpiredError() : cause;
5747
- const note = await ProfileManager.withLock(ctx.profile, async () => {
5748
- const profile = await ProfileManager.getProfile(ctx.profile).catch((error) => {
5749
- if (error instanceof CLIError && error.code === "PROFILE_NOT_FOUND") return null;
5750
- throw error;
5751
- });
5752
- const session = await ProfileManager.getSession(ctx.profile);
5753
- if (profile === null || BOOTSTRAP_PROFILE_FIELDS.some((field) => profile[field] !== written.profile[field]) || JSON.stringify(session) !== JSON.stringify(written.session)) {
5754
- return `Profile "${ctx.profile}" changed while the import was pending (another login, logout or profile update), so its newer state was kept and the provisional session was not rolled back.`;
5943
+ }
5944
+ });
5945
+
5946
+ // ../../node_modules/@noble/hashes/esm/blake3.js
5947
+ var B3_Flags, B3_IV, B3_SIGMA, BLAKE3, blake3;
5948
+ var init_blake3 = __esm({
5949
+ "../../node_modules/@noble/hashes/esm/blake3.js"() {
5950
+ "use strict";
5951
+ init_md();
5952
+ init_u64();
5953
+ init_blake2();
5954
+ init_utils();
5955
+ B3_Flags = {
5956
+ CHUNK_START: 1,
5957
+ CHUNK_END: 2,
5958
+ PARENT: 4,
5959
+ ROOT: 8,
5960
+ KEYED_HASH: 16,
5961
+ DERIVE_KEY_CONTEXT: 32,
5962
+ DERIVE_KEY_MATERIAL: 64
5963
+ };
5964
+ B3_IV = SHA256_IV.slice();
5965
+ B3_SIGMA = /* @__PURE__ */ (() => {
5966
+ const Id = Array.from({ length: 16 }, (_, i) => i);
5967
+ const permute = (arr) => [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]);
5968
+ const res = [];
5969
+ for (let i = 0, v = Id; i < 7; i++, v = permute(v))
5970
+ res.push(...v);
5971
+ return Uint8Array.from(res);
5972
+ })();
5973
+ BLAKE3 = class _BLAKE3 extends BLAKE2 {
5974
+ constructor(opts = {}, flags = 0) {
5975
+ super(64, opts.dkLen === void 0 ? 32 : opts.dkLen);
5976
+ this.chunkPos = 0;
5977
+ this.chunksDone = 0;
5978
+ this.flags = 0 | 0;
5979
+ this.stack = [];
5980
+ this.posOut = 0;
5981
+ this.bufferOut32 = new Uint32Array(16);
5982
+ this.chunkOut = 0;
5983
+ this.enableXOF = true;
5984
+ const { key, context } = opts;
5985
+ const hasContext = context !== void 0;
5986
+ if (key !== void 0) {
5987
+ if (hasContext)
5988
+ throw new Error('Only "key" or "context" can be specified at same time');
5989
+ const k = toBytes(key).slice();
5990
+ abytes(k, 32);
5991
+ this.IV = u32(k);
5992
+ swap32IfBE(this.IV);
5993
+ this.flags = flags | B3_Flags.KEYED_HASH;
5994
+ } else if (hasContext) {
5995
+ const ctx = toBytes(context);
5996
+ const contextKey = new _BLAKE3({ dkLen: 32 }, B3_Flags.DERIVE_KEY_CONTEXT).update(ctx).digest();
5997
+ this.IV = u32(contextKey);
5998
+ swap32IfBE(this.IV);
5999
+ this.flags = flags | B3_Flags.DERIVE_KEY_MATERIAL;
6000
+ } else {
6001
+ this.IV = B3_IV.slice();
6002
+ this.flags = flags;
6003
+ }
6004
+ this.state = this.IV.slice();
6005
+ this.bufferOut = u8(this.bufferOut32);
5755
6006
  }
5756
- return restoreBeforeBootstrap(ctx.profile, written.previousProfile);
5757
- }, { timeoutMs: PROFILE_COMMIT_LOCK_TIMEOUT_MS }).catch((error) => (
5758
- // The lock or a read failed: the import's error stays the one reported.
5759
- `Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check \`tc --profile ${ctx.profile} context\`.`
5760
- ));
5761
- throw annotate(reported, note);
5762
- };
5763
- let node;
5764
- try {
5765
- node = await createSDKInstance(ctx);
5766
- } catch (error) {
5767
- return abandon(error);
6007
+ // Unused
6008
+ get() {
6009
+ return [];
6010
+ }
6011
+ set() {
6012
+ }
6013
+ b2Compress(counter, flags, buf, bufPos = 0) {
6014
+ const { state: s, pos } = this;
6015
+ const { h, l } = fromBig(BigInt(counter), true);
6016
+ const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = compress(B3_SIGMA, bufPos, buf, 7, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], h, l, pos, flags);
6017
+ s[0] = v0 ^ v8;
6018
+ s[1] = v1 ^ v9;
6019
+ s[2] = v2 ^ v10;
6020
+ s[3] = v3 ^ v11;
6021
+ s[4] = v4 ^ v12;
6022
+ s[5] = v5 ^ v13;
6023
+ s[6] = v6 ^ v14;
6024
+ s[7] = v7 ^ v15;
6025
+ }
6026
+ compress(buf, bufPos = 0, isLast = false) {
6027
+ let flags = this.flags;
6028
+ if (!this.chunkPos)
6029
+ flags |= B3_Flags.CHUNK_START;
6030
+ if (this.chunkPos === 15 || isLast)
6031
+ flags |= B3_Flags.CHUNK_END;
6032
+ if (!isLast)
6033
+ this.pos = this.blockLen;
6034
+ this.b2Compress(this.chunksDone, flags, buf, bufPos);
6035
+ this.chunkPos += 1;
6036
+ if (this.chunkPos === 16 || isLast) {
6037
+ let chunk = this.state;
6038
+ this.state = this.IV.slice();
6039
+ for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) {
6040
+ if (!(last = this.stack.pop()))
6041
+ break;
6042
+ this.buffer32.set(last, 0);
6043
+ this.buffer32.set(chunk, 8);
6044
+ this.pos = this.blockLen;
6045
+ this.b2Compress(0, this.flags | B3_Flags.PARENT, this.buffer32, 0);
6046
+ chunk = this.state;
6047
+ this.state = this.IV.slice();
6048
+ }
6049
+ this.chunksDone++;
6050
+ this.chunkPos = 0;
6051
+ this.stack.push(chunk);
6052
+ }
6053
+ this.pos = 0;
6054
+ }
6055
+ _cloneInto(to) {
6056
+ to = super._cloneInto(to);
6057
+ const { IV, flags, state: state2, chunkPos, posOut, chunkOut, stack, chunksDone } = this;
6058
+ to.state.set(state2.slice());
6059
+ to.stack = stack.map((i) => Uint32Array.from(i));
6060
+ to.IV.set(IV);
6061
+ to.flags = flags;
6062
+ to.chunkPos = chunkPos;
6063
+ to.chunksDone = chunksDone;
6064
+ to.posOut = posOut;
6065
+ to.chunkOut = chunkOut;
6066
+ to.enableXOF = this.enableXOF;
6067
+ to.bufferOut32.set(this.bufferOut32);
6068
+ return to;
6069
+ }
6070
+ destroy() {
6071
+ this.destroyed = true;
6072
+ clean(this.state, this.buffer32, this.IV, this.bufferOut32);
6073
+ clean(...this.stack);
6074
+ }
6075
+ // Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8)
6076
+ b2CompressOut() {
6077
+ const { state: s, pos, flags, buffer32, bufferOut32: out32 } = this;
6078
+ const { h, l } = fromBig(BigInt(this.chunkOut++));
6079
+ swap32IfBE(buffer32);
6080
+ const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = compress(B3_SIGMA, 0, buffer32, 7, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], l, h, pos, flags);
6081
+ out32[0] = v0 ^ v8;
6082
+ out32[1] = v1 ^ v9;
6083
+ out32[2] = v2 ^ v10;
6084
+ out32[3] = v3 ^ v11;
6085
+ out32[4] = v4 ^ v12;
6086
+ out32[5] = v5 ^ v13;
6087
+ out32[6] = v6 ^ v14;
6088
+ out32[7] = v7 ^ v15;
6089
+ out32[8] = s[0] ^ v8;
6090
+ out32[9] = s[1] ^ v9;
6091
+ out32[10] = s[2] ^ v10;
6092
+ out32[11] = s[3] ^ v11;
6093
+ out32[12] = s[4] ^ v12;
6094
+ out32[13] = s[5] ^ v13;
6095
+ out32[14] = s[6] ^ v14;
6096
+ out32[15] = s[7] ^ v15;
6097
+ swap32IfBE(buffer32);
6098
+ swap32IfBE(out32);
6099
+ this.posOut = 0;
6100
+ }
6101
+ finish() {
6102
+ if (this.finished)
6103
+ return;
6104
+ this.finished = true;
6105
+ clean(this.buffer.subarray(this.pos));
6106
+ let flags = this.flags | B3_Flags.ROOT;
6107
+ if (this.stack.length) {
6108
+ flags |= B3_Flags.PARENT;
6109
+ swap32IfBE(this.buffer32);
6110
+ this.compress(this.buffer32, 0, true);
6111
+ swap32IfBE(this.buffer32);
6112
+ this.chunksDone = 0;
6113
+ this.pos = this.blockLen;
6114
+ } else {
6115
+ flags |= (!this.chunkPos ? B3_Flags.CHUNK_START : 0) | B3_Flags.CHUNK_END;
6116
+ }
6117
+ this.flags = flags;
6118
+ this.b2CompressOut();
6119
+ }
6120
+ writeInto(out) {
6121
+ aexists(this, false);
6122
+ abytes(out);
6123
+ this.finish();
6124
+ const { blockLen, bufferOut } = this;
6125
+ for (let pos = 0, len = out.length; pos < len; ) {
6126
+ if (this.posOut >= blockLen)
6127
+ this.b2CompressOut();
6128
+ const take = Math.min(blockLen - this.posOut, len - pos);
6129
+ out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
6130
+ this.posOut += take;
6131
+ pos += take;
6132
+ }
6133
+ return out;
6134
+ }
6135
+ xofInto(out) {
6136
+ if (!this.enableXOF)
6137
+ throw new Error("XOF is not possible after digest call");
6138
+ return this.writeInto(out);
6139
+ }
6140
+ xof(bytes) {
6141
+ anumber(bytes);
6142
+ return this.xofInto(new Uint8Array(bytes));
6143
+ }
6144
+ digestInto(out) {
6145
+ aoutput(out, this);
6146
+ if (this.finished)
6147
+ throw new Error("digest() was already called");
6148
+ this.enableXOF = false;
6149
+ this.writeInto(out);
6150
+ this.destroy();
6151
+ return out;
6152
+ }
6153
+ digest() {
6154
+ return this.digestInto(new Uint8Array(this.outputLen));
6155
+ }
6156
+ };
6157
+ blake3 = /* @__PURE__ */ createXOFer((opts) => new BLAKE3(opts));
5768
6158
  }
5769
- return { node, abandon };
6159
+ });
6160
+
6161
+ // ../../node_modules/@noble/curves/esm/utils.js
6162
+ function _abool2(value, title = "") {
6163
+ if (typeof value !== "boolean") {
6164
+ const prefix = title && `"${title}"`;
6165
+ throw new Error(prefix + "expected boolean, got type=" + typeof value);
6166
+ }
6167
+ return value;
5770
6168
  }
5771
- async function restoreBeforeBootstrap(profileName, previousProfile) {
5772
- const failures = [];
5773
- for (const write of [
5774
- () => ProfileManager.clearSession(profileName),
5775
- () => ProfileManager.updateProfile(profileName, (current) => {
5776
- const restored = { ...current };
5777
- for (const field of BOOTSTRAP_PROFILE_FIELDS) {
5778
- if (previousProfile[field] === void 0) delete restored[field];
5779
- else restored[field] = previousProfile[field];
5780
- }
5781
- return restored;
5782
- })
5783
- ]) {
5784
- await write().catch((error) => {
5785
- failures.push(failureName(error));
5786
- });
6169
+ function _abytes2(value, length4, title = "") {
6170
+ const bytes = isBytes(value);
6171
+ const len = value?.length;
6172
+ const needsLen = length4 !== void 0;
6173
+ if (!bytes || needsLen && len !== length4) {
6174
+ const prefix = title && `"${title}" `;
6175
+ const ofLen = needsLen ? ` of length ${length4}` : "";
6176
+ const got = bytes ? `length=${len}` : `type=${typeof value}`;
6177
+ throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
5787
6178
  }
5788
- if (failures.length === 0) return void 0;
5789
- return `Rolling back the provisional session of profile "${profileName}" failed too (${failures.join("; ")}); check \`tc --profile ${profileName} context\`.`;
6179
+ return value;
5790
6180
  }
5791
- function failureName(error) {
5792
- if (error instanceof SyntaxError) return "a profile file is not valid JSON";
5793
- if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") return error.code;
5794
- return error instanceof Error ? error.name : "unknown error";
6181
+ function hexToNumber(hex3) {
6182
+ if (typeof hex3 !== "string")
6183
+ throw new Error("hex string expected, got " + typeof hex3);
6184
+ return hex3 === "" ? _0n : BigInt("0x" + hex3);
5795
6185
  }
5796
- function annotate(error, note) {
5797
- if (note === void 0) return error;
5798
- const cause = wrapError(error);
5799
- return new CLIError(cause.code, `${cause.message} ${note}`, cause.exitCode, cause.metadata);
6186
+ function bytesToNumberBE(bytes) {
6187
+ return hexToNumber(bytesToHex(bytes));
5800
6188
  }
5801
- async function ensureAuthenticated(ctx, options) {
5802
- if (options?.privateKey) {
5803
- return createSDKInstance(ctx, options);
5804
- }
5805
- const profile = await ProfileManager.getProfile(ctx.profile).catch(() => null);
5806
- if (profile?.authMethod === "local" && profile.privateKey) {
5807
- return createSDKInstance(ctx);
5808
- }
5809
- const session = await ProfileManager.getSession(ctx.profile);
5810
- if (!session) {
5811
- throw new CLIError(
5812
- "AUTH_REQUIRED",
5813
- `Not authenticated. Run \`tc auth login\` or \`tc init\` first.`,
5814
- ExitCode.AUTH_REQUIRED
5815
- );
5816
- }
5817
- return createSDKInstance(ctx, options);
6189
+ function bytesToNumberLE(bytes) {
6190
+ abytes(bytes);
6191
+ return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
5818
6192
  }
5819
- var BOOTSTRAP_PROFILE_FIELDS;
5820
- var init_sdk = __esm({
5821
- "src/lib/sdk.ts"() {
5822
- "use strict";
5823
- init_profiles();
5824
- init_types();
5825
- init_errors();
5826
- init_constants();
5827
- init_permissions();
5828
- init_session_expired();
5829
- init_warnings();
5830
- init_delegation_expiry();
5831
- BOOTSTRAP_PROFILE_FIELDS = ["sessionDid", "spaceId"];
6193
+ function numberToBytesBE(n, len) {
6194
+ return hexToBytes(n.toString(16).padStart(len * 2, "0"));
6195
+ }
6196
+ function numberToBytesLE(n, len) {
6197
+ return numberToBytesBE(n, len).reverse();
6198
+ }
6199
+ function ensureBytes(title, hex3, expectedLength) {
6200
+ let res;
6201
+ if (typeof hex3 === "string") {
6202
+ try {
6203
+ res = hexToBytes(hex3);
6204
+ } catch (e) {
6205
+ throw new Error(title + " must be hex string or Uint8Array, cause: " + e);
6206
+ }
6207
+ } else if (isBytes(hex3)) {
6208
+ res = Uint8Array.from(hex3);
6209
+ } else {
6210
+ throw new Error(title + " must be hex string or Uint8Array");
5832
6211
  }
5833
- });
5834
-
5835
- // src/lib/host.ts
5836
- var host_exports = {};
5837
- __export(host_exports, {
5838
- completeDeferredLocalDiscovery: () => completeDeferredLocalDiscovery,
5839
- discoverLocalNodeHost: () => discoverLocalNodeHost,
5840
- isRootAuthority: () => isRootAuthority,
5841
- ownerDidFromSpaceUri: () => ownerDidFromSpaceUri,
5842
- profileLocalNodeIdentityStore: () => profileLocalNodeIdentityStore,
5843
- resolveHostSpace: () => resolveHostSpace,
5844
- spaceNameFromUri: () => spaceNameFromUri,
5845
- unhostedSpaceError: () => unhostedSpaceError
5846
- });
5847
- import {
5848
- discoverLocalTinyCloudNode
5849
- } from "@tinycloud/sdk-core";
5850
- function profileLocalNodeIdentityStore(profileName) {
5851
- return {
5852
- get: async (url) => {
5853
- const profile = await ProfileManager.getProfile(profileName).catch(
5854
- () => null
5855
- );
5856
- return profile?.pinnedLocalNodeDids?.[url];
5857
- },
5858
- set: async (url, nodeDid) => {
5859
- if (!await ProfileManager.profileExists(profileName)) return;
5860
- await ProfileManager.updateProfile(profileName, (profile) => ({
5861
- ...profile,
5862
- pinnedLocalNodeDids: {
5863
- ...profile.pinnedLocalNodeDids,
5864
- [url]: nodeDid
5865
- }
5866
- }));
5867
- }
5868
- };
6212
+ const len = res.length;
6213
+ if (typeof expectedLength === "number" && len !== expectedLength)
6214
+ throw new Error(title + " of length " + expectedLength + " expected, got " + len);
6215
+ return res;
5869
6216
  }
5870
- async function discoverLocalNodeHost(profileName, options) {
5871
- const profile = await ProfileManager.getProfile(profileName).catch(() => null);
5872
- if (profile?.autoDiscoverLocalNode === false) return { host: null, deferred: false };
5873
- if (!profile?.localNodeUrl && !profile?.localLinkName) return { host: null, deferred: false };
5874
- if (options.awaitStoredSession) {
5875
- const { storedSessionVerifiesLocally: storedSessionVerifiesLocally2 } = await Promise.resolve().then(() => (init_sdk(), sdk_exports));
5876
- if (!await storedSessionVerifiesLocally2(profileName)) return { host: null, deferred: true };
5877
- }
5878
- const discovered = await discoverLocalTinyCloudNode({
5879
- localNodeUrl: profile?.localNodeUrl,
5880
- localLinkName: profile?.localLinkName,
5881
- expectedNodeDid: profile?.expectedNodeDid,
5882
- identityStore: profileLocalNodeIdentityStore(profileName)
5883
- });
5884
- return { host: discovered?.url ?? null, deferred: false };
6217
+ function equalBytes(a, b) {
6218
+ if (a.length !== b.length)
6219
+ return false;
6220
+ let diff = 0;
6221
+ for (let i = 0; i < a.length; i++)
6222
+ diff |= a[i] ^ b[i];
6223
+ return diff === 0;
5885
6224
  }
5886
- async function completeDeferredLocalDiscovery(ctx) {
5887
- const deferred = ctx.localDiscovery;
5888
- if (deferred === void 0) return;
5889
- delete ctx.localDiscovery;
5890
- if (ctx.host !== deferred.deferredFrom) return;
5891
- const discovered = await discoverLocalNodeHost(ctx.profile, { awaitStoredSession: false });
5892
- if (discovered.host !== null) ctx.host = discovered.host;
6225
+ function copyBytes(bytes) {
6226
+ return Uint8Array.from(bytes);
5893
6227
  }
5894
- function canonicalizeAddress2(address) {
5895
- const trimmed = address.trim();
5896
- return trimmed.startsWith("0x") ? `0x${trimmed.slice(2).toLowerCase()}` : trimmed.toLowerCase();
6228
+ function inRange(n, min, max) {
6229
+ return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
5897
6230
  }
5898
- async function resolveLocalAddress(profile, profileName) {
5899
- const session = await ProfileManager.getSession(profileName);
5900
- const sessAddr = session?.address;
5901
- if (typeof sessAddr === "string" && sessAddr.length > 0) {
5902
- return canonicalizeAddress2(sessAddr);
6231
+ function aInRange(title, n, min, max) {
6232
+ if (!inRange(n, min, max))
6233
+ throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n);
6234
+ }
6235
+ function bitLen(n) {
6236
+ let len;
6237
+ for (len = 0; n > _0n; n >>= _1n, len += 1)
6238
+ ;
6239
+ return len;
6240
+ }
6241
+ function _validateObject(object3, fields, optFields = {}) {
6242
+ if (!object3 || typeof object3 !== "object")
6243
+ throw new Error("expected valid options object");
6244
+ function checkField(fieldName, expectedType, isOpt) {
6245
+ const val = object3[fieldName];
6246
+ if (isOpt && val === void 0)
6247
+ return;
6248
+ const current = typeof val;
6249
+ if (current !== expectedType || val === null)
6250
+ throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
5903
6251
  }
5904
- if (profile.address) return canonicalizeAddress2(profile.address);
5905
- if (profile.ownerDid) {
5906
- const match = profile.ownerDid.match(/^did:pkh:eip155:\d+:(0x[a-fA-F0-9]{40})$/);
5907
- if (match) return canonicalizeAddress2(match[1]);
6252
+ Object.entries(fields).forEach(([k, v]) => checkField(k, v, false));
6253
+ Object.entries(optFields).forEach(([k, v]) => checkField(k, v, true));
6254
+ }
6255
+ function memoized(fn) {
6256
+ const map = /* @__PURE__ */ new WeakMap();
6257
+ return (arg, ...args) => {
6258
+ const val = map.get(arg);
6259
+ if (val !== void 0)
6260
+ return val;
6261
+ const computed = fn(arg, ...args);
6262
+ map.set(arg, computed);
6263
+ return computed;
6264
+ };
6265
+ }
6266
+ var _0n, _1n, isPosBig, bitMask, notImplemented;
6267
+ var init_utils2 = __esm({
6268
+ "../../node_modules/@noble/curves/esm/utils.js"() {
6269
+ "use strict";
6270
+ init_utils();
6271
+ init_utils();
6272
+ _0n = /* @__PURE__ */ BigInt(0);
6273
+ _1n = /* @__PURE__ */ BigInt(1);
6274
+ isPosBig = (n) => typeof n === "bigint" && _0n <= n;
6275
+ bitMask = (n) => (_1n << BigInt(n)) - _1n;
6276
+ notImplemented = () => {
6277
+ throw new Error("not implemented");
6278
+ };
5908
6279
  }
5909
- return null;
6280
+ });
6281
+
6282
+ // ../../node_modules/@noble/curves/esm/abstract/modular.js
6283
+ function mod(a, b) {
6284
+ const result = a % b;
6285
+ return result >= _0n2 ? result : b + result;
5910
6286
  }
5911
- function ownerAddressFromSpaceUri(spaceUri) {
5912
- const match = spaceUri.match(/^tinycloud:pkh:eip155:\d+:(0x[a-fA-F0-9]{40}):/);
5913
- return match ? canonicalizeAddress2(match[1]) : null;
6287
+ function pow2(x, power, modulo) {
6288
+ let res = x;
6289
+ while (power-- > _0n2) {
6290
+ res *= res;
6291
+ res %= modulo;
6292
+ }
6293
+ return res;
5914
6294
  }
5915
- function ownerDidFromSpaceUri(spaceUri) {
5916
- const match = spaceUri.match(/^tinycloud:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40}):/);
5917
- if (!match) return null;
5918
- return `did:pkh:eip155:${match[1]}:${canonicalizeAddress2(match[2])}`;
6295
+ function invert(number, modulo) {
6296
+ if (number === _0n2)
6297
+ throw new Error("invert: expected non-zero number");
6298
+ if (modulo <= _0n2)
6299
+ throw new Error("invert: expected positive modulus, got " + modulo);
6300
+ let a = mod(number, modulo);
6301
+ let b = modulo;
6302
+ let x = _0n2, y = _1n2, u = _1n2, v = _0n2;
6303
+ while (a !== _0n2) {
6304
+ const q = b / a;
6305
+ const r = b % a;
6306
+ const m = x - u * q;
6307
+ const n = y - v * q;
6308
+ b = a, a = r, x = u, y = v, u = m, v = n;
6309
+ }
6310
+ const gcd = b;
6311
+ if (gcd !== _1n2)
6312
+ throw new Error("invert: does not exist");
6313
+ return mod(x, modulo);
5919
6314
  }
5920
- async function isRootAuthority(spaceUri, profileName) {
5921
- const profile = await ProfileManager.getProfile(profileName);
5922
- if (resolveProfilePosture(profile) === "delegate-session") return false;
5923
- const ownerAddr = ownerAddressFromSpaceUri(spaceUri);
5924
- if (!ownerAddr) return false;
5925
- const selfAddr = await resolveLocalAddress(profile, profileName);
5926
- return selfAddr !== null && selfAddr === ownerAddr;
6315
+ function assertIsSquare(Fp2, root, n) {
6316
+ if (!Fp2.eql(Fp2.sqr(root), n))
6317
+ throw new Error("Cannot find square root");
5927
6318
  }
5928
- function spaceNameFromUri(spaceUri) {
5929
- return spaceUri.slice(spaceUri.lastIndexOf(":") + 1);
6319
+ function sqrt3mod4(Fp2, n) {
6320
+ const p1div4 = (Fp2.ORDER + _1n2) / _4n;
6321
+ const root = Fp2.pow(n, p1div4);
6322
+ assertIsSquare(Fp2, root, n);
6323
+ return root;
5930
6324
  }
5931
- async function unhostedSpaceError(error, spaceUri, profileName) {
5932
- if (!spaceUri) return null;
5933
- const status = error.meta?.status;
5934
- const isUnhosted = status === 404 && /space not found/i.test(error.message);
5935
- if (!isUnhosted) return null;
5936
- const spaceName = spaceNameFromUri(spaceUri);
5937
- const owner = await isRootAuthority(spaceUri, profileName);
5938
- const hint = owner ? [
5939
- "You are the owner. Host it once:",
5940
- ` tc space host ${spaceName}`,
5941
- "Then retry."
5942
- ].join("\n") : [
5943
- "You are a delegate and CANNOT host this space \u2014 only its owner can.",
5944
- "Emit a host request:",
5945
- ` tc space host-request ${spaceName} --emit ./host-request.json`,
5946
- "Send it to the owner; they run `tc space host` and confirm. Then retry."
5947
- ].join("\n");
5948
- const message = owner ? `Space '${spaceName}' (${spaceUri}) is not hosted.` : `Space '${spaceName}' (owner ${ownerDidFromSpaceUri(spaceUri) ?? spaceUri}) is not hosted.`;
5949
- return new CLIError("SPACE_NOT_HOSTED", message, ExitCode.ERROR, { hint });
6325
+ function sqrt5mod8(Fp2, n) {
6326
+ const p5div8 = (Fp2.ORDER - _5n) / _8n;
6327
+ const n2 = Fp2.mul(n, _2n);
6328
+ const v = Fp2.pow(n2, p5div8);
6329
+ const nv = Fp2.mul(n, v);
6330
+ const i = Fp2.mul(Fp2.mul(nv, _2n), v);
6331
+ const root = Fp2.mul(nv, Fp2.sub(i, Fp2.ONE));
6332
+ assertIsSquare(Fp2, root, n);
6333
+ return root;
5950
6334
  }
5951
- async function resolveHostSpace(name, profileName) {
5952
- const resolved = await resolveSpaceUri(name, profileName);
5953
- if (!resolved) {
5954
- throw new Error(`Could not resolve a space for "${name}".`);
5955
- }
5956
- return resolved;
6335
+ function sqrt9mod16(P) {
6336
+ const Fp_ = Field(P);
6337
+ const tn = tonelliShanks(P);
6338
+ const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));
6339
+ const c2 = tn(Fp_, c1);
6340
+ const c3 = tn(Fp_, Fp_.neg(c1));
6341
+ const c4 = (P + _7n) / _16n;
6342
+ return (Fp2, n) => {
6343
+ let tv1 = Fp2.pow(n, c4);
6344
+ let tv2 = Fp2.mul(tv1, c1);
6345
+ const tv3 = Fp2.mul(tv1, c2);
6346
+ const tv4 = Fp2.mul(tv1, c3);
6347
+ const e1 = Fp2.eql(Fp2.sqr(tv2), n);
6348
+ const e2 = Fp2.eql(Fp2.sqr(tv3), n);
6349
+ tv1 = Fp2.cmov(tv1, tv2, e1);
6350
+ tv2 = Fp2.cmov(tv4, tv3, e2);
6351
+ const e3 = Fp2.eql(Fp2.sqr(tv2), n);
6352
+ const root = Fp2.cmov(tv1, tv2, e3);
6353
+ assertIsSquare(Fp2, root, n);
6354
+ return root;
6355
+ };
5957
6356
  }
5958
- var init_host = __esm({
5959
- "src/lib/host.ts"() {
5960
- "use strict";
5961
- init_profiles();
5962
- init_constants();
5963
- init_types();
5964
- init_errors();
5965
- init_space();
5966
- }
5967
- });
5968
-
5969
- // src/config/profiles.ts
5970
- import { chmod as chmod2, lstat, readdir as readdir2, rm as rm2, rmdir } from "fs/promises";
5971
- import { join as join4 } from "path";
5972
- import {
5973
- profilePath,
5974
- readSession,
5975
- recordProfileDeletion,
5976
- refuseWriteToDeletedProfile as refuseWriteToDeletedProfile2,
5977
- removeSession,
5978
- withProfileLock as withProfileLock2,
5979
- writeSession
5980
- } from "@tinycloud/operations/state";
5981
- var ProfileManager;
5982
- var init_profiles = __esm({
5983
- "src/config/profiles.ts"() {
5984
- "use strict";
5985
- init_constants();
5986
- init_storage2();
5987
- init_errors();
5988
- ProfileManager = class _ProfileManager {
5989
- // ── Initialization ──────────────────────────────────────────────────
5990
- /**
5991
- * Runs `action` holding the profile's store lock (shared with operations
5992
- * and MCP). Reentrant, so the writers below can be called inside it; hold
5993
- * it around a whole read-modify-write, not just each write.
5994
- */
5995
- static async withLock(name, action, options) {
5996
- return withProfileLock2(name, action, options);
5997
- }
5998
- /**
5999
- * Read-modify-write of profile.json under the profile lock, so concurrent
6000
- * updates (another command, a login commit) are never erased.
6001
- */
6002
- static async updateProfile(name, update) {
6003
- return _ProfileManager.withLock(name, async () => {
6004
- const next = update(await _ProfileManager.getProfile(name));
6005
- await _ProfileManager.setProfile(name, next);
6006
- return next;
6007
- });
6008
- }
6009
- /**
6010
- * Creates ~/.tinycloud/ and ~/.tinycloud/profiles/ if they don't exist and
6011
- * (re)sets both to 0700: older releases created them 0775.
6012
- */
6013
- static async ensureConfigDir() {
6014
- for (const directory of [CONFIG_DIR, PROFILES_DIR]) {
6015
- await ensureDir(directory);
6016
- await chmod2(directory, PRIVATE_DIR_MODE);
6017
- }
6018
- }
6019
- /** Owner-only profile directory (0700), created or tightened before any write into it. */
6020
- static async ensureProfileDir(name) {
6021
- await _ProfileManager.ensureConfigDir();
6022
- const profileDir = join4(PROFILES_DIR, name);
6023
- await ensureDir(profileDir);
6024
- await chmod2(profileDir, PRIVATE_DIR_MODE);
6025
- return profileDir;
6026
- }
6027
- // ── Global config ───────────────────────────────────────────────────
6028
- /**
6029
- * Reads config.json. Returns a default config if the file is missing.
6030
- */
6031
- static async getConfig() {
6032
- const config = await readJson(CONFIG_FILE);
6033
- if (!config) {
6034
- return { defaultProfile: DEFAULT_PROFILE, version: 1 };
6035
- }
6036
- return config;
6037
- }
6038
- /**
6039
- * Writes the global config to config.json.
6040
- */
6041
- static async setConfig(config) {
6042
- await _ProfileManager.ensureConfigDir();
6043
- await writeJson(CONFIG_FILE, config);
6044
- }
6045
- // ── Profile CRUD ────────────────────────────────────────────────────
6046
- /**
6047
- * Returns the profile config for the given name.
6048
- * Throws CLIError if the profile doesn't exist.
6049
- */
6050
- static async getProfile(name) {
6051
- const profilePath2 = join4(PROFILES_DIR, name, "profile.json");
6052
- const profile = await readJson(profilePath2);
6053
- if (!profile) {
6054
- throw new CLIError(
6055
- "PROFILE_NOT_FOUND",
6056
- `Profile "${name}" does not exist. Create it with \`tc init --name ${name}\`.`
6057
- );
6058
- }
6059
- return profile;
6060
- }
6061
- /**
6062
- * Saves a profile config under the profile lock, creating the profile
6063
- * directory if needed. Use `updateProfile` to change an existing profile.
6064
- */
6065
- static async setProfile(name, data) {
6066
- await _ProfileManager.withLock(name, async () => {
6067
- await writeJson(join4(await _ProfileManager.ensureProfileDir(name), "profile.json"), data);
6068
- });
6069
- }
6070
- /** Removes profile.json under the profile lock (rollback of a profile a failed login created). */
6071
- static async removeProfileConfig(name) {
6072
- await _ProfileManager.withLock(name, () => rm2(join4(PROFILES_DIR, name, "profile.json"), { force: true }));
6073
- }
6074
- /**
6075
- * Returns true if a profile directory exists.
6076
- */
6077
- static async profileExists(name) {
6078
- return fileExists(join4(PROFILES_DIR, name, "profile.json"));
6079
- }
6080
- /**
6081
- * Returns an array of profile directory names.
6082
- */
6083
- static async listProfiles() {
6084
- return listDirs(PROFILES_DIR);
6085
- }
6086
- /**
6087
- * Deletes a profile. Its key, session, settings, stores and cache are
6088
- * removed while holding the profile lock, so another writer's critical
6089
- * section never sees them vanish midway: session and key first, settings
6090
- * last, so a crash midway never leaves a session or key without its
6091
- * profile. The deletion is then recorded, so a store write that waited for
6092
- * the lock meanwhile refuses rather than recreating a profile with only a
6093
- * session in it. `.lock` itself is left to the lock's release; the
6094
- * then-empty directory is removed afterwards unless another writer took
6095
- * the lock (or wrote) meanwhile. A profile directory that is a symlink is
6096
- * unlinked, its target left alone.
6097
- * Throws if the name is not one path segment or names the default profile.
6098
- */
6099
- static async deleteProfile(name) {
6100
- let profileDir;
6101
- try {
6102
- profileDir = profilePath(name);
6103
- } catch {
6104
- throw new CLIError(
6105
- "INVALID_PROFILE_NAME",
6106
- `Invalid profile name "${name}": a profile name is one path segment (no "/", "\\", "." or "..").`,
6107
- ExitCode.USAGE_ERROR
6108
- );
6109
- }
6110
- const config = await _ProfileManager.getConfig();
6111
- if (config.defaultProfile === name) {
6112
- throw new CLIError(
6113
- "PROFILE_DELETE_DEFAULT",
6114
- `Cannot delete the default profile "${name}". Change the default first with \`tc profile default <other>\`.`
6115
- );
6116
- }
6117
- const kind = await lstat(profileDir).then((stats) => stats.isSymbolicLink() ? "link" : "present", (error) => {
6118
- if (error.code === "ENOENT") return "missing";
6119
- throw error;
6120
- });
6121
- if (kind === "missing") return;
6122
- if (kind === "link") {
6123
- await rm2(profileDir, { force: true });
6124
- return;
6125
- }
6126
- await _ProfileManager.withLock(name, async () => {
6127
- for (const file of ["session.json", "key.json"]) await rm2(join4(profileDir, file), { force: true });
6128
- for (const entry of await readdir2(profileDir)) {
6129
- if (entry !== ".lock" && entry !== "profile.json") await rm2(join4(profileDir, entry), { recursive: true, force: true });
6130
- }
6131
- await rm2(join4(profileDir, "profile.json"), { force: true });
6132
- await recordProfileDeletion(name);
6133
- });
6134
- await rmdir(profileDir).catch((error) => {
6135
- if (error.code !== "ENOENT" && error.code !== "ENOTEMPTY" && error.code !== "EEXIST") throw error;
6136
- });
6137
- }
6138
- // ── Key management ──────────────────────────────────────────────────
6139
- /**
6140
- * Returns the parsed JWK for a profile, or null if no key exists.
6141
- */
6142
- static async getKey(name) {
6143
- return readJson(join4(PROFILES_DIR, name, "key.json"));
6144
- }
6145
- /**
6146
- * Saves a JWK key under the profile lock (0600, in an owner-only profile
6147
- * directory). Refused (PROFILE_NOT_FOUND) if the profile was deleted while
6148
- * this waited for the lock, rather than leaving a key-only profile.
6149
- */
6150
- static async setKey(name, jwk) {
6151
- await _ProfileManager.withLock(name, async () => {
6152
- await refuseWriteToDeletedProfile2(name);
6153
- await writeJson(join4(await _ProfileManager.ensureProfileDir(name), "key.json"), jwk);
6154
- });
6155
- }
6156
- /** Removes key.json under the profile lock (rollback of a key a failed login created). */
6157
- static async removeKey(name) {
6158
- await _ProfileManager.withLock(name, () => rm2(join4(PROFILES_DIR, name, "key.json"), { force: true }));
6159
- }
6160
- // ── Session management ──────────────────────────────────────────────
6161
- /**
6162
- * Returns the parsed session for a profile, or null if none exists.
6163
- */
6164
- static async getSession(name) {
6165
- return readSession(name);
6166
- }
6167
- /**
6168
- * Saves session data for a profile.
6169
- */
6170
- static async setSession(name, session) {
6171
- await writeSession(name, session);
6172
- }
6173
- /**
6174
- * Removes the session file for a profile.
6175
- */
6176
- static async clearSession(name) {
6177
- await removeSession(name);
6178
- }
6179
- // ── Cache management ────────────────────────────────────────────────
6180
- /**
6181
- * Returns the profile's cache directory (share history lives here),
6182
- * created or tightened to 0700.
6183
- */
6184
- static async getCacheDir(name) {
6185
- const cacheDir = join4(await _ProfileManager.ensureProfileDir(name), "cache");
6186
- await ensureDir(cacheDir);
6187
- await chmod2(cacheDir, PRIVATE_DIR_MODE);
6188
- return cacheDir;
6189
- }
6190
- // ── Resolution helpers ──────────────────────────────────────────────
6191
- /**
6192
- * Resolves the full CLI context from flags, env vars, and config.
6193
- *
6194
- * Profile resolution: options.profile > TC_PROFILE env > config.defaultProfile > "default"
6195
- * Host resolution: options.host > TC_HOST env > discovered local node > profile.host > DEFAULT_HOST
6196
- *
6197
- * Local-node discovery (TC-106) only runs when no explicit host was given
6198
- * (`--host` / `TC_HOST`); it probes configured or registered local nodes and,
6199
- * after DID verification against the profile's pinned identity, prefers one
6200
- * over the stored/default host. For session-using commands, it verifies the
6201
- * stored session locally before probing. Loopback requires an explicit
6202
- * `localNodeUrl`; disable discovery with `autoDiscoverLocalNode: false`.
6203
- */
6204
- static async resolveContext(options, settings = {}) {
6205
- const config = await _ProfileManager.getConfig();
6206
- const profile = options.profile ?? process.env.TC_PROFILE ?? config.defaultProfile ?? DEFAULT_PROFILE;
6207
- let profileHost;
6208
- try {
6209
- const profileConfig = await _ProfileManager.getProfile(profile);
6210
- profileHost = profileConfig.host;
6211
- } catch {
6212
- }
6213
- const explicitHost = options.host ?? process.env.TC_HOST;
6214
- let host = explicitHost ?? profileHost ?? DEFAULT_HOST;
6215
- let localDiscovery;
6216
- if (explicitHost === void 0) {
6217
- const { discoverLocalNodeHost: discoverLocalNodeHost2 } = await Promise.resolve().then(() => (init_host(), host_exports));
6218
- const explicitKey = Boolean(options.privateKey || process.env.TC_PRIVATE_KEY);
6219
- const discovered = await discoverLocalNodeHost2(profile, {
6220
- awaitStoredSession: settings.storedSession !== "unused" && !explicitKey
6221
- });
6222
- if (discovered.host !== null) host = discovered.host;
6223
- if (discovered.deferred) localDiscovery = { deferredFrom: host };
6224
- }
6225
- setActiveProfileName(profile);
6226
- return {
6227
- profile,
6228
- host,
6229
- verbose: options.verbose ?? false,
6230
- noCache: options.noCache ?? false,
6231
- quiet: options.quiet ?? false,
6232
- ...localDiscovery === void 0 ? {} : { localDiscovery }
6233
- };
6234
- }
6235
- };
6236
- }
6237
- });
6238
-
6239
- // src/index.ts
6240
- init_errors();
6241
- import { readFileSync as readFileSync2 } from "fs";
6242
- import { Command } from "commander";
6243
-
6244
- // src/output/banner.ts
6245
- init_formatter();
6246
-
6247
- // src/output/taglines.ts
6248
- var HOLIDAY_TAGLINES = [
6249
- { month: 1, day: 1, range: 1, tagline: "New year, new keys, same cloud." },
6250
- { month: 2, day: 14, tagline: "We love your data as much as you do." },
6251
- { month: 3, day: 14, tagline: "3.14159 reasons to encrypt everything." },
6252
- { month: 5, day: 4, tagline: "May the fourth be with your keys." },
6253
- { month: 10, day: 31, tagline: "Nothing scarier than plaintext secrets." },
6254
- { month: 12, day: 25, range: 2, tagline: "Unwrap your data, not your keys." },
6255
- { month: 12, day: 31, tagline: "Encrypt your resolutions." }
6256
- ];
6257
- var TAGLINES = [
6258
- // Professional
6259
- "Your data, your keys, your cloud.",
6260
- "Self-sovereign storage for the modern web.",
6261
- "The cloud you actually own.",
6262
- "Encrypted by default, decentralized by design.",
6263
- "Where your data answers only to you.",
6264
- "End-to-end encrypted. No exceptions.",
6265
- "Like S3 but you hold the keys.",
6266
- "Privacy isn't a feature. It's the architecture.",
6267
- "Sovereign storage, zero knowledge.",
6268
- "Your .env is safe here \u2014 we use real cryptography.",
6269
- // Playful / nerdy
6270
- "UCAN do anything.",
6271
- "Keys generated, delegations granted, data liberated.",
6272
- "Decentralized storage, centralized vibes.",
6273
- "Trust nobody, delegate everything.",
6274
- "sudo make me a sandwich, encrypted.",
6275
- "Have you tried turning your keys off and on again?",
6276
- "All your base are belong to you.",
6277
- "In UCAN we trust.",
6278
- "0 knowledge, 100% confidence.",
6279
- "Keeping secrets since 2024."
6280
- ];
6281
- function getHolidayTagline() {
6282
- const now = /* @__PURE__ */ new Date();
6283
- const month = now.getMonth() + 1;
6284
- const day = now.getDate();
6285
- for (const h of HOLIDAY_TAGLINES) {
6286
- const range = h.range ?? 0;
6287
- if (h.month === month && Math.abs(day - h.day) <= range) {
6288
- return h.tagline;
6289
- }
6290
- }
6291
- return null;
6292
- }
6293
- function pickTagline() {
6294
- const holiday = getHolidayTagline();
6295
- if (holiday) return holiday;
6296
- return TAGLINES[Math.floor(Math.random() * TAGLINES.length)];
6297
- }
6298
-
6299
- // src/output/banner.ts
6300
- init_theme();
6301
- import { execSync } from "child_process";
6302
- var bannerEmitted = false;
6303
- function resolveCommitHash() {
6304
- try {
6305
- return execSync("git rev-parse --short HEAD", {
6306
- encoding: "utf-8",
6307
- stdio: ["pipe", "pipe", "pipe"]
6308
- }).trim() || null;
6309
- } catch {
6310
- return null;
6311
- }
6312
- }
6313
- function formatBannerLine(version2) {
6314
- const commit = resolveCommitHash();
6315
- const tagline = pickTagline();
6316
- const versionPart = `tc v${version2}`;
6317
- const commitPart = commit ? ` (${commit})` : "";
6318
- const separator = " \u2014 ";
6319
- if (!isInteractive()) {
6320
- return `${versionPart}${commitPart}${separator}${tagline}`;
6321
- }
6322
- return [
6323
- theme.brand("\u2601\uFE0F tc"),
6324
- " ",
6325
- theme.muted(`v${version2}`),
6326
- commit ? theme.dim(` (${commit})`) : "",
6327
- theme.dim(separator),
6328
- theme.primary(tagline)
6329
- ].join("");
6330
- }
6331
- function emitBanner(version2) {
6332
- if (bannerEmitted) return;
6333
- if (shouldOutputJson()) return;
6334
- if (process.env.TC_HIDE_BANNER === "1") return;
6335
- bannerEmitted = true;
6336
- process.stderr.write(formatBannerLine(version2) + "\n\n");
6337
- }
6338
-
6339
- // src/index.ts
6340
- init_theme();
6341
- init_formatter();
6342
- init_profiles();
6343
-
6344
- // ../share-sdk/dist/index.js
6345
- init_sha2();
6346
-
6347
- // ../../node_modules/@noble/curves/esm/ed25519.js
6348
- init_sha2();
6349
- init_utils();
6350
-
6351
- // ../../node_modules/@noble/curves/esm/utils.js
6352
- init_utils();
6353
- init_utils();
6354
- var _0n = /* @__PURE__ */ BigInt(0);
6355
- var _1n = /* @__PURE__ */ BigInt(1);
6356
- function _abool2(value, title = "") {
6357
- if (typeof value !== "boolean") {
6358
- const prefix = title && `"${title}"`;
6359
- throw new Error(prefix + "expected boolean, got type=" + typeof value);
6360
- }
6361
- return value;
6362
- }
6363
- function _abytes2(value, length4, title = "") {
6364
- const bytes = isBytes(value);
6365
- const len = value?.length;
6366
- const needsLen = length4 !== void 0;
6367
- if (!bytes || needsLen && len !== length4) {
6368
- const prefix = title && `"${title}" `;
6369
- const ofLen = needsLen ? ` of length ${length4}` : "";
6370
- const got = bytes ? `length=${len}` : `type=${typeof value}`;
6371
- throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
6372
- }
6373
- return value;
6374
- }
6375
- function hexToNumber(hex3) {
6376
- if (typeof hex3 !== "string")
6377
- throw new Error("hex string expected, got " + typeof hex3);
6378
- return hex3 === "" ? _0n : BigInt("0x" + hex3);
6379
- }
6380
- function bytesToNumberBE(bytes) {
6381
- return hexToNumber(bytesToHex(bytes));
6382
- }
6383
- function bytesToNumberLE(bytes) {
6384
- abytes(bytes);
6385
- return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
6386
- }
6387
- function numberToBytesBE(n, len) {
6388
- return hexToBytes(n.toString(16).padStart(len * 2, "0"));
6389
- }
6390
- function numberToBytesLE(n, len) {
6391
- return numberToBytesBE(n, len).reverse();
6392
- }
6393
- function ensureBytes(title, hex3, expectedLength) {
6394
- let res;
6395
- if (typeof hex3 === "string") {
6396
- try {
6397
- res = hexToBytes(hex3);
6398
- } catch (e) {
6399
- throw new Error(title + " must be hex string or Uint8Array, cause: " + e);
6400
- }
6401
- } else if (isBytes(hex3)) {
6402
- res = Uint8Array.from(hex3);
6403
- } else {
6404
- throw new Error(title + " must be hex string or Uint8Array");
6405
- }
6406
- const len = res.length;
6407
- if (typeof expectedLength === "number" && len !== expectedLength)
6408
- throw new Error(title + " of length " + expectedLength + " expected, got " + len);
6409
- return res;
6410
- }
6411
- function equalBytes(a, b) {
6412
- if (a.length !== b.length)
6413
- return false;
6414
- let diff = 0;
6415
- for (let i = 0; i < a.length; i++)
6416
- diff |= a[i] ^ b[i];
6417
- return diff === 0;
6418
- }
6419
- function copyBytes(bytes) {
6420
- return Uint8Array.from(bytes);
6421
- }
6422
- var isPosBig = (n) => typeof n === "bigint" && _0n <= n;
6423
- function inRange(n, min, max) {
6424
- return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
6425
- }
6426
- function aInRange(title, n, min, max) {
6427
- if (!inRange(n, min, max))
6428
- throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n);
6429
- }
6430
- function bitLen(n) {
6431
- let len;
6432
- for (len = 0; n > _0n; n >>= _1n, len += 1)
6433
- ;
6434
- return len;
6435
- }
6436
- var bitMask = (n) => (_1n << BigInt(n)) - _1n;
6437
- function _validateObject(object3, fields, optFields = {}) {
6438
- if (!object3 || typeof object3 !== "object")
6439
- throw new Error("expected valid options object");
6440
- function checkField(fieldName, expectedType, isOpt) {
6441
- const val = object3[fieldName];
6442
- if (isOpt && val === void 0)
6443
- return;
6444
- const current = typeof val;
6445
- if (current !== expectedType || val === null)
6446
- throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
6447
- }
6448
- Object.entries(fields).forEach(([k, v]) => checkField(k, v, false));
6449
- Object.entries(optFields).forEach(([k, v]) => checkField(k, v, true));
6450
- }
6451
- var notImplemented = () => {
6452
- throw new Error("not implemented");
6453
- };
6454
- function memoized(fn) {
6455
- const map = /* @__PURE__ */ new WeakMap();
6456
- return (arg, ...args) => {
6457
- const val = map.get(arg);
6458
- if (val !== void 0)
6459
- return val;
6460
- const computed = fn(arg, ...args);
6461
- map.set(arg, computed);
6462
- return computed;
6463
- };
6464
- }
6465
-
6466
- // ../../node_modules/@noble/curves/esm/abstract/modular.js
6467
- var _0n2 = BigInt(0);
6468
- var _1n2 = BigInt(1);
6469
- var _2n = /* @__PURE__ */ BigInt(2);
6470
- var _3n = /* @__PURE__ */ BigInt(3);
6471
- var _4n = /* @__PURE__ */ BigInt(4);
6472
- var _5n = /* @__PURE__ */ BigInt(5);
6473
- var _7n = /* @__PURE__ */ BigInt(7);
6474
- var _8n = /* @__PURE__ */ BigInt(8);
6475
- var _9n = /* @__PURE__ */ BigInt(9);
6476
- var _16n = /* @__PURE__ */ BigInt(16);
6477
- function mod(a, b) {
6478
- const result = a % b;
6479
- return result >= _0n2 ? result : b + result;
6480
- }
6481
- function pow2(x, power, modulo) {
6482
- let res = x;
6483
- while (power-- > _0n2) {
6484
- res *= res;
6485
- res %= modulo;
6486
- }
6487
- return res;
6488
- }
6489
- function invert(number, modulo) {
6490
- if (number === _0n2)
6491
- throw new Error("invert: expected non-zero number");
6492
- if (modulo <= _0n2)
6493
- throw new Error("invert: expected positive modulus, got " + modulo);
6494
- let a = mod(number, modulo);
6495
- let b = modulo;
6496
- let x = _0n2, y = _1n2, u = _1n2, v = _0n2;
6497
- while (a !== _0n2) {
6498
- const q = b / a;
6499
- const r = b % a;
6500
- const m = x - u * q;
6501
- const n = y - v * q;
6502
- b = a, a = r, x = u, y = v, u = m, v = n;
6503
- }
6504
- const gcd = b;
6505
- if (gcd !== _1n2)
6506
- throw new Error("invert: does not exist");
6507
- return mod(x, modulo);
6508
- }
6509
- function assertIsSquare(Fp2, root, n) {
6510
- if (!Fp2.eql(Fp2.sqr(root), n))
6511
- throw new Error("Cannot find square root");
6512
- }
6513
- function sqrt3mod4(Fp2, n) {
6514
- const p1div4 = (Fp2.ORDER + _1n2) / _4n;
6515
- const root = Fp2.pow(n, p1div4);
6516
- assertIsSquare(Fp2, root, n);
6517
- return root;
6518
- }
6519
- function sqrt5mod8(Fp2, n) {
6520
- const p5div8 = (Fp2.ORDER - _5n) / _8n;
6521
- const n2 = Fp2.mul(n, _2n);
6522
- const v = Fp2.pow(n2, p5div8);
6523
- const nv = Fp2.mul(n, v);
6524
- const i = Fp2.mul(Fp2.mul(nv, _2n), v);
6525
- const root = Fp2.mul(nv, Fp2.sub(i, Fp2.ONE));
6526
- assertIsSquare(Fp2, root, n);
6527
- return root;
6528
- }
6529
- function sqrt9mod16(P) {
6530
- const Fp_ = Field(P);
6531
- const tn = tonelliShanks(P);
6532
- const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));
6533
- const c2 = tn(Fp_, c1);
6534
- const c3 = tn(Fp_, Fp_.neg(c1));
6535
- const c4 = (P + _7n) / _16n;
6536
- return (Fp2, n) => {
6537
- let tv1 = Fp2.pow(n, c4);
6538
- let tv2 = Fp2.mul(tv1, c1);
6539
- const tv3 = Fp2.mul(tv1, c2);
6540
- const tv4 = Fp2.mul(tv1, c3);
6541
- const e1 = Fp2.eql(Fp2.sqr(tv2), n);
6542
- const e2 = Fp2.eql(Fp2.sqr(tv3), n);
6543
- tv1 = Fp2.cmov(tv1, tv2, e1);
6544
- tv2 = Fp2.cmov(tv4, tv3, e2);
6545
- const e3 = Fp2.eql(Fp2.sqr(tv2), n);
6546
- const root = Fp2.cmov(tv1, tv2, e3);
6547
- assertIsSquare(Fp2, root, n);
6548
- return root;
6549
- };
6550
- }
6551
- function tonelliShanks(P) {
6552
- if (P < _3n)
6553
- throw new Error("sqrt is not defined for small field");
6554
- let Q = P - _1n2;
6555
- let S = 0;
6556
- while (Q % _2n === _0n2) {
6557
- Q /= _2n;
6558
- S++;
6357
+ function tonelliShanks(P) {
6358
+ if (P < _3n)
6359
+ throw new Error("sqrt is not defined for small field");
6360
+ let Q = P - _1n2;
6361
+ let S = 0;
6362
+ while (Q % _2n === _0n2) {
6363
+ Q /= _2n;
6364
+ S++;
6559
6365
  }
6560
6366
  let Z = _2n;
6561
6367
  const _Fp = Field(P);
@@ -6606,26 +6412,6 @@ function FpSqrt(P) {
6606
6412
  return sqrt9mod16(P);
6607
6413
  return tonelliShanks(P);
6608
6414
  }
6609
- var isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n2) === _1n2;
6610
- var FIELD_FIELDS = [
6611
- "create",
6612
- "isValid",
6613
- "is0",
6614
- "neg",
6615
- "inv",
6616
- "sqrt",
6617
- "sqr",
6618
- "eql",
6619
- "add",
6620
- "sub",
6621
- "mul",
6622
- "pow",
6623
- "div",
6624
- "addN",
6625
- "subN",
6626
- "mulN",
6627
- "sqrN"
6628
- ];
6629
6415
  function validateField(field) {
6630
6416
  const initial = {
6631
6417
  ORDER: "bigint",
@@ -6788,10 +6574,45 @@ function Field(ORDER, bitLenOrOpts, isLE2 = false, opts = {}) {
6788
6574
  });
6789
6575
  return Object.freeze(f);
6790
6576
  }
6577
+ var _0n2, _1n2, _2n, _3n, _4n, _5n, _7n, _8n, _9n, _16n, isNegativeLE, FIELD_FIELDS;
6578
+ var init_modular = __esm({
6579
+ "../../node_modules/@noble/curves/esm/abstract/modular.js"() {
6580
+ "use strict";
6581
+ init_utils2();
6582
+ _0n2 = BigInt(0);
6583
+ _1n2 = BigInt(1);
6584
+ _2n = /* @__PURE__ */ BigInt(2);
6585
+ _3n = /* @__PURE__ */ BigInt(3);
6586
+ _4n = /* @__PURE__ */ BigInt(4);
6587
+ _5n = /* @__PURE__ */ BigInt(5);
6588
+ _7n = /* @__PURE__ */ BigInt(7);
6589
+ _8n = /* @__PURE__ */ BigInt(8);
6590
+ _9n = /* @__PURE__ */ BigInt(9);
6591
+ _16n = /* @__PURE__ */ BigInt(16);
6592
+ isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n2) === _1n2;
6593
+ FIELD_FIELDS = [
6594
+ "create",
6595
+ "isValid",
6596
+ "is0",
6597
+ "neg",
6598
+ "inv",
6599
+ "sqrt",
6600
+ "sqr",
6601
+ "eql",
6602
+ "add",
6603
+ "sub",
6604
+ "mul",
6605
+ "pow",
6606
+ "div",
6607
+ "addN",
6608
+ "subN",
6609
+ "mulN",
6610
+ "sqrN"
6611
+ ];
6612
+ }
6613
+ });
6791
6614
 
6792
6615
  // ../../node_modules/@noble/curves/esm/abstract/curve.js
6793
- var _0n3 = BigInt(0);
6794
- var _1n3 = BigInt(1);
6795
6616
  function negateCt(condition, item) {
6796
6617
  const neg = item.negate();
6797
6618
  return condition ? neg : item;
@@ -6845,8 +6666,6 @@ function validateMSMScalars(scalars, field) {
6845
6666
  throw new Error("invalid scalar at index " + i);
6846
6667
  });
6847
6668
  }
6848
- var pointPrecomputes = /* @__PURE__ */ new WeakMap();
6849
- var pointWindowSizes = /* @__PURE__ */ new WeakMap();
6850
6669
  function getW(P) {
6851
6670
  return pointWindowSizes.get(P) || 1;
6852
6671
  }
@@ -6854,133 +6673,6 @@ function assert0(n) {
6854
6673
  if (n !== _0n3)
6855
6674
  throw new Error("invalid wNAF");
6856
6675
  }
6857
- var wNAF = class {
6858
- // Parametrized with a given Point class (not individual point)
6859
- constructor(Point, bits) {
6860
- this.BASE = Point.BASE;
6861
- this.ZERO = Point.ZERO;
6862
- this.Fn = Point.Fn;
6863
- this.bits = bits;
6864
- }
6865
- // non-const time multiplication ladder
6866
- _unsafeLadder(elm, n, p = this.ZERO) {
6867
- let d = elm;
6868
- while (n > _0n3) {
6869
- if (n & _1n3)
6870
- p = p.add(d);
6871
- d = d.double();
6872
- n >>= _1n3;
6873
- }
6874
- return p;
6875
- }
6876
- /**
6877
- * Creates a wNAF precomputation window. Used for caching.
6878
- * Default window size is set by `utils.precompute()` and is equal to 8.
6879
- * Number of precomputed points depends on the curve size:
6880
- * 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
6881
- * - 𝑊 is the window size
6882
- * - 𝑛 is the bitlength of the curve order.
6883
- * For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
6884
- * @param point Point instance
6885
- * @param W window size
6886
- * @returns precomputed point tables flattened to a single array
6887
- */
6888
- precomputeWindow(point, W) {
6889
- const { windows, windowSize } = calcWOpts(W, this.bits);
6890
- const points = [];
6891
- let p = point;
6892
- let base4 = p;
6893
- for (let window = 0; window < windows; window++) {
6894
- base4 = p;
6895
- points.push(base4);
6896
- for (let i = 1; i < windowSize; i++) {
6897
- base4 = base4.add(p);
6898
- points.push(base4);
6899
- }
6900
- p = base4.double();
6901
- }
6902
- return points;
6903
- }
6904
- /**
6905
- * Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
6906
- * More compact implementation:
6907
- * https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
6908
- * @returns real and fake (for const-time) points
6909
- */
6910
- wNAF(W, precomputes, n) {
6911
- if (!this.Fn.isValid(n))
6912
- throw new Error("invalid scalar");
6913
- let p = this.ZERO;
6914
- let f = this.BASE;
6915
- const wo = calcWOpts(W, this.bits);
6916
- for (let window = 0; window < wo.windows; window++) {
6917
- const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window, wo);
6918
- n = nextN;
6919
- if (isZero) {
6920
- f = f.add(negateCt(isNegF, precomputes[offsetF]));
6921
- } else {
6922
- p = p.add(negateCt(isNeg, precomputes[offset]));
6923
- }
6924
- }
6925
- assert0(n);
6926
- return { p, f };
6927
- }
6928
- /**
6929
- * Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
6930
- * @param acc accumulator point to add result of multiplication
6931
- * @returns point
6932
- */
6933
- wNAFUnsafe(W, precomputes, n, acc = this.ZERO) {
6934
- const wo = calcWOpts(W, this.bits);
6935
- for (let window = 0; window < wo.windows; window++) {
6936
- if (n === _0n3)
6937
- break;
6938
- const { nextN, offset, isZero, isNeg } = calcOffsets(n, window, wo);
6939
- n = nextN;
6940
- if (isZero) {
6941
- continue;
6942
- } else {
6943
- const item = precomputes[offset];
6944
- acc = acc.add(isNeg ? item.negate() : item);
6945
- }
6946
- }
6947
- assert0(n);
6948
- return acc;
6949
- }
6950
- getPrecomputes(W, point, transform) {
6951
- let comp = pointPrecomputes.get(point);
6952
- if (!comp) {
6953
- comp = this.precomputeWindow(point, W);
6954
- if (W !== 1) {
6955
- if (typeof transform === "function")
6956
- comp = transform(comp);
6957
- pointPrecomputes.set(point, comp);
6958
- }
6959
- }
6960
- return comp;
6961
- }
6962
- cached(point, scalar, transform) {
6963
- const W = getW(point);
6964
- return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
6965
- }
6966
- unsafe(point, scalar, transform, prev) {
6967
- const W = getW(point);
6968
- if (W === 1)
6969
- return this._unsafeLadder(point, scalar, prev);
6970
- return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
6971
- }
6972
- // We calculate precomputes for elliptic curve point multiplication
6973
- // using windowed method. This specifies window size and
6974
- // stores precomputed values. Usually only base point would be precomputed.
6975
- createCache(P, W) {
6976
- validateW(W, this.bits);
6977
- pointWindowSizes.set(P, W);
6978
- pointPrecomputes.delete(P);
6979
- }
6980
- hasCache(elm) {
6981
- return getW(elm) !== 1;
6982
- }
6983
- };
6984
6676
  function pippenger(c, fieldN, points, scalars) {
6985
6677
  validateMSMPoints(points, c);
6986
6678
  validateMSMScalars(scalars, fieldN);
@@ -7051,12 +6743,147 @@ function _createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
7051
6743
  CURVE = Object.freeze(Object.assign({}, CURVE));
7052
6744
  return { CURVE, Fp: Fp2, Fn: Fn2 };
7053
6745
  }
6746
+ var _0n3, _1n3, pointPrecomputes, pointWindowSizes, wNAF;
6747
+ var init_curve = __esm({
6748
+ "../../node_modules/@noble/curves/esm/abstract/curve.js"() {
6749
+ "use strict";
6750
+ init_utils2();
6751
+ init_modular();
6752
+ _0n3 = BigInt(0);
6753
+ _1n3 = BigInt(1);
6754
+ pointPrecomputes = /* @__PURE__ */ new WeakMap();
6755
+ pointWindowSizes = /* @__PURE__ */ new WeakMap();
6756
+ wNAF = class {
6757
+ // Parametrized with a given Point class (not individual point)
6758
+ constructor(Point, bits) {
6759
+ this.BASE = Point.BASE;
6760
+ this.ZERO = Point.ZERO;
6761
+ this.Fn = Point.Fn;
6762
+ this.bits = bits;
6763
+ }
6764
+ // non-const time multiplication ladder
6765
+ _unsafeLadder(elm, n, p = this.ZERO) {
6766
+ let d = elm;
6767
+ while (n > _0n3) {
6768
+ if (n & _1n3)
6769
+ p = p.add(d);
6770
+ d = d.double();
6771
+ n >>= _1n3;
6772
+ }
6773
+ return p;
6774
+ }
6775
+ /**
6776
+ * Creates a wNAF precomputation window. Used for caching.
6777
+ * Default window size is set by `utils.precompute()` and is equal to 8.
6778
+ * Number of precomputed points depends on the curve size:
6779
+ * 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
6780
+ * - 𝑊 is the window size
6781
+ * - 𝑛 is the bitlength of the curve order.
6782
+ * For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
6783
+ * @param point Point instance
6784
+ * @param W window size
6785
+ * @returns precomputed point tables flattened to a single array
6786
+ */
6787
+ precomputeWindow(point, W) {
6788
+ const { windows, windowSize } = calcWOpts(W, this.bits);
6789
+ const points = [];
6790
+ let p = point;
6791
+ let base4 = p;
6792
+ for (let window = 0; window < windows; window++) {
6793
+ base4 = p;
6794
+ points.push(base4);
6795
+ for (let i = 1; i < windowSize; i++) {
6796
+ base4 = base4.add(p);
6797
+ points.push(base4);
6798
+ }
6799
+ p = base4.double();
6800
+ }
6801
+ return points;
6802
+ }
6803
+ /**
6804
+ * Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
6805
+ * More compact implementation:
6806
+ * https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
6807
+ * @returns real and fake (for const-time) points
6808
+ */
6809
+ wNAF(W, precomputes, n) {
6810
+ if (!this.Fn.isValid(n))
6811
+ throw new Error("invalid scalar");
6812
+ let p = this.ZERO;
6813
+ let f = this.BASE;
6814
+ const wo = calcWOpts(W, this.bits);
6815
+ for (let window = 0; window < wo.windows; window++) {
6816
+ const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window, wo);
6817
+ n = nextN;
6818
+ if (isZero) {
6819
+ f = f.add(negateCt(isNegF, precomputes[offsetF]));
6820
+ } else {
6821
+ p = p.add(negateCt(isNeg, precomputes[offset]));
6822
+ }
6823
+ }
6824
+ assert0(n);
6825
+ return { p, f };
6826
+ }
6827
+ /**
6828
+ * Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
6829
+ * @param acc accumulator point to add result of multiplication
6830
+ * @returns point
6831
+ */
6832
+ wNAFUnsafe(W, precomputes, n, acc = this.ZERO) {
6833
+ const wo = calcWOpts(W, this.bits);
6834
+ for (let window = 0; window < wo.windows; window++) {
6835
+ if (n === _0n3)
6836
+ break;
6837
+ const { nextN, offset, isZero, isNeg } = calcOffsets(n, window, wo);
6838
+ n = nextN;
6839
+ if (isZero) {
6840
+ continue;
6841
+ } else {
6842
+ const item = precomputes[offset];
6843
+ acc = acc.add(isNeg ? item.negate() : item);
6844
+ }
6845
+ }
6846
+ assert0(n);
6847
+ return acc;
6848
+ }
6849
+ getPrecomputes(W, point, transform) {
6850
+ let comp = pointPrecomputes.get(point);
6851
+ if (!comp) {
6852
+ comp = this.precomputeWindow(point, W);
6853
+ if (W !== 1) {
6854
+ if (typeof transform === "function")
6855
+ comp = transform(comp);
6856
+ pointPrecomputes.set(point, comp);
6857
+ }
6858
+ }
6859
+ return comp;
6860
+ }
6861
+ cached(point, scalar, transform) {
6862
+ const W = getW(point);
6863
+ return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
6864
+ }
6865
+ unsafe(point, scalar, transform, prev) {
6866
+ const W = getW(point);
6867
+ if (W === 1)
6868
+ return this._unsafeLadder(point, scalar, prev);
6869
+ return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
6870
+ }
6871
+ // We calculate precomputes for elliptic curve point multiplication
6872
+ // using windowed method. This specifies window size and
6873
+ // stores precomputed values. Usually only base point would be precomputed.
6874
+ createCache(P, W) {
6875
+ validateW(W, this.bits);
6876
+ pointWindowSizes.set(P, W);
6877
+ pointPrecomputes.delete(P);
6878
+ }
6879
+ hasCache(elm) {
6880
+ return getW(elm) !== 1;
6881
+ }
6882
+ };
6883
+ }
6884
+ });
7054
6885
 
7055
6886
  // ../../node_modules/@noble/curves/esm/abstract/edwards.js
7056
- var _0n4 = BigInt(0);
7057
- var _1n4 = BigInt(1);
7058
- var _2n2 = BigInt(2);
7059
- var _8n2 = BigInt(8);
7060
6887
  function isEdValidXY(Fp2, CURVE, x, y) {
7061
6888
  const x2 = Fp2.sqr(x);
7062
6889
  const y2 = Fp2.sqr(y);
@@ -7340,828 +7167,1513 @@ function edwards(params, extraOpts = {}) {
7340
7167
  Point.BASE.precompute(8);
7341
7168
  return Point;
7342
7169
  }
7343
- var PrimeEdwardsPoint = class {
7344
- constructor(ep) {
7345
- this.ep = ep;
7346
- }
7347
- // Static methods that must be implemented by subclasses
7348
- static fromBytes(_bytes) {
7349
- notImplemented();
7350
- }
7351
- static fromHex(_hex) {
7352
- notImplemented();
7353
- }
7354
- get x() {
7355
- return this.toAffine().x;
7356
- }
7357
- get y() {
7358
- return this.toAffine().y;
7359
- }
7360
- // Common implementations
7361
- clearCofactor() {
7362
- return this;
7363
- }
7364
- assertValidity() {
7365
- this.ep.assertValidity();
7366
- }
7367
- toAffine(invertedZ) {
7368
- return this.ep.toAffine(invertedZ);
7369
- }
7370
- toHex() {
7371
- return bytesToHex(this.toBytes());
7372
- }
7373
- toString() {
7374
- return this.toHex();
7375
- }
7376
- isTorsionFree() {
7377
- return true;
7378
- }
7379
- isSmallOrder() {
7380
- return false;
7170
+ function eddsa(Point, cHash, eddsaOpts = {}) {
7171
+ if (typeof cHash !== "function")
7172
+ throw new Error('"hash" function param is required');
7173
+ _validateObject(eddsaOpts, {}, {
7174
+ adjustScalarBytes: "function",
7175
+ randomBytes: "function",
7176
+ domain: "function",
7177
+ prehash: "function",
7178
+ mapToCurve: "function"
7179
+ });
7180
+ const { prehash } = eddsaOpts;
7181
+ const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
7182
+ const randomBytes3 = eddsaOpts.randomBytes || randomBytes;
7183
+ const adjustScalarBytes2 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
7184
+ const domain = eddsaOpts.domain || ((data, ctx, phflag) => {
7185
+ _abool2(phflag, "phflag");
7186
+ if (ctx.length || phflag)
7187
+ throw new Error("Contexts/pre-hash are not supported");
7188
+ return data;
7189
+ });
7190
+ function modN_LE(hash) {
7191
+ return Fn2.create(bytesToNumberLE(hash));
7192
+ }
7193
+ function getPrivateScalar(key) {
7194
+ const len = lengths.secretKey;
7195
+ key = ensureBytes("private key", key, len);
7196
+ const hashed = ensureBytes("hashed private key", cHash(key), 2 * len);
7197
+ const head = adjustScalarBytes2(hashed.slice(0, len));
7198
+ const prefix = hashed.slice(len, 2 * len);
7199
+ const scalar = modN_LE(head);
7200
+ return { head, prefix, scalar };
7201
+ }
7202
+ function getExtendedPublicKey(secretKey) {
7203
+ const { head, prefix, scalar } = getPrivateScalar(secretKey);
7204
+ const point = BASE.multiply(scalar);
7205
+ const pointBytes = point.toBytes();
7206
+ return { head, prefix, scalar, point, pointBytes };
7207
+ }
7208
+ function getPublicKey(secretKey) {
7209
+ return getExtendedPublicKey(secretKey).pointBytes;
7210
+ }
7211
+ function hashDomainToScalar(context = Uint8Array.of(), ...msgs) {
7212
+ const msg = concatBytes(...msgs);
7213
+ return modN_LE(cHash(domain(msg, ensureBytes("context", context), !!prehash)));
7214
+ }
7215
+ function sign(msg, secretKey, options = {}) {
7216
+ msg = ensureBytes("message", msg);
7217
+ if (prehash)
7218
+ msg = prehash(msg);
7219
+ const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
7220
+ const r = hashDomainToScalar(options.context, prefix, msg);
7221
+ const R = BASE.multiply(r).toBytes();
7222
+ const k = hashDomainToScalar(options.context, R, pointBytes, msg);
7223
+ const s = Fn2.create(r + k * scalar);
7224
+ if (!Fn2.isValid(s))
7225
+ throw new Error("sign failed: invalid s");
7226
+ const rs = concatBytes(R, Fn2.toBytes(s));
7227
+ return _abytes2(rs, lengths.signature, "result");
7228
+ }
7229
+ const verifyOpts = { zip215: true };
7230
+ function verify(sig, msg, publicKey, options = verifyOpts) {
7231
+ const { context, zip215 } = options;
7232
+ const len = lengths.signature;
7233
+ sig = ensureBytes("signature", sig, len);
7234
+ msg = ensureBytes("message", msg);
7235
+ publicKey = ensureBytes("publicKey", publicKey, lengths.publicKey);
7236
+ if (zip215 !== void 0)
7237
+ _abool2(zip215, "zip215");
7238
+ if (prehash)
7239
+ msg = prehash(msg);
7240
+ const mid = len / 2;
7241
+ const r = sig.subarray(0, mid);
7242
+ const s = bytesToNumberLE(sig.subarray(mid, len));
7243
+ let A, R, SB;
7244
+ try {
7245
+ A = Point.fromBytes(publicKey, zip215);
7246
+ R = Point.fromBytes(r, zip215);
7247
+ SB = BASE.multiplyUnsafe(s);
7248
+ } catch (error) {
7249
+ return false;
7250
+ }
7251
+ if (!zip215 && A.isSmallOrder())
7252
+ return false;
7253
+ const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
7254
+ const RkA = R.add(A.multiplyUnsafe(k));
7255
+ return RkA.subtract(SB).clearCofactor().is0();
7256
+ }
7257
+ const _size = Fp2.BYTES;
7258
+ const lengths = {
7259
+ secretKey: _size,
7260
+ publicKey: _size,
7261
+ signature: 2 * _size,
7262
+ seed: _size
7263
+ };
7264
+ function randomSecretKey(seed = randomBytes3(lengths.seed)) {
7265
+ return _abytes2(seed, lengths.seed, "seed");
7266
+ }
7267
+ function keygen(seed) {
7268
+ const secretKey = utils.randomSecretKey(seed);
7269
+ return { secretKey, publicKey: getPublicKey(secretKey) };
7270
+ }
7271
+ function isValidSecretKey(key) {
7272
+ return isBytes(key) && key.length === Fn2.BYTES;
7273
+ }
7274
+ function isValidPublicKey(key, zip215) {
7275
+ try {
7276
+ return !!Point.fromBytes(key, zip215);
7277
+ } catch (error) {
7278
+ return false;
7279
+ }
7280
+ }
7281
+ const utils = {
7282
+ getExtendedPublicKey,
7283
+ randomSecretKey,
7284
+ isValidSecretKey,
7285
+ isValidPublicKey,
7286
+ /**
7287
+ * Converts ed public key to x public key. Uses formula:
7288
+ * - ed25519:
7289
+ * - `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
7290
+ * - `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
7291
+ * - ed448:
7292
+ * - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
7293
+ * - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
7294
+ */
7295
+ toMontgomery(publicKey) {
7296
+ const { y } = Point.fromBytes(publicKey);
7297
+ const size = lengths.publicKey;
7298
+ const is25519 = size === 32;
7299
+ if (!is25519 && size !== 57)
7300
+ throw new Error("only defined for 25519 and 448");
7301
+ const u = is25519 ? Fp2.div(_1n4 + y, _1n4 - y) : Fp2.div(y - _1n4, y + _1n4);
7302
+ return Fp2.toBytes(u);
7303
+ },
7304
+ toMontgomerySecret(secretKey) {
7305
+ const size = lengths.secretKey;
7306
+ _abytes2(secretKey, size);
7307
+ const hashed = cHash(secretKey.subarray(0, size));
7308
+ return adjustScalarBytes2(hashed).subarray(0, size);
7309
+ },
7310
+ /** @deprecated */
7311
+ randomPrivateKey: randomSecretKey,
7312
+ /** @deprecated */
7313
+ precompute(windowSize = 8, point = Point.BASE) {
7314
+ return point.precompute(windowSize, false);
7315
+ }
7316
+ };
7317
+ return Object.freeze({
7318
+ keygen,
7319
+ getPublicKey,
7320
+ sign,
7321
+ verify,
7322
+ utils,
7323
+ Point,
7324
+ lengths
7325
+ });
7326
+ }
7327
+ function _eddsa_legacy_opts_to_new(c) {
7328
+ const CURVE = {
7329
+ a: c.a,
7330
+ d: c.d,
7331
+ p: c.Fp.ORDER,
7332
+ n: c.n,
7333
+ h: c.h,
7334
+ Gx: c.Gx,
7335
+ Gy: c.Gy
7336
+ };
7337
+ const Fp2 = c.Fp;
7338
+ const Fn2 = Field(CURVE.n, c.nBitLength, true);
7339
+ const curveOpts = { Fp: Fp2, Fn: Fn2, uvRatio: c.uvRatio };
7340
+ const eddsaOpts = {
7341
+ randomBytes: c.randomBytes,
7342
+ adjustScalarBytes: c.adjustScalarBytes,
7343
+ domain: c.domain,
7344
+ prehash: c.prehash,
7345
+ mapToCurve: c.mapToCurve
7346
+ };
7347
+ return { CURVE, curveOpts, hash: c.hash, eddsaOpts };
7348
+ }
7349
+ function _eddsa_new_output_to_legacy(c, eddsa2) {
7350
+ const Point = eddsa2.Point;
7351
+ const legacy = Object.assign({}, eddsa2, {
7352
+ ExtendedPoint: Point,
7353
+ CURVE: c,
7354
+ nBitLength: Point.Fn.BITS,
7355
+ nByteLength: Point.Fn.BYTES
7356
+ });
7357
+ return legacy;
7358
+ }
7359
+ function twistedEdwards(c) {
7360
+ const { CURVE, curveOpts, hash, eddsaOpts } = _eddsa_legacy_opts_to_new(c);
7361
+ const Point = edwards(CURVE, curveOpts);
7362
+ const EDDSA = eddsa(Point, hash, eddsaOpts);
7363
+ return _eddsa_new_output_to_legacy(c, EDDSA);
7364
+ }
7365
+ var _0n4, _1n4, _2n2, _8n2, PrimeEdwardsPoint;
7366
+ var init_edwards = __esm({
7367
+ "../../node_modules/@noble/curves/esm/abstract/edwards.js"() {
7368
+ "use strict";
7369
+ init_utils2();
7370
+ init_curve();
7371
+ init_modular();
7372
+ _0n4 = BigInt(0);
7373
+ _1n4 = BigInt(1);
7374
+ _2n2 = BigInt(2);
7375
+ _8n2 = BigInt(8);
7376
+ PrimeEdwardsPoint = class {
7377
+ constructor(ep) {
7378
+ this.ep = ep;
7379
+ }
7380
+ // Static methods that must be implemented by subclasses
7381
+ static fromBytes(_bytes) {
7382
+ notImplemented();
7383
+ }
7384
+ static fromHex(_hex) {
7385
+ notImplemented();
7386
+ }
7387
+ get x() {
7388
+ return this.toAffine().x;
7389
+ }
7390
+ get y() {
7391
+ return this.toAffine().y;
7392
+ }
7393
+ // Common implementations
7394
+ clearCofactor() {
7395
+ return this;
7396
+ }
7397
+ assertValidity() {
7398
+ this.ep.assertValidity();
7399
+ }
7400
+ toAffine(invertedZ) {
7401
+ return this.ep.toAffine(invertedZ);
7402
+ }
7403
+ toHex() {
7404
+ return bytesToHex(this.toBytes());
7405
+ }
7406
+ toString() {
7407
+ return this.toHex();
7408
+ }
7409
+ isTorsionFree() {
7410
+ return true;
7411
+ }
7412
+ isSmallOrder() {
7413
+ return false;
7414
+ }
7415
+ add(other) {
7416
+ this.assertSame(other);
7417
+ return this.init(this.ep.add(other.ep));
7418
+ }
7419
+ subtract(other) {
7420
+ this.assertSame(other);
7421
+ return this.init(this.ep.subtract(other.ep));
7422
+ }
7423
+ multiply(scalar) {
7424
+ return this.init(this.ep.multiply(scalar));
7425
+ }
7426
+ multiplyUnsafe(scalar) {
7427
+ return this.init(this.ep.multiplyUnsafe(scalar));
7428
+ }
7429
+ double() {
7430
+ return this.init(this.ep.double());
7431
+ }
7432
+ negate() {
7433
+ return this.init(this.ep.negate());
7434
+ }
7435
+ precompute(windowSize, isLazy) {
7436
+ return this.init(this.ep.precompute(windowSize, isLazy));
7437
+ }
7438
+ /** @deprecated use `toBytes` */
7439
+ toRawBytes() {
7440
+ return this.toBytes();
7441
+ }
7442
+ };
7381
7443
  }
7382
- add(other) {
7383
- this.assertSame(other);
7384
- return this.init(this.ep.add(other.ep));
7444
+ });
7445
+
7446
+ // ../../node_modules/@noble/curves/esm/ed25519.js
7447
+ function ed25519_pow_2_252_3(x) {
7448
+ const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
7449
+ const P = ed25519_CURVE_p;
7450
+ const x2 = x * x % P;
7451
+ const b2 = x2 * x % P;
7452
+ const b4 = pow2(b2, _2n3, P) * b2 % P;
7453
+ const b5 = pow2(b4, _1n5, P) * x % P;
7454
+ const b10 = pow2(b5, _5n2, P) * b5 % P;
7455
+ const b20 = pow2(b10, _10n, P) * b10 % P;
7456
+ const b40 = pow2(b20, _20n, P) * b20 % P;
7457
+ const b80 = pow2(b40, _40n, P) * b40 % P;
7458
+ const b160 = pow2(b80, _80n, P) * b80 % P;
7459
+ const b240 = pow2(b160, _80n, P) * b80 % P;
7460
+ const b250 = pow2(b240, _10n, P) * b10 % P;
7461
+ const pow_p_5_8 = pow2(b250, _2n3, P) * x % P;
7462
+ return { pow_p_5_8, b2 };
7463
+ }
7464
+ function adjustScalarBytes(bytes) {
7465
+ bytes[0] &= 248;
7466
+ bytes[31] &= 127;
7467
+ bytes[31] |= 64;
7468
+ return bytes;
7469
+ }
7470
+ function uvRatio(u, v) {
7471
+ const P = ed25519_CURVE_p;
7472
+ const v3 = mod(v * v * v, P);
7473
+ const v7 = mod(v3 * v3 * v, P);
7474
+ const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
7475
+ let x = mod(u * v3 * pow, P);
7476
+ const vx2 = mod(v * x * x, P);
7477
+ const root1 = x;
7478
+ const root2 = mod(x * ED25519_SQRT_M1, P);
7479
+ const useRoot1 = vx2 === u;
7480
+ const useRoot2 = vx2 === mod(-u, P);
7481
+ const noRoot = vx2 === mod(-u * ED25519_SQRT_M1, P);
7482
+ if (useRoot1)
7483
+ x = root1;
7484
+ if (useRoot2 || noRoot)
7485
+ x = root2;
7486
+ if (isNegativeLE(x, P))
7487
+ x = mod(-x, P);
7488
+ return { isValid: useRoot1 || useRoot2, value: x };
7489
+ }
7490
+ function calcElligatorRistrettoMap(r0) {
7491
+ const { d } = ed25519_CURVE;
7492
+ const P = ed25519_CURVE_p;
7493
+ const mod2 = (n) => Fp.create(n);
7494
+ const r = mod2(SQRT_M1 * r0 * r0);
7495
+ const Ns = mod2((r + _1n5) * ONE_MINUS_D_SQ);
7496
+ let c = BigInt(-1);
7497
+ const D = mod2((c - d * r) * mod2(r + d));
7498
+ let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D);
7499
+ let s_ = mod2(s * r0);
7500
+ if (!isNegativeLE(s_, P))
7501
+ s_ = mod2(-s_);
7502
+ if (!Ns_D_is_sq)
7503
+ s = s_;
7504
+ if (!Ns_D_is_sq)
7505
+ c = r;
7506
+ const Nt = mod2(c * (r - _1n5) * D_MINUS_ONE_SQ - D);
7507
+ const s2 = s * s;
7508
+ const W0 = mod2((s + s) * D);
7509
+ const W1 = mod2(Nt * SQRT_AD_MINUS_ONE);
7510
+ const W2 = mod2(_1n5 - s2);
7511
+ const W3 = mod2(_1n5 + s2);
7512
+ return new ed25519.Point(mod2(W0 * W3), mod2(W2 * W1), mod2(W1 * W3), mod2(W0 * W2));
7513
+ }
7514
+ function ristretto255_map(bytes) {
7515
+ abytes(bytes, 64);
7516
+ const r1 = bytes255ToNumberLE(bytes.subarray(0, 32));
7517
+ const R1 = calcElligatorRistrettoMap(r1);
7518
+ const r2 = bytes255ToNumberLE(bytes.subarray(32, 64));
7519
+ const R2 = calcElligatorRistrettoMap(r2);
7520
+ return new _RistrettoPoint(R1.add(R2));
7521
+ }
7522
+ var _0n5, _1n5, _2n3, _3n2, _5n2, _8n3, ed25519_CURVE_p, ed25519_CURVE, ED25519_SQRT_M1, Fp, Fn, ed25519Defaults, ed25519, SQRT_M1, SQRT_AD_MINUS_ONE, INVSQRT_A_MINUS_D, ONE_MINUS_D_SQ, D_MINUS_ONE_SQ, invertSqrt, MAX_255B, bytes255ToNumberLE, _RistrettoPoint;
7523
+ var init_ed25519 = __esm({
7524
+ "../../node_modules/@noble/curves/esm/ed25519.js"() {
7525
+ "use strict";
7526
+ init_sha2();
7527
+ init_utils();
7528
+ init_curve();
7529
+ init_edwards();
7530
+ init_modular();
7531
+ init_utils2();
7532
+ _0n5 = /* @__PURE__ */ BigInt(0);
7533
+ _1n5 = BigInt(1);
7534
+ _2n3 = BigInt(2);
7535
+ _3n2 = BigInt(3);
7536
+ _5n2 = BigInt(5);
7537
+ _8n3 = BigInt(8);
7538
+ ed25519_CURVE_p = BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed");
7539
+ ed25519_CURVE = /* @__PURE__ */ (() => ({
7540
+ p: ed25519_CURVE_p,
7541
+ n: BigInt("0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"),
7542
+ h: _8n3,
7543
+ a: BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec"),
7544
+ d: BigInt("0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3"),
7545
+ Gx: BigInt("0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a"),
7546
+ Gy: BigInt("0x6666666666666666666666666666666666666666666666666666666666666658")
7547
+ }))();
7548
+ ED25519_SQRT_M1 = /* @__PURE__ */ BigInt("19681161376707505956807079304988542015446066515923890162744021073123829784752");
7549
+ Fp = /* @__PURE__ */ (() => Field(ed25519_CURVE.p, { isLE: true }))();
7550
+ Fn = /* @__PURE__ */ (() => Field(ed25519_CURVE.n, { isLE: true }))();
7551
+ ed25519Defaults = /* @__PURE__ */ (() => ({
7552
+ ...ed25519_CURVE,
7553
+ Fp,
7554
+ hash: sha512,
7555
+ adjustScalarBytes,
7556
+ // dom2
7557
+ // Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
7558
+ // Constant-time, u/√v
7559
+ uvRatio
7560
+ }))();
7561
+ ed25519 = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();
7562
+ SQRT_M1 = ED25519_SQRT_M1;
7563
+ SQRT_AD_MINUS_ONE = /* @__PURE__ */ BigInt("25063068953384623474111414158702152701244531502492656460079210482610430750235");
7564
+ INVSQRT_A_MINUS_D = /* @__PURE__ */ BigInt("54469307008909316920995813868745141605393597292927456921205312896311721017578");
7565
+ ONE_MINUS_D_SQ = /* @__PURE__ */ BigInt("1159843021668779879193775521855586647937357759715417654439879720876111806838");
7566
+ D_MINUS_ONE_SQ = /* @__PURE__ */ BigInt("40440834346308536858101042469323190826248399146238708352240133220865137265952");
7567
+ invertSqrt = (number) => uvRatio(_1n5, number);
7568
+ MAX_255B = /* @__PURE__ */ BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
7569
+ bytes255ToNumberLE = (bytes) => ed25519.Point.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
7570
+ _RistrettoPoint = class __RistrettoPoint extends PrimeEdwardsPoint {
7571
+ constructor(ep) {
7572
+ super(ep);
7573
+ }
7574
+ static fromAffine(ap) {
7575
+ return new __RistrettoPoint(ed25519.Point.fromAffine(ap));
7576
+ }
7577
+ assertSame(other) {
7578
+ if (!(other instanceof __RistrettoPoint))
7579
+ throw new Error("RistrettoPoint expected");
7580
+ }
7581
+ init(ep) {
7582
+ return new __RistrettoPoint(ep);
7583
+ }
7584
+ /** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
7585
+ static hashToCurve(hex3) {
7586
+ return ristretto255_map(ensureBytes("ristrettoHash", hex3, 64));
7587
+ }
7588
+ static fromBytes(bytes) {
7589
+ abytes(bytes, 32);
7590
+ const { a, d } = ed25519_CURVE;
7591
+ const P = ed25519_CURVE_p;
7592
+ const mod2 = (n) => Fp.create(n);
7593
+ const s = bytes255ToNumberLE(bytes);
7594
+ if (!equalBytes(Fp.toBytes(s), bytes) || isNegativeLE(s, P))
7595
+ throw new Error("invalid ristretto255 encoding 1");
7596
+ const s2 = mod2(s * s);
7597
+ const u1 = mod2(_1n5 + a * s2);
7598
+ const u2 = mod2(_1n5 - a * s2);
7599
+ const u1_2 = mod2(u1 * u1);
7600
+ const u2_2 = mod2(u2 * u2);
7601
+ const v = mod2(a * d * u1_2 - u2_2);
7602
+ const { isValid: isValid3, value: I } = invertSqrt(mod2(v * u2_2));
7603
+ const Dx = mod2(I * u2);
7604
+ const Dy = mod2(I * Dx * v);
7605
+ let x = mod2((s + s) * Dx);
7606
+ if (isNegativeLE(x, P))
7607
+ x = mod2(-x);
7608
+ const y = mod2(u1 * Dy);
7609
+ const t = mod2(x * y);
7610
+ if (!isValid3 || isNegativeLE(t, P) || y === _0n5)
7611
+ throw new Error("invalid ristretto255 encoding 2");
7612
+ return new __RistrettoPoint(new ed25519.Point(x, y, _1n5, t));
7613
+ }
7614
+ /**
7615
+ * Converts ristretto-encoded string to ristretto point.
7616
+ * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode).
7617
+ * @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
7618
+ */
7619
+ static fromHex(hex3) {
7620
+ return __RistrettoPoint.fromBytes(ensureBytes("ristrettoHex", hex3, 32));
7621
+ }
7622
+ static msm(points, scalars) {
7623
+ return pippenger(__RistrettoPoint, ed25519.Point.Fn, points, scalars);
7624
+ }
7625
+ /**
7626
+ * Encodes ristretto point to Uint8Array.
7627
+ * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode).
7628
+ */
7629
+ toBytes() {
7630
+ let { X, Y, Z, T } = this.ep;
7631
+ const P = ed25519_CURVE_p;
7632
+ const mod2 = (n) => Fp.create(n);
7633
+ const u1 = mod2(mod2(Z + Y) * mod2(Z - Y));
7634
+ const u2 = mod2(X * Y);
7635
+ const u2sq = mod2(u2 * u2);
7636
+ const { value: invsqrt } = invertSqrt(mod2(u1 * u2sq));
7637
+ const D1 = mod2(invsqrt * u1);
7638
+ const D2 = mod2(invsqrt * u2);
7639
+ const zInv = mod2(D1 * D2 * T);
7640
+ let D;
7641
+ if (isNegativeLE(T * zInv, P)) {
7642
+ let _x = mod2(Y * SQRT_M1);
7643
+ let _y = mod2(X * SQRT_M1);
7644
+ X = _x;
7645
+ Y = _y;
7646
+ D = mod2(D1 * INVSQRT_A_MINUS_D);
7647
+ } else {
7648
+ D = D2;
7649
+ }
7650
+ if (isNegativeLE(X * zInv, P))
7651
+ Y = mod2(-Y);
7652
+ let s = mod2((Z - Y) * D);
7653
+ if (isNegativeLE(s, P))
7654
+ s = mod2(-s);
7655
+ return Fp.toBytes(s);
7656
+ }
7657
+ /**
7658
+ * Compares two Ristretto points.
7659
+ * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals).
7660
+ */
7661
+ equals(other) {
7662
+ this.assertSame(other);
7663
+ const { X: X1, Y: Y1 } = this.ep;
7664
+ const { X: X2, Y: Y2 } = other.ep;
7665
+ const mod2 = (n) => Fp.create(n);
7666
+ const one = mod2(X1 * Y2) === mod2(Y1 * X2);
7667
+ const two = mod2(Y1 * Y2) === mod2(X1 * X2);
7668
+ return one || two;
7669
+ }
7670
+ is0() {
7671
+ return this.equals(__RistrettoPoint.ZERO);
7672
+ }
7673
+ };
7674
+ _RistrettoPoint.BASE = /* @__PURE__ */ (() => new _RistrettoPoint(ed25519.Point.BASE))();
7675
+ _RistrettoPoint.ZERO = /* @__PURE__ */ (() => new _RistrettoPoint(ed25519.Point.ZERO))();
7676
+ _RistrettoPoint.Fp = /* @__PURE__ */ (() => Fp)();
7677
+ _RistrettoPoint.Fn = /* @__PURE__ */ (() => Fn)();
7385
7678
  }
7386
- subtract(other) {
7387
- this.assertSame(other);
7388
- return this.init(this.ep.subtract(other.ep));
7679
+ });
7680
+
7681
+ // ../replica/dist/index.js
7682
+ function assertFolderPrefix(prefix) {
7683
+ const validFolder = typeof prefix === "string" && prefix.length > 0 && !prefix.startsWith("/") && !prefix.includes("//") && prefix.endsWith("/");
7684
+ if (prefix !== "/" && !validFolder) {
7685
+ const folderHint = typeof prefix === "string" ? prefix.replace(/^\/+/, "").replace(/\/+$/, "") : "";
7686
+ const suggestion = folderHint.length > 0 && !folderHint.includes("//") ? ` Use ${JSON.stringify(`${folderHint}/`)}.` : ' Use "/" or a slash-terminated folder path such as "notes/".';
7687
+ throw new TypeError(`Replication prefix must be "/" or a non-empty slash-terminated folder path without empty segments.${suggestion}`);
7389
7688
  }
7390
- multiply(scalar) {
7391
- return this.init(this.ep.multiply(scalar));
7689
+ }
7690
+ function assertFolderPrefix2(prefix) {
7691
+ try {
7692
+ assertFolderPrefix(prefix);
7693
+ } catch {
7694
+ const folderHint = typeof prefix === "string" ? prefix.replace(/^\/+/, "").replace(/\/+$/, "") : "";
7695
+ const suggestion = prefix === "" ? 'use "/" for the whole space' : folderHint.length > 0 && !folderHint.includes("//") ? `use ${JSON.stringify(`${folderHint}/`)}` : 'use "/" for the whole space or a folder path such as "notes/"';
7696
+ throw new ReplicaError(
7697
+ ReplicaErrorCode.INVALID_ARGUMENT,
7698
+ `Replication prefix ${JSON.stringify(prefix)} must be "/" or a non-empty folder path ending in / without empty segments; ${suggestion}. Run \`tc replica reset --replica <name> --purge\` and recreate it with the folder prefix.`
7699
+ );
7392
7700
  }
7393
- multiplyUnsafe(scalar) {
7394
- return this.init(this.ep.multiplyUnsafe(scalar));
7701
+ }
7702
+ var ReplicaErrorCode, REPLICA_ERROR, ReplicaError, PENDING_REFUSALS, encoder;
7703
+ var init_dist = __esm({
7704
+ "../replica/dist/index.js"() {
7705
+ "use strict";
7706
+ ReplicaErrorCode = {
7707
+ /** Another process holds the sync lease. */
7708
+ BUSY: "REPLICA_BUSY",
7709
+ /** No replica with this name exists. */
7710
+ NOT_FOUND: "REPLICA_NOT_FOUND",
7711
+ /** The replica exists but its stored configuration differs from the request. */
7712
+ CONFIG_MISMATCH: "REPLICA_CONFIG_MISMATCH",
7713
+ /** The runtime has no supported durable store (e.g. Node < 22.13). */
7714
+ RUNTIME_UNSUPPORTED: "RUNTIME_UNSUPPORTED",
7715
+ /** A required open() argument is missing or malformed (e.g. `principal`). */
7716
+ INVALID_ARGUMENT: "REPLICA_INVALID_ARGUMENT",
7717
+ /** The local store failed (I/O, corruption, schema). */
7718
+ STORAGE_ERROR: "STORAGE_ERROR",
7719
+ /** The local disk is full. */
7720
+ STORAGE_FULL: "STORAGE_FULL",
7721
+ /** The key is outside the replica's prefix. */
7722
+ NOT_COVERED: "NOT_COVERED",
7723
+ /** Syncing the secrets space or the vault namespace needs an explicit opt-in. */
7724
+ SECRETS_OPT_IN_REQUIRED: "SECRETS_OPT_IN_REQUIRED",
7725
+ /** No grant is installed, or none covers the requested scope. */
7726
+ GRANT_MISSING: "GRANT_MISSING",
7727
+ /** The grant bytes are not a verifiable UCAN, or its signature is wrong. */
7728
+ GRANT_INVALID: "GRANT_INVALID",
7729
+ /** SIWE/CACAO and other non-UCAN grants cannot be used for a replica. */
7730
+ GRANT_FORMAT_UNSUPPORTED: "GRANT_FORMAT_UNSUPPORTED",
7731
+ /** The grant was issued to a different device. */
7732
+ GRANT_AUDIENCE_MISMATCH: "GRANT_AUDIENCE_MISMATCH",
7733
+ /** The grant lacks `tinycloud.kv/sync` or `tinycloud.kv/get` on the prefix. */
7734
+ GRANT_NOT_COVERING: "GRANT_NOT_COVERING",
7735
+ GRANT_NOT_YET_VALID: "GRANT_NOT_YET_VALID",
7736
+ GRANT_EXPIRED: "GRANT_EXPIRED",
7737
+ /** The configured node does not advertise whole-space KV sync support. */
7738
+ REPLICATION_ROOT_UNSUPPORTED: "REPLICATION_ROOT_UNSUPPORTED",
7739
+ GRANT_REVOKED: "GRANT_REVOKED",
7740
+ /** The node refused the invocation for a reason other than revocation. */
7741
+ GRANT_UNAUTHORIZED: "GRANT_UNAUTHORIZED",
7742
+ /** The node refused the retention grant. */
7743
+ RETENTION_GRANT_REFUSED: "RETENTION_GRANT_REFUSED",
7744
+ /** The node could not be reached. */
7745
+ NETWORK_ERROR: "NETWORK_ERROR",
7746
+ /** The node answered with an unexpected status. */
7747
+ NODE_ERROR: "NODE_ERROR",
7748
+ /** The node's answer violates the wire contract. */
7749
+ PROTOCOL_ERROR: "PROTOCOL_ERROR",
7750
+ /** The node asked for a fresh bootstrap (410). Handled inside sync. */
7751
+ RESET_REQUIRED: "RESET_REQUIRED",
7752
+ /** The feed now comes from a different node than the one pinned. */
7753
+ SOURCE_CHANGED: "SOURCE_CHANGED",
7754
+ /** The feed delivered a key outside the replica's prefix. */
7755
+ SCOPE_VIOLATION: "SCOPE_VIOLATION",
7756
+ /** Fetched bytes do not hash to the ETag the node attested for them. */
7757
+ CONTENT_MISMATCH: "CONTENT_MISMATCH",
7758
+ /** A stored blob no longer hashes to its name. */
7759
+ INTEGRITY_ERROR: "INTEGRITY_ERROR",
7760
+ /** The replica handle was closed; in-flight calls settle with this code. */
7761
+ CLOSED: "REPLICA_CLOSED"
7762
+ };
7763
+ REPLICA_ERROR = /* @__PURE__ */ Symbol.for("tinycloud.replica.error");
7764
+ ReplicaError = class extends Error {
7765
+ [REPLICA_ERROR] = true;
7766
+ code;
7767
+ detail;
7768
+ constructor(code3, message, detail, options) {
7769
+ super(message, options);
7770
+ this.name = "ReplicaError";
7771
+ this.code = code3;
7772
+ if (detail !== void 0) this.detail = detail;
7773
+ }
7774
+ };
7775
+ PENDING_REFUSALS = /* @__PURE__ */ new Set([
7776
+ ReplicaErrorCode.GRANT_UNAUTHORIZED,
7777
+ ReplicaErrorCode.GRANT_EXPIRED,
7778
+ ReplicaErrorCode.GRANT_NOT_YET_VALID
7779
+ ]);
7780
+ encoder = new TextEncoder();
7395
7781
  }
7396
- double() {
7397
- return this.init(this.ep.double());
7782
+ });
7783
+
7784
+ // src/lib/replication-config.ts
7785
+ import { join as join5 } from "path";
7786
+ import { profilePath, withProfileLock as withProfileLock2 } from "@tinycloud/operations/state";
7787
+ import { sqliteReplicaStorage } from "@tinycloud/node-sdk";
7788
+ function createCliReplicationConfig(profile, settings, output, enabled) {
7789
+ if (!enabled || !settings || settings.prefixes.length === 0) return void 0;
7790
+ for (const prefix of settings.prefixes) {
7791
+ try {
7792
+ assertFolderPrefix2(prefix);
7793
+ } catch (error) {
7794
+ const trimmed = prefix.replace(/^\/+/, "").replace(/\/+$/, "");
7795
+ const replacement = trimmed.length > 0 && !trimmed.includes("//") ? `${trimmed}/` : "/";
7796
+ throw new CLIError(
7797
+ "INVALID_REPLICATION_PREFIX",
7798
+ error instanceof Error ? error.message : String(error),
7799
+ ExitCode.USAGE_ERROR,
7800
+ { hint: `Replace the saved replication prefix with \`tc auth login --replication-prefix ${replacement}\`; purging a replica alone does not change this profile setting.` }
7801
+ );
7802
+ }
7398
7803
  }
7399
- negate() {
7400
- return this.init(this.ep.negate());
7804
+ const root = profilePath(profile);
7805
+ return {
7806
+ enabled: true,
7807
+ prefixes: settings.prefixes,
7808
+ allowSecrets: settings.allowSecrets === true,
7809
+ mode: "foreground",
7810
+ verify: process.env.TC_REPLICATION_VERIFY === "1",
7811
+ maxStalenessMs: Number(process.env.TC_REPLICATION_MAX_STALENESS_MS ?? 6e4),
7812
+ staleSyncTimeoutMs: Number(process.env.TC_REPLICATION_SYNC_TIMEOUT_MS ?? 1e4),
7813
+ storage: sqliteReplicaStorage({
7814
+ dir: join5(root, "replication"),
7815
+ guard: (section) => withProfileLock2(profile, section, { requireProfile: true, timeoutMs: 35e3 })
7816
+ }),
7817
+ onEvent: createReplicationEventSink(root, output)
7818
+ };
7819
+ }
7820
+ var init_replication_config = __esm({
7821
+ "src/lib/replication-config.ts"() {
7822
+ "use strict";
7823
+ init_replication_log();
7824
+ init_dist();
7825
+ init_errors();
7826
+ init_constants();
7401
7827
  }
7402
- precompute(windowSize, isLazy) {
7403
- return this.init(this.ep.precompute(windowSize, isLazy));
7828
+ });
7829
+
7830
+ // src/lib/sdk.ts
7831
+ var sdk_exports = {};
7832
+ __export(sdk_exports, {
7833
+ bootstrapDelegatedSession: () => bootstrapDelegatedSession,
7834
+ closeReplication: () => closeReplication,
7835
+ createSDKInstance: () => createSDKInstance,
7836
+ ensureAuthenticated: () => ensureAuthenticated,
7837
+ jwkHasPrivateParameter: () => jwkHasPrivateParameter,
7838
+ registerReplication: () => registerReplication,
7839
+ registeredReplications: () => registeredReplications,
7840
+ replicationForProfile: () => replicationForProfile,
7841
+ restoreProfileSessionNode: () => restoreProfileSessionNode,
7842
+ selectSignerJwk: () => selectSignerJwk,
7843
+ storedSessionVerifiesLocally: () => storedSessionVerifiesLocally
7844
+ });
7845
+ import { TinyCloudNode, signedCompactUcanExpiry } from "@tinycloud/node-sdk";
7846
+ function jwkHasPrivateParameter(jwk) {
7847
+ if (!jwk || typeof jwk !== "object") return false;
7848
+ const d = jwk.d;
7849
+ return typeof d === "string" && d.length > 0;
7850
+ }
7851
+ function selectSignerJwk(sessionJwk, key) {
7852
+ if (jwkHasPrivateParameter(sessionJwk)) {
7853
+ return sessionJwk;
7404
7854
  }
7405
- /** @deprecated use `toBytes` */
7406
- toRawBytes() {
7407
- return this.toBytes();
7855
+ return key ?? void 0;
7856
+ }
7857
+ function signerJwkForProfile(profileName, sessionJwk, key) {
7858
+ const jwk = selectSignerJwk(sessionJwk, key);
7859
+ if (jwkHasPrivateParameter(jwk)) {
7860
+ return jwk;
7408
7861
  }
7409
- };
7410
- function eddsa(Point, cHash, eddsaOpts = {}) {
7411
- if (typeof cHash !== "function")
7412
- throw new Error('"hash" function param is required');
7413
- _validateObject(eddsaOpts, {}, {
7414
- adjustScalarBytes: "function",
7415
- randomBytes: "function",
7416
- domain: "function",
7417
- prehash: "function",
7418
- mapToCurve: "function"
7419
- });
7420
- const { prehash } = eddsaOpts;
7421
- const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
7422
- const randomBytes3 = eddsaOpts.randomBytes || randomBytes;
7423
- const adjustScalarBytes2 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
7424
- const domain = eddsaOpts.domain || ((data, ctx, phflag) => {
7425
- _abool2(phflag, "phflag");
7426
- if (ctx.length || phflag)
7427
- throw new Error("Contexts/pre-hash are not supported");
7428
- return data;
7429
- });
7430
- function modN_LE(hash) {
7431
- return Fn2.create(bytesToNumberLE(hash));
7862
+ throw new CLIError(
7863
+ "AUTH_REQUIRED",
7864
+ `Profile "${profileName}" cannot restore its session because its private key material is missing.`,
7865
+ ExitCode.AUTH_REQUIRED,
7866
+ {
7867
+ hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey`
7868
+ }
7869
+ );
7870
+ }
7871
+ function signedLifetimePassed(delegation) {
7872
+ const authorization = delegation.delegationHeader?.Authorization;
7873
+ const expiry = typeof authorization === "string" ? signedCompactUcanExpiry(authorization) : void 0;
7874
+ return expiry !== void 0 && expiry.getTime() <= Date.now();
7875
+ }
7876
+ async function restoreProfileSession(node, profileName, profile, sessionData) {
7877
+ try {
7878
+ await node.restoreSession(sessionData);
7879
+ } catch (error) {
7880
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "AUTH_EXPIRED") {
7881
+ throw sessionExpiredError(profileName, profile === null ? void 0 : resolveProfilePosture(profile));
7882
+ }
7883
+ throw error;
7432
7884
  }
7433
- function getPrivateScalar(key) {
7434
- const len = lengths.secretKey;
7435
- key = ensureBytes("private key", key, len);
7436
- const hashed = ensureBytes("hashed private key", cHash(key), 2 * len);
7437
- const head = adjustScalarBytes2(hashed.slice(0, len));
7438
- const prefix = hashed.slice(len, 2 * len);
7439
- const scalar = modN_LE(head);
7440
- return { head, prefix, scalar };
7885
+ }
7886
+ function persistedSessionData(profileName, profile, session, key) {
7887
+ return {
7888
+ delegationHeader: session.delegationHeader,
7889
+ delegationCid: session.delegationCid,
7890
+ spaceId: session.spaceId,
7891
+ jwk: signerJwkForProfile(profileName, session.jwk, key),
7892
+ verificationMethod: session.verificationMethod ?? profile.sessionDid ?? profile.did,
7893
+ address: session.address,
7894
+ chainId: session.chainId,
7895
+ siwe: session.siwe,
7896
+ signature: session.signature
7897
+ };
7898
+ }
7899
+ async function restoreProfileSessionNode(ctx) {
7900
+ const profile = await ProfileManager.getProfile(ctx.profile);
7901
+ const session = await ProfileManager.getSession(ctx.profile);
7902
+ if (!session) return { unavailable: "session_missing" };
7903
+ if (typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || session.delegationHeader === null || typeof session.delegationHeader !== "object" || !("Authorization" in session.delegationHeader) || typeof session.delegationHeader.Authorization !== "string") {
7904
+ return { unavailable: "session_invalid" };
7441
7905
  }
7442
- function getExtendedPublicKey(secretKey) {
7443
- const { head, prefix, scalar } = getPrivateScalar(secretKey);
7444
- const point = BASE.multiply(scalar);
7445
- const pointBytes = point.toBytes();
7446
- return { head, prefix, scalar, point, pointBytes };
7906
+ if (typeof session.expiresAt === "string" && Date.parse(session.expiresAt) <= Date.now()) {
7907
+ return { unavailable: "session_expired" };
7447
7908
  }
7448
- function getPublicKey(secretKey) {
7449
- return getExtendedPublicKey(secretKey).pointBytes;
7909
+ const key = await ProfileManager.getKey(ctx.profile);
7910
+ try {
7911
+ const node = new TinyCloudNode({
7912
+ host: ctx.host,
7913
+ ...profile.authMethod === "local" && profile.privateKey ? { privateKey: profile.privateKey } : {}
7914
+ });
7915
+ await restoreProfileSession(node, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
7916
+ return { node };
7917
+ } catch (error) {
7918
+ if (error instanceof CLIError && error.metadata?.reason === "session_expired") {
7919
+ return { unavailable: "session_expired" };
7920
+ }
7921
+ return { unavailable: "session_invalid" };
7450
7922
  }
7451
- function hashDomainToScalar(context = Uint8Array.of(), ...msgs) {
7452
- const msg = concatBytes(...msgs);
7453
- return modN_LE(cHash(domain(msg, ensureBytes("context", context), !!prehash)));
7923
+ }
7924
+ async function storedSessionVerifiesLocally(profileName) {
7925
+ const profile = await ProfileManager.getProfile(profileName).catch(() => null);
7926
+ if (profile === null) return true;
7927
+ const local = profile.authMethod === "local" && typeof profile.privateKey === "string";
7928
+ try {
7929
+ const session = await ProfileManager.getSession(profileName);
7930
+ if (!session || typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || session.delegationHeader === null || typeof session.delegationHeader !== "object" || !("Authorization" in session.delegationHeader) || typeof session.delegationHeader.Authorization !== "string") return true;
7931
+ const key = await ProfileManager.getKey(profileName);
7932
+ const node = new TinyCloudNode({
7933
+ host: profile.host ?? DEFAULT_HOST,
7934
+ ...local ? { privateKey: profile.privateKey } : {}
7935
+ });
7936
+ await restoreProfileSession(node, profileName, profile, persistedSessionData(profileName, profile, session, key));
7937
+ return true;
7938
+ } catch {
7939
+ return false;
7454
7940
  }
7455
- function sign(msg, secretKey, options = {}) {
7456
- msg = ensureBytes("message", msg);
7457
- if (prehash)
7458
- msg = prehash(msg);
7459
- const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
7460
- const r = hashDomainToScalar(options.context, prefix, msg);
7461
- const R = BASE.multiply(r).toBytes();
7462
- const k = hashDomainToScalar(options.context, R, pointBytes, msg);
7463
- const s = Fn2.create(r + k * scalar);
7464
- if (!Fn2.isValid(s))
7465
- throw new Error("sign failed: invalid s");
7466
- const rs = concatBytes(R, Fn2.toBytes(s));
7467
- return _abytes2(rs, lengths.signature, "result");
7941
+ }
7942
+ async function createSDKInstance(ctx, options) {
7943
+ const profile = options?.privateKey ? await ProfileManager.getProfile(ctx.profile).catch(() => null) : await ProfileManager.getProfile(ctx.profile);
7944
+ const session = await ProfileManager.getSession(ctx.profile);
7945
+ const key = await ProfileManager.getKey(ctx.profile);
7946
+ const effectivePrivateKey = options?.privateKey ?? profile?.privateKey;
7947
+ if (!key && !effectivePrivateKey && !(profile?.authMethod === "openkey" && session !== null)) {
7948
+ throw new CLIError(
7949
+ "AUTH_REQUIRED",
7950
+ `No key found for profile "${ctx.profile}". Run \`tc --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`,
7951
+ ExitCode.AUTH_REQUIRED
7952
+ );
7468
7953
  }
7469
- const verifyOpts = { zip215: true };
7470
- function verify(sig, msg, publicKey, options = verifyOpts) {
7471
- const { context, zip215 } = options;
7472
- const len = lengths.signature;
7473
- sig = ensureBytes("signature", sig, len);
7474
- msg = ensureBytes("message", msg);
7475
- publicKey = ensureBytes("publicKey", publicKey, lengths.publicKey);
7476
- if (zip215 !== void 0)
7477
- _abool2(zip215, "zip215");
7478
- if (prehash)
7479
- msg = prehash(msg);
7480
- const mid = len / 2;
7481
- const r = sig.subarray(0, mid);
7482
- const s = bytesToNumberLE(sig.subarray(mid, len));
7483
- let A, R, SB;
7484
- try {
7485
- A = Point.fromBytes(publicKey, zip215);
7486
- R = Point.fromBytes(r, zip215);
7487
- SB = BASE.multiplyUnsafe(s);
7488
- } catch (error) {
7489
- return false;
7954
+ if (profile?.authMethod === "local" && effectivePrivateKey) {
7955
+ const replication2 = createCliReplicationConfig(
7956
+ ctx.profile,
7957
+ profile?.replication,
7958
+ { debug: ctx.replicationDebug, quiet: ctx.quiet },
7959
+ ctx.replication
7960
+ );
7961
+ if (ctx.replication && !replication2) {
7962
+ process.stderr.write("[replication] Enabled, but this profile has no configured prefixes; reads use the network.\n");
7490
7963
  }
7491
- if (!zip215 && A.isSmallOrder())
7492
- return false;
7493
- const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
7494
- const RkA = R.add(A.multiplyUnsafe(k));
7495
- return RkA.subtract(SB).clearCofactor().is0();
7496
- }
7497
- const _size = Fp2.BYTES;
7498
- const lengths = {
7499
- secretKey: _size,
7500
- publicKey: _size,
7501
- signature: 2 * _size,
7502
- seed: _size
7503
- };
7504
- function randomSecretKey(seed = randomBytes3(lengths.seed)) {
7505
- return _abytes2(seed, lengths.seed, "seed");
7964
+ const node2 = new TinyCloudNode({
7965
+ host: ctx.host,
7966
+ privateKey: effectivePrivateKey,
7967
+ ...replication2 ? { replication: replication2 } : {}
7968
+ });
7969
+ if (node2.replication) registerReplication(ctx.profile, node2.replication);
7970
+ const restoredOwnSession = Boolean(session && session.delegationHeader && session.delegationCid && session.spaceId);
7971
+ if (restoredOwnSession) {
7972
+ await restoreProfileSession(node2, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
7973
+ } else {
7974
+ await node2.signIn();
7975
+ }
7976
+ if (restoredOwnSession) {
7977
+ recordWarnings(await replayAdditionalDelegations(node2, ctx.profile, {
7978
+ host: ctx.host,
7979
+ ownerSpace: profile.spaceId,
7980
+ migrate: options?.privateKey === void 0
7981
+ }));
7982
+ }
7983
+ return node2;
7506
7984
  }
7507
- function keygen(seed) {
7508
- const secretKey = utils.randomSecretKey(seed);
7509
- return { secretKey, publicKey: getPublicKey(secretKey) };
7985
+ const replication = createCliReplicationConfig(
7986
+ ctx.profile,
7987
+ profile?.replication,
7988
+ { debug: ctx.replicationDebug, quiet: ctx.quiet },
7989
+ ctx.replication
7990
+ );
7991
+ if (ctx.replication && !replication) {
7992
+ process.stderr.write("[replication] Enabled, but this profile has no configured prefixes; reads use the network.\n");
7510
7993
  }
7511
- function isValidSecretKey(key) {
7512
- return isBytes(key) && key.length === Fn2.BYTES;
7994
+ const node = new TinyCloudNode({
7995
+ host: ctx.host,
7996
+ privateKey: options?.privateKey,
7997
+ ...replication ? { replication } : {}
7998
+ });
7999
+ if (node.replication) registerReplication(ctx.profile, node.replication);
8000
+ if (options?.privateKey) {
8001
+ await node.signIn();
8002
+ } else if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
8003
+ await restoreProfileSession(node, ctx.profile, profile, persistedSessionData(ctx.profile, profile, session, key));
8004
+ recordWarnings(await replayAdditionalDelegations(node, ctx.profile, {
8005
+ host: ctx.host,
8006
+ ownerSpace: profile?.spaceId,
8007
+ migrate: true
8008
+ }));
7513
8009
  }
7514
- function isValidPublicKey(key, zip215) {
7515
- try {
7516
- return !!Point.fromBytes(key, zip215);
7517
- } catch (error) {
7518
- return false;
8010
+ return node;
8011
+ }
8012
+ async function bootstrapDelegatedSession(ctx, delegation) {
8013
+ const written = await ProfileManager.withLock(ctx.profile, async () => {
8014
+ const previousProfile = await ProfileManager.getProfile(ctx.profile);
8015
+ if (resolveProfilePosture(previousProfile) !== "delegate-session") {
8016
+ throw new CLIError(
8017
+ "AUTH_REQUIRED",
8018
+ `Profile "${ctx.profile}" is not a delegate-session profile.`,
8019
+ ExitCode.AUTH_REQUIRED
8020
+ );
7519
8021
  }
7520
- }
7521
- const utils = {
7522
- getExtendedPublicKey,
7523
- randomSecretKey,
7524
- isValidSecretKey,
7525
- isValidPublicKey,
7526
- /**
7527
- * Converts ed public key to x public key. Uses formula:
7528
- * - ed25519:
7529
- * - `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
7530
- * - `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
7531
- * - ed448:
7532
- * - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
7533
- * - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
7534
- */
7535
- toMontgomery(publicKey) {
7536
- const { y } = Point.fromBytes(publicKey);
7537
- const size = lengths.publicKey;
7538
- const is25519 = size === 32;
7539
- if (!is25519 && size !== 57)
7540
- throw new Error("only defined for 25519 and 448");
7541
- const u = is25519 ? Fp2.div(_1n4 + y, _1n4 - y) : Fp2.div(y - _1n4, y + _1n4);
7542
- return Fp2.toBytes(u);
7543
- },
7544
- toMontgomerySecret(secretKey) {
7545
- const size = lengths.secretKey;
7546
- _abytes2(secretKey, size);
7547
- const hashed = cHash(secretKey.subarray(0, size));
7548
- return adjustScalarBytes2(hashed).subarray(0, size);
7549
- },
7550
- /** @deprecated */
7551
- randomPrivateKey: randomSecretKey,
7552
- /** @deprecated */
7553
- precompute(windowSize = 8, point = Point.BASE) {
7554
- return point.precompute(windowSize, false);
8022
+ const sessionDid = previousProfile.sessionDid ?? previousProfile.did;
8023
+ if (delegation.delegateDID.split("#", 1)[0] !== sessionDid.split("#", 1)[0]) {
8024
+ throw new CLIError(
8025
+ "DELEGATION_AUDIENCE_MISMATCH",
8026
+ `Delegation targets ${delegation.delegateDID}, but profile "${ctx.profile}" uses ${sessionDid}.`,
8027
+ ExitCode.PERMISSION_DENIED
8028
+ );
8029
+ }
8030
+ if (await ProfileManager.getSession(ctx.profile) !== null) {
8031
+ throw new CLIError(
8032
+ "PROFILE_CHANGED_DURING_IMPORT",
8033
+ `Profile "${ctx.profile}" gained a session (another login or import) after this import checked it. Nothing was saved; run the import again.`,
8034
+ ExitCode.ERROR
8035
+ );
8036
+ }
8037
+ if (signedLifetimePassed(delegation)) throw delegationExpiredError();
8038
+ const jwk = signerJwkForProfile(ctx.profile, void 0, await ProfileManager.getKey(ctx.profile));
8039
+ const session = {
8040
+ delegationHeader: delegation.delegationHeader,
8041
+ delegationCid: delegation.cid,
8042
+ spaceId: delegation.spaceId,
8043
+ jwk,
8044
+ verificationMethod: sessionDid
8045
+ };
8046
+ const profile = { ...previousProfile, sessionDid, spaceId: delegation.spaceId };
8047
+ try {
8048
+ await ProfileManager.setSession(ctx.profile, session);
8049
+ await ProfileManager.setProfile(ctx.profile, profile);
8050
+ } catch (error) {
8051
+ throw annotate(error, await restoreBeforeBootstrap(ctx.profile, previousProfile));
7555
8052
  }
7556
- };
7557
- return Object.freeze({
7558
- keygen,
7559
- getPublicKey,
7560
- sign,
7561
- verify,
7562
- utils,
7563
- Point,
7564
- lengths
8053
+ return { previousProfile, profile, session };
7565
8054
  });
7566
- }
7567
- function _eddsa_legacy_opts_to_new(c) {
7568
- const CURVE = {
7569
- a: c.a,
7570
- d: c.d,
7571
- p: c.Fp.ORDER,
7572
- n: c.n,
7573
- h: c.h,
7574
- Gx: c.Gx,
7575
- Gy: c.Gy
7576
- };
7577
- const Fp2 = c.Fp;
7578
- const Fn2 = Field(CURVE.n, c.nBitLength, true);
7579
- const curveOpts = { Fp: Fp2, Fn: Fn2, uvRatio: c.uvRatio };
7580
- const eddsaOpts = {
7581
- randomBytes: c.randomBytes,
7582
- adjustScalarBytes: c.adjustScalarBytes,
7583
- domain: c.domain,
7584
- prehash: c.prehash,
7585
- mapToCurve: c.mapToCurve
8055
+ const abandon = async (cause) => {
8056
+ const reported = cause instanceof CLIError && cause.code === "AUTH_REQUIRED" && signedLifetimePassed(delegation) ? delegationExpiredError() : cause;
8057
+ const note = await ProfileManager.withLock(ctx.profile, async () => {
8058
+ const profile = await ProfileManager.getProfile(ctx.profile).catch((error) => {
8059
+ if (error instanceof CLIError && error.code === "PROFILE_NOT_FOUND") return null;
8060
+ throw error;
8061
+ });
8062
+ const session = await ProfileManager.getSession(ctx.profile);
8063
+ if (profile === null || BOOTSTRAP_PROFILE_FIELDS.some((field) => profile[field] !== written.profile[field]) || JSON.stringify(session) !== JSON.stringify(written.session)) {
8064
+ return `Profile "${ctx.profile}" changed while the import was pending (another login, logout or profile update), so its newer state was kept and the provisional session was not rolled back.`;
8065
+ }
8066
+ return restoreBeforeBootstrap(ctx.profile, written.previousProfile);
8067
+ }, { timeoutMs: PROFILE_COMMIT_LOCK_TIMEOUT_MS }).catch((error) => (
8068
+ // The lock or a read failed: the import's error stays the one reported.
8069
+ `Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check \`tc --profile ${ctx.profile} context\`.`
8070
+ ));
8071
+ throw annotate(reported, note);
7586
8072
  };
7587
- return { CURVE, curveOpts, hash: c.hash, eddsaOpts };
8073
+ let node;
8074
+ try {
8075
+ node = await createSDKInstance(ctx);
8076
+ } catch (error) {
8077
+ return abandon(error);
8078
+ }
8079
+ return { node, abandon };
7588
8080
  }
7589
- function _eddsa_new_output_to_legacy(c, eddsa2) {
7590
- const Point = eddsa2.Point;
7591
- const legacy = Object.assign({}, eddsa2, {
7592
- ExtendedPoint: Point,
7593
- CURVE: c,
7594
- nBitLength: Point.Fn.BITS,
7595
- nByteLength: Point.Fn.BYTES
7596
- });
7597
- return legacy;
8081
+ async function restoreBeforeBootstrap(profileName, previousProfile) {
8082
+ const failures = [];
8083
+ for (const write of [
8084
+ () => ProfileManager.clearSession(profileName),
8085
+ () => ProfileManager.updateProfile(profileName, (current) => {
8086
+ const restored = { ...current };
8087
+ for (const field of BOOTSTRAP_PROFILE_FIELDS) {
8088
+ if (previousProfile[field] === void 0) delete restored[field];
8089
+ else restored[field] = previousProfile[field];
8090
+ }
8091
+ return restored;
8092
+ })
8093
+ ]) {
8094
+ await write().catch((error) => {
8095
+ failures.push(failureName(error));
8096
+ });
8097
+ }
8098
+ if (failures.length === 0) return void 0;
8099
+ return `Rolling back the provisional session of profile "${profileName}" failed too (${failures.join("; ")}); check \`tc --profile ${profileName} context\`.`;
7598
8100
  }
7599
- function twistedEdwards(c) {
7600
- const { CURVE, curveOpts, hash, eddsaOpts } = _eddsa_legacy_opts_to_new(c);
7601
- const Point = edwards(CURVE, curveOpts);
7602
- const EDDSA = eddsa(Point, hash, eddsaOpts);
7603
- return _eddsa_new_output_to_legacy(c, EDDSA);
8101
+ function failureName(error) {
8102
+ if (error instanceof SyntaxError) return "a profile file is not valid JSON";
8103
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") return error.code;
8104
+ return error instanceof Error ? error.name : "unknown error";
7604
8105
  }
7605
-
7606
- // ../../node_modules/@noble/curves/esm/ed25519.js
7607
- var _0n5 = /* @__PURE__ */ BigInt(0);
7608
- var _1n5 = BigInt(1);
7609
- var _2n3 = BigInt(2);
7610
- var _3n2 = BigInt(3);
7611
- var _5n2 = BigInt(5);
7612
- var _8n3 = BigInt(8);
7613
- var ed25519_CURVE_p = BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed");
7614
- var ed25519_CURVE = /* @__PURE__ */ (() => ({
7615
- p: ed25519_CURVE_p,
7616
- n: BigInt("0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"),
7617
- h: _8n3,
7618
- a: BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec"),
7619
- d: BigInt("0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3"),
7620
- Gx: BigInt("0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a"),
7621
- Gy: BigInt("0x6666666666666666666666666666666666666666666666666666666666666658")
7622
- }))();
7623
- function ed25519_pow_2_252_3(x) {
7624
- const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
7625
- const P = ed25519_CURVE_p;
7626
- const x2 = x * x % P;
7627
- const b2 = x2 * x % P;
7628
- const b4 = pow2(b2, _2n3, P) * b2 % P;
7629
- const b5 = pow2(b4, _1n5, P) * x % P;
7630
- const b10 = pow2(b5, _5n2, P) * b5 % P;
7631
- const b20 = pow2(b10, _10n, P) * b10 % P;
7632
- const b40 = pow2(b20, _20n, P) * b20 % P;
7633
- const b80 = pow2(b40, _40n, P) * b40 % P;
7634
- const b160 = pow2(b80, _80n, P) * b80 % P;
7635
- const b240 = pow2(b160, _80n, P) * b80 % P;
7636
- const b250 = pow2(b240, _10n, P) * b10 % P;
7637
- const pow_p_5_8 = pow2(b250, _2n3, P) * x % P;
7638
- return { pow_p_5_8, b2 };
8106
+ function annotate(error, note) {
8107
+ if (note === void 0) return error;
8108
+ const cause = wrapError(error);
8109
+ return new CLIError(cause.code, `${cause.message} ${note}`, cause.exitCode, cause.metadata);
7639
8110
  }
7640
- function adjustScalarBytes(bytes) {
7641
- bytes[0] &= 248;
7642
- bytes[31] &= 127;
7643
- bytes[31] |= 64;
7644
- return bytes;
8111
+ async function ensureAuthenticated(ctx, options) {
8112
+ if (options?.privateKey) {
8113
+ return createSDKInstance(ctx, options);
8114
+ }
8115
+ const profile = await ProfileManager.getProfile(ctx.profile).catch(() => null);
8116
+ if (profile?.authMethod === "local" && profile.privateKey) {
8117
+ return createSDKInstance(ctx);
8118
+ }
8119
+ const session = await ProfileManager.getSession(ctx.profile);
8120
+ if (!session) {
8121
+ throw new CLIError(
8122
+ "AUTH_REQUIRED",
8123
+ `Not authenticated. Run \`tc auth login\` or \`tc init\` first.`,
8124
+ ExitCode.AUTH_REQUIRED
8125
+ );
8126
+ }
8127
+ return createSDKInstance(ctx, options);
7645
8128
  }
7646
- var ED25519_SQRT_M1 = /* @__PURE__ */ BigInt("19681161376707505956807079304988542015446066515923890162744021073123829784752");
7647
- function uvRatio(u, v) {
7648
- const P = ed25519_CURVE_p;
7649
- const v3 = mod(v * v * v, P);
7650
- const v7 = mod(v3 * v3 * v, P);
7651
- const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
7652
- let x = mod(u * v3 * pow, P);
7653
- const vx2 = mod(v * x * x, P);
7654
- const root1 = x;
7655
- const root2 = mod(x * ED25519_SQRT_M1, P);
7656
- const useRoot1 = vx2 === u;
7657
- const useRoot2 = vx2 === mod(-u, P);
7658
- const noRoot = vx2 === mod(-u * ED25519_SQRT_M1, P);
7659
- if (useRoot1)
7660
- x = root1;
7661
- if (useRoot2 || noRoot)
7662
- x = root2;
7663
- if (isNegativeLE(x, P))
7664
- x = mod(-x, P);
7665
- return { isValid: useRoot1 || useRoot2, value: x };
8129
+ var BOOTSTRAP_PROFILE_FIELDS;
8130
+ var init_sdk = __esm({
8131
+ "src/lib/sdk.ts"() {
8132
+ "use strict";
8133
+ init_profiles();
8134
+ init_types();
8135
+ init_errors();
8136
+ init_constants();
8137
+ init_permissions();
8138
+ init_session_expired();
8139
+ init_warnings();
8140
+ init_delegation_expiry();
8141
+ init_replication_config();
8142
+ init_replication_registry();
8143
+ init_replication_registry();
8144
+ BOOTSTRAP_PROFILE_FIELDS = ["sessionDid", "spaceId"];
8145
+ }
8146
+ });
8147
+
8148
+ // src/lib/host.ts
8149
+ var host_exports = {};
8150
+ __export(host_exports, {
8151
+ completeDeferredLocalDiscovery: () => completeDeferredLocalDiscovery,
8152
+ discoverLocalNodeHost: () => discoverLocalNodeHost,
8153
+ isRootAuthority: () => isRootAuthority,
8154
+ ownerDidFromSpaceUri: () => ownerDidFromSpaceUri,
8155
+ profileLocalNodeIdentityStore: () => profileLocalNodeIdentityStore,
8156
+ resolveHostSpace: () => resolveHostSpace,
8157
+ spaceNameFromUri: () => spaceNameFromUri,
8158
+ unhostedSpaceError: () => unhostedSpaceError
8159
+ });
8160
+ import {
8161
+ discoverLocalTinyCloudNode
8162
+ } from "@tinycloud/sdk-core";
8163
+ function profileLocalNodeIdentityStore(profileName) {
8164
+ return {
8165
+ get: async (url) => {
8166
+ const profile = await ProfileManager.getProfile(profileName).catch(
8167
+ () => null
8168
+ );
8169
+ return profile?.pinnedLocalNodeDids?.[url];
8170
+ },
8171
+ set: async (url, nodeDid) => {
8172
+ if (!await ProfileManager.profileExists(profileName)) return;
8173
+ await ProfileManager.updateProfile(profileName, (profile) => ({
8174
+ ...profile,
8175
+ pinnedLocalNodeDids: {
8176
+ ...profile.pinnedLocalNodeDids,
8177
+ [url]: nodeDid
8178
+ }
8179
+ }));
8180
+ }
8181
+ };
7666
8182
  }
7667
- var Fp = /* @__PURE__ */ (() => Field(ed25519_CURVE.p, { isLE: true }))();
7668
- var Fn = /* @__PURE__ */ (() => Field(ed25519_CURVE.n, { isLE: true }))();
7669
- var ed25519Defaults = /* @__PURE__ */ (() => ({
7670
- ...ed25519_CURVE,
7671
- Fp,
7672
- hash: sha512,
7673
- adjustScalarBytes,
7674
- // dom2
7675
- // Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
7676
- // Constant-time, u/√v
7677
- uvRatio
7678
- }))();
7679
- var ed25519 = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();
7680
- var SQRT_M1 = ED25519_SQRT_M1;
7681
- var SQRT_AD_MINUS_ONE = /* @__PURE__ */ BigInt("25063068953384623474111414158702152701244531502492656460079210482610430750235");
7682
- var INVSQRT_A_MINUS_D = /* @__PURE__ */ BigInt("54469307008909316920995813868745141605393597292927456921205312896311721017578");
7683
- var ONE_MINUS_D_SQ = /* @__PURE__ */ BigInt("1159843021668779879193775521855586647937357759715417654439879720876111806838");
7684
- var D_MINUS_ONE_SQ = /* @__PURE__ */ BigInt("40440834346308536858101042469323190826248399146238708352240133220865137265952");
7685
- var invertSqrt = (number) => uvRatio(_1n5, number);
7686
- var MAX_255B = /* @__PURE__ */ BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
7687
- var bytes255ToNumberLE = (bytes) => ed25519.Point.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
7688
- function calcElligatorRistrettoMap(r0) {
7689
- const { d } = ed25519_CURVE;
7690
- const P = ed25519_CURVE_p;
7691
- const mod2 = (n) => Fp.create(n);
7692
- const r = mod2(SQRT_M1 * r0 * r0);
7693
- const Ns = mod2((r + _1n5) * ONE_MINUS_D_SQ);
7694
- let c = BigInt(-1);
7695
- const D = mod2((c - d * r) * mod2(r + d));
7696
- let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D);
7697
- let s_ = mod2(s * r0);
7698
- if (!isNegativeLE(s_, P))
7699
- s_ = mod2(-s_);
7700
- if (!Ns_D_is_sq)
7701
- s = s_;
7702
- if (!Ns_D_is_sq)
7703
- c = r;
7704
- const Nt = mod2(c * (r - _1n5) * D_MINUS_ONE_SQ - D);
7705
- const s2 = s * s;
7706
- const W0 = mod2((s + s) * D);
7707
- const W1 = mod2(Nt * SQRT_AD_MINUS_ONE);
7708
- const W2 = mod2(_1n5 - s2);
7709
- const W3 = mod2(_1n5 + s2);
7710
- return new ed25519.Point(mod2(W0 * W3), mod2(W2 * W1), mod2(W1 * W3), mod2(W0 * W2));
8183
+ async function discoverLocalNodeHost(profileName, options) {
8184
+ const profile = await ProfileManager.getProfile(profileName).catch(() => null);
8185
+ if (profile?.autoDiscoverLocalNode === false) return { host: null, deferred: false };
8186
+ if (!profile?.localNodeUrl && !profile?.localLinkName) return { host: null, deferred: false };
8187
+ if (options.awaitStoredSession) {
8188
+ const { storedSessionVerifiesLocally: storedSessionVerifiesLocally2 } = await Promise.resolve().then(() => (init_sdk(), sdk_exports));
8189
+ if (!await storedSessionVerifiesLocally2(profileName)) return { host: null, deferred: true };
8190
+ }
8191
+ const discovered = await discoverLocalTinyCloudNode({
8192
+ localNodeUrl: profile?.localNodeUrl,
8193
+ localLinkName: profile?.localLinkName,
8194
+ expectedNodeDid: profile?.expectedNodeDid,
8195
+ identityStore: profileLocalNodeIdentityStore(profileName)
8196
+ });
8197
+ return { host: discovered?.url ?? null, deferred: false };
7711
8198
  }
7712
- function ristretto255_map(bytes) {
7713
- abytes(bytes, 64);
7714
- const r1 = bytes255ToNumberLE(bytes.subarray(0, 32));
7715
- const R1 = calcElligatorRistrettoMap(r1);
7716
- const r2 = bytes255ToNumberLE(bytes.subarray(32, 64));
7717
- const R2 = calcElligatorRistrettoMap(r2);
7718
- return new _RistrettoPoint(R1.add(R2));
8199
+ async function completeDeferredLocalDiscovery(ctx) {
8200
+ const deferred = ctx.localDiscovery;
8201
+ if (deferred === void 0) return;
8202
+ delete ctx.localDiscovery;
8203
+ if (ctx.host !== deferred.deferredFrom) return;
8204
+ const discovered = await discoverLocalNodeHost(ctx.profile, { awaitStoredSession: false });
8205
+ if (discovered.host !== null) ctx.host = discovered.host;
7719
8206
  }
7720
- var _RistrettoPoint = class __RistrettoPoint extends PrimeEdwardsPoint {
7721
- constructor(ep) {
7722
- super(ep);
7723
- }
7724
- static fromAffine(ap) {
7725
- return new __RistrettoPoint(ed25519.Point.fromAffine(ap));
7726
- }
7727
- assertSame(other) {
7728
- if (!(other instanceof __RistrettoPoint))
7729
- throw new Error("RistrettoPoint expected");
7730
- }
7731
- init(ep) {
7732
- return new __RistrettoPoint(ep);
7733
- }
7734
- /** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
7735
- static hashToCurve(hex3) {
7736
- return ristretto255_map(ensureBytes("ristrettoHash", hex3, 64));
7737
- }
7738
- static fromBytes(bytes) {
7739
- abytes(bytes, 32);
7740
- const { a, d } = ed25519_CURVE;
7741
- const P = ed25519_CURVE_p;
7742
- const mod2 = (n) => Fp.create(n);
7743
- const s = bytes255ToNumberLE(bytes);
7744
- if (!equalBytes(Fp.toBytes(s), bytes) || isNegativeLE(s, P))
7745
- throw new Error("invalid ristretto255 encoding 1");
7746
- const s2 = mod2(s * s);
7747
- const u1 = mod2(_1n5 + a * s2);
7748
- const u2 = mod2(_1n5 - a * s2);
7749
- const u1_2 = mod2(u1 * u1);
7750
- const u2_2 = mod2(u2 * u2);
7751
- const v = mod2(a * d * u1_2 - u2_2);
7752
- const { isValid: isValid3, value: I } = invertSqrt(mod2(v * u2_2));
7753
- const Dx = mod2(I * u2);
7754
- const Dy = mod2(I * Dx * v);
7755
- let x = mod2((s + s) * Dx);
7756
- if (isNegativeLE(x, P))
7757
- x = mod2(-x);
7758
- const y = mod2(u1 * Dy);
7759
- const t = mod2(x * y);
7760
- if (!isValid3 || isNegativeLE(t, P) || y === _0n5)
7761
- throw new Error("invalid ristretto255 encoding 2");
7762
- return new __RistrettoPoint(new ed25519.Point(x, y, _1n5, t));
7763
- }
7764
- /**
7765
- * Converts ristretto-encoded string to ristretto point.
7766
- * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode).
7767
- * @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
7768
- */
7769
- static fromHex(hex3) {
7770
- return __RistrettoPoint.fromBytes(ensureBytes("ristrettoHex", hex3, 32));
8207
+ function canonicalizeAddress2(address) {
8208
+ const trimmed = address.trim();
8209
+ return trimmed.startsWith("0x") ? `0x${trimmed.slice(2).toLowerCase()}` : trimmed.toLowerCase();
8210
+ }
8211
+ async function resolveLocalAddress(profile, profileName) {
8212
+ const session = await ProfileManager.getSession(profileName);
8213
+ const sessAddr = session?.address;
8214
+ if (typeof sessAddr === "string" && sessAddr.length > 0) {
8215
+ return canonicalizeAddress2(sessAddr);
7771
8216
  }
7772
- static msm(points, scalars) {
7773
- return pippenger(__RistrettoPoint, ed25519.Point.Fn, points, scalars);
8217
+ if (profile.address) return canonicalizeAddress2(profile.address);
8218
+ if (profile.ownerDid) {
8219
+ const match = profile.ownerDid.match(/^did:pkh:eip155:\d+:(0x[a-fA-F0-9]{40})$/);
8220
+ if (match) return canonicalizeAddress2(match[1]);
7774
8221
  }
7775
- /**
7776
- * Encodes ristretto point to Uint8Array.
7777
- * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode).
7778
- */
7779
- toBytes() {
7780
- let { X, Y, Z, T } = this.ep;
7781
- const P = ed25519_CURVE_p;
7782
- const mod2 = (n) => Fp.create(n);
7783
- const u1 = mod2(mod2(Z + Y) * mod2(Z - Y));
7784
- const u2 = mod2(X * Y);
7785
- const u2sq = mod2(u2 * u2);
7786
- const { value: invsqrt } = invertSqrt(mod2(u1 * u2sq));
7787
- const D1 = mod2(invsqrt * u1);
7788
- const D2 = mod2(invsqrt * u2);
7789
- const zInv = mod2(D1 * D2 * T);
7790
- let D;
7791
- if (isNegativeLE(T * zInv, P)) {
7792
- let _x = mod2(Y * SQRT_M1);
7793
- let _y = mod2(X * SQRT_M1);
7794
- X = _x;
7795
- Y = _y;
7796
- D = mod2(D1 * INVSQRT_A_MINUS_D);
7797
- } else {
7798
- D = D2;
7799
- }
7800
- if (isNegativeLE(X * zInv, P))
7801
- Y = mod2(-Y);
7802
- let s = mod2((Z - Y) * D);
7803
- if (isNegativeLE(s, P))
7804
- s = mod2(-s);
7805
- return Fp.toBytes(s);
8222
+ return null;
8223
+ }
8224
+ function ownerAddressFromSpaceUri(spaceUri) {
8225
+ const match = spaceUri.match(/^tinycloud:pkh:eip155:\d+:(0x[a-fA-F0-9]{40}):/);
8226
+ return match ? canonicalizeAddress2(match[1]) : null;
8227
+ }
8228
+ function ownerDidFromSpaceUri(spaceUri) {
8229
+ const match = spaceUri.match(/^tinycloud:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40}):/);
8230
+ if (!match) return null;
8231
+ return `did:pkh:eip155:${match[1]}:${canonicalizeAddress2(match[2])}`;
8232
+ }
8233
+ async function isRootAuthority(spaceUri, profileName) {
8234
+ const profile = await ProfileManager.getProfile(profileName);
8235
+ if (resolveProfilePosture(profile) === "delegate-session") return false;
8236
+ const ownerAddr = ownerAddressFromSpaceUri(spaceUri);
8237
+ if (!ownerAddr) return false;
8238
+ const selfAddr = await resolveLocalAddress(profile, profileName);
8239
+ return selfAddr !== null && selfAddr === ownerAddr;
8240
+ }
8241
+ function spaceNameFromUri(spaceUri) {
8242
+ return spaceUri.slice(spaceUri.lastIndexOf(":") + 1);
8243
+ }
8244
+ async function unhostedSpaceError(error, spaceUri, profileName) {
8245
+ if (!spaceUri) return null;
8246
+ const status = error.meta?.status;
8247
+ const isUnhosted = status === 404 && /space not found/i.test(error.message);
8248
+ if (!isUnhosted) return null;
8249
+ const spaceName = spaceNameFromUri(spaceUri);
8250
+ const owner = await isRootAuthority(spaceUri, profileName);
8251
+ const hint = owner ? [
8252
+ "You are the owner. Host it once:",
8253
+ ` tc space host ${spaceName}`,
8254
+ "Then retry."
8255
+ ].join("\n") : [
8256
+ "You are a delegate and CANNOT host this space \u2014 only its owner can.",
8257
+ "Emit a host request:",
8258
+ ` tc space host-request ${spaceName} --emit ./host-request.json`,
8259
+ "Send it to the owner; they run `tc space host` and confirm. Then retry."
8260
+ ].join("\n");
8261
+ const message = owner ? `Space '${spaceName}' (${spaceUri}) is not hosted.` : `Space '${spaceName}' (owner ${ownerDidFromSpaceUri(spaceUri) ?? spaceUri}) is not hosted.`;
8262
+ return new CLIError("SPACE_NOT_HOSTED", message, ExitCode.ERROR, { hint });
8263
+ }
8264
+ async function resolveHostSpace(name, profileName) {
8265
+ const resolved = await resolveSpaceUri(name, profileName);
8266
+ if (!resolved) {
8267
+ throw new Error(`Could not resolve a space for "${name}".`);
7806
8268
  }
7807
- /**
7808
- * Compares two Ristretto points.
7809
- * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals).
7810
- */
7811
- equals(other) {
7812
- this.assertSame(other);
7813
- const { X: X1, Y: Y1 } = this.ep;
7814
- const { X: X2, Y: Y2 } = other.ep;
7815
- const mod2 = (n) => Fp.create(n);
7816
- const one = mod2(X1 * Y2) === mod2(Y1 * X2);
7817
- const two = mod2(Y1 * Y2) === mod2(X1 * X2);
7818
- return one || two;
8269
+ return resolved;
8270
+ }
8271
+ var init_host = __esm({
8272
+ "src/lib/host.ts"() {
8273
+ "use strict";
8274
+ init_profiles();
8275
+ init_constants();
8276
+ init_types();
8277
+ init_errors();
8278
+ init_space();
7819
8279
  }
7820
- is0() {
7821
- return this.equals(__RistrettoPoint.ZERO);
8280
+ });
8281
+
8282
+ // src/config/profiles.ts
8283
+ import { chmod as chmod2, lstat, readdir as readdir2, rm as rm2, rmdir } from "fs/promises";
8284
+ import { join as join6 } from "path";
8285
+ import {
8286
+ profilePath as profilePath2,
8287
+ readSession,
8288
+ recordProfileDeletion,
8289
+ refuseWriteToDeletedProfile as refuseWriteToDeletedProfile2,
8290
+ removeSession,
8291
+ withProfileLock as withProfileLock3,
8292
+ writeSession
8293
+ } from "@tinycloud/operations/state";
8294
+ var ProfileManager;
8295
+ var init_profiles = __esm({
8296
+ "src/config/profiles.ts"() {
8297
+ "use strict";
8298
+ init_constants();
8299
+ init_storage2();
8300
+ init_errors();
8301
+ ProfileManager = class _ProfileManager {
8302
+ // ── Initialization ──────────────────────────────────────────────────
8303
+ /**
8304
+ * Runs `action` holding the profile's store lock (shared with operations
8305
+ * and MCP). Reentrant, so the writers below can be called inside it; hold
8306
+ * it around a whole read-modify-write, not just each write.
8307
+ */
8308
+ static async withLock(name, action, options) {
8309
+ return withProfileLock3(name, action, options);
8310
+ }
8311
+ /**
8312
+ * Read-modify-write of profile.json under the profile lock, so concurrent
8313
+ * updates (another command, a login commit) are never erased.
8314
+ */
8315
+ static async updateProfile(name, update) {
8316
+ return _ProfileManager.withLock(name, async () => {
8317
+ const next = update(await _ProfileManager.getProfile(name));
8318
+ await _ProfileManager.setProfile(name, next);
8319
+ return next;
8320
+ });
8321
+ }
8322
+ /**
8323
+ * Creates ~/.tinycloud/ and ~/.tinycloud/profiles/ if they don't exist and
8324
+ * (re)sets both to 0700: older releases created them 0775.
8325
+ */
8326
+ static async ensureConfigDir() {
8327
+ for (const directory of [CONFIG_DIR, PROFILES_DIR]) {
8328
+ await ensureDir(directory);
8329
+ await chmod2(directory, PRIVATE_DIR_MODE);
8330
+ }
8331
+ }
8332
+ /** Owner-only profile directory (0700), created or tightened before any write into it. */
8333
+ static async ensureProfileDir(name) {
8334
+ await _ProfileManager.ensureConfigDir();
8335
+ const profileDir = join6(PROFILES_DIR, name);
8336
+ await ensureDir(profileDir);
8337
+ await chmod2(profileDir, PRIVATE_DIR_MODE);
8338
+ return profileDir;
8339
+ }
8340
+ // ── Global config ───────────────────────────────────────────────────
8341
+ /**
8342
+ * Reads config.json. Returns a default config if the file is missing.
8343
+ */
8344
+ static async getConfig() {
8345
+ const config = await readJson(CONFIG_FILE);
8346
+ if (!config) {
8347
+ return { defaultProfile: DEFAULT_PROFILE, version: 1 };
8348
+ }
8349
+ return config;
8350
+ }
8351
+ /**
8352
+ * Writes the global config to config.json.
8353
+ */
8354
+ static async setConfig(config) {
8355
+ await _ProfileManager.ensureConfigDir();
8356
+ await writeJson(CONFIG_FILE, config);
8357
+ }
8358
+ // ── Profile CRUD ────────────────────────────────────────────────────
8359
+ /**
8360
+ * Returns the profile config for the given name.
8361
+ * Throws CLIError if the profile doesn't exist.
8362
+ */
8363
+ static async getProfile(name) {
8364
+ const profilePath3 = join6(PROFILES_DIR, name, "profile.json");
8365
+ const profile = await readJson(profilePath3);
8366
+ if (!profile) {
8367
+ throw new CLIError(
8368
+ "PROFILE_NOT_FOUND",
8369
+ `Profile "${name}" does not exist. Create it with \`tc init --name ${name}\`.`
8370
+ );
8371
+ }
8372
+ return profile;
8373
+ }
8374
+ /**
8375
+ * Saves a profile config under the profile lock, creating the profile
8376
+ * directory if needed. Use `updateProfile` to change an existing profile.
8377
+ */
8378
+ static async setProfile(name, data) {
8379
+ await _ProfileManager.withLock(name, async () => {
8380
+ await writeJson(join6(await _ProfileManager.ensureProfileDir(name), "profile.json"), data);
8381
+ });
8382
+ }
8383
+ /** Removes profile.json under the profile lock (rollback of a profile a failed login created). */
8384
+ static async removeProfileConfig(name) {
8385
+ await _ProfileManager.withLock(name, () => rm2(join6(PROFILES_DIR, name, "profile.json"), { force: true }));
8386
+ }
8387
+ /**
8388
+ * Returns true if a profile directory exists.
8389
+ */
8390
+ static async profileExists(name) {
8391
+ return fileExists(join6(PROFILES_DIR, name, "profile.json"));
8392
+ }
8393
+ /**
8394
+ * Returns an array of profile directory names.
8395
+ */
8396
+ static async listProfiles() {
8397
+ return listDirs(PROFILES_DIR);
8398
+ }
8399
+ /**
8400
+ * Deletes a profile. Its key, session, settings, stores and cache are
8401
+ * removed while holding the profile lock, so another writer's critical
8402
+ * section never sees them vanish midway: session and key first, settings
8403
+ * last, so a crash midway never leaves a session or key without its
8404
+ * profile. The deletion is then recorded, so a store write that waited for
8405
+ * the lock meanwhile refuses rather than recreating a profile with only a
8406
+ * session in it. `.lock` itself is left to the lock's release; the
8407
+ * then-empty directory is removed afterwards unless another writer took
8408
+ * the lock (or wrote) meanwhile. A profile directory that is a symlink is
8409
+ * unlinked, its target left alone.
8410
+ * Throws if the name is not one path segment or names the default profile.
8411
+ */
8412
+ static async deleteProfile(name) {
8413
+ let profileDir;
8414
+ try {
8415
+ profileDir = profilePath2(name);
8416
+ } catch {
8417
+ throw new CLIError(
8418
+ "INVALID_PROFILE_NAME",
8419
+ `Invalid profile name "${name}": a profile name is one path segment (no "/", "\\", "." or "..").`,
8420
+ ExitCode.USAGE_ERROR
8421
+ );
8422
+ }
8423
+ const config = await _ProfileManager.getConfig();
8424
+ if (config.defaultProfile === name) {
8425
+ throw new CLIError(
8426
+ "PROFILE_DELETE_DEFAULT",
8427
+ `Cannot delete the default profile "${name}". Change the default first with \`tc profile default <other>\`.`
8428
+ );
8429
+ }
8430
+ const kind = await lstat(profileDir).then((stats) => stats.isSymbolicLink() ? "link" : "present", (error) => {
8431
+ if (error.code === "ENOENT") return "missing";
8432
+ throw error;
8433
+ });
8434
+ if (kind === "missing") return;
8435
+ if (kind === "link") {
8436
+ await rm2(profileDir, { force: true });
8437
+ return;
8438
+ }
8439
+ await _ProfileManager.withLock(name, async () => {
8440
+ for (const file of ["session.json", "key.json"]) await rm2(join6(profileDir, file), { force: true });
8441
+ for (const entry of await readdir2(profileDir)) {
8442
+ if (entry !== ".lock" && entry !== "profile.json") await rm2(join6(profileDir, entry), { recursive: true, force: true });
8443
+ }
8444
+ await rm2(join6(profileDir, "profile.json"), { force: true });
8445
+ await recordProfileDeletion(name);
8446
+ });
8447
+ await rmdir(profileDir).catch((error) => {
8448
+ if (error.code !== "ENOENT" && error.code !== "ENOTEMPTY" && error.code !== "EEXIST") throw error;
8449
+ });
8450
+ }
8451
+ // ── Key management ──────────────────────────────────────────────────
8452
+ /**
8453
+ * Returns the parsed JWK for a profile, or null if no key exists.
8454
+ */
8455
+ static async getKey(name) {
8456
+ return readJson(join6(PROFILES_DIR, name, "key.json"));
8457
+ }
8458
+ /**
8459
+ * Saves a JWK key under the profile lock (0600, in an owner-only profile
8460
+ * directory). Refused (PROFILE_NOT_FOUND) if the profile was deleted while
8461
+ * this waited for the lock, rather than leaving a key-only profile.
8462
+ */
8463
+ static async setKey(name, jwk) {
8464
+ await _ProfileManager.withLock(name, async () => {
8465
+ await refuseWriteToDeletedProfile2(name);
8466
+ await writeJson(join6(await _ProfileManager.ensureProfileDir(name), "key.json"), jwk);
8467
+ });
8468
+ }
8469
+ /** Removes key.json under the profile lock (rollback of a key a failed login created). */
8470
+ static async removeKey(name) {
8471
+ await _ProfileManager.withLock(name, () => rm2(join6(PROFILES_DIR, name, "key.json"), { force: true }));
8472
+ }
8473
+ // ── Session management ──────────────────────────────────────────────
8474
+ /**
8475
+ * Returns the parsed session for a profile, or null if none exists.
8476
+ */
8477
+ static async getSession(name) {
8478
+ return readSession(name);
8479
+ }
8480
+ /**
8481
+ * Saves session data for a profile.
8482
+ */
8483
+ static async setSession(name, session) {
8484
+ await writeSession(name, session);
8485
+ }
8486
+ /**
8487
+ * Removes the session file for a profile.
8488
+ */
8489
+ static async clearSession(name) {
8490
+ await removeSession(name);
8491
+ }
8492
+ // ── Cache management ────────────────────────────────────────────────
8493
+ /**
8494
+ * Returns the profile's cache directory (share history lives here),
8495
+ * created or tightened to 0700.
8496
+ */
8497
+ static async getCacheDir(name) {
8498
+ const cacheDir = join6(await _ProfileManager.ensureProfileDir(name), "cache");
8499
+ await ensureDir(cacheDir);
8500
+ await chmod2(cacheDir, PRIVATE_DIR_MODE);
8501
+ return cacheDir;
8502
+ }
8503
+ // ── Resolution helpers ──────────────────────────────────────────────
8504
+ /**
8505
+ * Resolves the full CLI context from flags, env vars, and config.
8506
+ *
8507
+ * Profile resolution: options.profile > TC_PROFILE env > config.defaultProfile > "default"
8508
+ * Host resolution: options.host > TC_HOST env > discovered local node > profile.host > DEFAULT_HOST
8509
+ *
8510
+ * Local-node discovery (TC-106) only runs when no explicit host was given
8511
+ * (`--host` / `TC_HOST`); it probes configured or registered local nodes and,
8512
+ * after DID verification against the profile's pinned identity, prefers one
8513
+ * over the stored/default host. For session-using commands, it verifies the
8514
+ * stored session locally before probing. Loopback requires an explicit
8515
+ * `localNodeUrl`; disable discovery with `autoDiscoverLocalNode: false`.
8516
+ */
8517
+ static async resolveContext(options, settings = {}) {
8518
+ const config = await _ProfileManager.getConfig();
8519
+ const profile = options.profile ?? process.env.TC_PROFILE ?? config.defaultProfile ?? DEFAULT_PROFILE;
8520
+ let profileHost;
8521
+ let profileConfig;
8522
+ try {
8523
+ profileConfig = await _ProfileManager.getProfile(profile);
8524
+ profileHost = profileConfig.host;
8525
+ } catch {
8526
+ }
8527
+ const envReplication = process.env.TC_REPLICATION?.toLowerCase();
8528
+ const envEnabled = envReplication === "1" || envReplication === "true" || envReplication === "on";
8529
+ const explicitEnvOff = envReplication === "0" || envReplication === "false" || envReplication === "off";
8530
+ if (envReplication !== void 0 && !envEnabled && !explicitEnvOff) {
8531
+ throw new CLIError("INVALID_ARGUMENT", "TC_REPLICATION must be one of 1, true, on, 0, false or off.", ExitCode.USAGE_ERROR);
8532
+ }
8533
+ const explicitOff = options.noReplication === true || options.replication === false;
8534
+ const replication = options.replication === true ? true : explicitOff ? false : envEnabled;
8535
+ const explicitHost = options.host ?? process.env.TC_HOST;
8536
+ let host = explicitHost ?? profileHost ?? DEFAULT_HOST;
8537
+ let localDiscovery;
8538
+ if (explicitHost === void 0) {
8539
+ const { discoverLocalNodeHost: discoverLocalNodeHost2 } = await Promise.resolve().then(() => (init_host(), host_exports));
8540
+ const explicitKey = Boolean(options.privateKey || process.env.TC_PRIVATE_KEY);
8541
+ const discovered = await discoverLocalNodeHost2(profile, {
8542
+ awaitStoredSession: settings.storedSession !== "unused" && !explicitKey
8543
+ });
8544
+ if (discovered.host !== null) host = discovered.host;
8545
+ if (discovered.deferred) localDiscovery = { deferredFrom: host };
8546
+ }
8547
+ setActiveProfileName(profile);
8548
+ return {
8549
+ profile,
8550
+ host,
8551
+ verbose: options.verbose ?? false,
8552
+ noCache: options.noCache ?? false,
8553
+ quiet: options.quiet ?? false,
8554
+ replication,
8555
+ replicationDebug: options.replicationDebug === true || process.env.TC_REPLICATION_DEBUG === "1",
8556
+ ...localDiscovery === void 0 ? {} : { localDiscovery }
8557
+ };
8558
+ }
8559
+ };
7822
8560
  }
7823
- };
7824
- _RistrettoPoint.BASE = /* @__PURE__ */ (() => new _RistrettoPoint(ed25519.Point.BASE))();
7825
- _RistrettoPoint.ZERO = /* @__PURE__ */ (() => new _RistrettoPoint(ed25519.Point.ZERO))();
7826
- _RistrettoPoint.Fp = /* @__PURE__ */ (() => Fp)();
7827
- _RistrettoPoint.Fn = /* @__PURE__ */ (() => Fn)();
8561
+ });
7828
8562
 
7829
- // ../../node_modules/@noble/hashes/esm/blake3.js
7830
- init_md();
7831
- init_u64();
8563
+ // src/commands/courier.ts
8564
+ var TC_OPTIONS_BEFORE_COURIER = 'tc courier takes no tc options and does not use tc profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. tc courier init --host <url>.';
7832
8565
 
7833
- // ../../node_modules/@noble/hashes/esm/_blake.js
7834
- init_utils();
7835
- function G1s(a, b, c, d, x) {
7836
- a = a + b + x | 0;
7837
- d = rotr(d ^ a, 16);
7838
- c = c + d | 0;
7839
- b = rotr(b ^ c, 12);
7840
- return { a, b, c, d };
7841
- }
7842
- function G2s(a, b, c, d, x) {
7843
- a = a + b + x | 0;
7844
- d = rotr(d ^ a, 8);
7845
- c = c + d | 0;
7846
- b = rotr(b ^ c, 7);
7847
- return { a, b, c, d };
7848
- }
8566
+ // src/index.ts
8567
+ init_errors();
8568
+ import { readFileSync as readFileSync2 } from "fs";
8569
+ import { Command } from "commander";
7849
8570
 
7850
- // ../../node_modules/@noble/hashes/esm/blake2.js
7851
- init_utils();
7852
- var BLAKE2 = class extends Hash {
7853
- constructor(blockLen, outputLen) {
7854
- super();
7855
- this.finished = false;
7856
- this.destroyed = false;
7857
- this.length = 0;
7858
- this.pos = 0;
7859
- anumber(blockLen);
7860
- anumber(outputLen);
7861
- this.blockLen = blockLen;
7862
- this.outputLen = outputLen;
7863
- this.buffer = new Uint8Array(blockLen);
7864
- this.buffer32 = u32(this.buffer);
7865
- }
7866
- update(data) {
7867
- aexists(this);
7868
- data = toBytes(data);
7869
- abytes(data);
7870
- const { blockLen, buffer, buffer32 } = this;
7871
- const len = data.length;
7872
- const offset = data.byteOffset;
7873
- const buf = data.buffer;
7874
- for (let pos = 0; pos < len; ) {
7875
- if (this.pos === blockLen) {
7876
- swap32IfBE(buffer32);
7877
- this.compress(buffer32, 0, false);
7878
- swap32IfBE(buffer32);
7879
- this.pos = 0;
7880
- }
7881
- const take = Math.min(blockLen - this.pos, len - pos);
7882
- const dataOffset = offset + pos;
7883
- if (take === blockLen && !(dataOffset % 4) && pos + take < len) {
7884
- const data32 = new Uint32Array(buf, dataOffset, Math.floor((len - pos) / 4));
7885
- swap32IfBE(data32);
7886
- for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {
7887
- this.length += blockLen;
7888
- this.compress(data32, pos32, false);
7889
- }
7890
- swap32IfBE(data32);
7891
- continue;
7892
- }
7893
- buffer.set(data.subarray(pos, pos + take), this.pos);
7894
- this.pos += take;
7895
- this.length += take;
7896
- pos += take;
8571
+ // src/output/banner.ts
8572
+ init_formatter();
8573
+
8574
+ // src/output/taglines.ts
8575
+ var HOLIDAY_TAGLINES = [
8576
+ { month: 1, day: 1, range: 1, tagline: "New year, new keys, same cloud." },
8577
+ { month: 2, day: 14, tagline: "We love your data as much as you do." },
8578
+ { month: 3, day: 14, tagline: "3.14159 reasons to encrypt everything." },
8579
+ { month: 5, day: 4, tagline: "May the fourth be with your keys." },
8580
+ { month: 10, day: 31, tagline: "Nothing scarier than plaintext secrets." },
8581
+ { month: 12, day: 25, range: 2, tagline: "Unwrap your data, not your keys." },
8582
+ { month: 12, day: 31, tagline: "Encrypt your resolutions." }
8583
+ ];
8584
+ var TAGLINES = [
8585
+ // Professional
8586
+ "Your data, your keys, your cloud.",
8587
+ "Self-sovereign storage for the modern web.",
8588
+ "The cloud you actually own.",
8589
+ "Encrypted by default, decentralized by design.",
8590
+ "Where your data answers only to you.",
8591
+ "End-to-end encrypted. No exceptions.",
8592
+ "Like S3 but you hold the keys.",
8593
+ "Privacy isn't a feature. It's the architecture.",
8594
+ "Sovereign storage, zero knowledge.",
8595
+ "Your .env is safe here \u2014 we use real cryptography.",
8596
+ // Playful / nerdy
8597
+ "UCAN do anything.",
8598
+ "Keys generated, delegations granted, data liberated.",
8599
+ "Decentralized storage, centralized vibes.",
8600
+ "Trust nobody, delegate everything.",
8601
+ "sudo make me a sandwich, encrypted.",
8602
+ "Have you tried turning your keys off and on again?",
8603
+ "All your base are belong to you.",
8604
+ "In UCAN we trust.",
8605
+ "0 knowledge, 100% confidence.",
8606
+ "Keeping secrets since 2024."
8607
+ ];
8608
+ function getHolidayTagline() {
8609
+ const now = /* @__PURE__ */ new Date();
8610
+ const month = now.getMonth() + 1;
8611
+ const day = now.getDate();
8612
+ for (const h of HOLIDAY_TAGLINES) {
8613
+ const range = h.range ?? 0;
8614
+ if (h.month === month && Math.abs(day - h.day) <= range) {
8615
+ return h.tagline;
7897
8616
  }
7898
- return this;
7899
- }
7900
- digestInto(out) {
7901
- aexists(this);
7902
- aoutput(out, this);
7903
- const { pos, buffer32 } = this;
7904
- this.finished = true;
7905
- clean(this.buffer.subarray(pos));
7906
- swap32IfBE(buffer32);
7907
- this.compress(buffer32, 0, true);
7908
- swap32IfBE(buffer32);
7909
- const out32 = u32(out);
7910
- this.get().forEach((v, i) => out32[i] = swap8IfBE(v));
7911
- }
7912
- digest() {
7913
- const { buffer, outputLen } = this;
7914
- this.digestInto(buffer);
7915
- const res = buffer.slice(0, outputLen);
7916
- this.destroy();
7917
- return res;
7918
- }
7919
- _cloneInto(to) {
7920
- const { buffer, length: length4, finished, destroyed, outputLen, pos } = this;
7921
- to || (to = new this.constructor({ dkLen: outputLen }));
7922
- to.set(...this.get());
7923
- to.buffer.set(buffer);
7924
- to.destroyed = destroyed;
7925
- to.finished = finished;
7926
- to.length = length4;
7927
- to.pos = pos;
7928
- to.outputLen = outputLen;
7929
- return to;
7930
- }
7931
- clone() {
7932
- return this._cloneInto();
7933
- }
7934
- };
7935
- function compress(s, offset, msg, rounds, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) {
7936
- let j = 0;
7937
- for (let i = 0; i < rounds; i++) {
7938
- ({ a: v0, b: v4, c: v8, d: v12 } = G1s(v0, v4, v8, v12, msg[offset + s[j++]]));
7939
- ({ a: v0, b: v4, c: v8, d: v12 } = G2s(v0, v4, v8, v12, msg[offset + s[j++]]));
7940
- ({ a: v1, b: v5, c: v9, d: v13 } = G1s(v1, v5, v9, v13, msg[offset + s[j++]]));
7941
- ({ a: v1, b: v5, c: v9, d: v13 } = G2s(v1, v5, v9, v13, msg[offset + s[j++]]));
7942
- ({ a: v2, b: v6, c: v10, d: v14 } = G1s(v2, v6, v10, v14, msg[offset + s[j++]]));
7943
- ({ a: v2, b: v6, c: v10, d: v14 } = G2s(v2, v6, v10, v14, msg[offset + s[j++]]));
7944
- ({ a: v3, b: v7, c: v11, d: v15 } = G1s(v3, v7, v11, v15, msg[offset + s[j++]]));
7945
- ({ a: v3, b: v7, c: v11, d: v15 } = G2s(v3, v7, v11, v15, msg[offset + s[j++]]));
7946
- ({ a: v0, b: v5, c: v10, d: v15 } = G1s(v0, v5, v10, v15, msg[offset + s[j++]]));
7947
- ({ a: v0, b: v5, c: v10, d: v15 } = G2s(v0, v5, v10, v15, msg[offset + s[j++]]));
7948
- ({ a: v1, b: v6, c: v11, d: v12 } = G1s(v1, v6, v11, v12, msg[offset + s[j++]]));
7949
- ({ a: v1, b: v6, c: v11, d: v12 } = G2s(v1, v6, v11, v12, msg[offset + s[j++]]));
7950
- ({ a: v2, b: v7, c: v8, d: v13 } = G1s(v2, v7, v8, v13, msg[offset + s[j++]]));
7951
- ({ a: v2, b: v7, c: v8, d: v13 } = G2s(v2, v7, v8, v13, msg[offset + s[j++]]));
7952
- ({ a: v3, b: v4, c: v9, d: v14 } = G1s(v3, v4, v9, v14, msg[offset + s[j++]]));
7953
- ({ a: v3, b: v4, c: v9, d: v14 } = G2s(v3, v4, v9, v14, msg[offset + s[j++]]));
7954
8617
  }
7955
- return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 };
8618
+ return null;
8619
+ }
8620
+ function pickTagline() {
8621
+ const holiday = getHolidayTagline();
8622
+ if (holiday) return holiday;
8623
+ return TAGLINES[Math.floor(Math.random() * TAGLINES.length)];
7956
8624
  }
7957
8625
 
7958
- // ../../node_modules/@noble/hashes/esm/blake3.js
7959
- init_utils();
7960
- var B3_Flags = {
7961
- CHUNK_START: 1,
7962
- CHUNK_END: 2,
7963
- PARENT: 4,
7964
- ROOT: 8,
7965
- KEYED_HASH: 16,
7966
- DERIVE_KEY_CONTEXT: 32,
7967
- DERIVE_KEY_MATERIAL: 64
7968
- };
7969
- var B3_IV = SHA256_IV.slice();
7970
- var B3_SIGMA = /* @__PURE__ */ (() => {
7971
- const Id = Array.from({ length: 16 }, (_, i) => i);
7972
- const permute = (arr) => [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]);
7973
- const res = [];
7974
- for (let i = 0, v = Id; i < 7; i++, v = permute(v))
7975
- res.push(...v);
7976
- return Uint8Array.from(res);
7977
- })();
7978
- var BLAKE3 = class _BLAKE3 extends BLAKE2 {
7979
- constructor(opts = {}, flags = 0) {
7980
- super(64, opts.dkLen === void 0 ? 32 : opts.dkLen);
7981
- this.chunkPos = 0;
7982
- this.chunksDone = 0;
7983
- this.flags = 0 | 0;
7984
- this.stack = [];
7985
- this.posOut = 0;
7986
- this.bufferOut32 = new Uint32Array(16);
7987
- this.chunkOut = 0;
7988
- this.enableXOF = true;
7989
- const { key, context } = opts;
7990
- const hasContext = context !== void 0;
7991
- if (key !== void 0) {
7992
- if (hasContext)
7993
- throw new Error('Only "key" or "context" can be specified at same time');
7994
- const k = toBytes(key).slice();
7995
- abytes(k, 32);
7996
- this.IV = u32(k);
7997
- swap32IfBE(this.IV);
7998
- this.flags = flags | B3_Flags.KEYED_HASH;
7999
- } else if (hasContext) {
8000
- const ctx = toBytes(context);
8001
- const contextKey = new _BLAKE3({ dkLen: 32 }, B3_Flags.DERIVE_KEY_CONTEXT).update(ctx).digest();
8002
- this.IV = u32(contextKey);
8003
- swap32IfBE(this.IV);
8004
- this.flags = flags | B3_Flags.DERIVE_KEY_MATERIAL;
8005
- } else {
8006
- this.IV = B3_IV.slice();
8007
- this.flags = flags;
8008
- }
8009
- this.state = this.IV.slice();
8010
- this.bufferOut = u8(this.bufferOut32);
8011
- }
8012
- // Unused
8013
- get() {
8014
- return [];
8626
+ // src/output/banner.ts
8627
+ init_theme();
8628
+ import { execSync } from "child_process";
8629
+ var bannerEmitted = false;
8630
+ function resolveCommitHash() {
8631
+ try {
8632
+ return execSync("git rev-parse --short HEAD", {
8633
+ encoding: "utf-8",
8634
+ stdio: ["pipe", "pipe", "pipe"]
8635
+ }).trim() || null;
8636
+ } catch {
8637
+ return null;
8015
8638
  }
8016
- set() {
8017
- }
8018
- b2Compress(counter, flags, buf, bufPos = 0) {
8019
- const { state: s, pos } = this;
8020
- const { h, l } = fromBig(BigInt(counter), true);
8021
- const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = compress(B3_SIGMA, bufPos, buf, 7, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], h, l, pos, flags);
8022
- s[0] = v0 ^ v8;
8023
- s[1] = v1 ^ v9;
8024
- s[2] = v2 ^ v10;
8025
- s[3] = v3 ^ v11;
8026
- s[4] = v4 ^ v12;
8027
- s[5] = v5 ^ v13;
8028
- s[6] = v6 ^ v14;
8029
- s[7] = v7 ^ v15;
8030
- }
8031
- compress(buf, bufPos = 0, isLast = false) {
8032
- let flags = this.flags;
8033
- if (!this.chunkPos)
8034
- flags |= B3_Flags.CHUNK_START;
8035
- if (this.chunkPos === 15 || isLast)
8036
- flags |= B3_Flags.CHUNK_END;
8037
- if (!isLast)
8038
- this.pos = this.blockLen;
8039
- this.b2Compress(this.chunksDone, flags, buf, bufPos);
8040
- this.chunkPos += 1;
8041
- if (this.chunkPos === 16 || isLast) {
8042
- let chunk = this.state;
8043
- this.state = this.IV.slice();
8044
- for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) {
8045
- if (!(last = this.stack.pop()))
8046
- break;
8047
- this.buffer32.set(last, 0);
8048
- this.buffer32.set(chunk, 8);
8049
- this.pos = this.blockLen;
8050
- this.b2Compress(0, this.flags | B3_Flags.PARENT, this.buffer32, 0);
8051
- chunk = this.state;
8052
- this.state = this.IV.slice();
8053
- }
8054
- this.chunksDone++;
8055
- this.chunkPos = 0;
8056
- this.stack.push(chunk);
8057
- }
8058
- this.pos = 0;
8059
- }
8060
- _cloneInto(to) {
8061
- to = super._cloneInto(to);
8062
- const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this;
8063
- to.state.set(state.slice());
8064
- to.stack = stack.map((i) => Uint32Array.from(i));
8065
- to.IV.set(IV);
8066
- to.flags = flags;
8067
- to.chunkPos = chunkPos;
8068
- to.chunksDone = chunksDone;
8069
- to.posOut = posOut;
8070
- to.chunkOut = chunkOut;
8071
- to.enableXOF = this.enableXOF;
8072
- to.bufferOut32.set(this.bufferOut32);
8073
- return to;
8074
- }
8075
- destroy() {
8076
- this.destroyed = true;
8077
- clean(this.state, this.buffer32, this.IV, this.bufferOut32);
8078
- clean(...this.stack);
8079
- }
8080
- // Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8)
8081
- b2CompressOut() {
8082
- const { state: s, pos, flags, buffer32, bufferOut32: out32 } = this;
8083
- const { h, l } = fromBig(BigInt(this.chunkOut++));
8084
- swap32IfBE(buffer32);
8085
- const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = compress(B3_SIGMA, 0, buffer32, 7, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], l, h, pos, flags);
8086
- out32[0] = v0 ^ v8;
8087
- out32[1] = v1 ^ v9;
8088
- out32[2] = v2 ^ v10;
8089
- out32[3] = v3 ^ v11;
8090
- out32[4] = v4 ^ v12;
8091
- out32[5] = v5 ^ v13;
8092
- out32[6] = v6 ^ v14;
8093
- out32[7] = v7 ^ v15;
8094
- out32[8] = s[0] ^ v8;
8095
- out32[9] = s[1] ^ v9;
8096
- out32[10] = s[2] ^ v10;
8097
- out32[11] = s[3] ^ v11;
8098
- out32[12] = s[4] ^ v12;
8099
- out32[13] = s[5] ^ v13;
8100
- out32[14] = s[6] ^ v14;
8101
- out32[15] = s[7] ^ v15;
8102
- swap32IfBE(buffer32);
8103
- swap32IfBE(out32);
8104
- this.posOut = 0;
8105
- }
8106
- finish() {
8107
- if (this.finished)
8108
- return;
8109
- this.finished = true;
8110
- clean(this.buffer.subarray(this.pos));
8111
- let flags = this.flags | B3_Flags.ROOT;
8112
- if (this.stack.length) {
8113
- flags |= B3_Flags.PARENT;
8114
- swap32IfBE(this.buffer32);
8115
- this.compress(this.buffer32, 0, true);
8116
- swap32IfBE(this.buffer32);
8117
- this.chunksDone = 0;
8118
- this.pos = this.blockLen;
8119
- } else {
8120
- flags |= (!this.chunkPos ? B3_Flags.CHUNK_START : 0) | B3_Flags.CHUNK_END;
8121
- }
8122
- this.flags = flags;
8123
- this.b2CompressOut();
8124
- }
8125
- writeInto(out) {
8126
- aexists(this, false);
8127
- abytes(out);
8128
- this.finish();
8129
- const { blockLen, bufferOut } = this;
8130
- for (let pos = 0, len = out.length; pos < len; ) {
8131
- if (this.posOut >= blockLen)
8132
- this.b2CompressOut();
8133
- const take = Math.min(blockLen - this.posOut, len - pos);
8134
- out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
8135
- this.posOut += take;
8136
- pos += take;
8137
- }
8138
- return out;
8139
- }
8140
- xofInto(out) {
8141
- if (!this.enableXOF)
8142
- throw new Error("XOF is not possible after digest call");
8143
- return this.writeInto(out);
8144
- }
8145
- xof(bytes) {
8146
- anumber(bytes);
8147
- return this.xofInto(new Uint8Array(bytes));
8148
- }
8149
- digestInto(out) {
8150
- aoutput(out, this);
8151
- if (this.finished)
8152
- throw new Error("digest() was already called");
8153
- this.enableXOF = false;
8154
- this.writeInto(out);
8155
- this.destroy();
8156
- return out;
8157
- }
8158
- digest() {
8159
- return this.digestInto(new Uint8Array(this.outputLen));
8639
+ }
8640
+ function formatBannerLine(version2) {
8641
+ const commit = resolveCommitHash();
8642
+ const tagline = pickTagline();
8643
+ const versionPart = `tc v${version2}`;
8644
+ const commitPart = commit ? ` (${commit})` : "";
8645
+ const separator = " \u2014 ";
8646
+ if (!isInteractive()) {
8647
+ return `${versionPart}${commitPart}${separator}${tagline}`;
8160
8648
  }
8161
- };
8162
- var blake3 = /* @__PURE__ */ createXOFer((opts) => new BLAKE3(opts));
8649
+ return [
8650
+ theme.brand("\u2601\uFE0F tc"),
8651
+ " ",
8652
+ theme.muted(`v${version2}`),
8653
+ commit ? theme.dim(` (${commit})`) : "",
8654
+ theme.dim(separator),
8655
+ theme.primary(tagline)
8656
+ ].join("");
8657
+ }
8658
+ function emitBanner(version2) {
8659
+ if (bannerEmitted) return;
8660
+ if (shouldOutputJson()) return;
8661
+ if (process.env.TC_HIDE_BANNER === "1") return;
8662
+ bannerEmitted = true;
8663
+ process.stderr.write(formatBannerLine(version2) + "\n\n");
8664
+ }
8665
+
8666
+ // src/index.ts
8667
+ init_replication_registry();
8668
+ init_theme();
8669
+ init_formatter();
8670
+ init_profiles();
8163
8671
 
8164
8672
  // ../share-sdk/dist/index.js
8673
+ init_sha2();
8674
+ init_ed25519();
8675
+ init_blake3();
8676
+ init_ed25519();
8165
8677
  init_sha256();
8166
8678
  init_sha256();
8167
8679
  var __defProp2 = Object.defineProperty;
@@ -15280,12 +15792,12 @@ async function notifyShare(input) {
15280
15792
  if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
15281
15793
  attempts += 1;
15282
15794
  try {
15283
- const state = await input.adapter.deliver({ shareId: input.shareId, recipient, idempotencyKey, ...input.record === void 0 ? {} : { record: input.record }, ...input.signal === void 0 ? {} : { signal: input.signal } });
15795
+ const state2 = await input.adapter.deliver({ shareId: input.shareId, recipient, idempotencyKey, ...input.record === void 0 ? {} : { record: input.record }, ...input.signal === void 0 ? {} : { signal: input.signal } });
15284
15796
  return {
15285
15797
  protocol: "tinycloud-share",
15286
15798
  version: 1,
15287
15799
  shareId: input.shareId,
15288
- state: state === "delivered" && input.record?.deliveredRecipients?.includes(recipient) ? "already-delivered" : state,
15800
+ state: state2 === "delivered" && input.record?.deliveredRecipients?.includes(recipient) ? "already-delivered" : state2,
15289
15801
  idempotencyKey,
15290
15802
  attempts
15291
15803
  };
@@ -16982,7 +17494,7 @@ function receiveJson(result, path) {
16982
17494
  import { constants } from "fs";
16983
17495
  import { lstat as lstat2, mkdir as mkdir2, mkdtemp, open as open2, readFile as readFile3, realpath, stat as stat2, link, rename as rename2, rm as rm3, unlink } from "fs/promises";
16984
17496
  import { randomBytes as randomBytes2 } from "crypto";
16985
- import { basename as basename2, join as join5, resolve, sep } from "path";
17497
+ import { basename as basename2, join as join7, resolve, sep } from "path";
16986
17498
  init_errors();
16987
17499
  var MAX_SHARE_STDIN_BYTES = 100 * 1024 * 1024;
16988
17500
  var MAX_SHARE_URL_BYTES = 64 * 1024;
@@ -17036,7 +17548,7 @@ async function assertDirectory(path) {
17036
17548
  const segments = absolute.split(sep).filter(Boolean);
17037
17549
  let current = absolute.startsWith(sep) ? sep : "";
17038
17550
  for (const segment of segments) {
17039
- current = current === sep ? join5(current, segment) : join5(current, segment);
17551
+ current = current === sep ? join7(current, segment) : join7(current, segment);
17040
17552
  try {
17041
17553
  const info = await lstat2(current);
17042
17554
  if (info.isSymbolicLink()) {
@@ -17058,17 +17570,17 @@ async function writeShareOutput(directory, filename, bytes, force) {
17058
17570
  const safeName = safeFilename(filename);
17059
17571
  const directoryHandle = await open2(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
17060
17572
  const stableDirectory = await realpath(outputDirectory);
17061
- const outputPath = join5(stableDirectory, safeName);
17573
+ const outputPath = join7(stableDirectory, safeName);
17062
17574
  const directoryIdentity = await directoryHandle.stat();
17063
17575
  const assertStableDirectory = async () => {
17064
17576
  const current = await stat2(stableDirectory);
17065
17577
  if (current.dev !== directoryIdentity.dev || current.ino !== directoryIdentity.ino) throw new Error("OUTPUT_EXISTS");
17066
17578
  };
17067
17579
  await assertStableDirectory();
17068
- const stagingDirectory = await mkdtemp(join5(stableDirectory, ".tinycloud-share-stage-"));
17580
+ const stagingDirectory = await mkdtemp(join7(stableDirectory, ".tinycloud-share-stage-"));
17069
17581
  const stagingInfo = await lstat2(stagingDirectory);
17070
17582
  if (!stagingInfo.isDirectory() || (stagingInfo.mode & 511) !== 448) throw new Error("OUTPUT_EXISTS");
17071
- const stagingPath = join5(stagingDirectory, `.tinycloud-share-${randomBytes2(16).toString("hex")}.tmp`);
17583
+ const stagingPath = join7(stagingDirectory, `.tinycloud-share-${randomBytes2(16).toString("hex")}.tmp`);
17072
17584
  let temporaryPath;
17073
17585
  let handle;
17074
17586
  try {
@@ -17105,7 +17617,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
17105
17617
  await rm3(stagingDirectory, { recursive: true, force: true });
17106
17618
  await directoryHandle.close();
17107
17619
  }
17108
- return join5(outputDirectory, safeName);
17620
+ return join7(outputDirectory, safeName);
17109
17621
  }
17110
17622
 
17111
17623
  // src/share/errors.ts
@@ -17391,7 +17903,7 @@ function registerShareCommand(program2) {
17391
17903
  if (json) writeJson2({ ...redactPublishedShare(result), expiryClamped: result.metadata.expiryClamped === true, ...notification === void 0 ? {} : { notification } });
17392
17904
  else publishHuman(result);
17393
17905
  } catch (error) {
17394
- handleError(shareCliError(error));
17906
+ return handleError(shareCliError(error));
17395
17907
  }
17396
17908
  });
17397
17909
  share.command("inspect [url]").description("Verify a share link and print safe metadata").option("--stdin", "Read the complete URL from stdin").option("--json", "Print versioned redacted JSON").option("--viewer-origin <origin>", "Require this canonical Share origin", SHARE_ORIGIN).action(async (url, options, command) => {
@@ -17401,7 +17913,7 @@ function registerShareCommand(program2) {
17401
17913
  if (json) writeJson2(result);
17402
17914
  else inspectHuman(result);
17403
17915
  } catch (error) {
17404
- handleError(shareCliError(error));
17916
+ return handleError(shareCliError(error));
17405
17917
  }
17406
17918
  });
17407
17919
  share.command("receive [url]").description("Verify and receive a share link").option("--stdin", "Read the complete URL from stdin").option("--output <directory>", "Create the file in this directory").option("--stdout", "Write verified plaintext bytes to stdout").option("--force", "Allow replacing an existing non-symlink output").option("--max-bytes <bytes>", "Bound received content bytes").option("--json", "Print versioned redacted JSON").option("--viewer-origin <origin>", "Require this canonical Share origin", SHARE_ORIGIN).action(async (url, options, command) => {
@@ -17449,7 +17961,7 @@ function registerShareCommand(program2) {
17449
17961
  if (json) receiveJson(result, output);
17450
17962
  else receiveHuman(output);
17451
17963
  } catch (error) {
17452
- handleError(shareCliError(error));
17964
+ return handleError(shareCliError(error));
17453
17965
  }
17454
17966
  });
17455
17967
  share.command("list").description("List encrypted sender history without complete bearer URLs").option("--json", "Print versioned redacted JSON").action(async (options, command) => {
@@ -17460,7 +17972,7 @@ function registerShareCommand(program2) {
17460
17972
  if (json) writeJson2({ protocol: "tinycloud-share", version: 1, shares: result });
17461
17973
  else process.stdout.write(result.map((item) => `${item.shareId} ${item.target} ${item.expiresAt}`).join("\n") + (result.length ? "\n" : ""));
17462
17974
  } catch (error) {
17463
- handleError(shareCliError(error));
17975
+ return handleError(shareCliError(error));
17464
17976
  }
17465
17977
  });
17466
17978
  share.command("show <id>").description("Show one redacted sender-history record").option("--reveal-link", "Explicitly include the complete link").option("--json", "Print versioned redacted JSON").action(async (id, options, command) => {
@@ -17472,7 +17984,7 @@ function registerShareCommand(program2) {
17472
17984
  if (json) writeJson2({ protocol: "tinycloud-share", version: 1, share: result });
17473
17985
  else writeJson2(result);
17474
17986
  } catch (error) {
17475
- handleError(shareCliError(error));
17987
+ return handleError(shareCliError(error));
17476
17988
  }
17477
17989
  });
17478
17990
  share.command("notify <id>").description("Retry idempotent delivery within 5 minutes of publication; after that publish a new share").requiredOption("--to <address>", "Recipient email").option("--json", "Print versioned JSON").action(async (id, options, command) => {
@@ -17499,7 +18011,7 @@ function registerShareCommand(program2) {
17499
18011
  if (result.reason === "delivery-window-expired") process.stderr.write(NOTIFY_WINDOW_MESSAGE);
17500
18012
  if (result.state === "partial-failure") process.exitCode = 9;
17501
18013
  } catch (error) {
17502
- handleError(shareCliError(error, "notify"));
18014
+ return handleError(shareCliError(error, "notify"));
17503
18015
  }
17504
18016
  });
17505
18017
  share.command("revoke <id>").description("Revoke addressed shares; report bearer retention honestly").option("--ancestor", "Revoke the owner delegation ancestry").option("--json", "Print versioned JSON").action(async (id, options, command) => {
@@ -17516,7 +18028,7 @@ function registerShareCommand(program2) {
17516
18028
  else process.stdout.write(`${result.state}
17517
18029
  `);
17518
18030
  } catch (error) {
17519
- handleError(shareCliError(error));
18031
+ return handleError(shareCliError(error));
17520
18032
  }
17521
18033
  });
17522
18034
  }
@@ -17527,7 +18039,7 @@ init_profiles();
17527
18039
  init_constants();
17528
18040
  init_session_expired();
17529
18041
  import { readFile as readFile4 } from "fs/promises";
17530
- import { join as join6 } from "path";
18042
+ import { join as join8 } from "path";
17531
18043
  import { createHash } from "crypto";
17532
18044
  import { writeJsonAtomic as writeJsonAtomic2 } from "@tinycloud/operations/state";
17533
18045
  import { LocationRecordValidationError, LocationRegistryHttpError, revokePolicyRootV3 } from "@tinycloud/sdk-core";
@@ -17632,8 +18144,8 @@ function createEncryptedProfileHistory(profileName, sessionSigner) {
17632
18144
  const observedProfiles = /* @__PURE__ */ new Set();
17633
18145
  let preparedKeys;
17634
18146
  let preparedKey;
17635
- const path = async (profile) => join6(await ProfileManager.getCacheDir(profile), "share-history-v2.json");
17636
- const legacyPath = async (profile) => join6(await ProfileManager.getCacheDir(profile), "share-history-v1.bin");
18147
+ const path = async (profile) => join8(await ProfileManager.getCacheDir(profile), "share-history-v2.json");
18148
+ const legacyPath = async (profile) => join8(await ProfileManager.getCacheDir(profile), "share-history-v1.bin");
17637
18149
  const b64 = (value) => Buffer.from(value).toString("base64url");
17638
18150
  const unb64 = (value) => {
17639
18151
  if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share history is unavailable");
@@ -18179,6 +18691,12 @@ async function selectedProfileName() {
18179
18691
  return process.env.TC_PROFILE ?? config.defaultProfile;
18180
18692
  }
18181
18693
 
18694
+ // src/lib/courier-loader.ts
18695
+ async function loadCourierEntry() {
18696
+ const load = new Function("specifier", "return import(specifier)");
18697
+ return load(new URL("./courier-entry.js", import.meta.url).href);
18698
+ }
18699
+
18182
18700
  // src/index.ts
18183
18701
  var { version } = JSON.parse(
18184
18702
  readFileSync2(new URL("../package.json", import.meta.url), "utf-8")
@@ -18197,7 +18715,7 @@ function selectedShareProfile() {
18197
18715
  }
18198
18716
  return process.env.TC_PROFILE;
18199
18717
  }
18200
- program.name("tc").description("TinyCloud CLI \u2014 self-sovereign storage from the terminal").version(version).option("-p, --profile <name>", "Profile to use").option("-H, --host <url>", "TinyCloud node URL").option("-v, --verbose", "Enable verbose output").option("--no-cache", "Disable caching").option("-q, --quiet", "Suppress non-essential output").option("--json", "Force JSON output");
18718
+ program.name("tc").description("TinyCloud CLI \u2014 self-sovereign storage from the terminal").version(version).option("-p, --profile <name>", "Profile to use").option("-H, --host <url>", "TinyCloud node URL").option("-v, --verbose", "Enable verbose output").option("--no-cache", "Disable caching").option("-q, --quiet", "Suppress non-essential output").option("--json", "Force JSON output").option("--replication", "Enable read-through replication for this invocation").option("--no-replication", "Disable read-through replication for this invocation").option("--replication-debug", "Write replication diagnostics to stderr");
18201
18719
  program.hook("preAction", async (thisCommand) => {
18202
18720
  const opts = thisCommand.optsWithGlobals();
18203
18721
  setJsonOutputRequested(opts.json === true);
@@ -18238,30 +18756,28 @@ configureShareCommandServices({
18238
18756
  nativeReader: shareAuthority.nativeReader
18239
18757
  });
18240
18758
  var argv = process.argv.slice(2);
18241
- var globalOptionsWithValues = /* @__PURE__ */ new Set(["--profile", "-p", "--host", "-H"]);
18759
+ var globalLongOptionsWithValues = { "--profile": true, "--host": true };
18760
+ var globalShortOptionsWithValues = { p: true, H: true };
18242
18761
  function firstCommandToken(values) {
18243
18762
  for (let index = 0; index < values.length; index += 1) {
18244
18763
  const value = values[index];
18245
18764
  if (value === "--") return values[index + 1];
18246
- if (globalOptionsWithValues.has(value)) {
18247
- index += 1;
18765
+ if (value.startsWith("--")) {
18766
+ if (globalLongOptionsWithValues[value]) index += 1;
18767
+ continue;
18768
+ }
18769
+ if (value.length > 1 && value.startsWith("-")) {
18770
+ for (let char = 1; char < value.length; char += 1) {
18771
+ if (!globalShortOptionsWithValues[value[char]]) continue;
18772
+ if (char === value.length - 1) index += 1;
18773
+ break;
18774
+ }
18248
18775
  continue;
18249
18776
  }
18250
- if (value.startsWith("--profile=") || value.startsWith("--host=")) continue;
18251
- if (value === "--verbose" || value === "--no-cache" || value === "-q" || value === "--quiet" || value === "--json") continue;
18252
- if (value.startsWith("-")) continue;
18253
18777
  return value;
18254
18778
  }
18255
18779
  return void 0;
18256
18780
  }
18257
- var isShareInvocation = firstCommandToken(argv) === "share";
18258
- if (isShareInvocation) {
18259
- registerShareCommand(program);
18260
- } else {
18261
- const loadLegacy = new Function("specifier", "return import(specifier)");
18262
- const { registerTinyCloudCommands } = await loadLegacy(new URL("./legacy-entry.js", import.meta.url).href);
18263
- registerTinyCloudCommands(program);
18264
- }
18265
18781
  program.addHelpText("before", () => `${theme.label("Version:")} ${theme.value(version)}
18266
18782
  `);
18267
18783
  program.addHelpText("afterAll", () => {
@@ -18281,10 +18797,37 @@ ${theme.muted("Docs:")} ${theme.accent("https://docs.tinycloud.xyz/cli")}
18281
18797
  ${theme.muted("Repo:")} ${theme.accent("https://github.com/tinycloudlabs/web-sdk")}
18282
18798
  `;
18283
18799
  });
18284
- try {
18285
- await program.parseAsync(process.argv);
18286
- } catch (error) {
18287
- handleError(error);
18800
+ var commandToken = firstCommandToken(argv);
18801
+ if (argv[0] === "courier") {
18802
+ const code3 = await (await loadCourierEntry()).runCourier(argv.slice(1));
18803
+ if (code3 !== 0) process.exit(code3);
18804
+ } else if (commandToken === "courier") {
18805
+ process.stderr.write(`${TC_OPTIONS_BEFORE_COURIER}
18806
+ `);
18807
+ process.exit(2);
18808
+ } else {
18809
+ if (commandToken === "share") {
18810
+ registerShareCommand(program);
18811
+ } else {
18812
+ const loadLegacy = new Function("specifier", "return import(specifier)");
18813
+ const { registerTinyCloudCommands } = await loadLegacy(new URL("./legacy-entry.js", import.meta.url).href);
18814
+ registerTinyCloudCommands(program);
18815
+ }
18816
+ try {
18817
+ await program.parseAsync(process.argv);
18818
+ if (await closeReplication()) {
18819
+ process.exitCode = 0;
18820
+ const stdoutFlushed = Promise.withResolvers();
18821
+ process.stdout.write("", () => stdoutFlushed.resolve());
18822
+ await stdoutFlushed.promise;
18823
+ const stderrFlushed = Promise.withResolvers();
18824
+ process.stderr.write("", () => stderrFlushed.resolve());
18825
+ await stderrFlushed.promise;
18826
+ process.exit();
18827
+ }
18828
+ } catch (error) {
18829
+ await handleError(error);
18830
+ }
18288
18831
  }
18289
18832
  /*! Bundled license information:
18290
18833