@capxul/cli 4.20.0-beta.6 → 4.20.0-beta.8

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/main.mjs CHANGED
@@ -307,7 +307,7 @@ const privateTransition = (state, event) => {
307
307
  }
308
308
  }
309
309
  };
310
- const transition = (state, event, config = DEFAULT_CONFIG) => {
310
+ const transition$1 = (state, event, config = DEFAULT_CONFIG) => {
311
311
  if (isPrivate(event)) return privateTransition(state, event);
312
312
  if (event._tag === "VerifyOtp" && !/^\d{6}$/.test(event.otp)) return { refused: "INVALID_INPUT" };
313
313
  if (event._tag === "CreateOrganization" && event.draft.handle.trim() === "") return { refused: "INVALID_INPUT" };
@@ -2609,7 +2609,8 @@ const SETTLEMENT_ASSETS_BASE_SEPOLIA = Object.freeze({
2609
2609
  CAPXUL_TEST_WETH: SYNTHETIC_TEST_ASSET_FIXTURES.weth
2610
2610
  });
2611
2611
  const CONFIGURED_MONEY_ASSETS = Object.freeze(Object.values(SETTLEMENT_ASSETS_BASE_SEPOLIA));
2612
- Object.freeze({ BASE_SEPOLIA: Object.freeze({
2612
+ /** Public AssetIds for the admitted Base Sepolia test assets. */
2613
+ const TOKENS = Object.freeze({ BASE_SEPOLIA: Object.freeze({
2613
2614
  CAPXUL_TEST_USDC: SETTLEMENT_ASSETS_BASE_SEPOLIA.CAPXUL_TEST_USDC.assetId,
2614
2615
  CAPXUL_TEST_USDT: SETTLEMENT_ASSETS_BASE_SEPOLIA.CAPXUL_TEST_USDT.assetId,
2615
2616
  CAPXUL_TEST_WETH: SETTLEMENT_ASSETS_BASE_SEPOLIA.CAPXUL_TEST_WETH.assetId
@@ -4173,11 +4174,11 @@ function encodePacked(types, values) {
4173
4174
  for (let i = 0; i < types.length; i++) {
4174
4175
  const type = types[i];
4175
4176
  const value = values[i];
4176
- data.push(encode$2(type, value));
4177
+ data.push(encode$3(type, value));
4177
4178
  }
4178
4179
  return concatHex(data);
4179
4180
  }
4180
- function encode$2(type, value, isArray = false) {
4181
+ function encode$3(type, value, isArray = false) {
4181
4182
  if (type === "address") {
4182
4183
  const address = value;
4183
4184
  if (!isAddress(address)) throw new InvalidAddressError({ address });
@@ -4211,7 +4212,7 @@ function encode$2(type, value, isArray = false) {
4211
4212
  if (arrayMatch && Array.isArray(value)) {
4212
4213
  const [_type, childType] = arrayMatch;
4213
4214
  const data = [];
4214
- for (let i = 0; i < value.length; i++) data.push(encode$2(childType, value[i], true));
4215
+ for (let i = 0; i < value.length; i++) data.push(encode$3(childType, value[i], true));
4215
4216
  if (data.length === 0) return "0x";
4216
4217
  return concatHex(data);
4217
4218
  }
@@ -4258,6 +4259,28 @@ function getCreate2Address(opts) {
4258
4259
  //#region ../../packages/config/src/org-payments.ts
4259
4260
  /** Zodiac MultiSendCallOnly module on Base Sepolia. */
4260
4261
  const MULTI_SEND_CALL_ONLY = "0x9641d764fc13c8b624c04430c7356c1c7c8102e2";
4262
+ /** Supported Organization permission identities, newest first. */
4263
+ const ORG_PERMISSION_IDENTITY_PROFILES = [{
4264
+ id: "zodiac-roles-2.1.1",
4265
+ moduleFactoryRuntimeKeccak256: "0x01623cbcf010a1c326230f1b2d5f48a66b440232ee49096102bc84967dc5f21e",
4266
+ mastercopy: "0xf2964ce6161ce0e75964fe7927ce114cb0b283d5",
4267
+ mastercopyRuntimeKeccak256: "0x471d8b3b419f1eb955230c0326c8812176df49bf3c7b414a563fda5a3c6c10b6",
4268
+ proxyRuntimeKeccak256: "0x248f9591f94c24c199ade3e73feb477cf02d387b2bea91f2f931a0deb094f163",
4269
+ multiSendUnwrapper: "0xb4cd4bb764c089f20da18700ce8bc5e49f369efd",
4270
+ multiSendUnwrapperRuntimeKeccak256: "0x1f6e088be5e6ef9d0fbe0547d3fa9a9e40d823433fd8a4449215b5663209a1eb"
4271
+ }, {
4272
+ id: "zodiac-roles-2.1.0",
4273
+ moduleFactoryRuntimeKeccak256: "0x01623cbcf010a1c326230f1b2d5f48a66b440232ee49096102bc84967dc5f21e",
4274
+ mastercopy: "0x9646fdad06d3e24444381f44362a3b0eb343d337",
4275
+ mastercopyRuntimeKeccak256: "0x87911cbc6aa0496e6bcb07dab2462b9c76daea130dede6dcc57d0adf307fa7ec",
4276
+ proxyRuntimeKeccak256: "0x9d4059abf46db50e4b48d9b6404b1f200cb5a1efeb3725be2de630a2a9001944",
4277
+ multiSendUnwrapper: "0x93b7fcbc63ed8a3a24b59e1c3e6649d50b7427c0",
4278
+ multiSendUnwrapperRuntimeKeccak256: "0x4d8d8608b70a6e2f40726b9dd74406bbf73e90cac58e23ee953e2b45ad7dc3df"
4279
+ }];
4280
+ const CURRENT_ORG_PERMISSION_IDENTITY = ORG_PERMISSION_IDENTITY_PROFILES[0];
4281
+ ORG_PERMISSION_IDENTITY_PROFILES[1];
4282
+ CURRENT_ORG_PERMISSION_IDENTITY.mastercopy;
4283
+ CURRENT_ORG_PERMISSION_IDENTITY.multiSendUnwrapper;
4261
4284
  //#endregion
4262
4285
  //#region ../../packages/config/src/safe.ts
4263
4286
  const BASE_SEPOLIA_CHAIN_ID = 84532;
@@ -4480,14 +4503,6 @@ function deriveCapxulSafeAddressForOwner(input) {
4480
4503
  //#endregion
4481
4504
  //#region ../../packages/config/src/role-dsl.ts
4482
4505
  const ADMIN_ROLE_LABEL = "Admin";
4483
- function normalizeOrgRoleLabel(label) {
4484
- const normalized = label.trim().replace(/\s+/g, " ");
4485
- if (normalized.length === 0) throw Errors.invalidInput("role.label", "must be a non-empty role label");
4486
- return normalized;
4487
- }
4488
- function orgRoleKeyForLabel(label) {
4489
- return keccak256(toBytes(normalizeOrgRoleLabel(label).toLowerCase()));
4490
- }
4491
4506
  //#endregion
4492
4507
  //#region ../../packages/config/src/capxul-payments-v2.ts
4493
4508
  /** The exact compiled CapxulPaymentsV2 ABI. */
@@ -5159,6 +5174,44 @@ const ROLE_AUTHORITY_EVENT_BY_FUNCTION_SELECTOR = new Map([
5159
5174
  toEventSelector("AssignRoles(address,bytes32[],bool[])");
5160
5175
  new Set([...ROLE_AUTHORITY_EVENT_BY_FUNCTION_SELECTOR.values()].map((topic) => topic.toLowerCase()));
5161
5176
  //#endregion
5177
+ //#region ../../packages/sdk/src/domain/money/parse-money.ts
5178
+ const AMOUNT_PATTERN = /^(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?$/;
5179
+ const ZERO_PATTERN = /^0+(?:\.0+)?$/;
5180
+ /** Shared input grammar. Callers retain their own amount size limits. */
5181
+ function parseDecimalInput(input, decimals, maximumLength) {
5182
+ if (!Number.isInteger(decimals) || decimals < 0) throw Errors.invalidInput("asset.decimals", "must be a non-negative integer");
5183
+ const value = input.trim();
5184
+ if (value.length === 0) return {
5185
+ kind: "error",
5186
+ reason: "required"
5187
+ };
5188
+ if (value.length > maximumLength) return {
5189
+ kind: "error",
5190
+ reason: "too-long"
5191
+ };
5192
+ const negative = value.startsWith("-");
5193
+ const unsigned = negative ? value.slice(1) : value;
5194
+ if (!AMOUNT_PATTERN.test(unsigned)) return {
5195
+ kind: "error",
5196
+ reason: "invalid-format"
5197
+ };
5198
+ if (negative) return {
5199
+ kind: "error",
5200
+ reason: "non-positive"
5201
+ };
5202
+ const normalized = unsigned.replaceAll(",", "");
5203
+ const fraction = normalized.split(".")[1];
5204
+ if (fraction !== void 0 && fraction.length > decimals) return {
5205
+ kind: "error",
5206
+ reason: "too-many-decimals"
5207
+ };
5208
+ if (ZERO_PATTERN.test(normalized)) return {
5209
+ kind: "error",
5210
+ reason: "non-positive"
5211
+ };
5212
+ return { value: normalized };
5213
+ }
5214
+ //#endregion
5162
5215
  //#region ../../packages/sdk/src/domain/money/asset-amount.ts
5163
5216
  /** Convert raw units without rounding or changing asset identity. */
5164
5217
  function assetAmountFromRaw(raw, asset) {
@@ -5174,6 +5227,14 @@ function toRaw(amount, asset) {
5174
5227
  if (amount.asset !== asset.assetId) throw Errors.invalidInput("amount.asset", "must match the selected asset on its chain");
5175
5228
  return decimalToRaw(amount.value, asset.decimals);
5176
5229
  }
5230
+ function parseAssetAmount(input, asset) {
5231
+ validateMetadata(asset);
5232
+ const parsed = parseDecimalInput(input, asset.decimals, 100);
5233
+ return "kind" in parsed ? parsed : {
5234
+ asset: asset.assetId,
5235
+ value: parsed.value.replace(/^0+(?=\d)/, "")
5236
+ };
5237
+ }
5177
5238
  function requireAsset(id) {
5178
5239
  const asset = configuredMoneyAssetById(id);
5179
5240
  if (asset === null) throw Errors.invalidInput("amount.asset", "is not a registered asset");
@@ -5564,10 +5625,12 @@ const CAPXUL_OPERATIONS = {
5564
5625
  prepareOrganizationPaymentExecution: "moneyExecution.prepareOrganizationPaymentExecution",
5565
5626
  preparePaymentExecution: "moneyExecution.preparePaymentExecution",
5566
5627
  preparePaymentLifecycleExecution: "moneyExecution.preparePaymentLifecycleExecution",
5628
+ prepareInvitationConfiguration: "moneyExecution.prepareInvitationConfiguration",
5567
5629
  preparePermissionExecution: "moneyExecution.preparePermissionExecution",
5568
5630
  recoverSubmissionClaims: "moneyExecution.recoverSubmissionClaims",
5569
5631
  submit: "moneyExecution.submit",
5570
5632
  submitPaymentCommandExecution: "moneyExecution.submitPaymentCommandExecution",
5633
+ submitInvitationConfigurationExecution: "moneyExecution.submitInvitationConfigurationExecution",
5571
5634
  submitPaymentExecution: "moneyExecution.submitPaymentExecution",
5572
5635
  submitPermissionExecution: "moneyExecution.submitPermissionExecution"
5573
5636
  },
@@ -5589,20 +5652,52 @@ const CAPXUL_OPERATIONS = {
5589
5652
  completePerson: "onboarding.completePerson"
5590
5653
  },
5591
5654
  org: {
5592
- acceptInvitations: "org.acceptInvitations",
5593
5655
  account: { get: "org.account.get" },
5594
- activateMember: "org.activateMember",
5595
5656
  auditLog: "org.auditLog",
5596
5657
  confirmBootstrap: "org.confirmBootstrap",
5597
5658
  createOrg: "org.createOrg",
5598
5659
  create: "org.create",
5599
- detectAndAcceptPendingInvitations: "org.detectAndAcceptPendingInvitations",
5660
+ /**
5661
+ * §11: the consent mutation reports `org_invite_accepted` through its own
5662
+ * scheduled emitter. It is named for the emit, not the acceptance, because
5663
+ * the interactive acceptance itself owns `org.invitations.accept`.
5664
+ */
5665
+ emitInviteAccepted: "org.emitInviteAccepted",
5600
5666
  emitInviteProgressed: "org.emitInviteProgressed",
5601
5667
  expireInvitations: "org.expireInvitations",
5602
5668
  getLifecycle: "org.getLifecycle",
5603
5669
  getProofReceipt: "org.getProofReceipt",
5604
5670
  invite: "org.invite",
5605
- inviteMember: "org.inviteMember",
5671
+ /** D8 invitation lifecycle: preview, own list, read, and the four transitions. */
5672
+ invitations: {
5673
+ /** The exact grantee's consent commit (§8): one accepted digest, once. */
5674
+ accept: "org.invitations.accept",
5675
+ /**
5676
+ * The authoring write. It is named `authorize`, not `invite`, because the
5677
+ * backend already uses `org.invitations.invite` as a refusal label and
5678
+ * `lint:telemetry-spine` refuses a label that collides with an operation.
5679
+ */
5680
+ authorize: "org.invitations.authorize",
5681
+ cancel: "org.invitations.cancel",
5682
+ decline: "org.invitations.decline",
5683
+ get: "org.invitations.get",
5684
+ list: "org.invitations.list",
5685
+ /**
5686
+ * The Admin-scoped Organization list. It is named `listScoped`, not
5687
+ * `list`, because the own-offer list already owns `org.invitations.list`.
5688
+ */
5689
+ listScoped: "org.invitations.listScoped",
5690
+ /**
5691
+ * The authoring write's inner hops: the SDK drives the backend's existing
5692
+ * prepare/submit pair, and these names label the two Convex calls. The
5693
+ * owning `capxul_sdk_operation` record stays `org.invitations.authorize`.
5694
+ */
5695
+ prepareConfiguration: "org.invitations.prepareConfiguration",
5696
+ preview: "org.invitations.preview",
5697
+ resend: "org.invitations.resend",
5698
+ retry: "org.invitations.retry",
5699
+ submitConfiguration: "org.invitations.submitConfiguration"
5700
+ },
5606
5701
  list: "org.list",
5607
5702
  load: "org.load",
5608
5703
  me: "org.me",
@@ -5661,7 +5756,6 @@ const CAPXUL_OPERATIONS = {
5661
5756
  },
5662
5757
  recordFailure: "org.recordFailure",
5663
5758
  recordProgress: "org.recordProgress",
5664
- resendInviteToken: "org.resendInviteToken",
5665
5759
  resumeBootstrapSubmission: "org.resumeBootstrapSubmission",
5666
5760
  retry: "org.retry",
5667
5761
  retrySetup: "org.retrySetup",
@@ -5678,7 +5772,6 @@ const CAPXUL_OPERATIONS = {
5678
5772
  retry: "organizationPayments.retry"
5679
5773
  },
5680
5774
  organizationSetup: { getProofReceipt: "organizationSetup.getProofReceipt" },
5681
- organizations: { detectAndAcceptPendingInvitations: "orgs.detectAndAcceptPendingInvitations" },
5682
5775
  payments: {
5683
5776
  cancel: "payments.cancel",
5684
5777
  claim: "payments.claim",
@@ -5716,6 +5809,7 @@ const CAPXUL_OPERATIONS = {
5716
5809
  terms: "payroll.terms"
5717
5810
  },
5718
5811
  permission: {
5812
+ verifyInvitationConfiguration: "permission.verifyInvitationConfiguration",
5719
5813
  command: "permission.command",
5720
5814
  retryVerification: "permission.retryVerification",
5721
5815
  verify: "permission.verify",
@@ -6273,6 +6367,11 @@ const OrgCreateFailedProps = Schema.Struct({
6273
6367
  const OrgInviteSentProps = Schema.Struct({
6274
6368
  ...TelemetryEnvelopeProps,
6275
6369
  org_id: OptionalOrgId,
6370
+ invitation_id: OptionalString,
6371
+ generation: OptionalString,
6372
+ command_id: OptionalString,
6373
+ subject_party_id: OptionalString,
6374
+ persisted_at: OptionalEpochMs,
6276
6375
  email_domain: OptionalString,
6277
6376
  role: OptionalString,
6278
6377
  status: OptionalString
@@ -6280,6 +6379,11 @@ const OrgInviteSentProps = Schema.Struct({
6280
6379
  const OrgInviteAcceptedProps = Schema.Struct({
6281
6380
  ...TelemetryEnvelopeProps,
6282
6381
  org_id: OptionalOrgId,
6382
+ invitation_id: OptionalString,
6383
+ accepted_at: OptionalEpochMs,
6384
+ generation: OptionalString,
6385
+ command_id: OptionalString,
6386
+ subject_party_id: OptionalString,
6283
6387
  role: OptionalString,
6284
6388
  status: OptionalString
6285
6389
  });
@@ -6287,6 +6391,7 @@ const OrgInviteExpiredProps = Schema.Struct({
6287
6391
  ...TelemetryEnvelopeProps,
6288
6392
  org_id: OptionalOrgId,
6289
6393
  email_domain: OptionalString,
6394
+ invitation_id: OptionalString,
6290
6395
  reason: OptionalString,
6291
6396
  role: OptionalString,
6292
6397
  status: OptionalString
@@ -6439,9 +6544,24 @@ const PermissionMirrorVerificationProps = Schema.Struct({
6439
6544
  "applied",
6440
6545
  "replayed",
6441
6546
  "conflicting_replay",
6442
- "revision_mismatch"
6547
+ "revision_mismatch",
6548
+ "cleanup_required",
6549
+ "not_applied",
6550
+ "application_failure",
6551
+ "mismatch",
6552
+ "refused"
6443
6553
  ])),
6554
+ command_kind: Schema.optional(Schema.Literals(["invitation_configuration", "invitation_eoa"])),
6444
6555
  command_id: OptionalString,
6556
+ invitation_id: OptionalString,
6557
+ generation: OptionalString,
6558
+ purpose: Schema.optional(Schema.Literals([
6559
+ "configure",
6560
+ "grant",
6561
+ "disable",
6562
+ "compensate"
6563
+ ])),
6564
+ $insert_id: OptionalString,
6445
6565
  /**
6446
6566
  * #1961 R03. The authorized stored row names the Permission it changed; the
6447
6567
  * verified receipt names the chain facts that prove it. Every field is
@@ -6828,6 +6948,13 @@ const CLI_COMMAND_NAMES = [
6828
6948
  "auth.profile",
6829
6949
  "auth.status",
6830
6950
  "auth.logout",
6951
+ "contact",
6952
+ "contact.list",
6953
+ "contact.get",
6954
+ "contact.add",
6955
+ "contact.label",
6956
+ "contact.hide",
6957
+ "contact.unhide",
6831
6958
  "org",
6832
6959
  CAPXUL_OPERATIONS.org.list,
6833
6960
  "org.use",
@@ -6836,6 +6963,24 @@ const CLI_COMMAND_NAMES = [
6836
6963
  CAPXUL_OPERATIONS.org.members,
6837
6964
  "org.member",
6838
6965
  "org.member.get",
6966
+ "org.member.invite",
6967
+ "org.contact",
6968
+ "org.contact.list",
6969
+ "org.contact.get",
6970
+ "org.contact.add",
6971
+ "org.contact.label",
6972
+ "org.contact.hide",
6973
+ "org.contact.unhide",
6974
+ CAPXUL_OPERATIONS.org.invite,
6975
+ "org.invite.list",
6976
+ "org.invite.get",
6977
+ "org.invite.wait",
6978
+ "org.invite.review",
6979
+ "org.invite.accept",
6980
+ "org.invite.decline",
6981
+ "org.invite.cancel",
6982
+ "org.invite.resend",
6983
+ "org.invite.retry",
6839
6984
  "org.wait",
6840
6985
  CAPXUL_OPERATIONS.org.create,
6841
6986
  CAPXUL_OPERATIONS.org.retry,
@@ -6846,7 +6991,8 @@ const CLI_CAPABILITIES = [
6846
6991
  "diagnostics",
6847
6992
  "collection",
6848
6993
  "authentication",
6849
- "organization"
6994
+ "organization",
6995
+ "contacts"
6850
6996
  ];
6851
6997
  const CLI_INVOCATION_KINDS = [
6852
6998
  "command",
@@ -7090,6 +7236,7 @@ const TELEMETRY_EVENT_SCHEMAS = {
7090
7236
  ...TelemetryEnvelopeProps,
7091
7237
  organization_id: OrgIdTelemetrySchema,
7092
7238
  progress: Schema.Literals([
7239
+ "configuration_authorized",
7093
7240
  "account_ready",
7094
7241
  "permission_applied",
7095
7242
  "membership_activated",
@@ -7097,6 +7244,9 @@ const TELEMETRY_EVENT_SCHEMAS = {
7097
7244
  ]),
7098
7245
  /** The invited Party. The initiating actor stays on the envelope's identity. */
7099
7246
  member_party_id: Schema.String,
7247
+ invitation_id: Schema.optional(Schema.String),
7248
+ generation: Schema.optional(Schema.String),
7249
+ command_id: Schema.optional(Schema.String),
7100
7250
  occurred_at: Schema.Number
7101
7251
  })
7102
7252
  }),
@@ -7800,8 +7950,10 @@ const CAPXUL_FUNCTIONS = {
7800
7950
  "movement/annotations": { overlay: "movement/annotations:overlay" },
7801
7951
  "moneyExecution/actions": {
7802
7952
  abandonPaymentExecution: "moneyExecution/actions:abandonPaymentExecution",
7953
+ prepareInvitationConfiguration: "moneyExecution/actions:prepareInvitationConfiguration",
7803
7954
  preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
7804
7955
  preparePaymentExecution: "moneyExecution/actions:preparePaymentExecution",
7956
+ submitInvitationConfigurationExecution: "moneyExecution/actions:submitInvitationConfigurationExecution",
7805
7957
  submitPermissionExecution: "moneyExecution/actions:submitPermissionExecution",
7806
7958
  submitPaymentExecution: "moneyExecution/actions:submitPaymentExecution"
7807
7959
  },
@@ -7818,8 +7970,6 @@ const CAPXUL_FUNCTIONS = {
7818
7970
  },
7819
7971
  "org/actions": {
7820
7972
  confirmBootstrap: "org/actions:confirmBootstrap",
7821
- detectAndAcceptPendingInvitations: "org/actions:detectAndAcceptPendingInvitations",
7822
- inviteMember: "org/actions:inviteMember",
7823
7973
  prepareBootstrap: "org/actions:prepareBootstrap",
7824
7974
  prepareFounderAccount: "org/actions:prepareFounderAccount",
7825
7975
  readTreasury: "org/actions:readTreasury",
@@ -7850,16 +8000,25 @@ const CAPXUL_FUNCTIONS = {
7850
8000
  startOrResume: "org/lifecycle:startOrResume"
7851
8001
  },
7852
8002
  "org/mutations": {
8003
+ accept: "org/mutations:accept",
8004
+ cancel: "org/mutations:cancel",
7853
8005
  createOrg: "org/mutations:createOrg",
7854
- resendInviteToken: "org/mutations:resendInviteToken"
8006
+ decline: "org/mutations:decline",
8007
+ invite: "org/mutations:invite",
8008
+ resend: "org/mutations:resend",
8009
+ retry: "org/mutations:retry"
7855
8010
  },
7856
8011
  "org/queries": {
8012
+ getInvitation: "org/queries:getInvitation",
7857
8013
  listAll: "org/queries:listAll",
8014
+ listInvitations: "org/queries:listInvitations",
7858
8015
  listMembersByOrgId: "org/queries:listMembersByOrgId",
7859
8016
  listMine: "org/queries:listMine",
8017
+ listMyInvitations: "org/queries:listMyInvitations",
7860
8018
  listRolesByOrgId: "org/queries:listRolesByOrgId",
7861
8019
  loadByOrgId: "org/queries:loadByOrgId",
7862
- me: "org/queries:me"
8020
+ me: "org/queries:me",
8021
+ previewInvite: "org/queries:previewInvite"
7863
8022
  },
7864
8023
  "smartAccount/actions": {
7865
8024
  claim: "smartAccount/actions:claim",
@@ -8819,6 +8978,94 @@ const EventRefSchema = Schema.Struct({
8819
8978
  topic0: Bytes32Schema,
8820
8979
  dataHash: Bytes32Schema
8821
8980
  });
8981
+ const InvitationEoaReceiptSchema = Schema.Struct({
8982
+ kind: Schema.Literal("invitation_eoa"),
8983
+ chainId: BaseSepoliaChainIdSchema,
8984
+ transactionHash: TxHashSchema,
8985
+ blockNumber: BlockNumberSchema,
8986
+ blockHash: Bytes32Schema,
8987
+ sender: AddressSchema,
8988
+ nonce: Schema.String.pipe(Schema.refine((value) => /^(0|[1-9][0-9]*)$/.test(value)))
8989
+ });
8990
+ const PermissionReceiptRefSchema = Schema.Union([Schema.Struct({
8991
+ kind: Schema.Literal("user_operation"),
8992
+ ...ReceiptRefSchema.fields
8993
+ }), InvitationEoaReceiptSchema]);
8994
+ const PermissionEventRefSchema = Schema.Struct({
8995
+ receipt: PermissionReceiptRefSchema,
8996
+ emitter: AddressSchema,
8997
+ transactionIndex: LogIndexSchema,
8998
+ logIndex: LogIndexSchema,
8999
+ topic0: Bytes32Schema,
9000
+ dataHash: Bytes32Schema
9001
+ });
9002
+ Schema.Union([
9003
+ Schema.Struct({
9004
+ status: Schema.Literal("pending"),
9005
+ reason: Schema.Literal("receipt_missing")
9006
+ }),
9007
+ Schema.Struct({
9008
+ status: Schema.Literal("provider_failure"),
9009
+ retryable: Schema.Literal(true)
9010
+ }),
9011
+ Schema.Struct({
9012
+ status: Schema.Literal("mismatch"),
9013
+ code: Schema.Literals([
9014
+ "wrong_chain",
9015
+ "wrong_sender",
9016
+ "wrong_transaction",
9017
+ "failed_transaction",
9018
+ "non_canonical_block",
9019
+ "wrong_contract_identity",
9020
+ "missing_event",
9021
+ "extra_authority_event",
9022
+ "wrong_fact_tuple",
9023
+ "wrong_state",
9024
+ "conflicting_replay"
9025
+ ]),
9026
+ attemptTransactionHash: Schema.optional(TxHashSchema),
9027
+ receipt: Schema.optional(InvitationEoaReceiptSchema)
9028
+ }),
9029
+ Schema.Struct({
9030
+ status: Schema.Literal("verified"),
9031
+ commandId: PaymentCommandIdSchema,
9032
+ receipt: InvitationEoaReceiptSchema,
9033
+ events: Schema.Array(PermissionEventRefSchema),
9034
+ factsHash: Bytes32Schema
9035
+ })
9036
+ ]);
9037
+ /**
9038
+ * One stored `setAllowance` call inside a command's expected authority set.
9039
+ *
9040
+ * The pinned Roles module emits `SetAllowance` as *post-state*, not as the call
9041
+ * arguments: `maxRefill = 0` becomes `type(uint128).max`, and `timestamp = 0`
9042
+ * becomes the receipt block's timestamp. A hash over the call arguments can
9043
+ * therefore never match that receipt. `index` names the entry in
9044
+ * `expectedAuthorityEvents` the call produced, and `data` is the call's own
9045
+ * calldata, so the verifier can rebuild the one hash the module actually emits.
9046
+ * The rule is pinned by `test:invitation-conformance`.
9047
+ */
9048
+ const AllowanceExpectationSchema = Schema.Struct({
9049
+ index: nonNegativeInteger("index"),
9050
+ data: Schema.String.pipe(Schema.refine((value) => /^0x[0-9a-fA-F]+$/u.test(value), { message: "data must be 0x-prefixed hex" }))
9051
+ });
9052
+ Schema.Struct({
9053
+ commandId: PaymentCommandIdSchema,
9054
+ userOpHash: Bytes32Schema,
9055
+ generationHash: Bytes32Schema,
9056
+ chainId: BaseSepoliaChainIdSchema,
9057
+ organizationSafe: SafeAddressSchema,
9058
+ rolesModule: ModuleAddressSchema,
9059
+ userOpSenderSafe: SafeAddressSchema,
9060
+ expectedAuthorityEvents: Schema.Array(Bytes32Schema),
9061
+ /**
9062
+ * Absent means "no normalization": every expected hash is compared as stored.
9063
+ * A command whose set contains `setAllowance` then refuses (`wrong_fact_tuple`)
9064
+ * rather than passing on a weakened proof, so an incomplete caller fails
9065
+ * closed.
9066
+ */
9067
+ allowanceExpectations: Schema.optional(Schema.Array(AllowanceExpectationSchema))
9068
+ });
8822
9069
  const PermissionOperationSchema = Schema.Literals([
8823
9070
  "create",
8824
9071
  "change",
@@ -8862,7 +9109,9 @@ const StoredPermissionCommandSchema = Schema.Struct({
8862
9109
  });
8863
9110
  Schema.Struct({
8864
9111
  command: StoredPermissionCommandSchema,
8865
- userOpHash: Bytes32Schema
9112
+ userOpHash: Bytes32Schema,
9113
+ /** The stored `setAllowance` calls of this command's own expectation. */
9114
+ allowanceExpectations: Schema.optional(Schema.Array(AllowanceExpectationSchema))
8866
9115
  });
8867
9116
  Schema.Union([
8868
9117
  Schema.Struct({
@@ -9706,6 +9955,27 @@ Schema.Struct({
9706
9955
  digest: Bytes32Schema,
9707
9956
  value: PermissionResourceSchema
9708
9957
  });
9958
+ Schema.Struct({
9959
+ commandId: PaymentCommandIdSchema,
9960
+ signerAddress: AddressSchema
9961
+ });
9962
+ Schema.Struct({
9963
+ executionId: MoneyExecutionIdSchema,
9964
+ commandId: PaymentCommandIdSchema,
9965
+ orgId: nonEmpty("orgId"),
9966
+ invitationId: nonEmpty("invitationId"),
9967
+ generationHash: Bytes32Schema,
9968
+ chainId: BaseSepoliaChainIdSchema,
9969
+ signerAddress: AddressSchema,
9970
+ userOpSenderSafe: SafeAddressSchema,
9971
+ digest: Bytes32Schema
9972
+ });
9973
+ Schema.Struct({
9974
+ status: Schema.Literal("submitted"),
9975
+ executionId: MoneyExecutionIdSchema,
9976
+ commandId: PaymentCommandIdSchema,
9977
+ userOpHash: Bytes32Schema
9978
+ });
9709
9979
  Schema.Struct({
9710
9980
  status: Schema.Literal("submitted"),
9711
9981
  executionId: MoneyExecutionIdSchema,
@@ -10279,6 +10549,160 @@ const CurrentPolicySchema = Schema.Union([Schema.Struct({
10279
10549
  actions: Schema.NonEmptyArray(Schema.Literals(["pay", "commitments"]))
10280
10550
  })
10281
10551
  }), Schema.Struct({ type: Schema.Literal("managePeople") })]);
10552
+ const BudgetIntentSchema = CurrentPolicySchema.pipe(Schema.refine((value) => {
10553
+ if (value.type !== "budget") return false;
10554
+ const { actions, recipients } = value.scope;
10555
+ if (new Set(actions).size !== actions.length) return false;
10556
+ return recipients.type === "anyone" || recipients.accounts.length >= 1 && recipients.accounts.length <= 32 && new Set(recipients.accounts).size === recipients.accounts.length;
10557
+ }));
10558
+ const InvitationIdSchema = Schema.NonEmptyString;
10559
+ const InvitationTargetSchema = Schema.Struct({ invitationId: InvitationIdSchema });
10560
+ Schema.Struct({
10561
+ ...InvitationTargetSchema.fields,
10562
+ expectedOfferDigest: Bytes32Schema
10563
+ });
10564
+ /** The requested grantee of one invitation. */
10565
+ const GranteeRefSchema = Schema.Union([Schema.Struct({
10566
+ type: Schema.Literal("email"),
10567
+ email: Schema.NonEmptyString
10568
+ }), Schema.Struct({
10569
+ type: Schema.Literal("account"),
10570
+ accountId: AccountIdSchema
10571
+ })]);
10572
+ Schema.Struct({
10573
+ requestKey: Schema.String.pipe(Schema.refine((value) => /^[\x20-\x7e]{1,128}$/.test(value))),
10574
+ to: GranteeRefSchema,
10575
+ grants: Schema.Array(Schema.Union([
10576
+ Schema.Struct({
10577
+ type: Schema.Literal("budget"),
10578
+ budgetId: PermissionIdSchema
10579
+ }),
10580
+ Schema.Struct({
10581
+ type: Schema.Literal("managePeople"),
10582
+ permissionId: PermissionIdSchema
10583
+ }),
10584
+ Schema.Struct({
10585
+ type: Schema.Literal("new_budget"),
10586
+ label: Schema.String.pipe(Schema.refine((value) => value.trim().length >= 1 && value.trim().length <= 128)),
10587
+ intent: BudgetIntentSchema
10588
+ })
10589
+ ]))
10590
+ }).pipe(Schema.refine((value) => {
10591
+ if (value.grants.length < 1 || value.grants.length > 16) return false;
10592
+ const existing = value.grants.flatMap((grant) => grant.type === "new_budget" ? [] : [grant.type === "budget" ? grant.budgetId : grant.permissionId]);
10593
+ return new Set(existing).size === existing.length && value.grants.filter((grant) => grant.type === "new_budget").length <= 4;
10594
+ }));
10595
+ const InvitationPhaseSchema = Schema.Literals([
10596
+ "authorizing",
10597
+ "pending",
10598
+ "pending_grant",
10599
+ "active",
10600
+ "revoking",
10601
+ "declined",
10602
+ "cancelled",
10603
+ "expired",
10604
+ "revoked"
10605
+ ]);
10606
+ const EndingReasonSchema = Schema.Literals([
10607
+ "declined",
10608
+ "cancelled",
10609
+ "expired",
10610
+ "revoked"
10611
+ ]);
10612
+ const OfferedAuthoritySchema = Schema.Union([Schema.Struct({
10613
+ type: Schema.Literal("budget"),
10614
+ budgetId: Schema.NonEmptyString.pipe(Schema.refine((value) => value.length > 0)),
10615
+ label: Schema.String,
10616
+ policy: BudgetIntentSchema,
10617
+ policyDigest: Bytes32Schema
10618
+ }), Schema.Struct({
10619
+ type: Schema.Literal("managePeople"),
10620
+ permissionId: PermissionIdSchema,
10621
+ label: Schema.String
10622
+ })]);
10623
+ const NewBudgetOfferSchema = Schema.Struct({
10624
+ type: Schema.Literal("new_budget"),
10625
+ label: Schema.String,
10626
+ policy: BudgetIntentSchema,
10627
+ policyDigest: Bytes32Schema
10628
+ });
10629
+ const PreviewGranteeSchema = Schema.Struct({
10630
+ partyId: Schema.NullOr(PartyIdSchema),
10631
+ accountId: Schema.NullOr(AccountIdSchema),
10632
+ safeAddress: AddressSchema,
10633
+ accountReady: Schema.NullOr(Schema.Boolean)
10634
+ });
10635
+ Schema.Struct({
10636
+ orgId: Schema.NonEmptyString.pipe(Schema.refine((value) => value.length > 0)),
10637
+ grantee: PreviewGranteeSchema,
10638
+ grants: Schema.Array(Schema.Union([OfferedAuthoritySchema, NewBudgetOfferSchema])),
10639
+ existingAssignments: Schema.Array(PermissionAssignmentIdSchema),
10640
+ includesManagement: Schema.Boolean,
10641
+ observedAt: EpochMsSchema
10642
+ });
10643
+ Schema.Struct({
10644
+ cursor: Schema.optional(Schema.NonEmptyString),
10645
+ limit: Schema.optional(Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 1 && value <= 100))),
10646
+ phases: Schema.optional(Schema.Array(InvitationPhaseSchema).pipe(Schema.refine((value) => new Set(value).size === value.length)))
10647
+ });
10648
+ const InvitationRecoverySchema = Schema.Struct({
10649
+ actor: Schema.Literals([
10650
+ "system",
10651
+ "invitee",
10652
+ "admin"
10653
+ ]),
10654
+ action: Schema.Literals([
10655
+ "wait",
10656
+ "retry",
10657
+ "review_offer",
10658
+ "reauthorize"
10659
+ ]),
10660
+ reason: Schema.Literals([
10661
+ "configuration_pending",
10662
+ "grant_pending",
10663
+ "provider_unavailable",
10664
+ "policy_changed",
10665
+ "cleanup_pending",
10666
+ "signer_unavailable"
10667
+ ])
10668
+ });
10669
+ const InvitationViewSchema = Schema.Struct({
10670
+ id: InvitationIdSchema,
10671
+ orgId: Schema.NonEmptyString.pipe(Schema.refine((value) => value.length > 0)),
10672
+ organizationName: Schema.String,
10673
+ grantee: Schema.Struct({
10674
+ partyId: PartyIdSchema,
10675
+ accountId: Schema.NullOr(AccountIdSchema),
10676
+ safeAddress: AddressSchema,
10677
+ accountReady: Schema.NullOr(Schema.Boolean)
10678
+ }),
10679
+ phase: InvitationPhaseSchema,
10680
+ grants: Schema.Array(Schema.Union([Schema.Struct({
10681
+ type: Schema.Literal("budget"),
10682
+ budgetId: Schema.NonEmptyString.pipe(Schema.refine((value) => value.length > 0))
10683
+ }), Schema.Struct({
10684
+ type: Schema.Literal("managePeople"),
10685
+ permissionId: PermissionIdSchema
10686
+ })])),
10687
+ offers: Schema.Array(OfferedAuthoritySchema),
10688
+ recovery: Schema.NullOr(InvitationRecoverySchema),
10689
+ offerDigest: Bytes32Schema,
10690
+ authorizedAt: Schema.NullOr(EpochMsSchema),
10691
+ acceptedAt: Schema.NullOr(EpochMsSchema),
10692
+ expiresAt: Schema.NullOr(EpochMsSchema),
10693
+ commandId: Schema.NullOr(PaymentCommandIdSchema),
10694
+ endingReason: Schema.NullOr(EndingReasonSchema),
10695
+ delivery: Schema.Literals([
10696
+ "not_confirmed",
10697
+ "sent",
10698
+ "failed"
10699
+ ])
10700
+ });
10701
+ Schema.Struct({
10702
+ items: Schema.Array(InvitationViewSchema),
10703
+ nextCursor: Schema.NullOr(Schema.NonEmptyString),
10704
+ observedAt: EpochMsSchema
10705
+ });
10282
10706
  const ActiveBudgetSchema = Schema.Struct({
10283
10707
  id: PermissionIdSchema,
10284
10708
  label: Schema.String,
@@ -10577,6 +11001,18 @@ function withInvocationParentSpan(effect, controls) {
10577
11001
  const parent = controls?.[INVOCATION_PARENT_SPAN];
10578
11002
  return parent === void 0 ? effect : effect.pipe(Effect.withParentSpan(parent));
10579
11003
  }
11004
+ /**
11005
+ * The input field a refused request named.
11006
+ *
11007
+ * The surfaces that call the port directly already carry `details.field` for an
11008
+ * `INVALID_INPUT` refusal. The actor keeps that one allowlisted detail instead of
11009
+ * dropping it, so a caller sees the same refusal either way.
11010
+ */
11011
+ const refusedInputField = (failure) => {
11012
+ if (failure.code !== "INVALID_INPUT") return void 0;
11013
+ const field = failure.error?.details?.field;
11014
+ return typeof field === "string" && field.length > 0 ? { field } : void 0;
11015
+ };
10580
11016
  var ActorFailure = class extends Error {
10581
11017
  reason;
10582
11018
  machine;
@@ -10742,7 +11178,7 @@ const boot = (spec, options = {}) => Effect.gen(function* () {
10742
11178
  const event = { _tag: env.origin.event };
10743
11179
  const failedState = exit.failure === timeoutFailure || exit.failure === cancelledFailure ? yield* recoverFailure(state, env.origin, exit.failure) : state;
10744
11180
  yield* emit(nonApplied(failedState, event, exit.failure.code, outcome, env, exit.failure));
10745
- const details = exit.failure === timeoutFailure ? { reason: "timeout" } : void 0;
11181
+ const details = exit.failure === timeoutFailure ? { reason: "timeout" } : refusedInputField(exit.failure);
10746
11182
  yield* failReply(held.reply, makeFailure(failedState, event, exit.failure.code, details, exit.failure.error ?? exit.failure));
10747
11183
  return;
10748
11184
  }
@@ -12214,11 +12650,13 @@ const financialOpsContract = {
12214
12650
  //#region ../../packages/sdk/src/contract/money-execution.ts
12215
12651
  const moneyExecutionContract = {
12216
12652
  abandonPaymentExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/actions"].abandonPaymentExecution),
12653
+ prepareInvitationConfiguration: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/actions"].prepareInvitationConfiguration),
12217
12654
  preparePermissionExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/actions"].preparePermissionExecution),
12218
12655
  preparePaymentExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/actions"].preparePaymentExecution),
12219
12656
  prepareOrganizationPaymentExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/paymentCommandActions"].prepareOrganizationPaymentExecution),
12220
12657
  preparePaymentLifecycleExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/paymentCommandActions"].preparePaymentLifecycleExecution),
12221
12658
  submitPaymentExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/actions"].submitPaymentExecution),
12659
+ submitInvitationConfigurationExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/actions"].submitInvitationConfigurationExecution),
12222
12660
  submitPermissionExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/actions"].submitPermissionExecution),
12223
12661
  submitPaymentCommandExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/paymentCommandActions"].submitPaymentCommandExecution)
12224
12662
  };
@@ -12799,7 +13237,7 @@ function subscribeActivity(deps, actor, controls, onChange) {
12799
13237
  }
12800
13238
  //#endregion
12801
13239
  //#region ../../packages/sdk/package.json
12802
- var version$1 = "4.20.0-beta.6";
13240
+ var version$1 = "4.20.0-beta.8";
12803
13241
  //#endregion
12804
13242
  //#region ../../packages/sdk/src/telemetry/exception-projection.ts
12805
13243
  /** Fixed fallback for failures that have no safe message. */
@@ -13635,7 +14073,7 @@ function isMoneyTelemetryDebugEnabled() {
13635
14073
  }
13636
14074
  //#endregion
13637
14075
  //#region ../../packages/sdk/src/surface/payment-command-execution.ts
13638
- function isAborted$2(signal) {
14076
+ function isAborted$3(signal) {
13639
14077
  return signal?.aborted === true;
13640
14078
  }
13641
14079
  function canonicalJson(value) {
@@ -13661,7 +14099,7 @@ async function abandonPrepared$1(deps, executionId, closePayment, reason, result
13661
14099
  return result;
13662
14100
  }
13663
14101
  async function executePrepared(deps, prepare, isExpectedRequest, closePaymentOnExit, onPreparedClosed, signal, controls) {
13664
- if (isAborted$2(signal)) return {
14102
+ if (isAborted$3(signal)) return {
13665
14103
  ok: false,
13666
14104
  error: Errors.cancelled({ operation: "payments" })
13667
14105
  };
@@ -13690,7 +14128,7 @@ async function executePrepared(deps, prepare, isExpectedRequest, closePaymentOnE
13690
14128
  ok: false,
13691
14129
  error: Errors.invalidInput("payment", "prepared authority mismatch")
13692
14130
  }, onPreparedClosed, controls);
13693
- if (isAborted$2(signal)) return abandonPrepared$1(deps, prepared.value.executionId, closePaymentOnExit, "cancelled", {
14131
+ if (isAborted$3(signal)) return abandonPrepared$1(deps, prepared.value.executionId, closePaymentOnExit, "cancelled", {
13694
14132
  ok: false,
13695
14133
  error: Errors.cancelled({ operation: "payments" })
13696
14134
  }, onPreparedClosed, controls);
@@ -13716,7 +14154,7 @@ async function executePrepared(deps, prepare, isExpectedRequest, closePaymentOnE
13716
14154
  error: signerFailure(deps.signer.source, "signUserOpHash", cause)
13717
14155
  }, onPreparedClosed, controls);
13718
14156
  }
13719
- if (isAborted$2(signal)) return abandonPrepared$1(deps, prepared.value.executionId, closePaymentOnExit, "cancelled", {
14157
+ if (isAborted$3(signal)) return abandonPrepared$1(deps, prepared.value.executionId, closePaymentOnExit, "cancelled", {
13720
14158
  ok: false,
13721
14159
  error: Errors.cancelled({ operation: "payments" })
13722
14160
  }, onPreparedClosed, controls);
@@ -13751,7 +14189,7 @@ function cancelled(operation) {
13751
14189
  error: Errors.cancelled({ operation })
13752
14190
  };
13753
14191
  }
13754
- function isAborted$1(signal) {
14192
+ function isAborted$2(signal) {
13755
14193
  return signal?.aborted === true;
13756
14194
  }
13757
14195
  async function signerCall(source, operation, run) {
@@ -13777,7 +14215,7 @@ async function abandonPrepared(deps, executionId, reason, result, onPreparedClos
13777
14215
  return result;
13778
14216
  }
13779
14217
  async function executePersonalPayment(deps, payment, signal, requestKey, onPreparedClosed) {
13780
- if (isAborted$1(signal)) return cancelled(CAPXUL_OPERATIONS.payments.pay);
14218
+ if (isAborted$2(signal)) return cancelled(CAPXUL_OPERATIONS.payments.pay);
13781
14219
  if (deps.signer === void 0) return mismatch("signer", "payments.pay requires a configured CapxulSigner");
13782
14220
  const session = deps.actor.authSession();
13783
14221
  if (session === null) return {
@@ -13805,10 +14243,10 @@ async function executePersonalPayment(deps, payment, signal, requestKey, onPrepa
13805
14243
  executionId: prepared.value.executionId,
13806
14244
  recipient: prepared.value.recipientObservation
13807
14245
  });
13808
- if (isAborted$1(signal)) return abandonPrepared(deps, prepared.value.executionId, "cancelled", cancelled(CAPXUL_OPERATIONS.payments.pay), onPreparedClosed);
14246
+ if (isAborted$2(signal)) return abandonPrepared(deps, prepared.value.executionId, "cancelled", cancelled(CAPXUL_OPERATIONS.payments.pay), onPreparedClosed);
13809
14247
  const signature = await signerCall(deps.signer.source, "signUserOpHash", () => deps.signer.signUserOpHash(prepared.value.digest));
13810
14248
  if (!signature.ok) return abandonPrepared(deps, prepared.value.executionId, "signer_rejected", signature, onPreparedClosed);
13811
- if (isAborted$1(signal)) return abandonPrepared(deps, prepared.value.executionId, "cancelled", cancelled(CAPXUL_OPERATIONS.payments.pay), onPreparedClosed);
14249
+ if (isAborted$2(signal)) return abandonPrepared(deps, prepared.value.executionId, "cancelled", cancelled(CAPXUL_OPERATIONS.payments.pay), onPreparedClosed);
13812
14250
  return runIfActive(void 0, CAPXUL_OPERATIONS.payments.submitExecution, () => deps.convexCall.action(deps.functions.submitPaymentExecution, { input: {
13813
14251
  executionId: prepared.value.executionId,
13814
14252
  signature: signature.value
@@ -24661,7 +25099,7 @@ function toFormData$1(obj, formData, options) {
24661
25099
  *
24662
25100
  * @returns {string} The encoded string.
24663
25101
  */
24664
- function encode$1(str) {
25102
+ function encode$2(str) {
24665
25103
  const charMap = {
24666
25104
  "!": "%21",
24667
25105
  "'": "%27",
@@ -24692,8 +25130,8 @@ prototype.append = function append(name, value) {
24692
25130
  };
24693
25131
  prototype.toString = function toString(encoder) {
24694
25132
  const _encode = encoder ? function(value) {
24695
- return encoder.call(this, value, encode$1);
24696
- } : encode$1;
25133
+ return encoder.call(this, value, encode$2);
25134
+ } : encode$2;
24697
25135
  return this._pairs.map(function each(pair) {
24698
25136
  return _encode(pair[0]) + "=" + _encode(pair[1]);
24699
25137
  }, "").join("&");
@@ -24708,7 +25146,7 @@ prototype.toString = function toString(encoder) {
24708
25146
  *
24709
25147
  * @returns {string} The encoded value.
24710
25148
  */
24711
- function encode(val) {
25149
+ function encode$1(val) {
24712
25150
  return encodeURIComponent(val).replace(/%3A/gi, ":").replace(/%24/g, "$").replace(/%2C/gi, ",").replace(/%20/g, "+");
24713
25151
  }
24714
25152
  /**
@@ -24722,7 +25160,7 @@ function encode(val) {
24722
25160
  */
24723
25161
  function buildURL(url, params, options) {
24724
25162
  if (!params) return url;
24725
- const _encode = options && options.encode || encode;
25163
+ const _encode = options && options.encode || encode$1;
24726
25164
  const _options = utils_default.isFunction(options) ? { serialize: options } : options;
24727
25165
  const serializeFn = _options && _options.serialize;
24728
25166
  let serializedParams;
@@ -40603,6 +41041,7 @@ function mapBetterAuthError(operation, body) {
40603
41041
  const code = typeof errBody.code === "string" ? errBody.code : "";
40604
41042
  if (code === "OTP_EXPIRED") return Errors.otpExpired();
40605
41043
  if (code === "INVALID_OTP") return Errors.invalidInput("otp", errBody.message ?? "invalid OTP");
41044
+ if (code === "TOO_MANY_ATTEMPTS") return Errors.rateLimited({ resource: "better-auth/verifyOtp" });
40606
41045
  if (code === "VALIDATION_ERROR" || code === "INVALID_EMAIL") return Errors.invalidInput("email", errBody.message ?? "invalid email");
40607
41046
  }
40608
41047
  return Errors.providerError("better-auth", operation, new Error(String(body)));
@@ -41220,6 +41659,10 @@ var BetterAuthNodeAdapter = class {
41220
41659
  ok: false,
41221
41660
  error: Errors.invalidInput("otp", errBody.message ?? "invalid OTP")
41222
41661
  };
41662
+ if (errBody.code === "TOO_MANY_ATTEMPTS") return {
41663
+ ok: false,
41664
+ error: Errors.rateLimited({ resource: "better-auth/verifyOtp" })
41665
+ };
41223
41666
  }
41224
41667
  }
41225
41668
  return {
@@ -44732,6 +45175,12 @@ const stderrConvexLogger = {
44732
45175
  error: writeConvexDiagnostic,
44733
45176
  logVerbose: () => void 0
44734
45177
  };
45178
+ const silentConvexLogger = {
45179
+ log: () => void 0,
45180
+ warn: () => void 0,
45181
+ error: () => void 0,
45182
+ logVerbose: () => void 0
45183
+ };
44735
45184
  /** Exact floor-first allowlist; every additional handler must migrate its validator first. */
44736
45185
  const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
44737
45186
  "system:healthObserved",
@@ -44803,7 +45252,9 @@ const OBSERVED_CONVEX_MUTATIONS = /* @__PURE__ */ new Set([
44803
45252
  "smartAccount/mutations:provision",
44804
45253
  "org/lifecycle:startOrResume",
44805
45254
  "org/lifecycle:recordFailure",
44806
- "org/lifecycle:retry"
45255
+ "org/lifecycle:retry",
45256
+ "org/mutations:invite",
45257
+ "org/mutations:accept"
44807
45258
  ]);
44808
45259
  var ConvexCallAdapter = class {
44809
45260
  #client;
@@ -44818,7 +45269,7 @@ var ConvexCallAdapter = class {
44818
45269
  const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
44819
45270
  this.#client = deps.client ?? new ConvexClient(deps.convexUrl, {
44820
45271
  ...webSocketConstructor === void 0 ? {} : { webSocketConstructor },
44821
- ...deps.logToStderr === true ? { logger: stderrConvexLogger } : {}
45272
+ logger: deps.localDiagnostics === true ? stderrConvexLogger : silentConvexLogger
44822
45273
  });
44823
45274
  this.#tokenProvider = deps.tokenProvider;
44824
45275
  this.#applicationId = deps.applicationId;
@@ -45476,10 +45927,7 @@ const DEFAULT_FUNCTIONS$1 = {
45476
45927
  listAll: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listAll),
45477
45928
  listRoles: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listRolesByOrgId),
45478
45929
  listMembers: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listMembersByOrgId),
45479
- readTreasury: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].readTreasury),
45480
- inviteMember: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].inviteMember),
45481
- resendInvite: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].resendInviteToken),
45482
- detectInvitations: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].detectAndAcceptPendingInvitations)
45930
+ readTreasury: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].readTreasury)
45483
45931
  };
45484
45932
  /**
45485
45933
  * Standard production Organization read adapter. It intentionally has no
@@ -45515,22 +45963,6 @@ var ConvexOrganizationAdapter = class {
45515
45963
  pendingMembers(input) {
45516
45964
  return this.listMembers(input).pipe(Effect.map((members) => members.filter((member) => member.status === "pending" || member.status === "pending_safe" || member.status === "pending_grant")));
45517
45965
  }
45518
- inviteMember(input) {
45519
- return this.#convex.action(this.#fns.inviteMember, copyInvocationObservation(input, {
45520
- orgId: input.orgId,
45521
- email: input.input.email,
45522
- role: input.input.role
45523
- })).pipe(Effect.mapError((error) => orgErrorFromCapxul("inviteMember", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchDefect((cause) => Effect.fail(toOrgError("inviteMember", cause))));
45524
- }
45525
- resendInviteToken(input) {
45526
- return this.#convex.mutation(this.#fns.resendInvite, {
45527
- orgId: input.orgId,
45528
- email: input.email
45529
- }).pipe(Effect.mapError((error) => orgErrorFromCapxul("resendInviteToken", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchDefect((cause) => Effect.fail(toOrgError("resendInviteToken", cause))));
45530
- }
45531
- detectAndAcceptPendingInvitations(_input) {
45532
- return this.#convex.action(this.#fns.detectInvitations, {}).pipe(Effect.mapError((error) => orgErrorFromCapxul("detectAndAcceptPendingInvitations", error.publicError, error)), Effect.map((result) => ({ matched: result.matched.map(toOrgId) })), Effect.catchDefect((cause) => Effect.fail(toOrgError("detectAndAcceptPendingInvitations", cause))));
45533
- }
45534
45966
  #readTreasuryWire(input) {
45535
45967
  const orgId = String(input.orgId);
45536
45968
  return retryIdempotentRead(this.#convex.action(this.#fns.readTreasury, copyInvocationObservation(input, { orgId })), CAPXUL_OPERATIONS.org.treasury, this.#telemetry, input).pipe(Effect.mapError((error) => orgErrorFromCapxul("readTreasury", error.publicError, error)), Effect.map((wire) => {
@@ -45575,11 +46007,12 @@ var ConvexOrganizationSetupAdapter = class {
45575
46007
  #fns;
45576
46008
  constructor(input) {
45577
46009
  this.#convex = input.convex;
45578
- this.#signer = input.signer;
46010
+ this.#signer = input.signer ?? null;
45579
46011
  this.#chainId = input.chainId;
45580
46012
  this.#fns = input.functions ?? DEFAULT_FUNCTIONS;
45581
46013
  }
45582
46014
  async startOrResume(input) {
46015
+ if (this.#signer === null) return fail$2(Errors.capabilityUnavailable("signer", "startOrResume"));
45583
46016
  const result = await runCall("startOrResume", this.#convex.mutation(this.#fns.startOrResume, copyInvocationObservation(input, {
45584
46017
  ...input,
45585
46018
  chainId: this.#chainId
@@ -45607,7 +46040,9 @@ var ConvexOrganizationSetupAdapter = class {
45607
46040
  async authorizeAndSubmitBootstrap(input) {
45608
46041
  const cancelled = cancellation(input.signal);
45609
46042
  if (cancelled !== void 0) return cancelled;
45610
- const signerAddress = await signerResult(this.#signer.source, "getAddress", () => this.#signer.getAddress());
46043
+ const signer = this.#signer;
46044
+ if (signer === null) return fail$2(Errors.capabilityUnavailable("signer", "authorizeAndSubmitBootstrap"));
46045
+ const signerAddress = await signerResult(signer.source, "getAddress", () => signer.getAddress());
45611
46046
  if (!signerAddress.ok) return signerAddress;
45612
46047
  const prepared = await runCall("prepareBootstrap", this.#convex.action(this.#fns.prepareBootstrap, copyInvocationObservation(input, {
45613
46048
  orgId: input.orgId,
@@ -45619,7 +46054,7 @@ var ConvexOrganizationSetupAdapter = class {
45619
46054
  if (!authority.ok) return authority;
45620
46055
  const cancelledAfterPrepare = cancellation(input.signal);
45621
46056
  if (cancelledAfterPrepare !== void 0) return cancelledAfterPrepare;
45622
- const signature = await signerResult(this.#signer.source, "signUserOpHash", () => this.#signer.signUserOpHash(prepared.value.digest));
46057
+ const signature = await signerResult(signer.source, "signUserOpHash", () => signer.signUserOpHash(prepared.value.digest));
45623
46058
  if (!signature.ok) return signature;
45624
46059
  const cancelledAfterSign = cancellation(input.signal);
45625
46060
  if (cancelledAfterSign !== void 0) return cancelledAfterSign;
@@ -45675,7 +46110,8 @@ var ConvexOrganizationSetupAdapter = class {
45675
46110
  ...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
45676
46111
  });
45677
46112
  if (!current.ok) return current;
45678
- if (current.value.status === "failed" && current.value.retryable && (current.value.at === "awaitingFounderAuthorization" || current.value.at === "submittingBootstrap")) {
46113
+ if (current.value.status === "failed" && current.value.retryable && current.value.at === "awaitingFounderAuthorization") {
46114
+ if (this.#signer === null) return fail$2(Errors.capabilityUnavailable("signer", "retry"));
45679
46115
  const reset = resetSignerSession(this.#signer);
45680
46116
  if (!reset.ok) return reset;
45681
46117
  }
@@ -46338,7 +46774,7 @@ const identitySpec = (input, sessionStore = { current: null }) => {
46338
46774
  initial: RESTORING,
46339
46775
  label,
46340
46776
  slot: identitySlot,
46341
- transition: (state, event) => transition(state, event, config),
46777
+ transition: (state, event) => transition$1(state, event, config),
46342
46778
  recoverFailure: recoverIdentityFailure,
46343
46779
  invalidates: (event) => event._tag === "Reset" ? [
46344
46780
  IDENTITY_SLOT.auth,
@@ -47723,6 +48159,271 @@ function makeOrgMeMethod(deps, orgId) {
47723
48159
  };
47724
48160
  }
47725
48161
  //#endregion
48162
+ //#region ../../packages/sdk/src/contract/invitations.ts
48163
+ const invitationContract = {
48164
+ preview: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].previewInvite),
48165
+ listMine: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listMyInvitations),
48166
+ listScoped: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listInvitations),
48167
+ get: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].getInvitation),
48168
+ invite: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].invite),
48169
+ accept: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].accept),
48170
+ decline: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].decline),
48171
+ cancel: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].cancel),
48172
+ resend: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].resend),
48173
+ retry: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].retry)
48174
+ };
48175
+ //#endregion
48176
+ //#region ../../packages/sdk/src/surface/invitations.ts
48177
+ /**
48178
+ * The one refusal a scoped invitation result that names another Organization
48179
+ * receives, whether it was read or returned by the authoring write.
48180
+ */
48181
+ function scopeRefusal(orgId) {
48182
+ return Errors.invalidInput("orgId", `The invitation result does not belong to Organization ${JSON.stringify(orgId)}.`);
48183
+ }
48184
+ /**
48185
+ * A scoped invitation result belongs to the Organization it was read under.
48186
+ *
48187
+ * One owner holds the refusal copy and the verified-scope span: a single view
48188
+ * reports whether it names the scope, and a page — which names no Organization
48189
+ * of its own — reports whether every row does. A result that does not belong is
48190
+ * refused here rather than handed to a consumer, so a future route change
48191
+ * cannot turn into a silent cross-Organization read on a caller that trusted
48192
+ * the scope it passed. A successful scoped read also marks the verifying
48193
+ * Organization on the owning span, exactly as `org.me` does — the caller's
48194
+ * scope hint is not authority, the completed scoped read is.
48195
+ */
48196
+ function verifyScope(orgId, value, belongsToScope) {
48197
+ if (!belongsToScope(value)) return Effect.fail(scopeRefusal(orgId));
48198
+ return Effect.annotateCurrentSpan({
48199
+ verified_organization_id: orgId,
48200
+ scope_kind: "organization"
48201
+ }).pipe(Effect.as(value));
48202
+ }
48203
+ /** One view names its own Organization. */
48204
+ const namesScope = (orgId) => (value) => String(value.orgId) === orgId;
48205
+ /** A page names no Organization of its own, so every row must name the scope. */
48206
+ const rowsNameScope = (orgId) => (page) => page.items.every((item) => String(item.orgId) === orgId);
48207
+ const ops = CAPXUL_OPERATIONS.org.invitations;
48208
+ /** Truth for a caller-owned signal, without narrowing its type. */
48209
+ function isAborted$1(signal) {
48210
+ return signal?.aborted === true;
48211
+ }
48212
+ /**
48213
+ * One observed operation around one Convex hop, exactly like `org.me`. The
48214
+ * call-start invocation snapshot rides the arguments, so the backend continues
48215
+ * the attempt this call started rather than resolving a new one.
48216
+ */
48217
+ function observed$1(deps, operation, options, args, call, check) {
48218
+ const controls = deps.invocationControls?.(options) ?? options;
48219
+ return runCapxulEffect(call(copyInvocationObservation(controls, args)).pipe(Effect.mapError((failure) => failure.publicError), Effect.flatMap((value) => check === void 0 ? Effect.succeed(value) : check(value))), controls, operation, deps.runPromise);
48220
+ }
48221
+ /**
48222
+ * The Admin's preview stays a read: it reaches the preview query and nothing
48223
+ * else. It reads current policies and live assignments, so the operator sees
48224
+ * what the offered authority is now, not a frozen copy.
48225
+ */
48226
+ function makeOrgInvitationMethods(deps, orgId) {
48227
+ const scopeCheck = (value) => verifyScope(orgId, value, namesScope(orgId));
48228
+ return {
48229
+ preview: (input, options) => observed$1(deps, ops.preview, options, {
48230
+ orgId,
48231
+ request: input
48232
+ }, (args) => deps.convexCall.query(invitationContract.preview, args), scopeCheck),
48233
+ invite: makeInviteMethod(deps, orgId),
48234
+ accept: (input, options) => observed$1(deps, ops.accept, options, {
48235
+ orgId,
48236
+ invitationId: input.invitationId,
48237
+ expectedOfferDigest: input.expectedOfferDigest
48238
+ }, (args) => deps.convexCall.mutation(invitationContract.accept, args), scopeCheck),
48239
+ list: (input, options) => observed$1(deps, ops.listScoped, options, {
48240
+ orgId,
48241
+ ...pageArgs(input)
48242
+ }, (args) => deps.convexCall.query(invitationContract.listScoped, args), (page) => verifyScope(orgId, page, rowsNameScope(orgId))),
48243
+ get: (input, options) => observed$1(deps, ops.get, options, {
48244
+ orgId,
48245
+ invitationId: input.invitationId
48246
+ }, (args) => deps.convexCall.query(invitationContract.get, args), scopeCheck),
48247
+ decline: (input, options) => observed$1(deps, ops.decline, options, {
48248
+ orgId,
48249
+ invitationId: input.invitationId
48250
+ }, (args) => deps.convexCall.mutation(invitationContract.decline, args), scopeCheck),
48251
+ cancel: (input, options) => observed$1(deps, ops.cancel, options, {
48252
+ orgId,
48253
+ invitationId: input.invitationId
48254
+ }, (args) => deps.convexCall.mutation(invitationContract.cancel, args), scopeCheck),
48255
+ resend: (input, options) => observed$1(deps, ops.resend, options, {
48256
+ orgId,
48257
+ invitationId: input.invitationId
48258
+ }, (args) => deps.convexCall.mutation(invitationContract.resend, args), scopeCheck),
48259
+ retry: (input, options) => observed$1(deps, ops.retry, options, {
48260
+ orgId,
48261
+ invitationId: input.invitationId
48262
+ }, (args) => deps.convexCall.mutation(invitationContract.retry, args), scopeCheck)
48263
+ };
48264
+ }
48265
+ /**
48266
+ * The authoring write, including its configuration hop.
48267
+ *
48268
+ * The mutation persists the invitation and its configure command before any
48269
+ * signature exists. The write then drives the existing human configuration
48270
+ * owner exactly as the Permission and payment surfaces drive theirs: prepare
48271
+ * the stored command for this Account's Safe, check the prepared authority
48272
+ * against the request, deliver the durable identity to the caller, sign the
48273
+ * returned digest, and submit it. The backend keeps the only authorization
48274
+ * rule (`prepareInvitationConfigurationIntentInTx` /
48275
+ * `submitInvitationConfigurationExecution`); this edge adds no second one.
48276
+ *
48277
+ * Cancellation owns the whole compound: an abort before the digest is signed
48278
+ * stops the write and forwards no signature, and an already-signed submission
48279
+ * completes.
48280
+ */
48281
+ async function executeInvite(deps, orgId, input, signal, controls, verifiedOrganization, onPrepared) {
48282
+ if (isAborted$1(signal)) return {
48283
+ ok: false,
48284
+ error: Errors.cancelled({ operation: ops.authorize })
48285
+ };
48286
+ if (deps.actor === void 0 || deps.chainId === void 0 || deps.signer === void 0) return {
48287
+ ok: false,
48288
+ error: Errors.notImplemented("invitations", "executionComposition")
48289
+ };
48290
+ const session = deps.actor.authSession();
48291
+ if (session === null) return {
48292
+ ok: false,
48293
+ error: Errors.notAuthenticated()
48294
+ };
48295
+ const authored = await runIfActive(signal, ops.authorize, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.mutation(invitationContract.invite, copyInvocationObservation(controls, {
48296
+ orgId,
48297
+ request: input
48298
+ }))), controls), deps.runPromise);
48299
+ if (!authored.ok) return authored;
48300
+ const view = authored.value;
48301
+ if (String(view.orgId) !== orgId) return {
48302
+ ok: false,
48303
+ error: scopeRefusal(orgId)
48304
+ };
48305
+ if (view.phase !== "authorizing") return {
48306
+ ok: true,
48307
+ value: view
48308
+ };
48309
+ if (view.commandId === null) return {
48310
+ ok: false,
48311
+ error: Errors.invalidInput("invitationId", "The invitation does not name its configuration command.")
48312
+ };
48313
+ const commandId = view.commandId;
48314
+ let signerAddress;
48315
+ try {
48316
+ signerAddress = await deps.signer.getAddress();
48317
+ } catch (cause) {
48318
+ return {
48319
+ ok: false,
48320
+ error: signerFailure(deps.signer.source, "getAddress", cause)
48321
+ };
48322
+ }
48323
+ const prepared = await runIfActive(signal, ops.prepareConfiguration, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(moneyExecutionContract.prepareInvitationConfiguration, copyInvocationObservation(controls, { input: {
48324
+ commandId,
48325
+ signerAddress
48326
+ } }))), controls), deps.runPromise);
48327
+ if (!prepared.ok) return prepared;
48328
+ const expectedSafe = deriveCapxulSafeAddress({ email: session.email });
48329
+ if (!preparedAuthorityMatches({
48330
+ prepared: prepared.value,
48331
+ chainId: deps.chainId,
48332
+ signerAddress,
48333
+ expectedSafe,
48334
+ commandId,
48335
+ invitationId: view.id,
48336
+ orgId
48337
+ })) return {
48338
+ ok: false,
48339
+ error: Errors.invalidInput("invitation", "prepared authority mismatch")
48340
+ };
48341
+ verifiedOrganization(prepared.value.orgId);
48342
+ if (isAborted$1(signal)) return {
48343
+ ok: false,
48344
+ error: Errors.cancelled({ operation: ops.authorize })
48345
+ };
48346
+ if (onPrepared !== void 0) try {
48347
+ await onPrepared({
48348
+ invitationId: prepared.value.invitationId,
48349
+ commandId: prepared.value.commandId
48350
+ });
48351
+ } catch (cause) {
48352
+ return {
48353
+ ok: false,
48354
+ error: cause instanceof CapxulError ? cause : Errors.unknown(cause)
48355
+ };
48356
+ }
48357
+ if (isAborted$1(signal)) return {
48358
+ ok: false,
48359
+ error: Errors.cancelled({ operation: ops.authorize })
48360
+ };
48361
+ let signature;
48362
+ try {
48363
+ signature = await deps.signer.signUserOpHash(prepared.value.digest);
48364
+ } catch (cause) {
48365
+ return {
48366
+ ok: false,
48367
+ error: signerFailure(deps.signer.source, "signUserOpHash", cause)
48368
+ };
48369
+ }
48370
+ if (isAborted$1(signal)) return {
48371
+ ok: false,
48372
+ error: Errors.cancelled({ operation: ops.authorize })
48373
+ };
48374
+ const submitted = await runIfActive(void 0, ops.submitConfiguration, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(moneyExecutionContract.submitInvitationConfigurationExecution, copyInvocationObservation(controls, { input: {
48375
+ executionId: prepared.value.executionId,
48376
+ signature
48377
+ } }))), controls), deps.runPromise);
48378
+ if (!submitted.ok) return submitted;
48379
+ return {
48380
+ ok: true,
48381
+ value: view
48382
+ };
48383
+ }
48384
+ /**
48385
+ * The prepared authority must be exactly the one this write asked for: the same
48386
+ * chain, the caller's signer, this Account's Safe, and the exact command and
48387
+ * invitation the authoring view named.
48388
+ */
48389
+ function preparedAuthorityMatches(input) {
48390
+ return input.prepared.chainId === input.chainId && input.prepared.signerAddress.toLowerCase() === input.signerAddress.toLowerCase() && input.prepared.userOpSenderSafe.toLowerCase() === input.expectedSafe.toLowerCase() && input.prepared.commandId === input.commandId && input.prepared.invitationId === input.invitationId && input.prepared.orgId === input.orgId;
48391
+ }
48392
+ function makeInviteMethod(deps, orgId) {
48393
+ return (input, options) => {
48394
+ const { onPrepared, ...supplied } = options ?? {};
48395
+ const controls = deps.invocationControls?.(supplied) ?? supplied;
48396
+ let verifiedOrganizationId;
48397
+ return runCapxulEffect(Effect.gen(function* () {
48398
+ if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation: ops.authorize }));
48399
+ const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
48400
+ const captured = carryInvocationParentSpan(controls, parent);
48401
+ const result = yield* Effect.tryPromise({
48402
+ try: () => executeInvite(deps, orgId, input, controls?.signal, captured, (id) => {
48403
+ verifiedOrganizationId = id;
48404
+ }, onPrepared),
48405
+ catch: (cause) => cause instanceof CapxulError ? cause : Errors.unknown(cause)
48406
+ });
48407
+ return yield* result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
48408
+ }).pipe(Effect.ensuring(Effect.suspend(() => verifiedOrganizationId === void 0 ? Effect.void : Effect.annotateCurrentSpan({
48409
+ verified_organization_id: verifiedOrganizationId,
48410
+ scope_kind: "organization"
48411
+ })).pipe(Effect.catchCause(() => Effect.void)))), observationOnlyControls(controls), ops.authorize, deps.runPromise);
48412
+ };
48413
+ }
48414
+ /** Drop the page fields the caller did not supply; a bounded page sends only its bounds. */
48415
+ function pageArgs(input) {
48416
+ if (input === void 0) return {};
48417
+ return {
48418
+ ...input.cursor === void 0 ? {} : { cursor: input.cursor },
48419
+ ...input.limit === void 0 ? {} : { limit: input.limit },
48420
+ ...input.phases === void 0 ? {} : { phases: input.phases }
48421
+ };
48422
+ }
48423
+ function makeOwnInvitationsMethod(deps) {
48424
+ return (input, options) => observed$1(deps, ops.list, options, pageArgs(input), (args) => deps.convexCall.query(invitationContract.listMine, args));
48425
+ }
48426
+ //#endregion
47726
48427
  //#region ../../packages/sdk/src/contract/permission.ts
47727
48428
  const permissionContract = { read: makeFunctionReference(CAPXUL_FUNCTIONS["permission/queries"].read) };
47728
48429
  //#endregion
@@ -48410,20 +49111,6 @@ function hermeticOrgView(input) {
48410
49111
  observedAt: 0
48411
49112
  };
48412
49113
  }
48413
- function hermeticMember(input) {
48414
- return {
48415
- orgId: input.orgId,
48416
- accountId: null,
48417
- email: toEmail(input.email),
48418
- name: null,
48419
- personalSafeAddress: input.personalSafeAddress ?? null,
48420
- role: input.role,
48421
- roleKey: toRoleKey(orgRoleKeyForLabel(input.role)),
48422
- status: input.status,
48423
- grantTxHash: input.grantTxHash ?? null,
48424
- revokeTxHash: input.revokeTxHash ?? null
48425
- };
48426
- }
48427
49114
  function createOrgProgram(input) {
48428
49115
  return Effect.gen(function* () {
48429
49116
  const deps = yield* OrgDepsTag;
@@ -48524,35 +49211,6 @@ function listMembersProgram(orgId) {
48524
49211
  return [];
48525
49212
  });
48526
49213
  }
48527
- /**
48528
- * R06 (#1964): the SDK no longer emits `org_invite_sent`. The backend action
48529
- * that writes the invitation is its ONE producer, so a hermetic call — which
48530
- * writes nothing — can no longer report an invitation that never happened, and
48531
- * a live call is counted once instead of twice. The captured attempt rides the
48532
- * port input, so the durable event carries the correlation this call ran under.
48533
- */
48534
- function inviteMemberProgram(orgId, input) {
48535
- return Effect.gen(function* () {
48536
- const deps = yield* OrgDepsTag;
48537
- if (deps.orgPort === void 0) return hermeticMember({
48538
- orgId,
48539
- email: input.email,
48540
- role: input.role,
48541
- status: "pending"
48542
- });
48543
- return yield* deps.orgPort.inviteMember(copyInvocationObservation(deps.invocationSource, {
48544
- orgId,
48545
- input
48546
- })).pipe(Effect.mapError((error) => error.publicError));
48547
- });
48548
- }
48549
- function detectAndAcceptPendingInvitationsProgram() {
48550
- return Effect.gen(function* () {
48551
- const deps = yield* OrgDepsTag;
48552
- if (deps.orgPort !== void 0) return yield* deps.orgPort.detectAndAcceptPendingInvitations({}).pipe(Effect.mapError((error) => error.publicError));
48553
- return { matched: [] };
48554
- });
48555
- }
48556
49214
  function listOrgsProgram() {
48557
49215
  return Effect.gen(function* () {
48558
49216
  const deps = yield* OrgDepsTag;
@@ -48658,6 +49316,20 @@ function observedRunnerDeps(deps) {
48658
49316
  ...deps.invocationControls === void 0 ? {} : { invocationControls: deps.invocationControls }
48659
49317
  };
48660
49318
  }
49319
+ /**
49320
+ * The invitation edge needs the call seam, the observed-runner pair, and — for
49321
+ * the authoring write's configuration hop — the same execution composition the
49322
+ * Permission, payment, and payroll bundles receive.
49323
+ */
49324
+ function invitationMethodsDeps(deps, convexCall) {
49325
+ return {
49326
+ convexCall,
49327
+ ...observedRunnerDeps(deps),
49328
+ ...deps.actor === void 0 ? {} : { actor: deps.actor },
49329
+ ...deps.chainId === void 0 ? {} : { chainId: Number(deps.chainId) },
49330
+ ...deps.signer === void 0 ? {} : { signer: deps.signer }
49331
+ };
49332
+ }
48661
49333
  function makeOrgMethods(deps) {
48662
49334
  const orgDeps = {
48663
49335
  ...deps.orgPort === void 0 ? {} : { orgPort: deps.orgPort },
@@ -48689,13 +49361,10 @@ function makeOrgMethods(deps) {
48689
49361
  });
48690
49362
  return toCapxulResult(createOrgProgram(input), layer);
48691
49363
  },
48692
- orgs: Object.assign((options) => listOrganizations(deps.invocationControls?.(options) ?? options), { detectAndAcceptPendingInvitations(options) {
48693
- if (options?.signal?.aborted) return Promise.resolve({
48694
- ok: false,
48695
- error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.organizations.detectAndAcceptPendingInvitations })
48696
- });
48697
- return toCapxulResult(detectAndAcceptPendingInvitationsProgram(), layer);
48698
- } }),
49364
+ orgs: Object.assign((options) => listOrganizations(deps.invocationControls?.(options) ?? options), { invitations: deps.convexCall === void 0 ? () => Promise.resolve({
49365
+ ok: false,
49366
+ error: Errors.notImplemented("invitations", "listOwn")
49367
+ }) : makeOwnInvitationsMethod(invitationMethodsDeps(deps, deps.convexCall)) }),
48699
49368
  org(orgId) {
48700
49369
  const convexCall = deps.convexCall;
48701
49370
  return {
@@ -48803,17 +49472,7 @@ function makeOrgMethods(deps) {
48803
49472
  });
48804
49473
  return toCapxulResult(listRolesProgram(orgId), layer);
48805
49474
  },
48806
- invite(_input, _options) {
48807
- const controls = deps.invocationControls?.(_options) ?? _options;
48808
- if (controls?.signal?.aborted) return Promise.resolve({
48809
- ok: false,
48810
- error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.org.invite })
48811
- });
48812
- return runCapxulEffect(inviteMemberProgram(orgId, _input).pipe(Effect.provide(orgDepsLayer({
48813
- ...orgDeps,
48814
- ...controls === void 0 ? {} : { invocationSource: controls }
48815
- }))), controls, CAPXUL_OPERATIONS.org.invite, deps.runPromise);
48816
- },
49475
+ invitations: convexCall === void 0 ? makeNotImplementedInvitationMethods() : makeOrgInvitationMethods(invitationMethodsDeps(deps, convexCall), String(orgId)),
48817
49476
  auditLog(_options) {
48818
49477
  if (_options?.signal?.aborted) return Promise.resolve({
48819
49478
  ok: false,
@@ -48876,6 +49535,28 @@ const orgMeUnavailable = () => Promise.resolve({
48876
49535
  ok: false,
48877
49536
  error: Errors.notImplemented("org", "me")
48878
49537
  });
49538
+ /**
49539
+ * The closed invitation surface for a client with no backend seam. It reports
49540
+ * `NOT_IMPLEMENTED` for every method rather than an empty list or a fabricated
49541
+ * view: an absent composition root is not an absent invitation.
49542
+ */
49543
+ function makeNotImplementedInvitationMethods() {
49544
+ return {
49545
+ preview: invitationExecutionUnavailable,
49546
+ invite: invitationExecutionUnavailable,
49547
+ accept: invitationExecutionUnavailable,
49548
+ list: invitationExecutionUnavailable,
49549
+ get: invitationExecutionUnavailable,
49550
+ decline: invitationExecutionUnavailable,
49551
+ cancel: invitationExecutionUnavailable,
49552
+ resend: invitationExecutionUnavailable,
49553
+ retry: invitationExecutionUnavailable
49554
+ };
49555
+ }
49556
+ const invitationExecutionUnavailable = () => Promise.resolve({
49557
+ ok: false,
49558
+ error: Errors.notImplemented("invitations", "executionComposition")
49559
+ });
48879
49560
  const permissionExecutionUnavailable = () => Promise.resolve({
48880
49561
  ok: false,
48881
49562
  error: Errors.notImplemented("permissions", "executionComposition")
@@ -49375,7 +50056,6 @@ function assembleCapxulClient(input) {
49375
50056
  };
49376
50057
  if (input.signal !== void 0) input.signal.addEventListener("abort", () => void stopActor(), { once: true });
49377
50058
  const selectedOrgPort = input.orgPort ?? input.orgDeploymentPort;
49378
- let detectPendingOrgInvitations;
49379
50059
  let kickProvisioning;
49380
50060
  const financialOps = makeFinancialOpsMethods({
49381
50061
  runPromise: effectRunner.runPromise,
@@ -49424,7 +50104,6 @@ function assembleCapxulClient(input) {
49424
50104
  if (browserSigner.statusStore?.status() === "unknown" && typeof browserSigner.ensureWalletReady === "function") browserSigner.ensureWalletReady().catch(() => {});
49425
50105
  }
49426
50106
  if (accountSetup === "explicit") return;
49427
- await detectPendingOrgInvitations?.();
49428
50107
  kickProvisioning?.();
49429
50108
  }
49430
50109
  });
@@ -49492,9 +50171,6 @@ function assembleCapxulClient(input) {
49492
50171
  ...input.signer === void 0 ? {} : { signer: input.signer },
49493
50172
  ...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup }
49494
50173
  });
49495
- if (selectedOrgPort !== void 0) detectPendingOrgInvitations = async () => {
49496
- await orgMethods.orgs.detectAndAcceptPendingInvitations();
49497
- };
49498
50174
  const currentUser = makeCurrentUserMethods({
49499
50175
  me: financialOps.me,
49500
50176
  accounts,
@@ -50387,7 +51063,7 @@ function productionAuthCacheLayer(input) {
50387
51063
  function productionConvexCallLayer(input, refreshConvexAuthRef) {
50388
51064
  return Layer.unwrap(Effect.map(AuthClientPortTag, (authClient) => ConvexCallLayer({
50389
51065
  convexUrl: input.runtimeUrls.convexUrl,
50390
- logToStderr: input.runtime === "node",
51066
+ localDiagnostics: input.localDiagnostics,
50391
51067
  ...input.applicationId === void 0 ? {} : { applicationId: input.applicationId },
50392
51068
  ...input.observation === void 0 ? {} : { observation: input.observation },
50393
51069
  ...input.convexClient === void 0 ? {} : { client: input.convexClient },
@@ -50426,6 +51102,7 @@ function resolveProductionObservability(input) {
50426
51102
  };
50427
51103
  }
50428
51104
  async function createProductionAdapters(input) {
51105
+ const localDiagnostics = productionLocalDiagnostics(input);
50429
51106
  const { observation, telemetry, invocationObservation, invocationTelemetry } = resolveProductionObservability(input);
50430
51107
  const resolvedInput = resolveInput(input);
50431
51108
  if (!resolvedInput.ok) return resolvedInput;
@@ -50496,6 +51173,7 @@ async function createProductionAdapters(input) {
50496
51173
  const portsLayer = mergeProductionAdapterLayers(makeProductionAdapterLayerEntries({
50497
51174
  bootstrap,
50498
51175
  runtime: resolvedInput.value.runtime,
51176
+ localDiagnostics,
50499
51177
  ...resolvedInput.value.origin === void 0 ? {} : { origin: resolvedInput.value.origin },
50500
51178
  runtimeUrls: runtimeUrls.value,
50501
51179
  chainId: bootstrapResult.value.chainId,
@@ -50510,7 +51188,7 @@ async function createProductionAdapters(input) {
50510
51188
  ...input.signal === void 0 ? {} : { signal: input.signal },
50511
51189
  ...input.resetSignerSession === void 0 ? {} : { resetSignerSession: input.resetSignerSession }
50512
51190
  }));
50513
- const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime, hostEngineeringPolicy(input.observability))).pipe(Layer.provideMerge(resolvedInput.value.runtime === "node" ? Layer.succeed(Logger.LogToStderr, true) : Layer.empty));
51191
+ const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime, hostEngineeringPolicy(input.observability))).pipe(Layer.provideMerge(localDiagnosticsLayer(localDiagnostics, resolvedInput.value.runtime)));
50514
51192
  const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
50515
51193
  const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
50516
51194
  const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
@@ -50574,6 +51252,13 @@ function optionalEngineeringTelemetryLayer(policy, runtime, hostPolicy) {
50574
51252
  return Layer.empty;
50575
51253
  }
50576
51254
  }
51255
+ function localDiagnosticsLayer(enabled, runtime) {
51256
+ if (!enabled) return Logger.layer([]);
51257
+ return runtime === "node" ? Layer.succeed(Logger.LogToStderr, true) : Layer.empty;
51258
+ }
51259
+ function productionLocalDiagnostics(input) {
51260
+ return input.localDiagnostics !== false;
51261
+ }
50577
51262
  function refreshConvexAuthOnSession(authClient, refresh) {
50578
51263
  return {
50579
51264
  ...authClient,
@@ -50638,7 +51323,7 @@ async function createCapxulClientWithSignerControls(input, controls) {
50638
51323
  runtime,
50639
51324
  authBaseUrl: resolvedAuthBaseUrl,
50640
51325
  createBrowserSigner: createOpenfortBrowserSignerFromBootstrap,
50641
- diagnostic: new EffectLogDiagnosticAdapter(adapters.value.context, new ConsoleDiagnosticAdapter()),
51326
+ diagnostic: new EffectLogDiagnosticAdapter(adapters.value.context, productionLocalDiagnostics(input) ? new ConsoleDiagnosticAdapter() : void 0),
50642
51327
  signer: input.signer,
50643
51328
  factory: input.openfortSignerFactory,
50644
51329
  finalSign: controls.signUserOpHash,
@@ -50667,11 +51352,11 @@ async function createCapxulClientWithSignerControls(input, controls) {
50667
51352
  accountSetup: input.accountSetup,
50668
51353
  ...signer === void 0 ? {} : { signer },
50669
51354
  orgPort: Context.get(adapters.value.context, OrgPortTag),
50670
- ...signer === void 0 ? {} : { organizationSetup: new ConvexOrganizationSetupAdapter({
51355
+ organizationSetup: new ConvexOrganizationSetupAdapter({
50671
51356
  convex: adapters.value.ports.convexCall,
50672
- signer,
51357
+ ...signer === void 0 ? {} : { signer },
50673
51358
  chainId: adapters.value.bootstrap.chainId
50674
- }) },
51359
+ }),
50675
51360
  ...input.signal === void 0 ? {} : { signal: input.signal },
50676
51361
  ...input.otpTtlMs === void 0 ? {} : { otpTtlMs: input.otpTtlMs },
50677
51362
  invokeTimeoutMs: input.invokeTimeoutMs ?? 3e4,
@@ -51010,8 +51695,8 @@ function captureException(telemetry, error, context) {
51010
51695
  }
51011
51696
  //#endregion
51012
51697
  //#region src/version.ts
51013
- const cliVersion = "4.20.0-beta.6";
51014
- const sdkVersion = "4.20.0-beta.6";
51698
+ const cliVersion = "4.20.0-beta.8";
51699
+ const sdkVersion = "4.20.0-beta.8";
51015
51700
  const ReleaseVersion = Schema.String.check(Schema.isMaxLength(256), Schema.isPattern(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?$/u)).pipe(Schema.brand("CliReleaseVersion"));
51016
51701
  const parts = (version) => {
51017
51702
  const [core = "", ...prerelease] = version.split("+")[0].split("-");
@@ -51540,7 +52225,7 @@ const finishUpdateCheck = Effect.fn(function* (directory, attempt, target) {
51540
52225
  });
51541
52226
  //#endregion
51542
52227
  //#region src/platform/collection-policy.ts
51543
- const decode$2 = Schema.decodeUnknownOption(Schema.fromJsonString(Settings), { onExcessProperty: "error" });
52228
+ const decode$3 = Schema.decodeUnknownOption(Schema.fromJsonString(Settings), { onExcessProperty: "error" });
51544
52229
  const missing = (error) => error instanceof Error && "code" in error && error.code === "ENOENT";
51545
52230
  const resolveProtectedDirectory = (directory, uid) => {
51546
52231
  const stateDirectory = lstatSync(directory, { throwIfNoEntry: false });
@@ -51584,7 +52269,7 @@ const collectionPolicy = (settingsPath, forcedDisabled) => () => {
51584
52269
  length += count;
51585
52270
  }
51586
52271
  if (length > 65536) return false;
51587
- const settings = decode$2(bytes.toString("utf8", 0, length));
52272
+ const settings = decode$3(bytes.toString("utf8", 0, length));
51588
52273
  return Option.isSome(settings) && settings.value.collection.enabled;
51589
52274
  } catch {
51590
52275
  return false;
@@ -51605,9 +52290,31 @@ var CliFailure = class extends Data.TaggedError("CliFailure") {};
51605
52290
  */
51606
52291
  const WRONG_STATE_MESSAGE = "The command cannot run from the current state.";
51607
52292
  const ORGANIZATION_WRONG_STATE_MESSAGE = "The Organization cannot run this command from its current state. Resume that exact Organization with capxul org retry --org instead of creating a replacement.";
52293
+ const PERSONAL_ACCOUNT_WRONG_STATE_MESSAGE = "The personal Account cannot run this command from its current state. Resume the same Account and Profile with capxul auth signup instead of creating a replacement.";
51608
52294
  const INPUT_MESSAGE = "The input was refused. Check the email and verification code, or request a new code.";
52295
+ const PROFILE_INPUT_MESSAGE = "The Profile input was refused. Correct the display name, country, or handle, then run capxul auth signup again.";
52296
+ const HANDLE_INPUT_MESSAGE = "That handle is not available. Choose another handle, then run capxul auth signup again.";
51609
52297
  const organizationCommand = (command) => command === "org" || command?.startsWith("org.") === true;
51610
52298
  /**
52299
+ * The authentication journey family. A Profile or personal Account refusal is
52300
+ * not an email or verification-code problem, so it never reuses OTP copy.
52301
+ */
52302
+ const authCommand = (command) => command === "auth" || command?.startsWith("auth.") === true;
52303
+ /** The Profile fields a personal signup states. */
52304
+ const PROFILE_FIELDS = /* @__PURE__ */ new Set([
52305
+ "profile",
52306
+ "displayName",
52307
+ "country",
52308
+ "countryCode",
52309
+ "handle"
52310
+ ]);
52311
+ const authInputMessage = (error) => {
52312
+ const field = typeof error.details?.field === "string" ? error.details.field : "";
52313
+ if (field === "handle") return HANDLE_INPUT_MESSAGE;
52314
+ if (PROFILE_FIELDS.has(field)) return PROFILE_INPUT_MESSAGE;
52315
+ return INPUT_MESSAGE;
52316
+ };
52317
+ /**
51611
52318
  * The one allowlist. Only a WRONG_STATE whose SDK details carry an Organization
51612
52319
  * ID the public constructor accepts emits that ID, and it never enters message
51613
52320
  * text or valid states. No other error detail reaches terminal output, and a
@@ -51657,7 +52364,7 @@ function classifyFailure(cause, command) {
51657
52364
  const details = allowlistedDetails(error);
51658
52365
  return {
51659
52366
  code: error.code,
51660
- message: auth ? "Authentication is required." : denied ? "This action is not permitted." : wrongState ? organizationCommand(command) ? ORGANIZATION_WRONG_STATE_MESSAGE : WRONG_STATE_MESSAGE : input ? INPUT_MESSAGE : "The Capxul request failed.",
52367
+ message: auth ? "Authentication is required." : denied ? "This action is not permitted." : wrongState ? organizationCommand(command) ? ORGANIZATION_WRONG_STATE_MESSAGE : authCommand(command) ? PERSONAL_ACCOUNT_WRONG_STATE_MESSAGE : WRONG_STATE_MESSAGE : input ? authCommand(command) ? authInputMessage(error) : INPUT_MESSAGE : "The Capxul request failed.",
51661
52368
  exitCode: auth ? 3 : denied ? 4 : input || wrongState ? 2 : 5,
51662
52369
  outcome: auth || denied || input || wrongState ? "refused" : "failed",
51663
52370
  ...details === void 0 ? {} : { details }
@@ -52238,9 +52945,17 @@ const boundedLine = (value) => typeof value === "number" && Number.isSafeInteger
52238
52945
  const writeStdout = (text) => Effect.flatMap(Stdio.Stdio, (stdio) => Stream.run(Stream.make(text), stdio.stdout()));
52239
52946
  const writeStderr = (text) => Effect.flatMap(Stdio.Stdio, (stdio) => Stream.run(Stream.make(text), stdio.stderr()));
52240
52947
  function renderResult(mode, envelope, lines = []) {
52241
- if (mode === "json") return writeStdout(`${JSON.stringify(envelope)}\n`);
52242
- return envelope.outcome === "succeeded" ? writeStdout(`${lines.join("\n")}\n`) : writeStderr(`${envelope.error.code}: ${envelope.error.message}\n`);
52948
+ const pretty = process.stdout.isTTY === true;
52949
+ if (mode === "json") return writeStdout(`${JSON.stringify(envelope, null, pretty ? 2 : void 0)}\n`);
52950
+ return envelope.outcome === "succeeded" ? writeStdout(`${(pretty ? lines.map(readableLine) : lines).join("\n")}\n`) : writeStderr(`${envelope.error.code}: ${envelope.error.message}\n`);
52243
52951
  }
52952
+ const readableLine = (line) => {
52953
+ try {
52954
+ return JSON.stringify(JSON.parse(line), null, 2);
52955
+ } catch {
52956
+ return line;
52957
+ }
52958
+ };
52244
52959
  //#endregion
52245
52960
  //#region src/runtime/command-metadata.ts
52246
52961
  var CliCommandMetadata = class extends Context.Service()("capxul-cli/CommandMetadata") {};
@@ -52344,9 +53059,14 @@ const runCommand = (options, work) => Effect.gen(function* () {
52344
53059
  //#endregion
52345
53060
  //#region src/platform/capxul.ts
52346
53061
  const resultValue = (result) => result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
53062
+ const localDiagnosticsEnabled = (args) => args.some((arg, index) => arg === "--log-level=debug" || arg === "--logLevel=debug" || (arg === "--log-level" || arg === "--logLevel") && args[index + 1] === "debug");
52347
53063
  const acquireClient = (input, onFinalize) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
53064
+ const runtime = yield* CliRuntime;
52348
53065
  const created = yield* restore(Effect.promise(async (signal) => {
52349
- const result = await createCapxulClient(input);
53066
+ const result = await createCapxulClient({
53067
+ ...input,
53068
+ localDiagnostics: localDiagnosticsEnabled(runtime.args ?? [])
53069
+ });
52350
53070
  if (signal.aborted && result.ok) await result.value.close();
52351
53071
  return result;
52352
53072
  }));
@@ -52380,7 +53100,7 @@ const checkDoctor = (config, options) => Effect.gen(function* () {
52380
53100
  };
52381
53101
  if (!options.online) return report;
52382
53102
  const key = config.publishableKey;
52383
- if (key === void 0 || Redacted.value(key).length === 0) return yield* Effect.fail(configurationFailure("Set CAPXUL_PUBLISHABLE_KEY for the Railway application before an online check."));
53103
+ if (key === void 0 || Redacted.value(key).length === 0) return yield* Effect.fail(configurationFailure("Set CAPXUL_PUBLISHABLE_KEY for the staging application before an online check."));
52384
53104
  const result = yield* Effect.gen(function* () {
52385
53105
  const started = yield* Clock.currentTimeMillis;
52386
53106
  const client = yield* acquireClient({
@@ -52505,11 +53225,11 @@ const CurrentSession = Schema.Struct({
52505
53225
  authUserId: Schema.NonEmptyString
52506
53226
  }))
52507
53227
  });
52508
- const decode$1 = (value) => Schema.decodeUnknownEffect(Schema.fromJsonString(CurrentSession), { onExcessProperty: "error" })(value).pipe(Effect.mapError(() => new StateFileError({ reason: "Corrupt" })));
53228
+ const decode$2 = (value) => Schema.decodeUnknownEffect(Schema.fromJsonString(CurrentSession), { onExcessProperty: "error" })(value).pipe(Effect.mapError(() => new StateFileError({ reason: "Corrupt" })));
52509
53229
  const readCurrentSession = (config) => Effect.gen(function* () {
52510
53230
  const { directory, scope } = yield* privateRecordScope(config, "current-session", "current");
52511
53231
  const value = yield* readPrivateRecord(directory, scope);
52512
- return value === null ? null : (yield* decode$1(value)).current;
53232
+ return value === null ? null : (yield* decode$2(value)).current;
52513
53233
  });
52514
53234
  const rememberCurrentSession = (config, session, stillAuthenticated) => Effect.gen(function* () {
52515
53235
  const { directory, scope } = yield* privateRecordScope(config, "current-session", "current");
@@ -52522,7 +53242,7 @@ const rememberCurrentSession = (config, session, stillAuthenticated) => Effect.g
52522
53242
  };
52523
53243
  for (let attempt = 0; attempt < 2; attempt += 1) {
52524
53244
  const expected = yield* readPrivateRecord(directory, scope);
52525
- if (expected !== null) yield* decode$1(expected);
53245
+ if (expected !== null) yield* decode$2(expected);
52526
53246
  if (yield* compareAndSetPrivateRecord(directory, scope, expected, JSON.stringify(next), stillAuthenticated)) return;
52527
53247
  if (!(yield* stillAuthenticated)) break;
52528
53248
  }
@@ -52532,7 +53252,7 @@ const forgetCurrentSession = (config, email, stillCleared) => Effect.gen(functio
52532
53252
  const { directory, scope } = yield* privateRecordScope(config, "current-session", "current");
52533
53253
  const expected = yield* readPrivateRecordSnapshot(directory, scope);
52534
53254
  if (expected === null) return false;
52535
- if ((yield* decode$1(expected)).current?.email !== email.toLowerCase()) return false;
53255
+ if ((yield* decode$2(expected)).current?.email !== email.toLowerCase()) return false;
52536
53256
  return yield* compareAndSetPrivateRecord(directory, scope, expected, JSON.stringify({
52537
53257
  version: 1,
52538
53258
  current: null
@@ -52549,48 +53269,118 @@ const inputRequired = (message) => new CliFailure({
52549
53269
  const readEmail = (email) => Effect.gen(function* () {
52550
53270
  const runtime = yield* CliRuntime;
52551
53271
  if (email === void 0) {
52552
- if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired("Provide --email. Use auth send, then auth verify --otp-stdin for non-interactive login."));
53272
+ if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired("Provide --email."));
52553
53273
  email = yield* Prompt.text({ message: "Email" });
52554
53274
  }
52555
53275
  const normalized = email.trim().toLowerCase();
52556
53276
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized)) return yield* Effect.fail(inputRequired("Provide a valid email."));
52557
53277
  return normalized;
52558
53278
  });
53279
+ const OTP_INPUT_ERROR = "OTP input must contain six digits only.";
53280
+ const OTP_BOUNDARY_MARKS = /^[\s\p{Cf}]+|[\s\p{Cf}]+$/gu;
53281
+ const normalizeOtpInput = (input) => {
53282
+ if (new TextEncoder().encode(input).byteLength > 64) return null;
53283
+ const normalized = input.replace(OTP_BOUNDARY_MARKS, "");
53284
+ return /^[0-9]{6}$/.test(normalized) ? normalized : null;
53285
+ };
52559
53286
  const readOtp = (stdin) => Effect.gen(function* () {
52560
53287
  const runtime = yield* CliRuntime;
52561
53288
  if (!stdin) {
52562
53289
  if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired("Provide the OTP on stdin with --otp-stdin; OTP arguments are not accepted."));
52563
- return Redacted.value(yield* Prompt.hidden({ message: "Email verification code" }));
53290
+ return Redacted.value(yield* Prompt.password({
53291
+ message: "Email OTP",
53292
+ validate: (input) => {
53293
+ const normalized = normalizeOtpInput(input);
53294
+ return normalized === null ? Effect.fail(OTP_INPUT_ERROR) : Effect.succeed(normalized);
53295
+ }
53296
+ }));
52564
53297
  }
52565
53298
  const stdio = yield* Stdio.Stdio;
52566
53299
  let length = 0;
52567
53300
  const chunks = yield* stdio.stdin.pipe(Stream.mapEffect((chunk) => {
52568
53301
  length += chunk.byteLength;
52569
- return length > 64 ? Effect.fail(inputRequired("OTP input must contain six digits only.")) : Effect.succeed(chunk);
53302
+ return length > 64 ? Effect.fail(inputRequired(OTP_INPUT_ERROR)) : Effect.succeed(chunk);
52570
53303
  }), Stream.decodeText(), Stream.runCollect);
52571
- const code = Array.from(chunks).join("").trim();
52572
- if (!/^\d{6}$/.test(code)) return yield* Effect.fail(inputRequired("OTP input must contain six digits only."));
53304
+ const code = normalizeOtpInput(Array.from(chunks).join(""));
53305
+ if (code === null) return yield* Effect.fail(inputRequired(OTP_INPUT_ERROR));
52573
53306
  return code;
52574
53307
  });
52575
- const readRequired = (label, flag, supplied) => Effect.gen(function* () {
52576
- const runtime = yield* CliRuntime;
52577
- let value = supplied;
52578
- if (value === void 0) {
52579
- if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired(`Provide --${flag}.`));
52580
- value = yield* Prompt.text({ message: label });
53308
+ const PROFILE_FLAGS = {
53309
+ displayName: "--display-name",
53310
+ country: "--country",
53311
+ handle: "--handle"
53312
+ };
53313
+ const PROFILE_REFUSALS = {
53314
+ displayName: "Provide --display-name with at least one visible character.",
53315
+ country: "Provide --country as a two-letter ISO country code, for example GH.",
53316
+ handle: "Provide --handle as 3 to 30 characters of a-z, 0-9, underscore, or hyphen."
53317
+ };
53318
+ /**
53319
+ * Accept one Profile value, or return the refusal that names its field.
53320
+ *
53321
+ * Country and handle use the public SDK constructors, so the CLI accepts
53322
+ * exactly the values the backend stores. A handle loses its leading `@` and
53323
+ * its case before the check, which is the canon spelling the owner persists.
53324
+ */
53325
+ const acceptPersonField = (field, raw) => {
53326
+ const trimmed = raw.trim();
53327
+ if (trimmed.length === 0) return { refusal: inputRequired(PROFILE_REFUSALS[field]) };
53328
+ try {
53329
+ if (field === "country") return { value: toCountryCode(trimmed) };
53330
+ if (field === "handle") return { value: toHandle(trimmed.replace(/^@/u, "").toLowerCase()) };
53331
+ return { value: trimmed };
53332
+ } catch {
53333
+ return { refusal: inputRequired(PROFILE_REFUSALS[field]) };
53334
+ }
53335
+ };
53336
+ /**
53337
+ * Validate the Profile values a caller supplied, before any client work.
53338
+ *
53339
+ * `auth signup` runs this first, so a malformed value refuses locally and
53340
+ * consumes no one-time code. An absent field stays with `readPersonInput`,
53341
+ * which prompts on a terminal and refuses headlessly.
53342
+ */
53343
+ const checkPersonFlags = (flags) => Effect.gen(function* () {
53344
+ for (const field of [
53345
+ "displayName",
53346
+ "country",
53347
+ "handle"
53348
+ ]) {
53349
+ const supplied = flags[field];
53350
+ if (supplied === void 0) continue;
53351
+ const accepted = acceptPersonField(field, supplied);
53352
+ if ("refusal" in accepted) return yield* Effect.fail(accepted.refusal);
52581
53353
  }
52582
- const trimmed = value.trim();
52583
- if (trimmed.length === 0) return yield* Effect.fail(inputRequired(`Provide --${flag}.`));
52584
- return trimmed;
53354
+ });
53355
+ /**
53356
+ * One required Profile field.
53357
+ *
53358
+ * A non-interactive run accepts only the explicit flag, so headless signup is
53359
+ * deterministic and never prompts. An interactive run prompts, and offers the
53360
+ * existing Profile value as the default so a continuation keeps it.
53361
+ */
53362
+ const readRequired = (label, field, supplied, existing) => Effect.gen(function* () {
53363
+ const runtime = yield* CliRuntime;
53364
+ if (!runtime.tty || runtime.ci || runtime.mode === "json") {
53365
+ if (supplied === void 0) return yield* Effect.fail(inputRequired(`Provide ${PROFILE_FLAGS[field]}.`));
53366
+ const accepted = acceptPersonField(field, supplied);
53367
+ if ("refusal" in accepted) return yield* Effect.fail(accepted.refusal);
53368
+ return accepted.value;
53369
+ }
53370
+ const fallback = supplied ?? existing;
53371
+ const value = yield* Prompt.text({
53372
+ message: label,
53373
+ ...fallback === void 0 || fallback.trim().length === 0 ? {} : { default: fallback }
53374
+ });
53375
+ const accepted = acceptPersonField(field, value);
53376
+ if ("refusal" in accepted) return yield* Effect.fail(accepted.refusal);
53377
+ return accepted.value;
52585
53378
  });
52586
53379
  const readPersonInput = (flags, profile) => Effect.gen(function* () {
52587
- const displayName = yield* readRequired("Display name", "display-name", flags.displayName ?? profile?.displayName ?? void 0);
52588
- const country = yield* readRequired("Country code", "country", flags.country ?? profile?.country ?? void 0);
52589
- const handle = yield* readRequired("Handle", "handle", flags.handle ?? profile?.handle ?? void 0);
52590
53380
  return {
52591
- displayName,
52592
- country: country.toUpperCase(),
52593
- handle
53381
+ displayName: yield* readRequired("Display name", "displayName", flags.displayName, profile?.displayName ?? void 0),
53382
+ country: yield* readRequired("Country code", "country", flags.country, profile?.country ?? void 0),
53383
+ handle: yield* readRequired("Handle", "handle", flags.handle, profile?.handle ?? void 0)
52594
53384
  };
52595
53385
  });
52596
53386
  //#endregion
@@ -52767,13 +53557,19 @@ main{display:flex;min-height:100dvh;flex-direction:column;align-items:center;jus
52767
53557
  .logo{width:48px;height:48px;flex:none;animation:capxul-pulse 2s ease-in-out infinite}
52768
53558
  [data-state=ready] .logo,[data-state=failed] .logo{animation:none}
52769
53559
  h1{max-width:448px;margin:0;font-size:18px;font-weight:500;line-height:28px;text-wrap:balance}
53560
+ [data-state=working] h1{animation:capxul-breathe 2.4s ease-in-out infinite}
53561
+ @keyframes capxul-breathe{50%{opacity:.5}}
52770
53562
  .message{max-width:448px;margin:0;color:#525252;font-size:14px;line-height:20px;text-wrap:pretty}
53563
+ .action{margin:0;padding:10px 18px;border:1px solid currentColor;border-radius:9999px;background:transparent;color:inherit;font:inherit;cursor:pointer}
53564
+ .action:hover{opacity:.75}
53565
+ .action:focus-visible{outline:2px solid #F97316;outline-offset:2px}
53566
+ [hidden]{display:none!important}
52771
53567
  .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
52772
53568
  @media(prefers-color-scheme:dark){.message{color:#D4D4D4}}
52773
- @media(prefers-reduced-motion:reduce){.logo{animation:none}}
53569
+ @media(prefers-reduced-motion:reduce){.logo,h1{animation:none}}
52774
53570
  @keyframes capxul-pulse{50%{opacity:.45}}
52775
53571
  </style>
52776
- </head><body><main data-state="working" aria-busy="true"><svg class="logo" role="img" aria-label="Capxul" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 10C0 4.47715 4.47715 0 10 0H26C31.5228 0 36 4.47715 36 10V26C36 31.5228 31.5228 36 26 36H10C4.47715 36 0 31.5228 0 26V10Z" fill="#F97316"/><path d="M26.642 12.0117H25.2845C24.9618 12.012 24.6495 11.8993 24.4036 11.6936C24.1576 11.4879 23.994 11.2028 23.9421 10.8892C23.9303 10.8185 23.9244 10.747 23.9245 10.6754V9.3377C23.9245 8.98325 23.7816 8.6433 23.5273 8.39248C23.2729 8.14167 22.9279 8.00051 22.5679 8H14.5592C14.1991 8.00088 13.854 8.14175 13.5988 8.39197L8.39806 13.5136C8.27192 13.6376 8.17184 13.785 8.10354 13.9472C8.03524 14.1094 8.00006 14.2832 8 14.4588V22.6511C8.00065 23.0055 8.14405 23.3452 8.39873 23.5956C8.65341 23.8461 8.99858 23.9868 9.3585 23.9869H10.716C11.0762 23.987 11.4217 24.128 11.6763 24.3789C11.931 24.6297 12.074 24.9699 12.074 25.3246V26.6613C12.0742 27.016 12.2174 27.3562 12.4721 27.6069C12.7269 27.8577 13.0723 27.9986 13.4325 27.9986H21.4408C21.6192 27.9989 21.7959 27.9644 21.9608 27.8971C22.1256 27.8298 22.2753 27.7311 22.4012 27.6066L24.324 25.7142L27.6005 22.4879C27.7273 22.3639 27.8279 22.2164 27.8967 22.0539C27.9654 21.8914 28.0008 21.7172 28.001 21.5412V13.3494C28.001 13.1736 27.9658 12.9997 27.8975 12.8373C27.8292 12.675 27.7291 12.5275 27.6029 12.4033C27.4767 12.2791 27.3269 12.1805 27.162 12.1133C26.9971 12.0461 26.8204 12.0116 26.642 12.0117Z" fill="white"/><path d="M25.2012 18.4218V20.3672L22.2138 23.3089H15.9015V20.3672H22.2138V18.4218H25.2012ZM15.9015 11.0154L12.9141 13.9571V20.3672H15.9015V13.9571H22.2138V11.0154H15.9015ZM22.2138 13.9571V15.8707H25.2012V13.9571H22.2138Z" fill="#F97316"/></svg><h1 id="title">Connecting to Capxul CLI</h1><p class="message" id="status" role="status" aria-live="polite">Keep this tab open. Continue in your terminal when the wallet is ready.</p><p class="sr-only" id="lead">Keep this tab open. Continue in your terminal when the wallet is ready.</p><p class="sr-only">Account <span id="account">Personal account</span>. Environment <span id="environment">Loading</span>.</p></main><script src="/signer.js" defer><\/script></body></html>`;
53572
+ </head><body><main data-state="working" aria-busy="true"><svg class="logo" role="img" aria-label="Capxul" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 10C0 4.47715 4.47715 0 10 0H26C31.5228 0 36 4.47715 36 10V26C36 31.5228 31.5228 36 26 36H10C4.47715 36 0 31.5228 0 26V10Z" fill="#F97316"/><path d="M26.642 12.0117H25.2845C24.9618 12.012 24.6495 11.8993 24.4036 11.6936C24.1576 11.4879 23.994 11.2028 23.9421 10.8892C23.9303 10.8185 23.9244 10.747 23.9245 10.6754V9.3377C23.9245 8.98325 23.7816 8.6433 23.5273 8.39248C23.2729 8.14167 22.9279 8.00051 22.5679 8H14.5592C14.1991 8.00088 13.854 8.14175 13.5988 8.39197L8.39806 13.5136C8.27192 13.6376 8.17184 13.785 8.10354 13.9472C8.03524 14.1094 8.00006 14.2832 8 14.4588V22.6511C8.00065 23.0055 8.14405 23.3452 8.39873 23.5956C8.65341 23.8461 8.99858 23.9868 9.3585 23.9869H10.716C11.0762 23.987 11.4217 24.128 11.6763 24.3789C11.931 24.6297 12.074 24.9699 12.074 25.3246V26.6613C12.0742 27.016 12.2174 27.3562 12.4721 27.6069C12.7269 27.8577 13.0723 27.9986 13.4325 27.9986H21.4408C21.6192 27.9989 21.7959 27.9644 21.9608 27.8971C22.1256 27.8298 22.2753 27.7311 22.4012 27.6066L24.324 25.7142L27.6005 22.4879C27.7273 22.3639 27.8279 22.2164 27.8967 22.0539C27.9654 21.8914 28.0008 21.7172 28.001 21.5412V13.3494C28.001 13.1736 27.9658 12.9997 27.8975 12.8373C27.8292 12.675 27.7291 12.5275 27.6029 12.4033C27.4767 12.2791 27.3269 12.1805 27.162 12.1133C26.9971 12.0461 26.8204 12.0116 26.642 12.0117Z" fill="white"/><path d="M25.2012 18.4218V20.3672L22.2138 23.3089H15.9015V20.3672H22.2138V18.4218H25.2012ZM15.9015 11.0154L12.9141 13.9571V20.3672H15.9015V13.9571H22.2138V11.0154H15.9015ZM22.2138 13.9571V15.8707H25.2012V13.9571H22.2138Z" fill="#F97316"/></svg><h1 id="title" role="status" aria-live="polite">Connecting to Capxul CLI</h1><p class="message" id="status"></p><button class="action" id="dismiss" type="button" hidden>Close this page</button><p class="sr-only">Account <span id="account">Personal account</span>. Environment <span id="environment">Loading</span>.</p></main><script src="/signer.js" defer><\/script></body></html>`;
52777
53573
  const json = (response, status, body) => {
52778
53574
  response.writeHead(status, {
52779
53575
  "cache-control": "no-store",
@@ -53261,6 +54057,17 @@ const verifiedSession = (client, email) => Effect.gen(function* () {
53261
54057
  if (session === null || session.email.toLowerCase() !== email) return yield* Effect.fail(Errors.notAuthenticated());
53262
54058
  return session;
53263
54059
  });
54060
+ const submitOtp = (otpStdin, verify) => Effect.gen(function* () {
54061
+ const submissionLimit = otpStdin ? 1 : 3;
54062
+ for (let submission = 1; submission <= submissionLimit; submission += 1) {
54063
+ const code = yield* readOtp(otpStdin);
54064
+ const result = yield* Effect.result(verify(code));
54065
+ if (Result.isSuccess(result)) return;
54066
+ const error = result.failure;
54067
+ if (!(error.code === "INVALID_INPUT" && error.details?.field === "otp") || submission === submissionLimit) return yield* Effect.fail(error);
54068
+ yield* writeStderr("Invalid OTP. Try again.\n");
54069
+ }
54070
+ });
53264
54071
  /**
53265
54072
  * Local authentication checks only: the current-session pointer, the explicit
53266
54073
  * email, and a stored credential. No bootstrap, client, or network work. A
@@ -53338,6 +54145,60 @@ const flushWalletDiagnostics = (bridge, invocationId) => Effect.suspend(() => br
53338
54145
  invocation_id: invocationId,
53339
54146
  journey_id: invocationId
53340
54147
  }), { discard: true }));
54148
+ const SIGNUP_OTP_INPUT_REQUIRED = "Use --otp-stdin for non-interactive signup. OTP arguments are not accepted.";
54149
+ /**
54150
+ * One account-setup continuation: profile input, then wallet and Account work.
54151
+ *
54152
+ * `signup` calls this after authentication. An interactive `login` calls it
54153
+ * only after the person accepts the setup offer. A headless `login` never calls
54154
+ * it, so sign-in cannot become signup.
54155
+ *
54156
+ * The step returns a ready report only. When the personal Account is not ready,
54157
+ * it returns a typed recoverable report: `setupState` names the state, the
54158
+ * lifecycle carries the failure code, and the copy names the exact resume
54159
+ * action. The step never starts wallet work for a ready Account, so a retry
54160
+ * reuses the one Profile, wallet, Smart Account, and Account.
54161
+ */
54162
+ const completeAccountSetup = (config, store, client, session, suppliedProfile) => Effect.gen(function* () {
54163
+ const current = yield* call((signal) => client.identity.loadCurrent({ signal }));
54164
+ const before = yield* profileReport(client, session);
54165
+ if (before.data.setupState === "ready") return before;
54166
+ const profile = yield* readPersonInput(suppliedProfile, current);
54167
+ const runtime = yield* CliRuntime;
54168
+ const run = Effect.runPromiseWith(yield* Effect.context());
54169
+ let bridge;
54170
+ const invocation = yield* Ref.get(runtime.current);
54171
+ const openfortSignerFactory = (bootstrap) => {
54172
+ const created = createBrowserSignerBridge({
54173
+ bootstrap,
54174
+ environment: config.environment,
54175
+ handle: () => profile.handle,
54176
+ trustedAuthOrigin: new URL(config.bootstrapBaseUrl).origin,
54177
+ sessionToken: () => run(store.tokenIfCurrent())
54178
+ });
54179
+ bridge = created;
54180
+ invocation?.onFinalize?.(Effect.promise(() => created.close()).pipe(Effect.asVoid, Effect.catchCause(() => Effect.void)));
54181
+ return created.signer;
54182
+ };
54183
+ const walletClient = yield* clientFor(config, store, {
54184
+ restore: true,
54185
+ requirement: "deployed",
54186
+ openfortSignerFactory
54187
+ });
54188
+ const verified = yield* verifiedSession(walletClient, session.email);
54189
+ yield* writeStderr("Preparing your wallet in the browser. Keep that tab open.\n");
54190
+ const report = yield* Effect.gen(function* () {
54191
+ yield* call((signal) => walletClient.onboarding.completePerson(profile, { signal }));
54192
+ return yield* profileReport(walletClient, verified);
54193
+ }).pipe(Effect.ensuring(Effect.suspend(() => flushWalletDiagnostics(bridge, runtime.invocationId))));
54194
+ return {
54195
+ data: {
54196
+ ...report.data,
54197
+ signerStatus: walletClient.signer.status()
54198
+ },
54199
+ lines: report.lines
54200
+ };
54201
+ });
53341
54202
  const sendOtp = (config, suppliedEmail) => Effect.gen(function* () {
53342
54203
  const email = yield* readEmail(suppliedEmail);
53343
54204
  const store = yield* openSessionRecord(config, email);
@@ -53362,7 +54223,6 @@ const verifyOtp = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
53362
54223
  const store = yield* openSessionRecord(config, email);
53363
54224
  const flow = store.current().flow;
53364
54225
  if (flow === null || flow.email !== email) return yield* Effect.fail(inputRequired("No pending OTP. Run auth send --email <email> first."));
53365
- const code = yield* readOtp(otpStdin);
53366
54226
  const client = yield* clientFor(config, store, { restore: false });
53367
54227
  yield* call((signal) => client.auth.getSession({ signal }));
53368
54228
  yield* call((signal) => client.auth.resumeOtp({
@@ -53372,13 +54232,13 @@ const verifyOtp = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
53372
54232
  signal,
53373
54233
  journey_id: flow.journeyId
53374
54234
  }));
53375
- yield* call((signal) => client.auth.verifyOtp({
54235
+ yield* submitOtp(otpStdin, (code) => call((signal) => client.auth.verifyOtp({
53376
54236
  email,
53377
54237
  code
53378
54238
  }, {
53379
54239
  signal,
53380
54240
  journey_id: flow.journeyId
53381
- }));
54241
+ })));
53382
54242
  const session = yield* verifiedSession(client, email);
53383
54243
  yield* bindPerson(config, store, session);
53384
54244
  const report = yield* profileReport(client, session);
@@ -53393,95 +54253,64 @@ const verifyOtp = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
53393
54253
  const login = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
53394
54254
  const email = yield* readEmail(suppliedEmail);
53395
54255
  const runtime = yield* CliRuntime;
53396
- if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired("Use auth send, then auth verify --otp-stdin. Interactive login reads the OTP without echo."));
54256
+ if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired("Provide the OTP on stdin with --otp-stdin. Use capxul auth send, then capxul auth verify, to split sign-in across two commands."));
53397
54257
  const store = yield* openSessionRecord(config, email);
53398
54258
  const client = yield* clientFor(config, store, { restore: false });
53399
54259
  yield* call((signal) => client.auth.getSession({ signal }));
53400
54260
  yield* store.begin(yield* Clock.currentTimeMillis, runtime.invocationId);
53401
54261
  yield* call((signal) => client.auth.signIn({ email }, { signal }));
53402
54262
  yield* writeStderr("Verification email sent. Enter the code to continue.\n");
53403
- const code = yield* readOtp(otpStdin);
53404
- yield* call((signal) => client.auth.verifyOtp({
54263
+ yield* submitOtp(otpStdin, (code) => call((signal) => client.auth.verifyOtp({
53405
54264
  email,
53406
54265
  code
53407
54266
  }, {
53408
54267
  signal,
53409
54268
  journey_id: runtime.invocationId
53410
- }));
54269
+ })));
53411
54270
  const session = yield* verifiedSession(client, email);
53412
54271
  yield* bindPerson(config, store, session);
53413
- return yield* profileReport(client, session);
54272
+ const report = yield* profileReport(client, session);
54273
+ if (report.data.setupState === "ready") return report;
54274
+ if (!runtime.tty || runtime.ci || runtime.mode === "json") return report;
54275
+ if (!(yield* Prompt.confirm({
54276
+ message: "Your Capxul account is not set up yet. Continue setup now?",
54277
+ initial: false
54278
+ }))) return report;
54279
+ return yield* completeAccountSetup(config, store, client, session, {});
53414
54280
  });
53415
54281
  const signup = (config, suppliedEmail, otpStdin, suppliedProfile) => Effect.gen(function* () {
53416
54282
  const email = yield* readEmail(suppliedEmail);
53417
54283
  const runtime = yield* CliRuntime;
54284
+ yield* checkPersonFlags(suppliedProfile);
53418
54285
  const store = yield* openSessionRecord(config, email);
53419
- const run = Effect.runPromiseWith(yield* Effect.context());
53420
54286
  const restore = store.current().token !== null;
53421
- let handle = suppliedProfile.handle ?? "personal";
53422
- let bridge;
54287
+ if (!restore && !otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired(SIGNUP_OTP_INPUT_REQUIRED));
53423
54288
  let client = yield* clientFor(config, store, { restore });
53424
54289
  let session = restore ? yield* verifiedSession(client, email).pipe(Effect.catch((error) => error.code === "NOT_AUTHENTICATED" ? store.clear.pipe(Effect.as(null)) : Effect.fail(error))) : null;
53425
54290
  if (session === null) {
53426
54291
  if (restore) client = yield* clientFor(config, store, { restore: false });
53427
- if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired("Use --otp-stdin for non-interactive signup. OTP arguments are not accepted."));
54292
+ if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired(SIGNUP_OTP_INPUT_REQUIRED));
53428
54293
  yield* call((signal) => client.auth.getSession({ signal }));
53429
54294
  yield* store.begin(yield* Clock.currentTimeMillis, runtime.invocationId);
53430
54295
  yield* call((signal) => client.auth.signIn({ email }, { signal }));
53431
54296
  yield* writeStderr("Verification email sent. Enter the code to continue.\n");
53432
- const code = yield* readOtp(otpStdin);
53433
- yield* call((signal) => client.auth.verifyOtp({
54297
+ yield* submitOtp(otpStdin, (code) => call((signal) => client.auth.verifyOtp({
53434
54298
  email,
53435
54299
  code
53436
54300
  }, {
53437
54301
  signal,
53438
54302
  journey_id: runtime.invocationId
53439
- }));
54303
+ })));
53440
54304
  session = yield* verifiedSession(client, email);
53441
54305
  }
53442
54306
  yield* bindPerson(config, store, session);
53443
- const current = yield* call((signal) => client.identity.loadCurrent({ signal }));
53444
- const before = yield* profileReport(client, session);
53445
- if (before.data.setupState === "ready") return before;
53446
- const profile = yield* readPersonInput(suppliedProfile, current);
53447
- handle = profile.handle;
53448
- const invocation = yield* Ref.get(runtime.current);
53449
- const openfortSignerFactory = (bootstrap) => {
53450
- const created = createBrowserSignerBridge({
53451
- bootstrap,
53452
- environment: config.environment,
53453
- handle: () => handle,
53454
- trustedAuthOrigin: new URL(config.bootstrapBaseUrl).origin,
53455
- sessionToken: () => run(store.tokenIfCurrent())
53456
- });
53457
- bridge = created;
53458
- invocation?.onFinalize?.(Effect.promise(() => created.close()).pipe(Effect.asVoid, Effect.catchCause(() => Effect.void)));
53459
- return created.signer;
53460
- };
53461
- const walletClient = yield* clientFor(config, store, {
53462
- restore: true,
53463
- requirement: "deployed",
53464
- openfortSignerFactory
53465
- });
53466
- session = yield* verifiedSession(walletClient, email);
53467
- yield* writeStderr("Preparing your wallet in the browser. Keep that tab open.\n");
53468
- const report = yield* Effect.gen(function* () {
53469
- yield* call((signal) => walletClient.onboarding.completePerson(profile, { signal }));
53470
- return yield* profileReport(walletClient, session);
53471
- }).pipe(Effect.ensuring(Effect.suspend(() => flushWalletDiagnostics(bridge, runtime.invocationId))));
53472
- return {
53473
- data: {
53474
- ...report.data,
53475
- signerStatus: walletClient.signer.status()
53476
- },
53477
- lines: report.lines
53478
- };
54307
+ return yield* completeAccountSetup(config, store, client, session, suppliedProfile);
53479
54308
  });
53480
54309
  const readProfile = (config, suppliedEmail) => Effect.gen(function* () {
53481
54310
  const email = yield* readEmail(suppliedEmail);
53482
54311
  const store = yield* openSessionRecord(config, email);
53483
54312
  if (store.current().token === null) {
53484
- if (usableGrant(yield* loadGrant(config, email))) return yield* Effect.fail(inputRequired("A legacy account:read grant is stored. Run auth login or auth send/verify to explicitly establish a broader first-party session."));
54313
+ if (usableGrant(yield* loadGrant(config, email))) return yield* Effect.fail(inputRequired("Sign in again with capxul auth login to use this command."));
53485
54314
  return yield* Effect.fail(Errors.notAuthenticated());
53486
54315
  }
53487
54316
  const client = yield* clientFor(config, store, { restore: true });
@@ -53540,61 +54369,61 @@ const logout = (config, suppliedEmail) => Effect.gen(function* () {
53540
54369
  });
53541
54370
  //#endregion
53542
54371
  //#region src/commands/auth.ts
53543
- const emailFlag$1 = Flag.string("email").pipe(Flag.withDescription("Account email"), Flag.optional);
53544
- const otpStdinFlag = Flag.boolean("otp-stdin").pipe(Flag.withDefault(false), Flag.withDescription("Read the six-digit OTP from stdin; never pass it as an argument"));
53545
- const displayNameFlag = Flag.string("display-name").pipe(Flag.withDescription("Profile display name"), Flag.optional);
53546
- const countryFlag = Flag.string("country").pipe(Flag.withDescription("Two-letter ISO country code"), Flag.optional);
53547
- const handleFlag = Flag.string("handle").pipe(Flag.withDescription("Globally unique Capxul handle"), Flag.optional);
53548
- const metadata$2 = (name) => ({
54372
+ const emailFlag$3 = Flag.string("email").pipe(Flag.withDescription("Email address"), Flag.optional);
54373
+ const otpStdinFlag = Flag.boolean("otp-stdin").pipe(Flag.withDefault(false), Flag.withDescription("Read the six-digit OTP from standard input; never pass it as an argument"));
54374
+ const displayNameFlag = Flag.string("display-name").pipe(Flag.withDescription("Your display name"), Flag.optional);
54375
+ const countryFlag = Flag.string("country").pipe(Flag.withDescription("Two-letter country code"), Flag.optional);
54376
+ const handleFlag = Flag.string("handle").pipe(Flag.withDescription("Your unique Capxul handle"), Flag.optional);
54377
+ const metadata$4 = (name) => ({
53549
54378
  name,
53550
54379
  capability: "authentication",
53551
54380
  observation: "command"
53552
54381
  });
53553
54382
  const loginCommand = Command.make("login", {
53554
- email: emailFlag$1,
54383
+ email: emailFlag$3,
53555
54384
  otpStdin: otpStdinFlag
53556
54385
  }, ({ email, otpStdin }) => runCommand({
53557
- ...metadata$2("auth.login"),
54386
+ ...metadata$4("auth.login"),
53558
54387
  network: true
53559
- }, (config) => login(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$2("auth.login")), Command.withDescription("Establish a first-party session with terminal-owned email OTP"));
54388
+ }, (config) => login(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.login")), Command.withDescription("Sign in with your email and a one-time code"));
53560
54389
  const signupCommand = Command.make("signup", {
53561
- email: emailFlag$1,
54390
+ email: emailFlag$3,
53562
54391
  otpStdin: otpStdinFlag,
53563
54392
  displayName: displayNameFlag,
53564
54393
  country: countryFlag,
53565
54394
  handle: handleFlag
53566
54395
  }, ({ email, otpStdin, displayName, country, handle }) => runCommand({
53567
- ...metadata$2("auth.signup"),
54396
+ ...metadata$4("auth.signup"),
53568
54397
  network: true
53569
54398
  }, (config) => signup(config, Option.getOrUndefined(email), otpStdin, {
53570
54399
  displayName: Option.getOrUndefined(displayName),
53571
54400
  country: Option.getOrUndefined(country),
53572
54401
  handle: Option.getOrUndefined(handle)
53573
- }))).pipe(Command.annotate(CliCommandMetadata, metadata$2("auth.signup")), Command.withDescription("Verify email and complete Profile, Openfort wallet, and Account setup"));
53574
- const sendCommand = Command.make("send", { email: emailFlag$1 }, ({ email }) => runCommand({
53575
- ...metadata$2("auth.send"),
54402
+ }))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.signup")), Command.withDescription("Create your Capxul account and wallet"));
54403
+ const sendCommand = Command.make("send", { email: emailFlag$3 }, ({ email }) => runCommand({
54404
+ ...metadata$4("auth.send"),
53576
54405
  network: true
53577
- }, (config) => sendOtp(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$2("auth.send")), Command.withDescription("Send an OTP and save non-secret continuation context"));
54406
+ }, (config) => sendOtp(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.send")), Command.withDescription("Send a sign-in code to your email"));
53578
54407
  const verifyCommand = Command.make("verify", {
53579
- email: emailFlag$1,
54408
+ email: emailFlag$3,
53580
54409
  otpStdin: otpStdinFlag
53581
54410
  }, ({ email, otpStdin }) => runCommand({
53582
- ...metadata$2("auth.verify"),
54411
+ ...metadata$4("auth.verify"),
53583
54412
  network: true
53584
- }, (config) => verifyOtp(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$2("auth.verify")), Command.withDescription("Verify a delivered OTP and persist the first-party session"));
53585
- const profileCommand = Command.make("profile", { email: emailFlag$1 }, ({ email }) => runCommand({
53586
- ...metadata$2("auth.profile"),
54413
+ }, (config) => verifyOtp(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.verify")), Command.withDescription("Finish signing in with the code from your email"));
54414
+ const profileCommand = Command.make("profile", { email: emailFlag$3 }, ({ email }) => runCommand({
54415
+ ...metadata$4("auth.profile"),
53587
54416
  network: true
53588
- }, (config) => readProfile(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$2("auth.profile")), Command.withDescription("Restore the session and read the backend profile and account lifecycle"));
53589
- const statusCommand = Command.make("status", { email: emailFlag$1 }, ({ email }) => runCommand({
53590
- ...metadata$2("auth.status"),
54417
+ }, (config) => readProfile(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.profile")), Command.withDescription("Show your profile and account readiness"));
54418
+ const statusCommand = Command.make("status", { email: emailFlag$3 }, ({ email }) => runCommand({
54419
+ ...metadata$4("auth.status"),
53591
54420
  network: true
53592
- }, (config) => status$2(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$2("auth.status")), Command.withDescription("Validate a first-party session or show a tagged legacy grant"));
53593
- const logoutCommand = Command.make("logout", { email: emailFlag$1 }, ({ email }) => runCommand({
53594
- ...metadata$2("auth.logout"),
54421
+ }, (config) => status$2(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.status")), Command.withDescription("Show whether you are signed in"));
54422
+ const logoutCommand = Command.make("logout", { email: emailFlag$3 }, ({ email }) => runCommand({
54423
+ ...metadata$4("auth.logout"),
53595
54424
  network: true
53596
- }, (config) => logout(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$2("auth.logout")), Command.withDescription("Forget local credentials and revoke their remote session or grant"));
53597
- const auth = Command.make("auth").pipe(Command.annotate(CliCommandMetadata, metadata$2("auth")), Command.withDescription("First-party email sessions (broader authority than legacy account:read grants)"), Command.withSubcommands([
54425
+ }, (config) => logout(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.logout")), Command.withDescription("Sign out on this device"));
54426
+ const auth = Command.make("auth").pipe(Command.annotate(CliCommandMetadata, metadata$4("auth")), Command.withDescription("Sign in, create your account, and manage your session"), Command.withSubcommands([
53598
54427
  loginCommand,
53599
54428
  signupCommand,
53600
54429
  sendCommand,
@@ -53605,7 +54434,7 @@ const auth = Command.make("auth").pipe(Command.annotate(CliCommandMetadata, meta
53605
54434
  ]));
53606
54435
  //#endregion
53607
54436
  //#region src/commands/doctor.ts
53608
- const metadata$1 = {
54437
+ const metadata$3 = {
53609
54438
  name: "doctor",
53610
54439
  capability: "diagnostics",
53611
54440
  observation: "command"
@@ -53614,14 +54443,14 @@ const doctor = Command.make("doctor", {
53614
54443
  online: Flag.boolean("online").pipe(Flag.withDefault(false), Flag.withDescription("Also check the backend through the public SDK")),
53615
54444
  timeoutMs: Flag.integer("timeout-ms").pipe(Flag.filter((value) => value >= 1 && value <= 3e5, () => "Use --timeout-ms between 1 and 300000."), Flag.withDefault(3e4), Flag.withDescription("Online check deadline in milliseconds (1 to 300000)"))
53616
54445
  }, ({ online, timeoutMs }) => runCommand({
53617
- ...metadata$1,
54446
+ ...metadata$3,
53618
54447
  network: online
53619
54448
  }, (config, observation, onFinalize) => checkDoctor(config, {
53620
54449
  online,
53621
54450
  timeoutMs,
53622
54451
  ...observation === void 0 ? {} : { observation },
53623
54452
  ...onFinalize === void 0 ? {} : { onFinalize }
53624
- }))).pipe(Command.annotate(CliCommandMetadata, metadata$1), Command.withDescription("Check local configuration; --online also checks the backend"));
54453
+ }))).pipe(Command.annotate(CliCommandMetadata, metadata$3), Command.withDescription("Check local configuration; --online also checks the backend"));
53625
54454
  //#endregion
53626
54455
  //#region src/commands/telemetry.ts
53627
54456
  const statusMetadata = {
@@ -53667,6 +54496,73 @@ const telemetry = Command.make("telemetry").pipe(Command.annotate(CliCommandMeta
53667
54496
  disable
53668
54497
  ]));
53669
54498
  //#endregion
54499
+ //#region src/org-recovery.ts
54500
+ /**
54501
+ * The one safe recovery handle for a person's Organization journey.
54502
+ *
54503
+ * `handle` is the exact business handle the create prepared. It is the only
54504
+ * identity the CLI knows while the first durable Organization write is in
54505
+ * flight, and the backend resumes the same Organization for that handle.
54506
+ * `organizationId` is added from the first committed Organization. After that
54507
+ * the ID is the recovery target.
54508
+ *
54509
+ * A record with no handle releases the journey: the backend refused that exact
54510
+ * handle before it wrote any Organization row, so no Organization waits to be
54511
+ * resumed. The record stores no draft, submission, credential, or token.
54512
+ */
54513
+ const Recovery = Schema.Struct({
54514
+ version: Schema.Literal(1),
54515
+ handle: Schema.optional(Schema.NonEmptyString),
54516
+ organizationId: Schema.optional(Schema.NonEmptyString)
54517
+ });
54518
+ const decode$1 = (value) => Schema.decodeUnknownEffect(Schema.fromJsonString(Recovery), { onExcessProperty: "error" })(value).pipe(Effect.flatMap((record) => {
54519
+ const organizationId = record.organizationId ?? null;
54520
+ if (record.handle === void 0) return organizationId === null ? Effect.succeed(null) : Effect.fail(new StateFileError({ reason: "Corrupt" }));
54521
+ const handle = record.handle;
54522
+ return Effect.try(() => ({
54523
+ handle: String(toHandle(handle)),
54524
+ organizationId: organizationId === null ? null : toOrgId(organizationId)
54525
+ }));
54526
+ }), Effect.mapError(() => new StateFileError({ reason: "Corrupt" })));
54527
+ const encode = (recovery) => JSON.stringify({
54528
+ version: 1,
54529
+ handle: recovery.handle,
54530
+ ...recovery.organizationId === null ? {} : { organizationId: String(recovery.organizationId) }
54531
+ });
54532
+ /** The released shape: no handle waits to be resumed. */
54533
+ const RELEASED = JSON.stringify({ version: 1 });
54534
+ /** Read the safe recovery handle without writing. A corrupt record refuses the command. */
54535
+ const readOrganizationRecovery = (config, authUserId) => Effect.gen(function* () {
54536
+ const { directory, scope } = yield* privateRecordScope(config, "organization-recovery", authUserId);
54537
+ const value = yield* readPrivateRecord(directory, scope);
54538
+ return value === null ? null : yield* decode$1(value);
54539
+ });
54540
+ /**
54541
+ * Open the recovery handle for one command.
54542
+ *
54543
+ * `save` and `release` replace only the exact record this command read, so two
54544
+ * commands cannot overwrite each other's Organization journey, and a delayed
54545
+ * write cannot follow a newer one. A record this command did not read refuses
54546
+ * the write.
54547
+ */
54548
+ const openOrganizationRecovery = (config, authUserId, stillAuthenticated) => Effect.gen(function* () {
54549
+ const { directory, scope } = yield* privateRecordScope(config, "organization-recovery", authUserId);
54550
+ let encoded = yield* readPrivateRecord(directory, scope);
54551
+ let record = encoded === null ? null : yield* decode$1(encoded);
54552
+ const write = (next) => Effect.gen(function* () {
54553
+ const value = next === null ? RELEASED : encode(next);
54554
+ if (value === encoded) return;
54555
+ if (!(yield* compareAndSetPrivateRecord(directory, scope, encoded, value, stillAuthenticated))) return yield* Effect.fail(Errors.notAuthenticated("The local Organization recovery state changed. Retry the command."));
54556
+ encoded = value;
54557
+ record = next;
54558
+ });
54559
+ return {
54560
+ current: () => record,
54561
+ save: (next) => write(next),
54562
+ release: () => write(null)
54563
+ };
54564
+ });
54565
+ //#endregion
53670
54566
  //#region src/org-selection.ts
53671
54567
  const Selection = Schema.Struct({
53672
54568
  version: Schema.Literal(1),
@@ -53689,6 +54585,37 @@ const saveOrganizationSelection = (config, authUserId, organizationId, stillAuth
53689
54585
  });
53690
54586
  //#endregion
53691
54587
  //#region src/org.ts
54588
+ /**
54589
+ * The exact Organization a read uses when the caller named no ID: the saved read
54590
+ * default first, then the recorded recovery handle for this person's
54591
+ * Organization journey. `recoverable` marks the handle source, whose Organization
54592
+ * may still run its setup lane and be absent from the own-access list.
54593
+ */
54594
+ const readResolvedOrganization = (config, authUserId, organizationId) => Effect.gen(function* () {
54595
+ if (organizationId !== null) return {
54596
+ organizationId,
54597
+ recoverable: false
54598
+ };
54599
+ const selected = yield* readOrganizationSelection(config, authUserId);
54600
+ if (selected !== null) return {
54601
+ organizationId: selected,
54602
+ recoverable: false
54603
+ };
54604
+ const recovery = yield* readOrganizationRecovery(config, authUserId);
54605
+ return {
54606
+ organizationId: recovery?.organizationId ?? null,
54607
+ recoverable: recovery?.organizationId !== null && recovery?.organizationId !== void 0
54608
+ };
54609
+ });
54610
+ /**
54611
+ * A ready Organization this command reports must satisfy current access.
54612
+ *
54613
+ * The own-access list carries a founder's unfinished Organizations through the
54614
+ * backend's founder fallback, and the backend lifecycle read admits the founder
54615
+ * for the durable lane. A confirmed result therefore reads the list again: an
54616
+ * Organization the person cannot reach refuses instead of reporting its state.
54617
+ */
54618
+ const requireCurrentAccess = (client, organizationId, lifecycle) => lifecycle.status !== "ready" ? Effect.void : Effect.promise((signal) => client.orgs({ signal })).pipe(Effect.flatMap(resultValue), Effect.flatMap((organizations) => organizations.some((organization) => organization.id === organizationId) ? Effect.void : Effect.fail(Errors.invalidInput("org", "The Organization is not accessible."))));
53692
54619
  const readOrganization = (config, command, input) => Effect.gen(function* () {
53693
54620
  const timeout = yield* Schema.decodeUnknownEffect(Schema.Int.check(Schema.isBetween({
53694
54621
  minimum: 1,
@@ -53715,7 +54642,8 @@ const readOrganization = (config, command, input) => Effect.gen(function* () {
53715
54642
  address: org.account.address
53716
54643
  }))
53717
54644
  };
53718
- organizationId ??= yield* readOrganizationSelection(config, session.authUserId);
54645
+ const resolved = yield* readResolvedOrganization(config, session.authUserId, organizationId);
54646
+ organizationId = resolved.organizationId;
53719
54647
  if (organizationId === null) {
53720
54648
  const runtime = yield* CliRuntime;
53721
54649
  if (!runtime.tty || runtime.ci || runtime.mode === "json" || organizations.length === 0) return yield* Effect.fail(inputRequired("Provide --org or select a read default with org use."));
@@ -53730,7 +54658,7 @@ const readOrganization = (config, command, input) => Effect.gen(function* () {
53730
54658
  }))
53731
54659
  });
53732
54660
  }
53733
- if (!organizations.some((org) => org.id === organizationId)) return yield* Effect.fail(Errors.invalidInput("org", "The Organization is not accessible."));
54661
+ if (!organizations.some((org) => org.id === organizationId) && !resolved.recoverable) return yield* Effect.fail(Errors.invalidInput("org", "The Organization is not accessible."));
53734
54662
  if (command === "use") {
53735
54663
  yield* saveOrganizationSelection(config, session.authUserId, organizationId, stillAuthenticated);
53736
54664
  return {
@@ -53783,11 +54711,13 @@ const readOrganization = (config, command, input) => Effect.gen(function* () {
53783
54711
  const read = Effect.promise((signal) => scoped.getLifecycle({ signal })).pipe(Effect.flatMap(resultValue));
53784
54712
  const observed = yield* Effect.gen(function* () {
53785
54713
  let lifecycle = yield* read;
54714
+ if (resolved.recoverable) yield* requireCurrentAccess(client, organizationId, lifecycle);
53786
54715
  if (command !== "wait") return lifecycle;
53787
54716
  while (lifecycle.status !== "ready" && lifecycle.status !== "failed") {
53788
54717
  yield* Effect.sleep(1e3);
53789
54718
  lifecycle = yield* read;
53790
54719
  }
54720
+ if (resolved.recoverable) yield* requireCurrentAccess(client, organizationId, lifecycle);
53791
54721
  return lifecycle;
53792
54722
  }).pipe(Effect.timeoutOption(timeout * 1e3));
53793
54723
  if (Option.isNone(observed)) return yield* Effect.fail(new CliFailure({
@@ -53871,45 +54801,89 @@ const requireWriteConfirmation = (supplied) => Effect.gen(function* () {
53871
54801
  *
53872
54802
  * The committed Organization ID is retained from the first state that carries
53873
54803
  * one and never replaced, so a later failed lane keeps reporting the ID it was
53874
- * committed to. `flush` never resolves a signature on an unwritten line: the
53875
- * command-scoped signer awaits it before forwarding a digest, and a failed or
53876
- * cancelled write throws here instead.
54804
+ * committed to. `flush` writes the queued lines first, then makes the ID durable
54805
+ * as this person's safe recovery handle, and only then resolves: the
54806
+ * command-scoped signer awaits it before forwarding a digest, so a failed or
54807
+ * cancelled write saves no handle and forwards no signature.
53877
54808
  */
53878
- const makeOrganizationProgress = (mode, run) => {
54809
+ const makeOrganizationProgress = (mode, run, persist) => {
53879
54810
  const queued = [];
54811
+ const held = [];
54812
+ const heldSet = /* @__PURE__ */ new Set();
53880
54813
  const written = /* @__PURE__ */ new Set();
53881
54814
  let committed = null;
54815
+ let persisted = null;
54816
+ let readyAllowed = false;
53882
54817
  let failure = null;
54818
+ /** One command owns one committed Organization. A second ID is a defect. */
54819
+ const retain = (observed) => {
54820
+ if (committed === null) {
54821
+ committed = observed;
54822
+ return true;
54823
+ }
54824
+ if (committed === observed) return true;
54825
+ failure ??= new CliFailure({
54826
+ code: "CLI_DEFECT",
54827
+ message: "The onboarding stream reported a different Organization.",
54828
+ exitCode: 1,
54829
+ outcome: "failed"
54830
+ });
54831
+ return false;
54832
+ };
54833
+ /**
54834
+ * A ready line waits until the command confirms current access. Every other
54835
+ * line is progress evidence and goes out with the committed ID.
54836
+ */
54837
+ const enqueue = (text, ready) => {
54838
+ if (written.has(text) || heldSet.has(text)) return;
54839
+ if (ready && !readyAllowed) {
54840
+ heldSet.add(text);
54841
+ held.push(text);
54842
+ return;
54843
+ }
54844
+ written.add(text);
54845
+ queued.push(text);
54846
+ };
53883
54847
  const render = (state) => {
53884
54848
  const organizationId = state.organizationId;
53885
54849
  if (organizationId === null) return null;
53886
- const observed = String(organizationId);
53887
- if (committed === null) committed = observed;
53888
- else if (committed !== observed) {
53889
- failure ??= new CliFailure({
53890
- code: "CLI_DEFECT",
53891
- message: "The onboarding stream reported a different Organization.",
53892
- exitCode: 1,
53893
- outcome: "failed"
53894
- });
53895
- return null;
53896
- }
54850
+ if (!retain(String(organizationId))) return null;
53897
54851
  const status = state.lifecycle?.status;
53898
54852
  if (status === void 0) return null;
53899
- return `${mode === "json" ? JSON.stringify({
53900
- type: "organization.lifecycle",
53901
- organizationId: committed,
53902
- status
53903
- }) : `Organization ${JSON.stringify(committed)}: ${status}`}\n`;
54853
+ return {
54854
+ text: `${mode === "json" ? JSON.stringify({
54855
+ type: "organization.lifecycle",
54856
+ organizationId: committed,
54857
+ status
54858
+ }) : `Organization ${JSON.stringify(committed)}: ${status}`}\n`,
54859
+ ready: status === "ready"
54860
+ };
53904
54861
  };
53905
54862
  return {
53906
54863
  /** Synchronous by contract: the SDK publishes state from its own fiber. */
53907
54864
  observe: (state) => {
53908
- const text = render(state);
53909
- if (text === null || written.has(text)) return;
53910
- written.add(text);
53911
- queued.push(text);
53912
- },
54865
+ const line = render(state);
54866
+ if (line !== null) enqueue(line.text, line.ready);
54867
+ },
54868
+ /** One safe recovery line the command writes before it resumes a lane. */
54869
+ notice: (text) => enqueue(text, false),
54870
+ /** The final result ID joins the same one-Organization rule. */
54871
+ commit: (organizationId) => {
54872
+ retain(organizationId);
54873
+ },
54874
+ /** Release the ready lines once current access is confirmed. */
54875
+ allowReady: () => {
54876
+ readyAllowed = true;
54877
+ while (held.length > 0) {
54878
+ const text = held.shift();
54879
+ heldSet.delete(text);
54880
+ if (written.has(text)) continue;
54881
+ written.add(text);
54882
+ queued.push(text);
54883
+ }
54884
+ },
54885
+ /** The committed ID this command observed, or null when it observed none. */
54886
+ committed: () => committed,
53913
54887
  failure: () => failure,
53914
54888
  flush: async () => {
53915
54889
  while (queued.length > 0) {
@@ -53922,6 +54896,13 @@ const makeOrganizationProgress = (mode, run) => {
53922
54896
  }
53923
54897
  queued.shift();
53924
54898
  }
54899
+ if (committed !== null && persisted !== committed) try {
54900
+ await persist(committed);
54901
+ persisted = committed;
54902
+ } catch (cause) {
54903
+ failure = cause;
54904
+ throw cause;
54905
+ }
53925
54906
  if (failure !== null) throw failure;
53926
54907
  }
53927
54908
  };
@@ -53932,19 +54913,67 @@ const makeOrganizationProgress = (mode, run) => {
53932
54913
  * resolved, and it always asks, even when `--confirm` was supplied. A prompt that
53933
54914
  * cannot be answered is a refusal.
53934
54915
  */
53935
- const confirmOrganizationWrite = (preview) => Effect.gen(function* () {
54916
+ const confirmWrite = (preview) => Effect.gen(function* () {
53936
54917
  yield* writeStderr(`${preview.lines.join("\n")}\n`);
53937
54918
  if (!(yield* Prompt.confirm({ message: preview.question }).pipe(Effect.catch(() => Effect.succeed(false))))) return yield* Effect.fail(inputRequired("The write was not confirmed."));
53938
54919
  });
53939
- /** The Organization reads a terminal preview shows before it asks. */
53940
- const organizationPreviewLines = (client, organizationId) => organizationId === null ? Effect.gen(function* () {
54920
+ /**
54921
+ * The Organization reads a terminal preview shows before it asks.
54922
+ *
54923
+ * A create preview also names the recovery handle this machine already holds, so
54924
+ * a person who confirms knows whether the command creates one more Organization
54925
+ * or continues the exact one their interrupted journey reached. The preview
54926
+ * reads that recorded Organization's lifecycle only when the requested handle
54927
+ * continues it; a different handle is a new creation, and the recorded
54928
+ * Organization must not block it.
54929
+ */
54930
+ const organizationPreview = (client, organizationId, recovery, requestedHandle) => organizationId === null ? Effect.gen(function* () {
53941
54931
  const organizations = yield* Effect.promise((signal) => client.orgs({ signal })).pipe(Effect.flatMap(resultValue));
53942
- if (organizations.length === 0) return ["Existing Organizations: none"];
53943
- return [`Existing Organizations: ${organizations.length}`, ...organizations.map((organization) => ` ${JSON.stringify(String(organization.id))} ${JSON.stringify(organization.name)}`)];
54932
+ const lines = organizations.length === 0 ? ["Existing Organizations: none"] : [`Existing Organizations: ${organizations.length}`, ...organizations.map((organization) => ` ${JSON.stringify(String(organization.id))} ${JSON.stringify(organization.name)}`)];
54933
+ if (recovery === null) return {
54934
+ lines,
54935
+ recovery,
54936
+ recoveryLifecycle: null
54937
+ };
54938
+ const exact = recovery.organizationId;
54939
+ if (exact === null) return {
54940
+ lines: [...lines, `Recovery: a create for handle @${recovery.handle} is prepared on this machine.`],
54941
+ recovery,
54942
+ recoveryLifecycle: null
54943
+ };
54944
+ if (requestedHandle !== recovery.handle) return {
54945
+ lines: [...lines, `Recovery: Organization ${JSON.stringify(String(exact))} (@${recovery.handle}) is recorded on this machine.`],
54946
+ recovery,
54947
+ recoveryLifecycle: null
54948
+ };
54949
+ const lifecycle = yield* Effect.promise((signal) => client.org(exact).getLifecycle({ signal })).pipe(Effect.flatMap(resultValue));
54950
+ yield* requireCurrentAccess(client, exact, lifecycle);
54951
+ return {
54952
+ lines: [
54953
+ ...lines,
54954
+ `Recovery: Organization ${JSON.stringify(String(exact))} (@${recovery.handle})`,
54955
+ `Current lifecycle: ${lifecycle.status}`
54956
+ ],
54957
+ recovery,
54958
+ recoveryLifecycle: lifecycle
54959
+ };
53944
54960
  }) : Effect.gen(function* () {
53945
- return [`Current lifecycle: ${(yield* Effect.promise((signal) => client.org(organizationId).getLifecycle({ signal })).pipe(Effect.flatMap(resultValue))).status}`];
54961
+ return {
54962
+ lines: [`Current lifecycle: ${(yield* Effect.promise((signal) => client.org(organizationId).getLifecycle({ signal })).pipe(Effect.flatMap(resultValue))).status}`],
54963
+ recovery,
54964
+ recoveryLifecycle: null
54965
+ };
53946
54966
  });
53947
54967
  /**
54968
+ * The confirmation question for a create. A matching recovery handle means the
54969
+ * command continues one exact Organization instead of creating another one.
54970
+ */
54971
+ const createOrganizationQuestion = (handle, preview) => {
54972
+ if (preview.recovery === null || preview.recovery.organizationId === null) return "Create this Organization?";
54973
+ if (preview.recovery.handle !== handle) return "Create this Organization?";
54974
+ return preview.recoveryLifecycle?.status === "ready" ? "Return this ready Organization?" : "Resume this Organization's setup?";
54975
+ };
54976
+ /**
53948
54977
  * Settle one exact Organization lifecycle read: poll every second with no
53949
54978
  * overlapping read until the lane is ready or failed. The command's own deadline
53950
54979
  * bounds this loop together with the write that preceded it.
@@ -53958,6 +54987,81 @@ const settleOrganizationLifecycle = (read) => Effect.gen(function* () {
53958
54987
  return lifecycle;
53959
54988
  });
53960
54989
  /**
54990
+ * Resume the exact durable Organization lane through the SDK recovery owner, then
54991
+ * settle that exact lifecycle. It never creates an Organization and never
54992
+ * resubmits a recorded bootstrap submission.
54993
+ */
54994
+ const resumeOrganizationSetup = (operation, scoped, deadline) => Effect.gen(function* () {
54995
+ yield* Effect.promise(() => scoped.retrySetup({ signal: deadline.signal })).pipe(Effect.flatMap(resultValue));
54996
+ const lifecycle = yield* settleOrganizationLifecycle(() => scoped.getLifecycle({ signal: deadline.signal }));
54997
+ yield* requireReadyLifecycle(operation, lifecycle);
54998
+ return lifecycle;
54999
+ });
55000
+ /**
55001
+ * A prepared handle owns the next durable Organization for that handle, so a
55002
+ * different handle would create a second Organization and leave the prepared lane
55003
+ * abandoned. The refusal names the exact handle to continue.
55004
+ */
55005
+ const preparedJourneyRefusal = (handle) => new CliFailure({
55006
+ code: "WRONG_STATE",
55007
+ message: `An Organization creation is already prepared for handle @${handle}. Run capxul org create with the same handle to continue that exact Organization.`,
55008
+ exitCode: 2,
55009
+ outcome: "refused"
55010
+ });
55011
+ /**
55012
+ * Read how this create continues the person's Organization journey.
55013
+ *
55014
+ * The recovery handle is the only source of the exact identity, so a matching
55015
+ * handle resumes the Organization its lane reached and never creates another
55016
+ * one. A different handle creates one Organization only after the recorded
55017
+ * Organization reaches ready.
55018
+ */
55019
+ const readCreateRoute = (client, recovery, handle, signal) => Effect.gen(function* () {
55020
+ if (recovery === null) return {
55021
+ kind: "create",
55022
+ continuesRecorded: false
55023
+ };
55024
+ if (recovery.organizationId === null) {
55025
+ if (recovery.handle === handle) return {
55026
+ kind: "create",
55027
+ continuesRecorded: true
55028
+ };
55029
+ return yield* Effect.fail(preparedJourneyRefusal(recovery.handle));
55030
+ }
55031
+ const lifecycle = yield* Effect.promise(() => client.org(recovery.organizationId).getLifecycle({ signal })).pipe(Effect.flatMap(resultValue));
55032
+ if (recovery.handle === handle) return {
55033
+ kind: "recover",
55034
+ organizationId: recovery.organizationId,
55035
+ handle: recovery.handle,
55036
+ lifecycle
55037
+ };
55038
+ if (lifecycle.status === "ready") return {
55039
+ kind: "create",
55040
+ continuesRecorded: false
55041
+ };
55042
+ return yield* Effect.fail(Errors.wrongState({
55043
+ method: CAPXUL_OPERATIONS.onboarding.completeOrganization,
55044
+ currentState: lifecycle.status,
55045
+ validStates: ["ready"],
55046
+ organizationId: String(recovery.organizationId)
55047
+ }));
55048
+ });
55049
+ /** One safe recovery line, in the same newline-delimited shape as the lifecycle. */
55050
+ const recoveryNotice = (mode, recovery) => mode === "json" ? `${JSON.stringify({
55051
+ type: "organization.recovery",
55052
+ organizationId: String(recovery.organizationId),
55053
+ handle: recovery.handle
55054
+ })}\n` : `Recovering Organization ${JSON.stringify(String(recovery.organizationId))} (@${recovery.handle}).\n`;
55055
+ /** A taken handle is refused before the backend writes any Organization row. */
55056
+ const refusedOrganizationHandle = (error) => isCapxulError(error) && error.code === "INVALID_INPUT" && error.details?.field === "handle";
55057
+ /** A write forwards no signature before its recovery handle exists; absence is a defect. */
55058
+ const organizationWriteDefect = () => new CliFailure({
55059
+ code: "CLI_DEFECT",
55060
+ message: "The Organization write lost its recovery handle.",
55061
+ exitCode: 1,
55062
+ outcome: "failed"
55063
+ });
55064
+ /**
53961
55065
  * The one deadline for a write: it aborts the active SDK signal, so the waiting
53962
55066
  * command stops and the signer forwards nothing further, while the durable
53963
55067
  * backend lane keeps running and stays resumable by `org retry`.
@@ -53980,10 +55084,11 @@ const organizationWaitTimeout = () => new CliFailure({
53980
55084
  * the label keeps the existing fallback.
53981
55085
  *
53982
55086
  * `resetSession` is part of the contract the SDK's Organization setup owner
53983
- * calls before it resumes a lane that failed at founder authorization or
53984
- * bootstrap submission. The bridge's own reset stops its loopback server, so a
53985
- * reset here clears the stale readiness, closes that bridge, and lets the next
53986
- * address or digest request open one for the same command.
55087
+ * calls before it resumes a lane that failed at founder authorization; a lane
55088
+ * that resumes a recorded bootstrap submission does not touch the signer. The
55089
+ * bridge's own reset stops its loopback server, so a reset here clears the
55090
+ * stale readiness, closes that bridge, and lets the next address or digest
55091
+ * request open one for the same command.
53987
55092
  */
53988
55093
  const organizationWriteSigner = (config, opened, run, beforeSignature) => Effect.gen(function* () {
53989
55094
  const runtime = yield* CliRuntime;
@@ -54066,10 +55171,15 @@ const requireReadyLifecycle = (operation, lifecycle) => lifecycle.status === "re
54066
55171
  * explicit create intent, then settle the exact Organization lifecycle. This
54067
55172
  * never saves a read default and never returns an unrelated ready Organization.
54068
55173
  *
55174
+ * A create that meets this person's prepared Organization journey continues that
55175
+ * exact Organization: the same handle resumes the lane its ID reached, and a
55176
+ * different handle refuses while that lane is unsettled. Neither path creates a
55177
+ * replacement Organization or treasury.
55178
+ *
54069
55179
  * One deadline covers the write and its settlement. At the deadline the active
54070
55180
  * SDK signal is aborted: the command stops waiting, forwards no further
54071
55181
  * signature, and returns the typed wait failure, while the durable backend lane
54072
- * keeps running and stays resumable by `org retry`.
55182
+ * keeps running and stays resumable by the same create or by `org retry`.
54073
55183
  */
54074
55184
  const createOrganization = (config, input) => Effect.gen(function* () {
54075
55185
  const operation = CAPXUL_OPERATIONS.org.create;
@@ -54079,50 +55189,99 @@ const createOrganization = (config, input) => Effect.gen(function* () {
54079
55189
  const opened = yield* openAuthenticatedSession(config, input.email);
54080
55190
  if (interactivelyConfirm) {
54081
55191
  const preview = yield* restoreVerifiedClient(config, opened, { requirement: "none" });
54082
- const resolved = yield* organizationPreviewLines(preview.client, null);
54083
- yield* confirmOrganizationWrite({
55192
+ const recovery = yield* readOrganizationRecovery(config, preview.session.authUserId);
55193
+ const resolved = yield* organizationPreview(preview.client, null, recovery, draft.handle);
55194
+ yield* confirmWrite({
54084
55195
  lines: [
54085
55196
  `Create Organization ${JSON.stringify(draft.name)} (@${draft.handle}, ${draft.country})`,
54086
55197
  ...draft.bio === void 0 ? [] : [`Bio: ${JSON.stringify(draft.bio)}`],
54087
55198
  ...draft.size === void 0 ? [] : [`Size: ${JSON.stringify(draft.size)}`],
54088
- ...resolved
55199
+ ...resolved.lines
54089
55200
  ],
54090
- question: "Create this Organization?"
55201
+ question: createOrganizationQuestion(draft.handle, resolved)
54091
55202
  });
54092
55203
  }
54093
55204
  const context = yield* Effect.context();
54094
55205
  const run = Effect.runPromiseWith(context);
54095
55206
  const runtime = yield* CliRuntime;
54096
- const progress = makeOrganizationProgress(runtime.mode, run);
55207
+ const progressRef = yield* Ref.make(null);
54097
55208
  const deadline = new AbortController();
54098
55209
  const openfortSignerFactory = yield* organizationWriteSigner(config, opened, run, async () => {
54099
55210
  const cancelled = cancelledWrite(operation, deadline.signal);
54100
55211
  if (cancelled !== null) throw cancelled;
54101
- await progress.flush();
55212
+ const live = await run(Ref.get(progressRef));
55213
+ if (live === null) throw organizationWriteDefect();
55214
+ await live.flush();
54102
55215
  const stopped = cancelledWrite(operation, deadline.signal);
54103
55216
  if (stopped !== null) throw stopped;
54104
55217
  });
54105
- const { client } = yield* restoreVerifiedClient(config, opened, {
55218
+ const { client, session, stillAuthenticated } = yield* restoreVerifiedClient(config, opened, {
54106
55219
  requirement: "deployed",
54107
55220
  openfortSignerFactory
54108
55221
  });
54109
- const unsubscribe = client.onboarding.subscribeOrganization(progress.observe);
54110
- const settled = yield* Effect.gen(function* () {
54111
- const completed = yield* Effect.promise(() => client.onboarding.completeOrganization(draft, {
54112
- intent: "create",
54113
- signal: deadline.signal
54114
- })).pipe(Effect.ensuring(Effect.sync(unsubscribe)));
55222
+ const recovery = yield* openOrganizationRecovery(config, session.authUserId, stillAuthenticated);
55223
+ const persistRecovery = (organizationId) => Effect.gen(function* () {
55224
+ const committed = yield* onboardingOrganizationId(organizationId);
55225
+ yield* recovery.save({
55226
+ handle: draft.handle,
55227
+ organizationId: committed
55228
+ });
55229
+ });
55230
+ const progress = makeOrganizationProgress(runtime.mode, run, (organizationId) => run(persistRecovery(organizationId)));
55231
+ yield* Ref.set(progressRef, progress);
55232
+ const flushProgress = Effect.gen(function* () {
54115
55233
  yield* Effect.tryPromise({
54116
55234
  try: progress.flush,
54117
55235
  catch: (cause) => cause
54118
55236
  }).pipe(Effect.catch(() => Effect.void));
54119
55237
  const localFailure = progress.failure();
54120
55238
  if (localFailure !== null && localFailure !== void 0) return yield* Effect.fail(localFailure);
54121
- const onboarding = yield* resultValue(completed);
55239
+ });
55240
+ const settled = yield* Effect.gen(function* () {
55241
+ const route = yield* readCreateRoute(client, recovery.current(), draft.handle, deadline.signal);
55242
+ if (route.kind === "recover") {
55243
+ if (route.lifecycle.status === "ready") {
55244
+ yield* requireCurrentAccess(client, route.organizationId, route.lifecycle);
55245
+ progress.allowReady();
55246
+ return {
55247
+ organizationId: route.organizationId,
55248
+ lifecycle: route.lifecycle
55249
+ };
55250
+ }
55251
+ progress.notice(recoveryNotice(runtime.mode, route));
55252
+ yield* flushProgress;
55253
+ const lifecycle = yield* resumeOrganizationSetup(operation, client.org(route.organizationId), deadline);
55254
+ yield* requireCurrentAccess(client, route.organizationId, lifecycle);
55255
+ progress.allowReady();
55256
+ return {
55257
+ organizationId: route.organizationId,
55258
+ lifecycle
55259
+ };
55260
+ }
55261
+ yield* recovery.save({
55262
+ handle: draft.handle,
55263
+ organizationId: null
55264
+ });
55265
+ const unsubscribe = client.onboarding.subscribeOrganization(progress.observe);
55266
+ const completed = yield* Effect.promise(() => client.onboarding.completeOrganization(draft, {
55267
+ intent: "create",
55268
+ signal: deadline.signal
55269
+ })).pipe(Effect.ensuring(Effect.sync(unsubscribe)));
55270
+ if (!completed.ok) {
55271
+ yield* flushProgress;
55272
+ if (refusedOrganizationHandle(completed.error) && progress.committed() === null) yield* recovery.release();
55273
+ return yield* Effect.fail(completed.error);
55274
+ }
55275
+ const onboarding = completed.value;
54122
55276
  const organizationId = yield* onboardingOrganizationId(onboarding.organizationId);
55277
+ progress.commit(String(organizationId));
55278
+ yield* flushProgress;
54123
55279
  const scoped = client.org(organizationId);
54124
55280
  const lifecycle = yield* settleOrganizationLifecycle(() => scoped.getLifecycle({ signal: deadline.signal }));
54125
55281
  yield* requireReadyLifecycle(operation, lifecycle);
55282
+ if (route.continuesRecorded) yield* requireCurrentAccess(client, organizationId, lifecycle);
55283
+ progress.allowReady();
55284
+ yield* flushProgress;
54126
55285
  return {
54127
55286
  organizationId,
54128
55287
  lifecycle
@@ -54167,9 +55326,9 @@ const retryOrganization = (config, input) => Effect.gen(function* () {
54167
55326
  const opened = yield* openAuthenticatedSession(config, input.email);
54168
55327
  if (interactivelyConfirm) {
54169
55328
  const preview = yield* restoreVerifiedClient(config, opened, { requirement: "none" });
54170
- const resolved = yield* organizationPreviewLines(preview.client, organizationId);
54171
- yield* confirmOrganizationWrite({
54172
- lines: [`Resume setup for Organization ${JSON.stringify(String(organizationId))}`, ...resolved],
55329
+ const resolved = yield* organizationPreview(preview.client, organizationId, null);
55330
+ yield* confirmWrite({
55331
+ lines: [`Resume setup for Organization ${JSON.stringify(String(organizationId))}`, ...resolved.lines],
54173
55332
  question: "Resume this Organization's setup?"
54174
55333
  });
54175
55334
  }
@@ -54185,12 +55344,7 @@ const retryOrganization = (config, input) => Effect.gen(function* () {
54185
55344
  openfortSignerFactory
54186
55345
  });
54187
55346
  const scoped = client.org(organizationId);
54188
- const settled = yield* Effect.gen(function* () {
54189
- yield* Effect.promise(() => scoped.retrySetup({ signal: deadline.signal })).pipe(Effect.flatMap(resultValue));
54190
- const lifecycle = yield* settleOrganizationLifecycle(() => scoped.getLifecycle({ signal: deadline.signal }));
54191
- yield* requireReadyLifecycle(operation, lifecycle);
54192
- return lifecycle;
54193
- }).pipe(Effect.onInterrupt(() => Effect.sync(() => deadline.abort()))).pipe(Effect.timeoutOption(timeout * 1e3));
55347
+ const settled = yield* resumeOrganizationSetup(operation, scoped, deadline).pipe(Effect.onInterrupt(() => Effect.sync(() => deadline.abort()))).pipe(Effect.timeoutOption(timeout * 1e3));
54194
55348
  if (Option.isNone(settled)) return yield* Effect.fail(organizationWaitTimeout());
54195
55349
  return {
54196
55350
  data: {
@@ -54204,6 +55358,979 @@ const retryOrganization = (config, input) => Effect.gen(function* () {
54204
55358
  };
54205
55359
  });
54206
55360
  //#endregion
55361
+ //#region src/invitation.ts
55362
+ const INVITATION_PHASES = [
55363
+ "authorizing",
55364
+ "pending",
55365
+ "pending_grant",
55366
+ "active",
55367
+ "revoking",
55368
+ "declined",
55369
+ "cancelled",
55370
+ "expired",
55371
+ "revoked"
55372
+ ];
55373
+ /** Phases at which a wait has nothing left to watch. */
55374
+ const SETTLED_PHASES = /* @__PURE__ */ new Set([
55375
+ "pending",
55376
+ "active",
55377
+ "declined",
55378
+ "cancelled",
55379
+ "expired",
55380
+ "revoked"
55381
+ ]);
55382
+ /** The own-offer projection the CLI reads to resolve one invitation's scope. */
55383
+ const OWN_OFFER_PAGE_LIMIT = 100;
55384
+ /** The Base Sepolia assets this CLI accepts, by exact key or exact AssetId. */
55385
+ const ACCEPTED_ASSETS = TOKENS.BASE_SEPOLIA;
55386
+ /** The timeout a read command may omit; the write grammar always carries one. */
55387
+ const readOptionalTimeout = (value) => readWriteTimeout(value ?? 120);
55388
+ /** One exact Organization scope. Reads may omit it; writes may not. */
55389
+ const readExactOrg = (orgs, requirement) => Effect.gen(function* () {
55390
+ const ids = [...new Set(orgs)];
55391
+ if (ids.length > 1) return yield* Effect.fail(inputRequired("Provide one exact Organization ID."));
55392
+ const raw = ids[0];
55393
+ if (raw === void 0) {
55394
+ if (requirement === "required") return yield* Effect.fail(inputRequired("Provide --org."));
55395
+ return null;
55396
+ }
55397
+ return yield* Effect.try({
55398
+ try: () => toOrgId(raw),
55399
+ catch: () => inputRequired("Provide a valid Organization ID.")
55400
+ });
55401
+ });
55402
+ const readInvitationId = (value) => Effect.gen(function* () {
55403
+ if (value === void 0) return yield* Effect.fail(inputRequired("Provide --invitation-id."));
55404
+ const trimmed = value.trim();
55405
+ if (trimmed.length === 0 || trimmed.length > 256 || /[^\x21-\x7e]/.test(trimmed)) return yield* Effect.fail(inputRequired("Provide a valid --invitation-id."));
55406
+ return trimmed;
55407
+ });
55408
+ /** One bounded page request. Unknown or duplicate phases refuse before any read. */
55409
+ const readPageBounds = (input) => Effect.gen(function* () {
55410
+ const limit = input.limit;
55411
+ if (limit !== void 0 && (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)) return yield* Effect.fail(inputRequired("Provide --limit as an integer from 1 to 100."));
55412
+ const cursor = input.cursor?.trim();
55413
+ if (input.cursor !== void 0 && (cursor === void 0 || cursor.length === 0)) return yield* Effect.fail(inputRequired("Provide a nonempty --cursor."));
55414
+ const requested = input.phases ?? [];
55415
+ if (requested.find((phase) => !INVITATION_PHASES.some((known) => known === phase)) !== void 0) return yield* Effect.fail(inputRequired("Provide --phase from the invitation phases."));
55416
+ if (new Set(requested).size !== requested.length) return yield* Effect.fail(inputRequired("Provide each --phase at most once."));
55417
+ return {
55418
+ ...cursor === void 0 ? {} : { cursor },
55419
+ ...limit === void 0 ? {} : { limit },
55420
+ ...requested.length === 0 ? {} : { phases: requested }
55421
+ };
55422
+ });
55423
+ /**
55424
+ * The Organization of one invitation the caller can already see.
55425
+ *
55426
+ * The grantee's bounded own-offer projection is what resolves an absent
55427
+ * `--org`, for `get` and `wait` alike: a caller who holds no Admin authority
55428
+ * still reads their own offer, and a saved read default is never consulted. The
55429
+ * row carries its own OrgId, and a row the caller cannot see is refused rather
55430
+ * than probed in another scope.
55431
+ *
55432
+ * The walk follows the versioned cursor until the row is found or the pages
55433
+ * end. One 100-row page is not the projection: an invitation the caller can see
55434
+ * would otherwise refuse only because it is not the newest.
55435
+ */
55436
+ const resolveOwningOrganization = (client, invitationId) => Effect.gen(function* () {
55437
+ let cursor;
55438
+ for (;;) {
55439
+ const page = yield* Effect.promise((signal) => client.orgs.invitations(cursor === void 0 ? { limit: OWN_OFFER_PAGE_LIMIT } : {
55440
+ limit: OWN_OFFER_PAGE_LIMIT,
55441
+ cursor
55442
+ }, { signal })).pipe(Effect.flatMap(resultValue));
55443
+ const match = page.items.find((invitation) => invitation.id === invitationId);
55444
+ if (match !== void 0) return match.orgId;
55445
+ if (page.nextCursor === null) return yield* Effect.fail(Errors.invalidInput("invitationId", "The invitation is not in your available offers. Provide --org to read it directly."));
55446
+ cursor = page.nextCursor;
55447
+ }
55448
+ });
55449
+ /** The exact scoped bundle a read, transition, or write is about to use. */
55450
+ const scopedInvitations = (client, organizationId) => client.org(organizationId).invitations;
55451
+ const scopedRead = (client, organizationId, invitationId) => Effect.promise((signal) => scopedInvitations(client, organizationId).get({ invitationId }, { signal })).pipe(Effect.flatMap(resultValue));
55452
+ const settled = (invitation) => SETTLED_PHASES.has(invitation.phase) || invitation.recovery !== null && invitation.recovery.action !== "wait";
55453
+ const invitationLines = (invitation) => [JSON.stringify({ invitation })];
55454
+ const waitTimeout = () => new CliFailure({
55455
+ code: "NETWORK_ERROR",
55456
+ message: "The invitation did not settle within the command deadline.",
55457
+ exitCode: 5,
55458
+ outcome: "failed"
55459
+ });
55460
+ /** The same dependency failure, named for the phase it happened in. */
55461
+ const resolutionTimeout = () => new CliFailure({
55462
+ code: "NETWORK_ERROR",
55463
+ message: "The invitation's Organization could not be resolved within the command deadline.",
55464
+ exitCode: 5,
55465
+ outcome: "failed"
55466
+ });
55467
+ const deadlineFailure = (message) => new CliFailure({
55468
+ code: "NETWORK_ERROR",
55469
+ message,
55470
+ exitCode: 5,
55471
+ outcome: "failed"
55472
+ });
55473
+ /** Bound one scoped operation by the command's own deadline; the backend keeps its state. */
55474
+ const withinDeadline = (effect, timeoutSeconds, message) => Effect.gen(function* () {
55475
+ const settledResult = yield* effect.pipe(Effect.timeoutOption(timeoutSeconds * 1e3));
55476
+ if (Option.isNone(settledResult)) return yield* Effect.fail(deadlineFailure(message));
55477
+ return settledResult.value;
55478
+ });
55479
+ /**
55480
+ * `capxul org invite list|get|wait|review`.
55481
+ *
55482
+ * `list` is the authenticated grantee's own offers. With `--org` it is the
55483
+ * Admin's view of that Organization's invitations instead: one explicit scope,
55484
+ * never a saved read default, and never a silent change of what plain `list`
55485
+ * returns. `--mine` stays the own-offer projection's declared no-op; combined
55486
+ * with `--org` it refuses, because the scoped route has no invitee filter and
55487
+ * ignoring the flag would return a wider view than the operator asked for.
55488
+ *
55489
+ * `get` and `wait` read one invitation in the Organization named by `--org`,
55490
+ * or resolved from the offer's own row when `--org` is absent. A wait only
55491
+ * reads: it never retries, resends, or accepts.
55492
+ */
55493
+ const readInvitation = (config, action, input) => {
55494
+ const timeout = readOptionalTimeout(input.timeoutSeconds);
55495
+ return Effect.gen(function* () {
55496
+ const bounds = yield* readPageBounds(input);
55497
+ const invitationId = action === "list" ? null : yield* readInvitationId(input.invitationId);
55498
+ const explicitOrg = yield* readExactOrg(input.orgs ?? [], "optional");
55499
+ if (action === "review" && explicitOrg === null) return yield* Effect.fail(inputRequired("Provide --org to review this invitation."));
55500
+ if (action === "list" && input.mine === true && explicitOrg !== null) return yield* Effect.fail(inputRequired("Provide either --mine or --org; the Organization list is not filtered to you."));
55501
+ const timeoutSeconds = yield* timeout;
55502
+ const { client } = yield* restoreAuthenticatedClient(config, input.email);
55503
+ if (action === "list") return listResult(yield* Effect.promise((signal) => explicitOrg === null ? client.orgs.invitations(bounds, { signal }) : client.org(explicitOrg).invitations.list(bounds, { signal })).pipe(Effect.flatMap(resultValue)));
55504
+ if (invitationId === null) return yield* Effect.fail(inputRequired("Provide --invitation-id."));
55505
+ const resolveOrganizationId = () => explicitOrg === null ? resolveOwningOrganization(client, invitationId) : Effect.succeed(explicitOrg);
55506
+ if (action === "get" || action === "review") {
55507
+ const invitation = yield* withinDeadline(Effect.gen(function* () {
55508
+ const resolved = action === "review" ? explicitOrg : yield* resolveOrganizationId();
55509
+ if (resolved === null) return yield* Effect.fail(inputRequired("Provide --org."));
55510
+ return yield* scopedRead(client, resolved, invitationId);
55511
+ }), timeoutSeconds, "The invitation read did not settle within the command deadline.");
55512
+ return {
55513
+ data: action === "review" ? invitation : { invitation },
55514
+ lines: invitationLines(invitation)
55515
+ };
55516
+ }
55517
+ let resolving = true;
55518
+ const observed = yield* Effect.gen(function* () {
55519
+ const resolved = yield* resolveOrganizationId();
55520
+ resolving = false;
55521
+ let invitation = yield* scopedRead(client, resolved, invitationId);
55522
+ while (!settled(invitation)) {
55523
+ yield* Effect.sleep(1e3);
55524
+ invitation = yield* scopedRead(client, resolved, invitationId);
55525
+ }
55526
+ return invitation;
55527
+ }).pipe(Effect.timeoutOption(timeoutSeconds * 1e3));
55528
+ if (Option.isNone(observed)) return yield* Effect.fail(resolving ? resolutionTimeout() : waitTimeout());
55529
+ return {
55530
+ data: { invitation: observed.value },
55531
+ lines: invitationLines(observed.value)
55532
+ };
55533
+ });
55534
+ };
55535
+ function listResult(page) {
55536
+ return {
55537
+ data: {
55538
+ invitations: page.items,
55539
+ nextCursor: page.nextCursor,
55540
+ observedAt: page.observedAt
55541
+ },
55542
+ lines: [...page.items.map((invitation) => JSON.stringify(invitation)), ...page.nextCursor === null ? [] : [`Next cursor: ${JSON.stringify(page.nextCursor)}`]]
55543
+ };
55544
+ }
55545
+ /**
55546
+ * `capxul org invite decline|cancel|resend|retry`.
55547
+ *
55548
+ * Every transition needs the exact Organization in both modes: an invitation
55549
+ * write never consults the read default. A terminal writes `--confirm`; a
55550
+ * terminal run always sees the resolved current offer and answers a default-no
55551
+ * prompt. The command forwards no acceptance consent: these four transitions
55552
+ * end or retry work that already exists.
55553
+ */
55554
+ const transitionInvitation = (config, action, input) => Effect.gen(function* () {
55555
+ const invitationId = yield* readInvitationId(input.invitationId);
55556
+ const organizationId = yield* readExactOrg(input.orgs, "required");
55557
+ if (organizationId === null) return yield* Effect.fail(inputRequired("Provide --org."));
55558
+ const timeoutSeconds = yield* readWriteTimeout(input.timeoutSeconds);
55559
+ const interactivelyConfirm = yield* requireWriteConfirmation(input.confirm);
55560
+ const { client } = yield* restoreAuthenticatedClient(config, input.email);
55561
+ if (interactivelyConfirm) {
55562
+ const current = yield* scopedRead(client, organizationId, invitationId);
55563
+ yield* confirmWrite({
55564
+ lines: [`${action} invitation ${JSON.stringify(invitationId)} in Organization ${JSON.stringify(String(organizationId))}`, `Current phase: ${current.phase}; delivery: ${current.delivery}`],
55565
+ question: `${action === "resend" ? "Resend" : action.charAt(0).toUpperCase() + action.slice(1)} this invitation?`
55566
+ });
55567
+ }
55568
+ const invitation = yield* withinDeadline(Effect.promise((signal) => scopedInvitations(client, organizationId)[action]({ invitationId }, { signal })).pipe(Effect.flatMap(resultValue)), timeoutSeconds, "The invitation transition did not settle within the command deadline.");
55569
+ return {
55570
+ data: { invitation },
55571
+ lines: invitationLines(invitation)
55572
+ };
55573
+ });
55574
+ /** The wire's bytes32 form, lowercased: the digest an acceptance states. */
55575
+ const readOfferDigest = (value, flag) => Effect.gen(function* () {
55576
+ const trimmed = value?.trim() ?? "";
55577
+ if (!/^0x[0-9a-fA-F]{64}$/.test(trimmed)) return yield* Effect.fail(inputRequired(`Provide ${flag} as a 0x-prefixed 32-byte digest.`));
55578
+ return trimmed.toLowerCase();
55579
+ });
55580
+ /**
55581
+ * `capxul org invite accept --invitation-id I --offer-digest D`.
55582
+ *
55583
+ * Acceptance is the grantee's explicit consent, so the digest is the whole
55584
+ * write: the command forwards the digest the operator states and never invents
55585
+ * one. A noninteractive run must state it (`--offer-digest`, with `--confirm`);
55586
+ * an interactive run may leave it out, and then the command reads the offer and
55587
+ * fills D from the exact digest it displayed, behind the same preview/confirm
55588
+ * prompt the other writes use. A stated digest is never replaced by the
55589
+ * displayed one — a mismatch is the backend's typed refusal, not a silent
55590
+ * rewrite of what the operator consented to.
55591
+ *
55592
+ * The command signs nothing: the grant is executed by the deployment's
55593
+ * technical granter after the consent commits. Only the exact grantee can
55594
+ * accept; Admin access to the offer does not grant consent authority.
55595
+ */
55596
+ const acceptInvitation = (config, input) => Effect.gen(function* () {
55597
+ const invitationId = yield* readInvitationId(input.invitationId);
55598
+ const organizationId = yield* readExactOrg(input.orgs, "required");
55599
+ if (organizationId === null) return yield* Effect.fail(inputRequired("Provide --org."));
55600
+ const timeoutSeconds = yield* readWriteTimeout(input.timeoutSeconds);
55601
+ const runtime = yield* CliRuntime;
55602
+ const interactive = runtime.tty && !runtime.ci && runtime.mode !== "json";
55603
+ const canonical = input.offerDigest === void 0 ? null : yield* readOfferDigest(input.offerDigest, "--offer-digest");
55604
+ const compatibility = input.expectedOfferDigest === void 0 ? null : yield* readOfferDigest(input.expectedOfferDigest, "--expected-offer");
55605
+ if (canonical !== null && compatibility !== null && canonical !== compatibility) return yield* Effect.fail(inputRequired("Provide matching --offer-digest and --expected-offer values."));
55606
+ const stated = canonical ?? compatibility;
55607
+ if (stated === null && !interactive) return yield* Effect.fail(inputRequired("Provide --offer-digest to accept this invitation."));
55608
+ const interactivelyConfirm = yield* requireWriteConfirmation(input.confirm);
55609
+ const { client } = yield* restoreAuthenticatedClient(config, input.email);
55610
+ let expectedOfferDigest = stated;
55611
+ if (interactivelyConfirm) {
55612
+ const current = yield* scopedRead(client, organizationId, invitationId);
55613
+ yield* confirmWrite({
55614
+ lines: [
55615
+ `Accept invitation ${JSON.stringify(invitationId)} in Organization ${JSON.stringify(String(organizationId))}`,
55616
+ `Current offer digest: ${current.offerDigest}`,
55617
+ `Current phase: ${current.phase}; expires at: ${String(current.expiresAt)}`
55618
+ ],
55619
+ question: "Accept this offer?"
55620
+ });
55621
+ expectedOfferDigest ??= current.offerDigest;
55622
+ }
55623
+ if (expectedOfferDigest === null) return yield* Effect.fail(inputRequired("Provide --offer-digest to accept."));
55624
+ const invitation = yield* withinDeadline(Effect.promise((signal) => scopedInvitations(client, organizationId).accept({
55625
+ invitationId,
55626
+ expectedOfferDigest
55627
+ }, { signal })).pipe(Effect.flatMap(resultValue)), timeoutSeconds, "The invitation acceptance did not settle within the command deadline.");
55628
+ return {
55629
+ data: invitation,
55630
+ lines: invitationLines(invitation)
55631
+ };
55632
+ });
55633
+ /**
55634
+ * An exact zero, in the helper's own zero form (`parse-money.ts` tests the same
55635
+ * shape, with commas already stripped). It discriminates a zero from a negative
55636
+ * value, which the helper reports under the same `non-positive` reason.
55637
+ */
55638
+ const ZERO_CAP = /^0+(?:\.0+)?$/;
55639
+ /**
55640
+ * One finite Budget cap.
55641
+ *
55642
+ * The value goes through the shared amount helper first, so the owner keeps its
55643
+ * grammar, its 100-character guard, its precision, and its sign rule. Exactly
55644
+ * one of the helper's verdicts does not apply to a Budget cap: `non-positive`
55645
+ * for an exact zero, which `d9.md` §3 set B permits. That verdict, and only
55646
+ * that one, becomes the legal zero cap.
55647
+ */
55648
+ const readFiniteLimit = (raw, metadata) => Effect.gen(function* () {
55649
+ const parsed = yield* Effect.try({
55650
+ try: () => parseAssetAmount(raw, metadata),
55651
+ catch: () => inputRequired("Provide --limit as an exact decimal quantity.")
55652
+ });
55653
+ if (!("kind" in parsed)) return parsed.value;
55654
+ if (parsed.reason === "non-positive" && ZERO_CAP.test(raw.trim().replaceAll(",", ""))) return "0";
55655
+ return yield* Effect.fail(inputRequired("Provide --limit as an exact decimal quantity."));
55656
+ });
55657
+ /** One exact asset: a catalog key or the exact AssetId those keys name. */
55658
+ function resolveAssetId(raw) {
55659
+ const candidate = raw.trim();
55660
+ const named = Object.entries(ACCEPTED_ASSETS).find(([key]) => key === candidate);
55661
+ if (named !== void 0) return named[1];
55662
+ return Object.values(ACCEPTED_ASSETS).find((assetId) => assetId === candidate) ?? null;
55663
+ }
55664
+ /** The complete `B` policy flag set for exactly one new Budget. */
55665
+ const readNewBudgetGrant = (flags) => Effect.gen(function* () {
55666
+ const label = flags.label.trim();
55667
+ if (label.length === 0 || label.length > 128) return yield* Effect.fail(inputRequired("Provide --new-budget-name with 1 to 128 characters."));
55668
+ const rawAsset = flags.asset?.trim() ?? "";
55669
+ if (rawAsset.length === 0) return yield* Effect.fail(inputRequired("Provide --asset for --new-budget-name."));
55670
+ const assetId = resolveAssetId(rawAsset);
55671
+ if (assetId === null) return yield* Effect.fail(inputRequired("Provide --asset as CAPXUL_TEST_USDC, CAPXUL_TEST_USDT, CAPXUL_TEST_WETH, or one exact AssetId."));
55672
+ const metadata = yield* Effect.try({
55673
+ try: () => requireAsset(assetId),
55674
+ catch: () => inputRequired("Provide an admitted --asset.")
55675
+ });
55676
+ if (flags.unlimited === (flags.limit !== void 0)) return yield* Effect.fail(inputRequired("Provide exactly one of --limit or --unlimited."));
55677
+ let limit = null;
55678
+ if (!flags.unlimited) limit = yield* readFiniteLimit(String(flags.limit), metadata);
55679
+ const recipients = yield* readRecipients(flags);
55680
+ const actions = yield* readActions(flags.actions);
55681
+ return {
55682
+ type: "new_budget",
55683
+ label,
55684
+ intent: {
55685
+ type: "budget",
55686
+ asset: assetId,
55687
+ limit,
55688
+ scope: {
55689
+ recipients,
55690
+ actions
55691
+ }
55692
+ }
55693
+ };
55694
+ });
55695
+ /** 1–32 distinct recipient Accounts, or the explicit anyone scope. */
55696
+ function readRecipients(flags) {
55697
+ return Effect.gen(function* () {
55698
+ if (flags.anyRecipient === flags.recipientAccounts.length > 0) return yield* Effect.fail(inputRequired("Provide exactly one of --any-recipient or --recipient-account."));
55699
+ if (flags.anyRecipient) return { type: "anyone" };
55700
+ const accounts = [...new Set(flags.recipientAccounts)];
55701
+ if (accounts.length > 32) return yield* Effect.fail(inputRequired("Provide at most 32 --recipient-account values."));
55702
+ return {
55703
+ type: "allowlist",
55704
+ accounts: yield* Effect.forEach(accounts, (accountId) => Effect.try({
55705
+ try: () => toAccountId(accountId),
55706
+ catch: () => inputRequired("Provide each --recipient-account as an Account ID.")
55707
+ }))
55708
+ };
55709
+ });
55710
+ }
55711
+ /** One or both distinct action codes, in the canonical pay-then-commitments order. */
55712
+ function readActions(raw) {
55713
+ return Effect.gen(function* () {
55714
+ const requested = raw.flatMap((value) => value.split(",")).map((value) => value.trim());
55715
+ if (requested.find((value) => value !== "pay" && value !== "commitments") !== void 0 || requested.some((value) => value.length === 0)) return yield* Effect.fail(inputRequired("Provide --actions as pay, commitments, or both."));
55716
+ const [first, ...rest] = ["pay", "commitments"].filter((action) => requested.some((value) => value === action));
55717
+ if (first === void 0) return yield* Effect.fail(inputRequired("Provide --actions as pay, commitments, or both."));
55718
+ return [first, ...rest];
55719
+ });
55720
+ }
55721
+ /**
55722
+ * The new-Budget policy flags that were supplied. None of them means anything
55723
+ * without `--new-budget-name`: a request that silently ignored them would
55724
+ * authorize an offer the operator never stated.
55725
+ */
55726
+ function orphanedNewBudgetFlags(input) {
55727
+ if (input.newBudgetName !== void 0) return [];
55728
+ return [
55729
+ ...input.asset === void 0 ? [] : ["--asset"],
55730
+ ...input.limit === void 0 ? [] : ["--limit"],
55731
+ ...input.unlimited ? ["--unlimited"] : [],
55732
+ ...input.recipientAccounts.length === 0 ? [] : ["--recipient-account"],
55733
+ ...input.anyRecipient ? ["--any-recipient"] : [],
55734
+ ...input.actions.length === 0 ? [] : ["--actions"]
55735
+ ];
55736
+ }
55737
+ /** The bounded request one invite submits, with every grant in canonical order. */
55738
+ const buildInviteRequest = (input) => Effect.gen(function* () {
55739
+ const orphaned = orphanedNewBudgetFlags(input);
55740
+ if (orphaned.length > 0) return yield* Effect.fail(inputRequired(`Provide --new-budget-name with ${orphaned.join(", ")}.`));
55741
+ const toEmail = input.toEmail?.trim() ?? "";
55742
+ const accountId = input.accountId?.trim() ?? "";
55743
+ if (toEmail.length === 0 === (accountId.length === 0)) return yield* Effect.fail(inputRequired("Provide exactly one of --to-email or --account-id."));
55744
+ const grants = [];
55745
+ for (const budgetId of new Set(input.budgetIds)) {
55746
+ if (budgetId.trim().length === 0) return yield* Effect.fail(inputRequired("Provide each --budget-id as a nonempty ID."));
55747
+ grants.push({
55748
+ type: "budget",
55749
+ budgetId: toPermissionId(budgetId.trim())
55750
+ });
55751
+ }
55752
+ for (const permissionId of new Set(input.permissionIds)) {
55753
+ if (permissionId.trim().length === 0) return yield* Effect.fail(inputRequired("Provide each --permission-id as a nonempty ID."));
55754
+ grants.push({
55755
+ type: "managePeople",
55756
+ permissionId: toPermissionId(permissionId.trim())
55757
+ });
55758
+ }
55759
+ if (input.newBudgetName !== void 0) grants.push(yield* readNewBudgetGrant({
55760
+ label: input.newBudgetName,
55761
+ asset: input.asset,
55762
+ limit: input.limit,
55763
+ unlimited: input.unlimited,
55764
+ recipientAccounts: input.recipientAccounts,
55765
+ anyRecipient: input.anyRecipient,
55766
+ actions: input.actions
55767
+ }));
55768
+ if (grants.length === 0) return yield* Effect.fail(inputRequired("Provide at least one of --budget-id, --permission-id, or --new-budget-name."));
55769
+ if (grants.length > 16) return yield* Effect.fail(inputRequired("Provide at most 16 grants."));
55770
+ return {
55771
+ to: toEmail.length > 0 ? {
55772
+ type: "email",
55773
+ email: toEmail
55774
+ } : {
55775
+ type: "account",
55776
+ accountId: yield* Effect.try({
55777
+ try: () => toAccountId(accountId),
55778
+ catch: () => inputRequired("Provide a valid --account-id.")
55779
+ })
55780
+ },
55781
+ grants
55782
+ };
55783
+ });
55784
+ const readRequestKey = (value) => Effect.gen(function* () {
55785
+ const trimmed = value?.trim() ?? "";
55786
+ if (trimmed.length === 0 || trimmed.length > 128 || !/^[A-Za-z0-9_.-]+$/.test(trimmed)) return yield* Effect.fail(inputRequired("Provide --request-key as 1 to 128 letters, digits, underscore, hyphen, or dot."));
55787
+ return trimmed;
55788
+ });
55789
+ /**
55790
+ * The D9 §6 authoring progress line: the durable `{invitationId, commandId}`
55791
+ * pair, written before the configuration digest exists so a lost response is
55792
+ * recoverable by the same identity.
55793
+ */
55794
+ const preparedLine = (mode, prepared) => mode === "json" ? `${JSON.stringify({
55795
+ type: "invitation.prepared",
55796
+ ...prepared
55797
+ })}\n` : `Invitation prepared: invitationId=${prepared.invitationId} commandId=${prepared.commandId}\n`;
55798
+ /**
55799
+ * `capxul org member invite`.
55800
+ *
55801
+ * `--preview` is a read: it shows the exact resolved offer, creates nothing, and
55802
+ * opens no browser. The write needs `--org` in both modes, `--confirm` and
55803
+ * `--request-key` non-interactively, and it prints the key it used before it
55804
+ * submits, so a lost response is replayed with the same identity instead of
55805
+ * authoring a second offer.
55806
+ *
55807
+ * The write also executes the offer's configuration hop: the Admin's personal
55808
+ * embedded wallet signs the stored configure UserOperation through the same
55809
+ * command-scoped browser bridge the other Organization writes use. The bridge
55810
+ * is composed only after confirmation, and the durable identity reaches stderr
55811
+ * before any digest is forwarded.
55812
+ */
55813
+ const inviteInvitationMember = (config, input) => Effect.gen(function* () {
55814
+ const request = yield* buildInviteRequest(input);
55815
+ const organizationId = yield* readExactOrg(input.orgs, "required");
55816
+ if (organizationId === null) return yield* Effect.fail(inputRequired("Provide --org."));
55817
+ const timeoutSeconds = yield* readWriteTimeout(input.timeoutSeconds);
55818
+ const runtime = yield* CliRuntime;
55819
+ const interactive = runtime.tty && !runtime.ci && runtime.mode !== "json";
55820
+ if (!input.preview && !interactive) {
55821
+ yield* readRequestKey(input.requestKey);
55822
+ yield* requireWriteConfirmation(input.confirm);
55823
+ }
55824
+ const opened = yield* openAuthenticatedSession(config, input.email);
55825
+ if (input.preview) {
55826
+ const { client } = yield* restoreVerifiedClient(config, opened);
55827
+ const preview = yield* withinDeadline(Effect.promise((signal) => scopedInvitations(client, organizationId).preview(request, { signal })).pipe(Effect.flatMap(resultValue)), timeoutSeconds, "The invitation preview did not settle within the command deadline.");
55828
+ return previewResult(preview);
55829
+ }
55830
+ const requestKey = input.requestKey === void 0 ? (yield* generateRequestKey()).trim() : yield* readRequestKey(input.requestKey);
55831
+ if (interactive) {
55832
+ yield* writeStderr(`${JSON.stringify({
55833
+ type: "invitation.requestKey",
55834
+ requestKey
55835
+ })}\n`);
55836
+ const { client } = yield* restoreVerifiedClient(config, opened);
55837
+ const preview = yield* withinDeadline(Effect.promise((signal) => scopedInvitations(client, organizationId).preview(request, { signal })).pipe(Effect.flatMap(resultValue)), timeoutSeconds, "The invitation preview did not settle within the command deadline.");
55838
+ yield* confirmWrite({
55839
+ lines: [`Invite ${JSON.stringify(request.to)} to Organization ${JSON.stringify(String(organizationId))}`, ...previewLines(preview)],
55840
+ question: "Authorize this invitation?"
55841
+ });
55842
+ }
55843
+ const context = yield* Effect.context();
55844
+ const run = Effect.runPromiseWith(context);
55845
+ const deadline = new AbortController();
55846
+ const openfortSignerFactory = yield* organizationWriteSigner(config, opened, run, async () => {
55847
+ const cancelled = cancelledWrite(CAPXUL_OPERATIONS.org.invitations.authorize, deadline.signal);
55848
+ if (cancelled !== null) throw cancelled;
55849
+ });
55850
+ const { client } = yield* restoreVerifiedClient(config, opened, {
55851
+ requirement: "deployed",
55852
+ openfortSignerFactory
55853
+ });
55854
+ const invitations = scopedInvitations(client, organizationId);
55855
+ const invitation = yield* withinDeadline(Effect.promise((signal) => {
55856
+ const forwardAbort = () => deadline.abort();
55857
+ if (signal.aborted) forwardAbort();
55858
+ else signal.addEventListener("abort", forwardAbort, { once: true });
55859
+ return invitations.invite({
55860
+ requestKey,
55861
+ ...request
55862
+ }, {
55863
+ signal,
55864
+ onPrepared: async (prepared) => {
55865
+ await run(writeStderr(preparedLine(runtime.mode, prepared)));
55866
+ }
55867
+ }).finally(() => signal.removeEventListener("abort", forwardAbort));
55868
+ }).pipe(Effect.flatMap(resultValue)), timeoutSeconds, "The invitation did not settle within the command deadline.");
55869
+ return {
55870
+ data: { invitation },
55871
+ lines: invitationLines(invitation)
55872
+ };
55873
+ });
55874
+ const generateRequestKey = () => Effect.gen(function* () {
55875
+ return yield* (yield* Crypto.Crypto).randomUUIDv4;
55876
+ });
55877
+ const previewResult = (preview) => ({
55878
+ data: { preview },
55879
+ lines: previewLines(preview)
55880
+ });
55881
+ const previewLines = (preview) => [
55882
+ `Grantee: ${JSON.stringify({
55883
+ safeAddress: preview.grantee.safeAddress,
55884
+ partyId: preview.grantee.partyId,
55885
+ accountReady: preview.grantee.accountReady
55886
+ })}`,
55887
+ ...preview.grants.map((grant) => `Offer: ${JSON.stringify(grant)}`),
55888
+ `Existing assignments: ${String(preview.existingAssignments.length)}`,
55889
+ `Includes management: ${String(preview.includesManagement)}`
55890
+ ];
55891
+ //#endregion
55892
+ //#region src/commands/org-invite.ts
55893
+ const emailFlag$2 = Flag.string("email").pipe(Flag.optional);
55894
+ const orgFlag$2 = Flag.string("org").pipe(Flag.withAlias("org-id"), Flag.atLeast(0));
55895
+ const invitationIdFlag = Flag.string("invitation-id").pipe(Flag.optional);
55896
+ const timeoutFlag$1 = Flag.integer("timeout-seconds").pipe(Flag.withDefault(120));
55897
+ const confirmFlag$2 = Flag.boolean("confirm").pipe(Flag.withDefault(false));
55898
+ const metadata$2 = (name) => ({
55899
+ name,
55900
+ capability: "organization",
55901
+ observation: "command"
55902
+ });
55903
+ /** Reads and transitions carry the exact invitation ID; only list does not. */
55904
+ const target = {
55905
+ email: emailFlag$2,
55906
+ orgs: orgFlag$2,
55907
+ invitationId: invitationIdFlag
55908
+ };
55909
+ const executeRead = (action, input) => runCommand({
55910
+ ...metadata$2(`org.invite.${action}`),
55911
+ network: true
55912
+ }, (config) => readInvitation(config, action, input));
55913
+ const executeTransition = (action, input) => runCommand({
55914
+ ...metadata$2(`org.invite.${action}`),
55915
+ network: true
55916
+ }, (config) => transitionInvitation(config, action, input));
55917
+ const list$1 = Command.make("list", {
55918
+ email: emailFlag$2,
55919
+ orgs: orgFlag$2,
55920
+ limit: Flag.integer("limit").pipe(Flag.optional),
55921
+ cursor: Flag.string("cursor").pipe(Flag.optional),
55922
+ phases: Flag.string("phase").pipe(Flag.atLeast(0)),
55923
+ mine: Flag.boolean("mine").pipe(Flag.withDescription("Your own offers; accepted as a no-op on the plain list"), Flag.withDefault(false))
55924
+ }, ({ email, orgs, limit, cursor, phases, mine }) => executeRead("list", {
55925
+ email: Option.getOrUndefined(email),
55926
+ orgs,
55927
+ limit: Option.getOrUndefined(limit),
55928
+ cursor: Option.getOrUndefined(cursor),
55929
+ phases: phases.length === 0 ? void 0 : phases,
55930
+ mine
55931
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$2("org.invite.list")), Command.withDescription("List your own invitation offers, or an Organization's with --org; the result carries invitations, nextCursor, and observedAt"));
55932
+ const get = Command.make("get", {
55933
+ ...target,
55934
+ timeoutSeconds: timeoutFlag$1
55935
+ }, ({ email, orgs, invitationId, timeoutSeconds }) => executeRead("get", {
55936
+ email: Option.getOrUndefined(email),
55937
+ orgs,
55938
+ invitationId: Option.getOrUndefined(invitationId),
55939
+ timeoutSeconds
55940
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$2("org.invite.get")), Command.withDescription("Read one invitation, in --org or its own resolved Organization"));
55941
+ const wait$1 = Command.make("wait", {
55942
+ ...target,
55943
+ timeoutSeconds: timeoutFlag$1
55944
+ }, ({ email, orgs, invitationId, timeoutSeconds }) => executeRead("wait", {
55945
+ email: Option.getOrUndefined(email),
55946
+ orgs,
55947
+ invitationId: Option.getOrUndefined(invitationId),
55948
+ timeoutSeconds
55949
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$2("org.invite.wait")), Command.withDescription("Poll one invitation read until it settles; never writes"));
55950
+ const review = Command.make("review", {
55951
+ ...target,
55952
+ timeoutSeconds: timeoutFlag$1
55953
+ }, ({ email, orgs, invitationId, timeoutSeconds }) => executeRead("review", {
55954
+ email: Option.getOrUndefined(email),
55955
+ orgs,
55956
+ invitationId: Option.getOrUndefined(invitationId),
55957
+ timeoutSeconds
55958
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$2("org.invite.review")), Command.withDescription("Review one exact current offer without writing or signing"));
55959
+ const transition = (name) => Command.make(name, {
55960
+ ...target,
55961
+ confirm: confirmFlag$2,
55962
+ timeoutSeconds: timeoutFlag$1
55963
+ }, ({ email, orgs, invitationId, confirm, timeoutSeconds }) => executeTransition(name, {
55964
+ email: Option.getOrUndefined(email),
55965
+ orgs,
55966
+ invitationId: Option.getOrUndefined(invitationId),
55967
+ confirm,
55968
+ timeoutSeconds
55969
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$2(`org.invite.${name}`)), Command.withDescription(name === "decline" ? "Decline your own pending offer; starts its cleanup" : name === "cancel" ? "Cancel a pending offer you administer; starts its cleanup" : name === "resend" ? "Retry delivery of a verified pending offer; never extends expiry" : "Resume the current offer state; creates no new consent or grant"));
55970
+ /**
55971
+ * The one consent command. `--offer-digest` is canonical; `--expected-offer`
55972
+ * remains a compatibility spelling and conflicting values refuse.
55973
+ */
55974
+ const accept = Command.make("accept", {
55975
+ ...target,
55976
+ offerDigest: Flag.string("offer-digest").pipe(Flag.optional),
55977
+ expectedOffer: Flag.string("expected-offer").pipe(Flag.optional),
55978
+ confirm: confirmFlag$2,
55979
+ timeoutSeconds: timeoutFlag$1
55980
+ }, ({ email, orgs, invitationId, offerDigest, expectedOffer, confirm, timeoutSeconds }) => runCommand({
55981
+ ...metadata$2("org.invite.accept"),
55982
+ network: true
55983
+ }, (config) => acceptInvitation(config, {
55984
+ email: Option.getOrUndefined(email),
55985
+ orgs,
55986
+ invitationId: Option.getOrUndefined(invitationId),
55987
+ offerDigest: Option.getOrUndefined(offerDigest),
55988
+ expectedOfferDigest: Option.getOrUndefined(expectedOffer),
55989
+ confirm,
55990
+ timeoutSeconds
55991
+ }))).pipe(Command.annotate(CliCommandMetadata, metadata$2("org.invite.accept")), Command.withDescription("Accept one exact offer by its current digest and start its grant; noninteractive runs must supply --offer-digest"));
55992
+ const invite = Command.make("invite").pipe(Command.annotate(CliCommandMetadata, metadata$2(CAPXUL_OPERATIONS.org.invite)), Command.withDescription("Invitation reads, the consent commit, and the transitions"), Command.withSubcommands([
55993
+ list$1,
55994
+ get,
55995
+ wait$1,
55996
+ review,
55997
+ accept,
55998
+ transition("decline"),
55999
+ transition("cancel"),
56000
+ transition("resend"),
56001
+ transition("retry")
56002
+ ]));
56003
+ const newBudgetFlags = {
56004
+ newBudgetName: Flag.string("new-budget-name").pipe(Flag.optional),
56005
+ asset: Flag.string("asset").pipe(Flag.optional),
56006
+ limit: Flag.string("limit").pipe(Flag.optional),
56007
+ unlimited: Flag.boolean("unlimited").pipe(Flag.withDefault(false)),
56008
+ recipientAccounts: Flag.string("recipient-account").pipe(Flag.atLeast(0)),
56009
+ anyRecipient: Flag.boolean("any-recipient").pipe(Flag.withDefault(false)),
56010
+ actions: Flag.string("actions").pipe(Flag.atLeast(0))
56011
+ };
56012
+ const memberInvite = Command.make("invite", {
56013
+ email: emailFlag$2,
56014
+ orgs: orgFlag$2,
56015
+ toEmail: Flag.string("to-email").pipe(Flag.optional),
56016
+ accountId: Flag.string("account-id").pipe(Flag.optional),
56017
+ budgetIds: Flag.string("budget-id").pipe(Flag.atLeast(0)),
56018
+ permissionIds: Flag.string("permission-id").pipe(Flag.atLeast(0)),
56019
+ ...newBudgetFlags,
56020
+ requestKey: Flag.string("request-key").pipe(Flag.optional),
56021
+ preview: Flag.boolean("preview").pipe(Flag.withDefault(false)),
56022
+ confirm: confirmFlag$2,
56023
+ timeoutSeconds: timeoutFlag$1
56024
+ }, ({ email, orgs, toEmail, accountId, budgetIds, permissionIds, newBudgetName, asset, limit, unlimited, recipientAccounts, anyRecipient, actions, requestKey, preview, confirm, timeoutSeconds }) => {
56025
+ return runCommand({
56026
+ ...metadata$2("org.member.invite"),
56027
+ network: true
56028
+ }, (config) => inviteInvitationMember(config, {
56029
+ email: Option.getOrUndefined(email),
56030
+ orgs,
56031
+ toEmail: Option.getOrUndefined(toEmail),
56032
+ accountId: Option.getOrUndefined(accountId),
56033
+ budgetIds,
56034
+ permissionIds,
56035
+ newBudgetName: Option.getOrUndefined(newBudgetName),
56036
+ asset: Option.getOrUndefined(asset),
56037
+ limit: Option.getOrUndefined(limit),
56038
+ unlimited,
56039
+ recipientAccounts,
56040
+ anyRecipient,
56041
+ actions,
56042
+ requestKey: Option.getOrUndefined(requestKey),
56043
+ preview,
56044
+ confirm,
56045
+ timeoutSeconds
56046
+ }));
56047
+ }).pipe(Command.annotate(CliCommandMetadata, metadata$2("org.member.invite")), Command.withDescription("Authorize one exact invitation offer; --preview writes nothing"));
56048
+ //#endregion
56049
+ //#region src/platform/bounded-text.ts
56050
+ var UpdateUnavailable = class extends Data.TaggedError("UpdateUnavailable") {};
56051
+ const boundedText = (stream, limit) => Effect.suspend(() => {
56052
+ let size = 0;
56053
+ return stream.pipe(Stream.mapEffect((chunk) => {
56054
+ size += chunk.byteLength;
56055
+ return size > limit ? Effect.fail(new UpdateUnavailable()) : Effect.succeed(chunk);
56056
+ }), Stream.decodeText(), Stream.runFold(() => "", (text, chunk) => text + chunk));
56057
+ });
56058
+ //#endregion
56059
+ //#region src/contact.ts
56060
+ const AddInputJson = Schema.Struct({
56061
+ ref: Schema.Union([
56062
+ Schema.Struct({
56063
+ kind: Schema.Literal("handle"),
56064
+ handle: Schema.NonEmptyString
56065
+ }),
56066
+ Schema.Struct({
56067
+ kind: Schema.Literal("email"),
56068
+ email: Schema.NonEmptyString
56069
+ }),
56070
+ Schema.Struct({
56071
+ kind: Schema.Literal("address"),
56072
+ address: Schema.NonEmptyString
56073
+ }),
56074
+ Schema.Struct({
56075
+ kind: Schema.Literal("party"),
56076
+ partyId: Schema.NonEmptyString
56077
+ })
56078
+ ]),
56079
+ label: Schema.optional(Schema.String)
56080
+ });
56081
+ const parseAddInput = (text) => Schema.decodeUnknownEffect(Schema.fromJsonString(AddInputJson), { onExcessProperty: "error" })(text).pipe(Effect.flatMap((value) => {
56082
+ try {
56083
+ const ref = value.ref.kind === "address" ? {
56084
+ kind: "address",
56085
+ address: toAddress(value.ref.address)
56086
+ } : value.ref.kind === "party" ? {
56087
+ kind: "party",
56088
+ partyId: toPartyId(value.ref.partyId)
56089
+ } : value.ref;
56090
+ return Effect.succeed({
56091
+ ref,
56092
+ ...value.label === void 0 ? {} : { label: value.label }
56093
+ });
56094
+ } catch (cause) {
56095
+ return Effect.fail(cause);
56096
+ }
56097
+ }), Effect.mapError(() => inputRequired("Provide --input with one valid AddressBookAddInput JSON object.")));
56098
+ const readAddInput = (source) => Effect.gen(function* () {
56099
+ if (source === void 0) {
56100
+ const runtime = yield* CliRuntime;
56101
+ if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired("Provide --input FILE|- with one AddressBookAddInput JSON object."));
56102
+ const kind = yield* Prompt.select({
56103
+ message: "Reference kind",
56104
+ choices: [
56105
+ "handle",
56106
+ "email",
56107
+ "address",
56108
+ "party"
56109
+ ].map((value) => ({
56110
+ title: value,
56111
+ value
56112
+ }))
56113
+ });
56114
+ const value = (yield* Prompt.text({ message: "Reference value" })).trim();
56115
+ const label = (yield* Prompt.text({ message: "Label (optional)" })).trim();
56116
+ return yield* parseAddInput(JSON.stringify({
56117
+ ref: kind === "handle" ? {
56118
+ kind,
56119
+ handle: value
56120
+ } : kind === "email" ? {
56121
+ kind,
56122
+ email: value
56123
+ } : kind === "address" ? {
56124
+ kind,
56125
+ address: value
56126
+ } : {
56127
+ kind,
56128
+ partyId: value
56129
+ },
56130
+ ...label === "" ? {} : { label }
56131
+ }));
56132
+ }
56133
+ const text = source === "-" ? yield* boundedText((yield* Stdio.Stdio).stdin, 65536) : yield* Effect.gen(function* () {
56134
+ const fs = yield* FileSystem.FileSystem;
56135
+ return yield* boundedText(fs.stream(source, { bytesToRead: 65537 }), 65536);
56136
+ });
56137
+ return yield* parseAddInput(text);
56138
+ }).pipe(Effect.mapError((error) => error instanceof CliFailure ? error : inputRequired("The contact input file could not be read or was too large.")));
56139
+ const partyId = (value) => Effect.try({
56140
+ try: () => toPartyId(value),
56141
+ catch: () => inputRequired("Provide a valid --entry-id.")
56142
+ });
56143
+ const selectedBook = (client, actor, orgs) => Effect.gen(function* () {
56144
+ const ids = [...new Set(orgs)];
56145
+ if (ids.length > 1) return yield* Effect.fail(inputRequired("Provide one exact Organization ID."));
56146
+ if (actor === "personal") {
56147
+ if (ids.length > 0) return yield* Effect.fail(inputRequired("Personal contacts do not accept --org."));
56148
+ return client.account.addressBook;
56149
+ }
56150
+ if (ids.length === 0) return yield* Effect.fail(inputRequired("Provide --org."));
56151
+ const organizationId = yield* Effect.try({
56152
+ try: () => toOrgId(ids[0]),
56153
+ catch: () => inputRequired("Provide a valid --org.")
56154
+ });
56155
+ return client.org(organizationId).addressBook;
56156
+ });
56157
+ const result = (value, lines) => ({
56158
+ data: value,
56159
+ lines
56160
+ });
56161
+ const requiredLabel = (value) => {
56162
+ const label = value?.trim() ?? "";
56163
+ return label.length === 0 ? Effect.fail(inputRequired("Provide --label.")) : Effect.succeed(label);
56164
+ };
56165
+ const actorLine = (orgs) => orgs[0] === void 0 ? "Actor: personal" : `Actor: Organization ${JSON.stringify(orgs[0])}`;
56166
+ const runContact = (config, action, input) => Effect.gen(function* () {
56167
+ const addInput = action === "add" ? yield* readAddInput(input.input) : void 0;
56168
+ const entryId = action === "get" || action === "label" || action === "hide" || action === "unhide" ? yield* partyId(input.entryId) : void 0;
56169
+ const label = action === "label" ? yield* requiredLabel(input.label) : void 0;
56170
+ const interactiveWrite = action === "add" || action === "label" || action === "hide" || action === "unhide" ? yield* requireWriteConfirmation(input.confirm) : false;
56171
+ const { client } = yield* restoreAuthenticatedClient(config, input.email);
56172
+ const book = yield* selectedBook(client, input.actor, input.orgs);
56173
+ if (action === "add") {
56174
+ if (interactiveWrite) yield* confirmWrite({
56175
+ lines: [actorLine(input.orgs), `Reference: ${JSON.stringify(addInput.ref)}`],
56176
+ question: "Add this contact?"
56177
+ });
56178
+ const entry = yield* Effect.promise((signal) => book.add(addInput, { signal })).pipe(Effect.flatMap(resultValue));
56179
+ return result(entry, [JSON.stringify(entry)]);
56180
+ }
56181
+ if (action === "get") {
56182
+ const entry = yield* Effect.promise((signal) => book.get(entryId, { signal })).pipe(Effect.flatMap(resultValue));
56183
+ return result(entry, [entry === null ? "No contact found" : JSON.stringify(entry)]);
56184
+ }
56185
+ if (action === "list") {
56186
+ const entries = yield* Effect.promise((signal) => book.list(input.includeHidden === true ? { includeHidden: true } : void 0, { signal })).pipe(Effect.flatMap(resultValue));
56187
+ return result(entries, entries.map((entry) => JSON.stringify(entry)));
56188
+ }
56189
+ if (interactiveWrite) {
56190
+ const current = yield* Effect.promise((signal) => book.get(entryId, { signal })).pipe(Effect.flatMap(resultValue));
56191
+ if (current === null) return yield* Effect.fail(inputRequired("No contact has that --entry-id."));
56192
+ yield* confirmWrite({
56193
+ lines: [
56194
+ actorLine(input.orgs),
56195
+ `PartyId: ${current.id}`,
56196
+ `Current: ${JSON.stringify({
56197
+ label: current.label,
56198
+ hidden: current.hidden
56199
+ })}`,
56200
+ `Result: ${JSON.stringify(action === "label" ? {
56201
+ label,
56202
+ hidden: current.hidden
56203
+ } : {
56204
+ label: current.label,
56205
+ hidden: action === "hide"
56206
+ })}`
56207
+ ],
56208
+ question: `${action === "label" ? "Label" : action === "hide" ? "Hide" : "Unhide"} this contact?`
56209
+ });
56210
+ }
56211
+ const entry = yield* Effect.promise((signal) => action === "label" ? book.label({
56212
+ entryId,
56213
+ label
56214
+ }, { signal }) : action === "hide" ? book.hide(entryId, { signal }) : book.unhide(entryId, { signal })).pipe(Effect.flatMap(resultValue));
56215
+ return result(entry, [JSON.stringify(entry)]);
56216
+ });
56217
+ //#endregion
56218
+ //#region src/commands/contact.ts
56219
+ const emailFlag$1 = Flag.string("email").pipe(Flag.optional);
56220
+ const orgFlag$1 = Flag.string("org").pipe(Flag.withAlias("org-id"), Flag.atLeast(1));
56221
+ const includeHiddenFlag = Flag.boolean("include-hidden").pipe(Flag.withDefault(false));
56222
+ const entryIdFlag = Flag.string("entry-id");
56223
+ const labelFlag = Flag.string("label");
56224
+ const inputFlag = Flag.string("input").pipe(Flag.optional);
56225
+ const confirmFlag$1 = Flag.boolean("confirm").pipe(Flag.withDefault(false));
56226
+ const names = {
56227
+ personal: {
56228
+ root: "contact",
56229
+ list: "contact.list",
56230
+ get: "contact.get",
56231
+ add: "contact.add",
56232
+ label: "contact.label",
56233
+ hide: "contact.hide",
56234
+ unhide: "contact.unhide"
56235
+ },
56236
+ organization: {
56237
+ root: "org.contact",
56238
+ list: "org.contact.list",
56239
+ get: "org.contact.get",
56240
+ add: "org.contact.add",
56241
+ label: "org.contact.label",
56242
+ hide: "org.contact.hide",
56243
+ unhide: "org.contact.unhide"
56244
+ }
56245
+ };
56246
+ const metadata$1 = (name) => ({
56247
+ name,
56248
+ capability: "contacts",
56249
+ observation: "command"
56250
+ });
56251
+ const makeContactCommand = (actor) => {
56252
+ const selectedNames = names[actor];
56253
+ const scope = actor === "organization" ? { orgs: orgFlag$1 } : {};
56254
+ const execute = (action, input) => runCommand({
56255
+ ...metadata$1(selectedNames[action]),
56256
+ network: true
56257
+ }, (config) => runContact(config, action, {
56258
+ actor,
56259
+ orgs: input.orgs ?? [],
56260
+ email: input.email,
56261
+ includeHidden: input.includeHidden,
56262
+ entryId: input.entryId,
56263
+ label: input.label,
56264
+ input: input.input,
56265
+ confirm: input.confirm
56266
+ }));
56267
+ const list = Command.make("list", {
56268
+ email: emailFlag$1,
56269
+ ...scope,
56270
+ includeHidden: includeHiddenFlag
56271
+ }, ({ email, includeHidden, ...scoped }) => execute("list", {
56272
+ email: Option.getOrUndefined(email),
56273
+ orgs: "orgs" in scoped ? scoped.orgs : [],
56274
+ includeHidden,
56275
+ confirm: false
56276
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$1(selectedNames.list)), Command.withDescription("List contacts in the selected actor's address book"));
56277
+ const get = Command.make("get", {
56278
+ email: emailFlag$1,
56279
+ ...scope,
56280
+ entryId: entryIdFlag
56281
+ }, ({ email, entryId, ...scoped }) => execute("get", {
56282
+ email: Option.getOrUndefined(email),
56283
+ orgs: "orgs" in scoped ? scoped.orgs : [],
56284
+ entryId,
56285
+ confirm: false
56286
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$1(selectedNames.get)), Command.withDescription("Read one contact by stable PartyId"));
56287
+ const add = Command.make("add", {
56288
+ email: emailFlag$1,
56289
+ ...scope,
56290
+ input: inputFlag,
56291
+ confirm: confirmFlag$1
56292
+ }, ({ email, input, confirm, ...scoped }) => execute("add", {
56293
+ email: Option.getOrUndefined(email),
56294
+ orgs: "orgs" in scoped ? scoped.orgs : [],
56295
+ input: Option.getOrUndefined(input),
56296
+ confirm
56297
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$1(selectedNames.add)), Command.withDescription("Add one contact from AddressBookAddInput JSON"));
56298
+ const labelContact = Command.make("label", {
56299
+ email: emailFlag$1,
56300
+ ...scope,
56301
+ entryId: entryIdFlag,
56302
+ label: labelFlag,
56303
+ confirm: confirmFlag$1
56304
+ }, ({ email, entryId, label, confirm, ...scoped }) => execute("label", {
56305
+ email: Option.getOrUndefined(email),
56306
+ orgs: "orgs" in scoped ? scoped.orgs : [],
56307
+ entryId,
56308
+ label,
56309
+ confirm
56310
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$1(selectedNames.label)), Command.withDescription("Change one contact label by stable PartyId"));
56311
+ const visibility = (action) => Command.make(action, {
56312
+ email: emailFlag$1,
56313
+ ...scope,
56314
+ entryId: entryIdFlag,
56315
+ confirm: confirmFlag$1
56316
+ }, ({ email, entryId, confirm, ...scoped }) => execute(action, {
56317
+ email: Option.getOrUndefined(email),
56318
+ orgs: "orgs" in scoped ? scoped.orgs : [],
56319
+ entryId,
56320
+ confirm
56321
+ })).pipe(Command.annotate(CliCommandMetadata, metadata$1(selectedNames[action])), Command.withDescription(`${action === "hide" ? "Hide" : "Restore"} one contact`));
56322
+ return Command.make("contact").pipe(Command.annotate(CliCommandMetadata, metadata$1(selectedNames.root)), Command.withDescription("Manage the selected actor's contacts"), Command.withSubcommands([
56323
+ list,
56324
+ get,
56325
+ add,
56326
+ labelContact,
56327
+ visibility("hide"),
56328
+ visibility("unhide")
56329
+ ]));
56330
+ };
56331
+ const contact = makeContactCommand("personal");
56332
+ const organizationContact = makeContactCommand("organization");
56333
+ //#endregion
54207
56334
  //#region src/commands/org.ts
54208
56335
  const emailFlag = Flag.string("email").pipe(Flag.optional);
54209
56336
  const orgFlag = Flag.string("org").pipe(Flag.withAlias("org-id"), Flag.atLeast(0));
@@ -54251,7 +56378,11 @@ const memberGet = Command.make("get", {
54251
56378
  orgs,
54252
56379
  accountId
54253
56380
  })).pipe(Command.annotate(CliCommandMetadata, metadata("org.member.get")), Command.withDescription("Read one member by exact Account ID"));
54254
- const member = Command.make("member").pipe(Command.annotate(CliCommandMetadata, metadata("org.member")), Command.withSubcommands([members("list"), memberGet]));
56381
+ const member = Command.make("member").pipe(Command.annotate(CliCommandMetadata, metadata("org.member")), Command.withSubcommands([
56382
+ members("list"),
56383
+ memberGet,
56384
+ memberInvite
56385
+ ]));
54255
56386
  const wait = Command.make("wait", {
54256
56387
  ...scoped,
54257
56388
  timeoutSeconds: Flag.integer("timeout-seconds").pipe(Flag.withDefault(120))
@@ -54281,7 +56412,7 @@ const create = Command.make("create", {
54281
56412
  size: Option.getOrUndefined(size),
54282
56413
  confirm,
54283
56414
  timeoutSeconds
54284
- }))).pipe(Command.annotate(CliCommandMetadata, metadata(CAPXUL_OPERATIONS.org.create)), Command.withDescription("Create one Organization; never saves a read default"));
56415
+ }))).pipe(Command.annotate(CliCommandMetadata, metadata(CAPXUL_OPERATIONS.org.create)), Command.withDescription("Create one Organization, or resume the exact one this machine recorded"));
54285
56416
  const retry = Command.make("retry", {
54286
56417
  ...orgScope,
54287
56418
  email: emailFlag,
@@ -54296,13 +56427,15 @@ const retry = Command.make("retry", {
54296
56427
  confirm,
54297
56428
  timeoutSeconds
54298
56429
  }))).pipe(Command.annotate(CliCommandMetadata, metadata(CAPXUL_OPERATIONS.org.retry)), Command.withDescription("Resume this exact Organization setup lane; never creates one"));
54299
- const org = Command.make("org").pipe(Command.annotate(CliCommandMetadata, metadata("org")), Command.withDescription("Organization reads, writes, and local read defaults"), Command.withSubcommands([
56430
+ const org = Command.make("org").pipe(Command.annotate(CliCommandMetadata, metadata("org")), Command.withDescription("Organization reads, writes, recovery, and local read defaults"), Command.withSubcommands([
54300
56431
  list,
54301
56432
  use,
54302
56433
  status,
54303
56434
  me,
54304
56435
  members("members"),
54305
56436
  member,
56437
+ organizationContact,
56438
+ invite,
54306
56439
  wait,
54307
56440
  create,
54308
56441
  retry
@@ -54340,19 +56473,10 @@ const command = Command.make("capxul").pipe(Command.annotate(CliCommandMetadata,
54340
56473
  auth,
54341
56474
  doctor,
54342
56475
  telemetry,
56476
+ contact,
54343
56477
  org
54344
56478
  ]), Command.withGlobalFlags([JsonOutput]));
54345
56479
  //#endregion
54346
- //#region src/platform/bounded-text.ts
54347
- var UpdateUnavailable = class extends Data.TaggedError("UpdateUnavailable") {};
54348
- const boundedText = (stream, limit) => Effect.suspend(() => {
54349
- let size = 0;
54350
- return stream.pipe(Stream.mapEffect((chunk) => {
54351
- size += chunk.byteLength;
54352
- return size > limit ? Effect.fail(new UpdateUnavailable()) : Effect.succeed(chunk);
54353
- }), Stream.decodeText(), Stream.runFold(() => "", (text, chunk) => text + chunk));
54354
- });
54355
- //#endregion
54356
56480
  //#region src/platform/install-provenance.ts
54357
56481
  const Manifest = Schema.Struct({
54358
56482
  name: Schema.Literal("@capxul/cli"),
@@ -54465,6 +56589,7 @@ const runCli = (platform) => Effect.uninterruptibleMask((restore) => Effect.gen(
54465
56589
  const ci = yield* Config.string("CI").pipe(Config.withDefault("false"), Effect.map((value) => value !== "" && value !== "false" && value !== "0"), Effect.catch(() => Effect.succeed(false)));
54466
56590
  const runtime = {
54467
56591
  mode,
56592
+ args,
54468
56593
  current,
54469
56594
  invocationId,
54470
56595
  observation,