@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/README.md +212 -17
- package/dist/browser/signer.js +446 -26
- package/dist/browser/signer.js.map +1 -1
- package/dist/main.mjs +2457 -332
- package/dist/main.mjs.map +1 -1
- package/package.json +3 -3
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
|
-
|
|
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$
|
|
4177
|
+
data.push(encode$3(type, value));
|
|
4177
4178
|
}
|
|
4178
4179
|
return concatHex(data);
|
|
4179
4180
|
}
|
|
4180
|
-
function encode$
|
|
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$
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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" } :
|
|
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.
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
24696
|
-
} : encode$
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 &&
|
|
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), {
|
|
48693
|
-
|
|
48694
|
-
|
|
48695
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
51014
|
-
const sdkVersion = "4.20.0-beta.
|
|
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$
|
|
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$
|
|
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
|
-
|
|
52242
|
-
|
|
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(
|
|
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
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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.
|
|
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.
|
|
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(
|
|
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("")
|
|
52572
|
-
if (
|
|
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
|
|
52576
|
-
|
|
52577
|
-
|
|
52578
|
-
|
|
52579
|
-
|
|
52580
|
-
|
|
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
|
-
|
|
52583
|
-
|
|
52584
|
-
|
|
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.
|
|
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"
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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$
|
|
53544
|
-
const otpStdinFlag = Flag.boolean("otp-stdin").pipe(Flag.withDefault(false), Flag.withDescription("Read the six-digit OTP from
|
|
53545
|
-
const displayNameFlag = Flag.string("display-name").pipe(Flag.withDescription("
|
|
53546
|
-
const countryFlag = Flag.string("country").pipe(Flag.withDescription("Two-letter
|
|
53547
|
-
const handleFlag = Flag.string("handle").pipe(Flag.withDescription("
|
|
53548
|
-
const metadata$
|
|
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$
|
|
54383
|
+
email: emailFlag$3,
|
|
53555
54384
|
otpStdin: otpStdinFlag
|
|
53556
54385
|
}, ({ email, otpStdin }) => runCommand({
|
|
53557
|
-
...metadata$
|
|
54386
|
+
...metadata$4("auth.login"),
|
|
53558
54387
|
network: true
|
|
53559
|
-
}, (config) => login(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$
|
|
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$
|
|
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$
|
|
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$
|
|
53574
|
-
const sendCommand = Command.make("send", { email: emailFlag$
|
|
53575
|
-
...metadata$
|
|
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$
|
|
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$
|
|
54408
|
+
email: emailFlag$3,
|
|
53580
54409
|
otpStdin: otpStdinFlag
|
|
53581
54410
|
}, ({ email, otpStdin }) => runCommand({
|
|
53582
|
-
...metadata$
|
|
54411
|
+
...metadata$4("auth.verify"),
|
|
53583
54412
|
network: true
|
|
53584
|
-
}, (config) => verifyOtp(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$
|
|
53585
|
-
const profileCommand = Command.make("profile", { email: emailFlag$
|
|
53586
|
-
...metadata$
|
|
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$
|
|
53589
|
-
const statusCommand = Command.make("status", { email: emailFlag$
|
|
53590
|
-
...metadata$
|
|
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$
|
|
53593
|
-
const logoutCommand = Command.make("logout", { email: emailFlag$
|
|
53594
|
-
...metadata$
|
|
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$
|
|
53597
|
-
const auth = Command.make("auth").pipe(Command.annotate(CliCommandMetadata, metadata$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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`
|
|
53875
|
-
*
|
|
53876
|
-
*
|
|
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
|
-
|
|
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
|
|
53900
|
-
|
|
53901
|
-
|
|
53902
|
-
|
|
53903
|
-
|
|
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
|
|
53909
|
-
if (
|
|
53910
|
-
|
|
53911
|
-
|
|
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
|
|
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
|
-
/**
|
|
53940
|
-
|
|
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
|
-
|
|
53943
|
-
|
|
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
|
|
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
|
|
53984
|
-
*
|
|
53985
|
-
* reset
|
|
53986
|
-
*
|
|
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
|
|
54083
|
-
yield*
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
54110
|
-
const
|
|
54111
|
-
const
|
|
54112
|
-
|
|
54113
|
-
|
|
54114
|
-
|
|
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
|
-
|
|
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*
|
|
54171
|
-
yield*
|
|
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.
|
|
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([
|
|
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
|
|
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,
|