@capxul/cli 4.20.0-beta.27 → 4.20.0-beta.28
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 +65 -32
- package/dist/browser/signer.js +86 -78
- package/dist/browser/signer.js.map +1 -1
- package/dist/{entry-GLdITgH4.mjs → entry-DXu0dr0S.mjs} +1784 -1076
- package/dist/entry-DXu0dr0S.mjs.map +1 -0
- package/dist/{fast-BQEiHMyp.mjs → fast-B1MSUn2P.mjs} +2 -2
- package/dist/{fast-BQEiHMyp.mjs.map → fast-B1MSUn2P.mjs.map} +1 -1
- package/dist/main.mjs +2 -2
- package/dist/{where-view-Csv4lEES.mjs → where-view-BJmsAHQb.mjs} +11 -2
- package/dist/{where-view-Csv4lEES.mjs.map → where-view-BJmsAHQb.mjs.map} +1 -1
- package/package.json +5 -5
- package/dist/entry-GLdITgH4.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as __exportAll, o as __require, r as __commonJSMin, s as __toESM } from "./lib-DnmstTkB.mjs";
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as concatHex, B as toHex, C as getAddress$1, D as toRlp, E as format, F as toBytes$1, G as isHex$1, H as padHex, I as boolToHex, J as BaseError$1, K as IntegerOutOfRangeError, L as bytesToHex, M as hexToBigInt, N as hexToNumber, O as createCursor, P as hexToBytes, R as numberToHex, S as checksumAddress, T as InvalidAddressError, U as trim$1, V as pad, W as size, _ as isRecord, a as ENVIRONMENT_NAMES, b as sliceBytes, d as explicitOrganization$2, f as flagValue, g as scopeArgs, h as parseWhere, j as assertSize, k as concat$1, l as describeWhere, m as networkName, n as styleFor, o as STAGING_ORIGIN, p as flagValues, q as InvalidBytesBooleanError, r as whoamiLines, s as chooseEnvironment, t as headerLine, u as environmentOf, v as readField, w as keccak256$1, x as isAddress$1, y as slice, z as stringToHex } from "./where-view-BJmsAHQb.mjs";
|
|
3
3
|
import { t as require_lib } from "./lib-CoGPQsbr.mjs";
|
|
4
4
|
import process$1, { arch, platform } from "node:process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -7,8 +7,7 @@ import { closeSync, constants, fstatSync, lstatSync, openSync, readSync, realpat
|
|
|
7
7
|
import { basename, dirname, join, resolve } from "node:path";
|
|
8
8
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
9
9
|
import { Cause, Clock, Config, Console, Context, Crypto, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, FileSystem, Layer, LogLevel, Logger, Metric, Option, Path, Predicate, Queue, Redacted, Ref, References, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Sink, Stdio, Stream, Terminal, Tracer } from "effect";
|
|
10
|
-
import { Argument, CliConfig, CliError, CliOutput, Command, Flag, GlobalFlag, Primitive, Prompt } from "effect/cli";
|
|
11
|
-
import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/http";
|
|
10
|
+
import { Argument, CliConfig, CliError, CliOutput, Command, Flag, GlobalFlag, Param, Primitive, Prompt } from "effect/cli";
|
|
12
11
|
import http from "http";
|
|
13
12
|
import https from "https";
|
|
14
13
|
import zlib from "zlib";
|
|
@@ -19,6 +18,7 @@ import url from "url";
|
|
|
19
18
|
import util from "util";
|
|
20
19
|
import { resolve as resolve$1 } from "path";
|
|
21
20
|
import http2 from "http2";
|
|
21
|
+
import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/http";
|
|
22
22
|
import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/observability";
|
|
23
23
|
import { createServer } from "node:http";
|
|
24
24
|
import { execFile, spawn } from "node:child_process";
|
|
@@ -1936,6 +1936,53 @@ function formatUnits(value, decimals) {
|
|
|
1936
1936
|
return format(value, decimals);
|
|
1937
1937
|
}
|
|
1938
1938
|
//#endregion
|
|
1939
|
+
//#region src/runtime/format.ts
|
|
1940
|
+
const jsonFlag = Flag.Boolean("json").pipe(Flag.withDefault(false), Flag.withDescription("Print a versioned JSON result"));
|
|
1941
|
+
const JsonOutput = GlobalFlag.Setting("json")({ flag: jsonFlag });
|
|
1942
|
+
/** `--org <handle>` for one command. Commands resolve it through `#auth/actor`. */
|
|
1943
|
+
const OrganizationFlag = GlobalFlag.Setting("org")({ flag: Flag.String("org").pipe(Flag.withMetavar("<handle>"), Flag.withDescription("act as this organization for one command"), Flag.optional) });
|
|
1944
|
+
/** `--fields <a,b>`: the JSON result keeps only these fields of `data`. */
|
|
1945
|
+
const FieldsFlag = GlobalFlag.Setting("fields")({ flag: Flag.String("fields").pipe(Flag.withMetavar("<a,b>"), Flag.withDescription("choose which JSON fields come back"), Flag.optional) });
|
|
1946
|
+
/** `--env <name>` for one command. The runner reads it with the other environment sources. */
|
|
1947
|
+
const EnvironmentFlag = GlobalFlag.Setting("env")({ flag: Flag.String("env").pipe(Flag.withMetavar("<name>"), Flag.withDescription("use this environment for one command"), Flag.optional) });
|
|
1948
|
+
/** What the flags many commands share say in `--help` and `capxul schema`, in one voice. */
|
|
1949
|
+
const FLAG_HELP = {
|
|
1950
|
+
email: "the signed-in person to use, when this machine has several",
|
|
1951
|
+
confirm: "make the change without asking; scripts must pass it for each write",
|
|
1952
|
+
input: "the JSON input, from a file or - for stdin",
|
|
1953
|
+
requestKey: "a key that makes repeating the same write safe",
|
|
1954
|
+
timeout: "give up after this many seconds, 1 to 3600 (default 120)"
|
|
1955
|
+
};
|
|
1956
|
+
/** Local diagnostics require an explicit debug level. */
|
|
1957
|
+
const localDiagnosticsEnabled = (args) => {
|
|
1958
|
+
const terminator = args.indexOf("--");
|
|
1959
|
+
const flags = terminator < 0 ? args : args.slice(0, terminator);
|
|
1960
|
+
let level;
|
|
1961
|
+
for (const [index, arg] of flags.entries()) if (arg.startsWith("--log-level=")) level = arg.slice(12);
|
|
1962
|
+
else if (arg.startsWith("--logLevel=")) level = arg.slice(11);
|
|
1963
|
+
else if (arg === "--log-level" || arg === "--logLevel") level = flags[index + 1];
|
|
1964
|
+
return level === "debug";
|
|
1965
|
+
};
|
|
1966
|
+
/** Negotiate only the global format before native CLI error rendering.
|
|
1967
|
+
* Native Flag decoding remains the value authority; the command runner parses everything else.
|
|
1968
|
+
*/
|
|
1969
|
+
const outputMode = (args) => Effect.gen(function* () {
|
|
1970
|
+
const terminator = args.indexOf("--");
|
|
1971
|
+
const flags = terminator < 0 ? args : args.slice(0, terminator);
|
|
1972
|
+
const values = [];
|
|
1973
|
+
for (const [index, arg] of flags.entries()) if (arg === "--no-json") values.push("false");
|
|
1974
|
+
else if (arg.startsWith("--json=")) values.push(arg.slice(7));
|
|
1975
|
+
else if (arg === "--json") {
|
|
1976
|
+
const next = flags[index + 1];
|
|
1977
|
+
const value = next === void 0 ? "true" : yield* Primitive.Boolean.parse(next).pipe(Effect.as(next), Effect.orElseSucceed(() => "true"));
|
|
1978
|
+
values.push(value);
|
|
1979
|
+
}
|
|
1980
|
+
return yield* jsonFlag.parse({
|
|
1981
|
+
flags: { json: values },
|
|
1982
|
+
arguments: []
|
|
1983
|
+
}).pipe(Effect.map(([, json]) => json ? "json" : "human"), Effect.orElseSucceed(() => values.includes("true") ? "json" : "human"));
|
|
1984
|
+
});
|
|
1985
|
+
//#endregion
|
|
1939
1986
|
//#region ../../node_modules/.pnpm/convex@1.39.1_react@19.2.7/node_modules/convex/dist/esm/values/base64.js
|
|
1940
1987
|
var lookup = [];
|
|
1941
1988
|
var revLookup = [];
|
|
@@ -2744,6 +2791,9 @@ function toCurrencyCode(raw) {
|
|
|
2744
2791
|
if (!isCurrencyCode(raw)) throw Errors.invalidInput("currencyCode", invalidValueReason("unsupported currency", raw));
|
|
2745
2792
|
return raw;
|
|
2746
2793
|
}
|
|
2794
|
+
function currencySymbolFor(code) {
|
|
2795
|
+
return CURRENCY_SYMBOLS[code];
|
|
2796
|
+
}
|
|
2747
2797
|
const MAJOR_UNITS_RE = /^[0-9]+(?:\.[0-9]+)?$/;
|
|
2748
2798
|
const MAX_UINT256 = 2n ** 256n;
|
|
2749
2799
|
/**
|
|
@@ -5033,7 +5083,7 @@ faucetMint: Operation.action("accounts.grants.faucetMint", {
|
|
|
5033
5083
|
]
|
|
5034
5084
|
}) };
|
|
5035
5085
|
/** Every approval call: the record is not the signed-in person's, or there is none. */
|
|
5036
|
-
const APPROVAL$
|
|
5086
|
+
const APPROVAL$2 = ["ROLE_PERMISSION_DENIED"];
|
|
5037
5087
|
/**
|
|
5038
5088
|
* A prepared money action waits here for the person to sign it with their own
|
|
5039
5089
|
* key, on the approval page. The backend never signs: it records the person's
|
|
@@ -5058,7 +5108,7 @@ const approvals = {
|
|
|
5058
5108
|
errors: [
|
|
5059
5109
|
...CALL$10,
|
|
5060
5110
|
...DEFECTS,
|
|
5061
|
-
...APPROVAL$
|
|
5111
|
+
...APPROVAL$2
|
|
5062
5112
|
]
|
|
5063
5113
|
}),
|
|
5064
5114
|
/** The person opened the approval page: stamp the first review of a pending approval. */
|
|
@@ -5068,7 +5118,7 @@ const approvals = {
|
|
|
5068
5118
|
errors: [
|
|
5069
5119
|
...CALL$10,
|
|
5070
5120
|
...DEFECTS,
|
|
5071
|
-
...APPROVAL$
|
|
5121
|
+
...APPROVAL$2
|
|
5072
5122
|
]
|
|
5073
5123
|
}),
|
|
5074
5124
|
/**
|
|
@@ -5084,7 +5134,7 @@ const approvals = {
|
|
|
5084
5134
|
errors: [
|
|
5085
5135
|
...CALL$10,
|
|
5086
5136
|
...DEFECTS,
|
|
5087
|
-
...APPROVAL$
|
|
5137
|
+
...APPROVAL$2,
|
|
5088
5138
|
"WRONG_STATE"
|
|
5089
5139
|
]
|
|
5090
5140
|
}),
|
|
@@ -5095,7 +5145,7 @@ const approvals = {
|
|
|
5095
5145
|
errors: [
|
|
5096
5146
|
...CALL$10,
|
|
5097
5147
|
...DEFECTS,
|
|
5098
|
-
...APPROVAL$
|
|
5148
|
+
...APPROVAL$2,
|
|
5099
5149
|
"WRONG_STATE"
|
|
5100
5150
|
]
|
|
5101
5151
|
}),
|
|
@@ -5106,7 +5156,7 @@ const approvals = {
|
|
|
5106
5156
|
errors: [
|
|
5107
5157
|
...CALL$10,
|
|
5108
5158
|
...DEFECTS,
|
|
5109
|
-
...APPROVAL$
|
|
5159
|
+
...APPROVAL$2,
|
|
5110
5160
|
"WRONG_STATE"
|
|
5111
5161
|
]
|
|
5112
5162
|
})
|
|
@@ -5576,6 +5626,28 @@ const probes = {
|
|
|
5576
5626
|
})
|
|
5577
5627
|
};
|
|
5578
5628
|
//#endregion
|
|
5629
|
+
//#region ../../packages/protocol/src/evm/role-dsl.ts
|
|
5630
|
+
const ORG_BUDGET_ACTIONS = [
|
|
5631
|
+
"send",
|
|
5632
|
+
"sendBatch",
|
|
5633
|
+
"createCommitment"
|
|
5634
|
+
];
|
|
5635
|
+
/**
|
|
5636
|
+
* How a capped Budget's allowance refills (#2298). `monthly` refills the
|
|
5637
|
+
* allowance to its cap every 30 days. Roles v2 counts fixed periods from the
|
|
5638
|
+
* moment the allowance is set; it has no calendar month. Absent means `none`:
|
|
5639
|
+
* the cap is one pot that spending drains for good.
|
|
5640
|
+
*/
|
|
5641
|
+
const ORG_BUDGET_REFILLS = ["monthly", "none"];
|
|
5642
|
+
/** The fixed Roles v2 refill period of a `monthly` Budget: 30 days, in seconds. */
|
|
5643
|
+
const MONTHLY_REFILL_PERIOD_SECONDS = 30n * 24n * 60n * 60n;
|
|
5644
|
+
/** Derive the Zodiac substrate key for one durable Permission identity. */
|
|
5645
|
+
function roleKeyForPermission(permissionId) {
|
|
5646
|
+
const normalized = permissionId.trim();
|
|
5647
|
+
if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
|
|
5648
|
+
return keccak256$1(toBytes$1(`permission ${permissionId}`));
|
|
5649
|
+
}
|
|
5650
|
+
//#endregion
|
|
5579
5651
|
//#region ../../packages/protocol/src/permissions/permission-schema.ts
|
|
5580
5652
|
/**
|
|
5581
5653
|
* One stored `setAllowance` call inside a command's expected authority set.
|
|
@@ -5614,12 +5686,19 @@ const BudgetScopeSchema = Schema.Struct({
|
|
|
5614
5686
|
})]),
|
|
5615
5687
|
actions: Schema.NonEmptyArray(Schema.Literals(["pay", "commitments"]))
|
|
5616
5688
|
}).pipe(Schema.refine((scope) => new Set(scope.actions).size === scope.actions.length && (scope.recipients.type === "anyone" || scope.recipients.accounts.length >= 1 && scope.recipients.accounts.length <= 32 && new Set(scope.recipients.accounts).size === scope.recipients.accounts.length), { message: "Budget scope requires unique actions and one to 32 unique allowed Accounts" }));
|
|
5689
|
+
/**
|
|
5690
|
+
* How a capped Budget refills (#2298): `monthly` tops the cap back up every 30
|
|
5691
|
+
* days (a fixed Roles v2 period, not a calendar month); `none` never refills.
|
|
5692
|
+
* Absent means `none`, and writers store only `monthly`.
|
|
5693
|
+
*/
|
|
5694
|
+
const BudgetRefillSchema = Schema.Literals(ORG_BUDGET_REFILLS);
|
|
5617
5695
|
/** The accepted product policy stored with one Permission revision. */
|
|
5618
5696
|
const CurrentPolicySchema = Schema.Union([Schema.Struct({
|
|
5619
5697
|
type: Schema.Literal("budget"),
|
|
5620
5698
|
asset: AssetIdSchema,
|
|
5621
5699
|
limit: Schema.NullOr(Schema.String),
|
|
5622
|
-
scope: BudgetScopeSchema
|
|
5700
|
+
scope: BudgetScopeSchema,
|
|
5701
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5623
5702
|
}), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
5624
5703
|
const BudgetIntentSchema = CurrentPolicySchema.pipe(Schema.refine((value) => {
|
|
5625
5704
|
if (value.type !== "budget") return false;
|
|
@@ -5684,7 +5763,8 @@ const ResolvedPermissionBudgetPolicySchema = Schema.Struct({
|
|
|
5684
5763
|
"send",
|
|
5685
5764
|
"sendBatch",
|
|
5686
5765
|
"createCommitment"
|
|
5687
|
-
]))
|
|
5766
|
+
])),
|
|
5767
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5688
5768
|
});
|
|
5689
5769
|
const PermissionRecipientBindingSchema = Schema.Struct({
|
|
5690
5770
|
accountId: AccountIdSchema$1,
|
|
@@ -5696,16 +5776,22 @@ const StoredPermissionIntentSchema = Schema.Union([Schema.Struct({
|
|
|
5696
5776
|
type: Schema.Literal("budget"),
|
|
5697
5777
|
asset: AssetIdSchema,
|
|
5698
5778
|
limit: Schema.NullOr(AssetAmountSchema),
|
|
5699
|
-
scope: Schema.optional(BudgetScopeSchema)
|
|
5779
|
+
scope: Schema.optional(BudgetScopeSchema),
|
|
5780
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5700
5781
|
}).pipe(Schema.refine((intent) => intent.limit === null || intent.limit.asset === intent.asset, { message: "Budget limit must use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
5701
5782
|
/** Writes state every granted right and use a finite positive AssetAmount cap. */
|
|
5702
5783
|
const PermissionIntentSchema = Schema.Union([Schema.Struct({
|
|
5703
5784
|
type: Schema.Literal("budget"),
|
|
5704
5785
|
asset: AssetIdSchema,
|
|
5705
5786
|
limit: AssetAmountSchema,
|
|
5706
|
-
scope: BudgetScopeSchema
|
|
5787
|
+
scope: BudgetScopeSchema,
|
|
5788
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5707
5789
|
}).pipe(Schema.refine((intent) => intent.limit.asset === intent.asset && /[1-9]/u.test(intent.limit.value), { message: "Budget cap must be positive and use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
5708
|
-
/**
|
|
5790
|
+
/**
|
|
5791
|
+
* Decode once and give set-like scope fields one stable command representation.
|
|
5792
|
+
* An explicit `refill: "none"` is kept: on a change it turns a refill off,
|
|
5793
|
+
* where a missing `refill` keeps the Budget's current one (`keepRefillOnChange`).
|
|
5794
|
+
*/
|
|
5709
5795
|
function decodePermissionIntent$1(input) {
|
|
5710
5796
|
const intent = Schema.decodeUnknownSync(PermissionIntentSchema, { onExcessProperty: "error" })(input);
|
|
5711
5797
|
if (intent.type === "managePeople") return intent;
|
|
@@ -5720,6 +5806,19 @@ function decodePermissionIntent$1(input) {
|
|
|
5720
5806
|
}
|
|
5721
5807
|
});
|
|
5722
5808
|
}
|
|
5809
|
+
/**
|
|
5810
|
+
* A Budget change that leaves `refill` out keeps the Budget's current refill:
|
|
5811
|
+
* a change never turns a refill off by omission. An explicit `"none"` turns it
|
|
5812
|
+
* off. The backend plans, the SDK verifies and the CLI confirms the same
|
|
5813
|
+
* result. A create has no current refill, so a missing one means `none`.
|
|
5814
|
+
*/
|
|
5815
|
+
function keepRefillOnChange(intent, before) {
|
|
5816
|
+
if (intent.type !== "budget" || intent.refill !== void 0 || before?.type !== "budget" || before.refill !== "monthly") return intent;
|
|
5817
|
+
return {
|
|
5818
|
+
...intent,
|
|
5819
|
+
refill: before.refill
|
|
5820
|
+
};
|
|
5821
|
+
}
|
|
5723
5822
|
//#endregion
|
|
5724
5823
|
//#region ../../packages/protocol/src/permissions/replacement-schema.ts
|
|
5725
5824
|
const InvitationPhaseSchema = Schema.Literals([
|
|
@@ -5924,7 +6023,7 @@ const InviteGrantsSchema = Schema.Array(Schema.Union([
|
|
|
5924
6023
|
Schema.Struct({
|
|
5925
6024
|
type: Schema.Literal("new_budget"),
|
|
5926
6025
|
label: Schema.String.pipe(Schema.refine((value) => value.trim().length >= 1 && value.trim().length <= 128)),
|
|
5927
|
-
intent: BudgetIntentSchema
|
|
6026
|
+
intent: BudgetIntentSchema.pipe(Schema.refine((intent) => intent.refill !== "monthly", { message: "An invitation's new Budget cannot refill; change it once granted" }))
|
|
5928
6027
|
})
|
|
5929
6028
|
])).pipe(Schema.refine((grants) => {
|
|
5930
6029
|
if (grants.length < 1 || grants.length > 16) return false;
|
|
@@ -9357,7 +9456,9 @@ const PEOPLE_OPERATIONS = {
|
|
|
9357
9456
|
emitSafeConfirmed: "accounts.safes.emitSafeConfirmed",
|
|
9358
9457
|
readSafe: "accounts.safes.readSafe",
|
|
9359
9458
|
recordDeployment: "accounts.safes.recordDeployment"
|
|
9360
|
-
}
|
|
9459
|
+
},
|
|
9460
|
+
/** The home's requests to pay and Payments to retry, read in one query. */
|
|
9461
|
+
summaries: { loadNeedsYou: "accounts.summaries.loadNeedsYou" }
|
|
9361
9462
|
},
|
|
9362
9463
|
/** The host observer's names for `client.approvals.*`. */
|
|
9363
9464
|
approvals: {
|
|
@@ -9616,7 +9717,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
9616
9717
|
pay: "organizationPayments.pay",
|
|
9617
9718
|
payBatch: "organizationPayments.payBatch",
|
|
9618
9719
|
prepareExecution: "organizationPayments.prepareExecution",
|
|
9619
|
-
retry: "organizationPayments.retry"
|
|
9720
|
+
retry: "organizationPayments.retry",
|
|
9721
|
+
cancel: "organizationPayments.cancel",
|
|
9722
|
+
redirect: "organizationPayments.redirect"
|
|
9620
9723
|
},
|
|
9621
9724
|
/**
|
|
9622
9725
|
* Payments' internal functions and the client's own method names; its public
|
|
@@ -9638,7 +9741,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
9638
9741
|
loadContext: "payments.commands.loadContext",
|
|
9639
9742
|
loadExecutionContext: "payments.commands.loadExecutionContext",
|
|
9640
9743
|
/** The capped Budget a prepared Organization command spends from, before its allowance is read on chain. */
|
|
9641
|
-
loadBudgetAllowance: "payments.commands.loadBudgetAllowance"
|
|
9744
|
+
loadBudgetAllowance: "payments.commands.loadBudgetAllowance",
|
|
9745
|
+
/** What a new Payment command would spend and from which Safe, read before it is born. */
|
|
9746
|
+
loadBirthFunding: "payments.commands.loadBirthFunding"
|
|
9642
9747
|
},
|
|
9643
9748
|
lifecycles: { prepareIntent: "payments.lifecycles.prepareIntent" },
|
|
9644
9749
|
/** The stored UserOperation of every money command: Payment, Permission and configuration. */
|
|
@@ -9997,6 +10102,13 @@ const OptionalReasonCode = Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES));
|
|
|
9997
10102
|
* no catalog schema validates it.
|
|
9998
10103
|
*/
|
|
9999
10104
|
const EXCEPTION_EVENT_NAME = "$exception";
|
|
10105
|
+
/**
|
|
10106
|
+
* The SDK's twin of `capxul_backend_expected_outcome`: an expected outcome the
|
|
10107
|
+
* SDK's failure projection captures through the host's PostHog client. Like
|
|
10108
|
+
* the backend's, its properties are the error evidence, so no catalog schema
|
|
10109
|
+
* validates it.
|
|
10110
|
+
*/
|
|
10111
|
+
const SDK_EXPECTED_OUTCOME_EVENT_NAME = "capxul_sdk_expected_outcome";
|
|
10000
10112
|
const PII = Schema.String.pipe(Schema.brand("PII"));
|
|
10001
10113
|
/**
|
|
10002
10114
|
* The canonical envelope (ADR-0020 A4): snake_case ONLY. `capxul_env` and
|
|
@@ -10096,6 +10208,42 @@ const UiStepProps = {
|
|
|
10096
10208
|
organization_id: OptionalOrgId,
|
|
10097
10209
|
reason_code: OptionalReasonCode
|
|
10098
10210
|
};
|
|
10211
|
+
/**
|
|
10212
|
+
* A journey's variants (`JOURNEYS`): the way a person came into a journey, or
|
|
10213
|
+
* the way they took through it. A variant is a property of the journey's step
|
|
10214
|
+
* events, never a journey of its own, so each journey funnel breaks down by
|
|
10215
|
+
* it. A step event takes only the variants that a journey contract names on
|
|
10216
|
+
* it, picked one by one; the analytics plan test checks both directions.
|
|
10217
|
+
* `pay_path` and `funding_source` have no carrier until their Phase 2 events.
|
|
10218
|
+
* `payment_*` events already carry `recipient_is_capxul_account`, and
|
|
10219
|
+
* `invoice_issued` carries `issuer_scope_kind`; neither is repeated here.
|
|
10220
|
+
*/
|
|
10221
|
+
const JourneyVariantProps = {
|
|
10222
|
+
/** How a person came to Get started: by themselves, an invite, money that waited, or an invoice. */
|
|
10223
|
+
entry_path: Schema.optional(Schema.Literals([
|
|
10224
|
+
"signup",
|
|
10225
|
+
"invite",
|
|
10226
|
+
"claim",
|
|
10227
|
+
"invoice"
|
|
10228
|
+
])),
|
|
10229
|
+
/** Where the person acted. `agent` is an AI agent driving the CLI. */
|
|
10230
|
+
surface: Schema.optional(Schema.Literals([
|
|
10231
|
+
"web",
|
|
10232
|
+
"cli",
|
|
10233
|
+
"agent"
|
|
10234
|
+
])),
|
|
10235
|
+
/** Who pays: a person from their own account, or an Organization from a Budget. */
|
|
10236
|
+
payer_kind: Schema.optional(Schema.Literals(["person", "organization"])),
|
|
10237
|
+
/** How a request was paid: from the payer's inbox, or by its link at checkout. */
|
|
10238
|
+
pay_path: Schema.optional(Schema.Literals(["inbox", "link"])),
|
|
10239
|
+
/** Where an account's first money came from. `bank` is the planned on-ramp. */
|
|
10240
|
+
funding_source: Schema.optional(Schema.Literals([
|
|
10241
|
+
"deposit",
|
|
10242
|
+
"payment",
|
|
10243
|
+
"claim",
|
|
10244
|
+
"bank"
|
|
10245
|
+
]))
|
|
10246
|
+
};
|
|
10099
10247
|
//#endregion
|
|
10100
10248
|
//#region ../../packages/observability/src/events/money.ts
|
|
10101
10249
|
/**
|
|
@@ -10185,6 +10333,8 @@ const PaymentInitiatedProps = Schema.Struct({
|
|
|
10185
10333
|
execution_id: OptionalString,
|
|
10186
10334
|
payment_lifecycle_id: OptionalString,
|
|
10187
10335
|
...TelemetryEnvelopeProps,
|
|
10336
|
+
payer_kind: JourneyVariantProps.payer_kind,
|
|
10337
|
+
surface: JourneyVariantProps.surface,
|
|
10188
10338
|
...RecipientTelemetryProps,
|
|
10189
10339
|
kind: Schema.String,
|
|
10190
10340
|
payment_id: Schema.String
|
|
@@ -10200,6 +10350,8 @@ const PaymentLifecycleProps = {
|
|
|
10200
10350
|
const PaymentSettledL2Props = Schema.Struct({
|
|
10201
10351
|
...PaymentLifecycleProps,
|
|
10202
10352
|
...TelemetryEnvelopeProps,
|
|
10353
|
+
payer_kind: JourneyVariantProps.payer_kind,
|
|
10354
|
+
surface: JourneyVariantProps.surface,
|
|
10203
10355
|
...RecipientTelemetryProps,
|
|
10204
10356
|
kind: Schema.String,
|
|
10205
10357
|
chain_id: Schema.Finite,
|
|
@@ -10407,6 +10559,7 @@ const OrganizationCreationStartedProps = Schema.Struct({
|
|
|
10407
10559
|
});
|
|
10408
10560
|
const OrganizationCreationReadyProps = Schema.Struct({
|
|
10409
10561
|
...TelemetryEnvelopeProps,
|
|
10562
|
+
surface: JourneyVariantProps.surface,
|
|
10410
10563
|
$insert_id: Schema.String,
|
|
10411
10564
|
organization_id: OrgIdTelemetrySchema,
|
|
10412
10565
|
attempt_number: AttemptNumberSchema,
|
|
@@ -10431,45 +10584,6 @@ const OrganizationCreationFailedProps = Schema.Struct({
|
|
|
10431
10584
|
...ORGANIZATION_SETUP_REASONS
|
|
10432
10585
|
]))
|
|
10433
10586
|
});
|
|
10434
|
-
const OrgCreateStartedProps = Schema.Struct({
|
|
10435
|
-
...TelemetryEnvelopeProps,
|
|
10436
|
-
org_id: OptionalOrgId,
|
|
10437
|
-
email_domain: OptionalString,
|
|
10438
|
-
template: OptionalString
|
|
10439
|
-
});
|
|
10440
|
-
const OrgSafeCreatedProps = Schema.Struct({
|
|
10441
|
-
...TelemetryEnvelopeProps,
|
|
10442
|
-
org_id: OptionalOrgId,
|
|
10443
|
-
chainId: OptionalChainId,
|
|
10444
|
-
safe_address: OptionalAddress
|
|
10445
|
-
});
|
|
10446
|
-
const OrgSafeConfirmedProps = Schema.Struct({
|
|
10447
|
-
...TelemetryEnvelopeProps,
|
|
10448
|
-
org_id: OptionalOrgId,
|
|
10449
|
-
chainId: OptionalChainId,
|
|
10450
|
-
safe_address: OptionalAddress,
|
|
10451
|
-
durationMs: OptionalDurationMs
|
|
10452
|
-
});
|
|
10453
|
-
const OrgRolesSeededProps = Schema.Struct({
|
|
10454
|
-
...TelemetryEnvelopeProps,
|
|
10455
|
-
org_id: OptionalOrgId,
|
|
10456
|
-
chainId: OptionalChainId,
|
|
10457
|
-
role: OptionalString,
|
|
10458
|
-
txHash: OptionalTxHash
|
|
10459
|
-
});
|
|
10460
|
-
const OrgCreatedProps = Schema.Struct({
|
|
10461
|
-
...TelemetryEnvelopeProps,
|
|
10462
|
-
org_id: OptionalOrgId,
|
|
10463
|
-
chainId: OptionalChainId,
|
|
10464
|
-
safe_address: OptionalAddress,
|
|
10465
|
-
durationMs: OptionalDurationMs
|
|
10466
|
-
});
|
|
10467
|
-
const OrgCreateFailedProps = Schema.Struct({
|
|
10468
|
-
...TelemetryEnvelopeProps,
|
|
10469
|
-
...ChainCauseTelemetryProps,
|
|
10470
|
-
org_id: OptionalOrgId,
|
|
10471
|
-
reason: OptionalString
|
|
10472
|
-
});
|
|
10473
10587
|
const OrgInviteSentProps = Schema.Struct({
|
|
10474
10588
|
...TelemetryEnvelopeProps,
|
|
10475
10589
|
org_id: OptionalOrgId,
|
|
@@ -10514,30 +10628,6 @@ const OrganizationCreationFailedTelemetryEventSchema = Schema.Struct({
|
|
|
10514
10628
|
name: Schema.Literal("organization_creation_failed"),
|
|
10515
10629
|
props: OrganizationCreationFailedProps
|
|
10516
10630
|
});
|
|
10517
|
-
const OrgCreateStartedTelemetryEventSchema = Schema.Struct({
|
|
10518
|
-
name: Schema.Literal("org_create_started"),
|
|
10519
|
-
props: OrgCreateStartedProps
|
|
10520
|
-
});
|
|
10521
|
-
const OrgSafeCreatedTelemetryEventSchema = Schema.Struct({
|
|
10522
|
-
name: Schema.Literal("org_safe_created"),
|
|
10523
|
-
props: OrgSafeCreatedProps
|
|
10524
|
-
});
|
|
10525
|
-
const OrgSafeConfirmedTelemetryEventSchema = Schema.Struct({
|
|
10526
|
-
name: Schema.Literal("org_safe_confirmed"),
|
|
10527
|
-
props: OrgSafeConfirmedProps
|
|
10528
|
-
});
|
|
10529
|
-
const OrgRolesSeededTelemetryEventSchema = Schema.Struct({
|
|
10530
|
-
name: Schema.Literal("org_roles_seeded"),
|
|
10531
|
-
props: OrgRolesSeededProps
|
|
10532
|
-
});
|
|
10533
|
-
const OrgCreatedTelemetryEventSchema = Schema.Struct({
|
|
10534
|
-
name: Schema.Literal("org_created"),
|
|
10535
|
-
props: OrgCreatedProps
|
|
10536
|
-
});
|
|
10537
|
-
const OrgCreateFailedTelemetryEventSchema = Schema.Struct({
|
|
10538
|
-
name: Schema.Literal("org_create_failed"),
|
|
10539
|
-
props: OrgCreateFailedProps
|
|
10540
|
-
});
|
|
10541
10631
|
const OrgInviteSentTelemetryEventSchema = Schema.Struct({
|
|
10542
10632
|
name: Schema.Literal("org_invite_sent"),
|
|
10543
10633
|
props: OrgInviteSentProps
|
|
@@ -11436,10 +11526,14 @@ const BootstrapFailedProps = Schema.Struct({
|
|
|
11436
11526
|
});
|
|
11437
11527
|
const MemberActivationStartedProps = Schema.Struct({
|
|
11438
11528
|
...TelemetryEnvelopeProps,
|
|
11529
|
+
entry_path: JourneyVariantProps.entry_path,
|
|
11530
|
+
surface: JourneyVariantProps.surface,
|
|
11439
11531
|
$insert_id: Schema.String
|
|
11440
11532
|
});
|
|
11441
11533
|
const MemberActivationReadyProps = Schema.Struct({
|
|
11442
11534
|
...TelemetryEnvelopeProps,
|
|
11535
|
+
entry_path: JourneyVariantProps.entry_path,
|
|
11536
|
+
surface: JourneyVariantProps.surface,
|
|
11443
11537
|
$insert_id: Schema.String,
|
|
11444
11538
|
duration_ms: DurationMsSchema
|
|
11445
11539
|
});
|
|
@@ -11564,12 +11658,6 @@ const TELEMETRY_EVENT_SCHEMAS = {
|
|
|
11564
11658
|
bundler_error: BundlerErrorTelemetryEventSchema,
|
|
11565
11659
|
receipt_pending: ReceiptPendingTelemetryEventSchema,
|
|
11566
11660
|
operation_retried: OperationRetriedTelemetryEventSchema,
|
|
11567
|
-
org_create_started: OrgCreateStartedTelemetryEventSchema,
|
|
11568
|
-
org_safe_created: OrgSafeCreatedTelemetryEventSchema,
|
|
11569
|
-
org_safe_confirmed: OrgSafeConfirmedTelemetryEventSchema,
|
|
11570
|
-
org_roles_seeded: OrgRolesSeededTelemetryEventSchema,
|
|
11571
|
-
org_created: OrgCreatedTelemetryEventSchema,
|
|
11572
|
-
org_create_failed: OrgCreateFailedTelemetryEventSchema,
|
|
11573
11661
|
org_invite_sent: OrgInviteSentTelemetryEventSchema,
|
|
11574
11662
|
org_invite_accepted: OrgInviteAcceptedTelemetryEventSchema,
|
|
11575
11663
|
org_invite_expired: OrgInviteExpiredTelemetryEventSchema,
|
|
@@ -11929,56 +12017,6 @@ function stripUrlQueryAndFragment(value) {
|
|
|
11929
12017
|
return value.slice(0, Math.min(queryIndex, fragmentIndex));
|
|
11930
12018
|
}
|
|
11931
12019
|
//#endregion
|
|
11932
|
-
//#region src/runtime/command-metadata.ts
|
|
11933
|
-
var CliCommandMetadata = class extends Context.Service()("capxul-cli/CommandMetadata") {};
|
|
11934
|
-
const collectionControl = (name) => name === "config" || name === "config.telemetry" || name.startsWith("config.telemetry.");
|
|
11935
|
-
/** Resolve only a complete path supplied by the native parser. */
|
|
11936
|
-
function commandAtPath(root, path) {
|
|
11937
|
-
if (path[0] !== root.name) return void 0;
|
|
11938
|
-
let selected = root;
|
|
11939
|
-
for (const part of path.slice(1)) {
|
|
11940
|
-
const child = selected.subcommands.flatMap((group) => group.commands).find((candidate) => candidate.name === part || candidate.alias === part);
|
|
11941
|
-
if (child === void 0) return void 0;
|
|
11942
|
-
selected = child;
|
|
11943
|
-
}
|
|
11944
|
-
return selected;
|
|
11945
|
-
}
|
|
11946
|
-
const validateNativeScope = (input) => {
|
|
11947
|
-
if (typeof input === "object" && input !== null && "orgs" in input && Array.isArray(input.orgs) && input.orgs.every((id) => typeof id === "string") && new Set(input.orgs).size > 1) return Effect.fail(new CliError.InvalidValue({
|
|
11948
|
-
option: "org",
|
|
11949
|
-
value: "multiple IDs",
|
|
11950
|
-
expected: "one exact Organization ID",
|
|
11951
|
-
kind: "flag"
|
|
11952
|
-
}));
|
|
11953
|
-
return Effect.void;
|
|
11954
|
-
};
|
|
11955
|
-
/** Keep the native grammar and validate scope without business actions. */
|
|
11956
|
-
function nativeInputTree(node) {
|
|
11957
|
-
const input = Command.withHandler(node, validateNativeScope);
|
|
11958
|
-
const children = node.subcommands.flatMap((group) => {
|
|
11959
|
-
const commands = group.commands.map(nativeInputTree);
|
|
11960
|
-
return group.group === void 0 ? commands : [{
|
|
11961
|
-
group: group.group,
|
|
11962
|
-
commands
|
|
11963
|
-
}];
|
|
11964
|
-
});
|
|
11965
|
-
return Command.withSubcommands(input, children);
|
|
11966
|
-
}
|
|
11967
|
-
function metadataAtPath(root, path) {
|
|
11968
|
-
let selected = root;
|
|
11969
|
-
for (const part of path.slice(1)) {
|
|
11970
|
-
const child = selected.subcommands.flatMap((group) => group.commands).find((command) => command.name === part || command.alias === part);
|
|
11971
|
-
if (child === void 0) break;
|
|
11972
|
-
selected = child;
|
|
11973
|
-
}
|
|
11974
|
-
const metadata = Context.getOption(selected.annotations, CliCommandMetadata);
|
|
11975
|
-
return Option.isSome(metadata) && CLI_COMMAND_NAMES.includes(metadata.value.name) ? metadata.value : {
|
|
11976
|
-
name: "unknown",
|
|
11977
|
-
capability: "cli",
|
|
11978
|
-
observation: "command"
|
|
11979
|
-
};
|
|
11980
|
-
}
|
|
11981
|
-
//#endregion
|
|
11982
12020
|
//#region ../../packages/sdk/src/access/actor-references.ts
|
|
11983
12021
|
function actorReferenceToBackend(actor) {
|
|
11984
12022
|
if (actor === void 0) return void 0;
|
|
@@ -12222,7 +12260,7 @@ const FIELD_RULES = {
|
|
|
12222
12260
|
},
|
|
12223
12261
|
anonymousId: {
|
|
12224
12262
|
maxLength: 128,
|
|
12225
|
-
pattern: /^anon_[A-Za-z0-9-]
|
|
12263
|
+
pattern: /^(?:anon_[A-Za-z0-9-]+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/u
|
|
12226
12264
|
},
|
|
12227
12265
|
traceparent: {
|
|
12228
12266
|
maxLength: 55,
|
|
@@ -14870,7 +14908,7 @@ function mapAccountLifecycle(input) {
|
|
|
14870
14908
|
}
|
|
14871
14909
|
//#endregion
|
|
14872
14910
|
//#region ../../packages/sdk/package.json
|
|
14873
|
-
var version$2 = "4.20.0-beta.
|
|
14911
|
+
var version$2 = "4.20.0-beta.28";
|
|
14874
14912
|
//#endregion
|
|
14875
14913
|
//#region ../../packages/sdk/src/observability/telemetry/exception-projection.ts
|
|
14876
14914
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -15308,7 +15346,7 @@ const REVERT_SELECTOR_RE = /^0x(?:[0-9a-fA-F]{8})?$/u;
|
|
|
15308
15346
|
//#region ../../packages/sdk/src/observability/posthog-failure.ts
|
|
15309
15347
|
/** The expected-outcome codes, read as plain strings so a free-form kind can be looked up. */
|
|
15310
15348
|
/** Stable PostHog event used for typed failures that are expected product outcomes. */
|
|
15311
|
-
const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT =
|
|
15349
|
+
const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT = SDK_EXPECTED_OUTCOME_EVENT_NAME;
|
|
15312
15350
|
/**
|
|
15313
15351
|
* Adapt the host's already-initialized PostHog-like client. This function does
|
|
15314
15352
|
* not import, initialize, configure, or own PostHog.
|
|
@@ -18160,6 +18198,12 @@ function paymentsV2Signature(name) {
|
|
|
18160
18198
|
const SEND_SIGNATURE = paymentsV2Signature("send");
|
|
18161
18199
|
const SEND_BATCH_SIGNATURE = paymentsV2Signature("sendBatch");
|
|
18162
18200
|
const CREATE_COMMITMENT_SIGNATURE = paymentsV2Signature("createCommitment");
|
|
18201
|
+
/**
|
|
18202
|
+
* #2299 ruling C: only an Owner cancels or redirects an Organization-funded
|
|
18203
|
+
* commitment, so these two sit in the Owner role's scope, never a Budget's.
|
|
18204
|
+
* The contract admits only the commitment's creator, the Organization Safe.
|
|
18205
|
+
*/
|
|
18206
|
+
const OWNER_COMMITMENT_SIGNATURES = [paymentsV2Signature("cancel"), paymentsV2Signature("redirect")];
|
|
18163
18207
|
//#endregion
|
|
18164
18208
|
//#region ../../packages/protocol/src/evm/organization-payments.ts
|
|
18165
18209
|
/** Zodiac ModuleProxyFactory on Base Sepolia. */
|
|
@@ -18396,24 +18440,18 @@ function encodePreparedPaymentCallData(command) {
|
|
|
18396
18440
|
}));
|
|
18397
18441
|
}
|
|
18398
18442
|
const [item] = command.items;
|
|
18399
|
-
|
|
18400
|
-
|
|
18443
|
+
const route = command.executionRoute;
|
|
18444
|
+
if (command.items.length !== 1 || item === void 0 || isBirth(item) || route.kind !== "direct" && item.operation === "claim_commitment") throw Errors.invalidInput("items", "Commitment lifecycle requires one lone execution");
|
|
18445
|
+
const call = {
|
|
18401
18446
|
to: command.contractAddress,
|
|
18447
|
+
value: 0n,
|
|
18402
18448
|
data: lifecycleData(item)
|
|
18403
|
-
}
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
"sendBatch",
|
|
18410
|
-
"createCommitment"
|
|
18411
|
-
];
|
|
18412
|
-
/** Derive the Zodiac substrate key for one durable Permission identity. */
|
|
18413
|
-
function roleKeyForPermission(permissionId) {
|
|
18414
|
-
const normalized = permissionId.trim();
|
|
18415
|
-
if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
|
|
18416
|
-
return keccak256$1(toBytes$1(`permission ${permissionId}`));
|
|
18449
|
+
};
|
|
18450
|
+
return route.kind === "direct" ? safe4337ExecuteUserOpCallData(call) : buildOrgRoleSpendPersonalCallData(buildOrgRoleMultiCall({
|
|
18451
|
+
rolesModuleAddress: route.rolesModule,
|
|
18452
|
+
roleKey: route.roleKey,
|
|
18453
|
+
calls: [call]
|
|
18454
|
+
}));
|
|
18417
18455
|
}
|
|
18418
18456
|
//#endregion
|
|
18419
18457
|
//#region ../../packages/sdk/src/payments/operation-verification.ts
|
|
@@ -18442,6 +18480,16 @@ function moneySafeOpDigest(operation, chainId) {
|
|
|
18442
18480
|
userOp: safe4337UserOperation(operation)
|
|
18443
18481
|
});
|
|
18444
18482
|
}
|
|
18483
|
+
/**
|
|
18484
|
+
* An Organization-funded cancel or redirect runs through the Owner role
|
|
18485
|
+
* (#2299), which no Budget names. Its role key cannot widen what the signer
|
|
18486
|
+
* may do: the Roles module admits only a role the signer's Safe holds, and the
|
|
18487
|
+
* contract admits only the commitment's creator.
|
|
18488
|
+
*/
|
|
18489
|
+
function isOwnerLifecycle(command) {
|
|
18490
|
+
const [item] = command.items;
|
|
18491
|
+
return command.items.length === 1 && (item?.operation === "cancel_commitment" || item?.operation === "redirect_commitment");
|
|
18492
|
+
}
|
|
18445
18493
|
/** Verify every actual Money signing target against configured trust anchors. */
|
|
18446
18494
|
function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
|
|
18447
18495
|
try {
|
|
@@ -18452,7 +18500,7 @@ function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
|
|
|
18452
18500
|
const route = command.executionRoute;
|
|
18453
18501
|
if (route.kind === "direct") {
|
|
18454
18502
|
if (permissionId !== void 0 || command.senderSafe.toLowerCase() !== expectedSafe.toLowerCase()) return false;
|
|
18455
|
-
} else if (permissionId === void 0
|
|
18503
|
+
} else if ((permissionId === void 0 ? !isOwnerLifecycle(command) : route.roleKey.toLowerCase() !== roleKeyForPermission(permissionId).toLowerCase()) || command.senderSafe.toLowerCase() !== route.organizationSafe.toLowerCase()) return false;
|
|
18456
18504
|
if (operation.callData.toLowerCase() !== encodePreparedPaymentCallData(command).toLowerCase()) return false;
|
|
18457
18505
|
const digest = moneySafeOpDigest(operation, chainId);
|
|
18458
18506
|
return evidence.digest.toLowerCase() === digest.toLowerCase() && evidence.executionId === `money_execution_${digest.slice(2).toLowerCase()}`;
|
|
@@ -19231,9 +19279,11 @@ function subscribeFreshQuery(deps, args, listener, operation, projection, option
|
|
|
19231
19279
|
//#region ../../packages/sdk/src/payments/lifecycle-execution.ts
|
|
19232
19280
|
/**
|
|
19233
19281
|
* The prepared command is this lifecycle intent; a retry also names the
|
|
19234
|
-
* original birth command of the scope it was asked for.
|
|
19282
|
+
* original birth command of the scope it was asked for. A claim, and an
|
|
19283
|
+
* Account's own cancel or redirect, runs directly from the signer's Safe; an
|
|
19284
|
+
* Organization's cancel or redirect runs through its Owner role (#2299).
|
|
19235
19285
|
*/
|
|
19236
|
-
const lifecycleCommandCheck = (intent, expectedOrganizationId) => async (request, evidence) => await matchesIntent(intent)(request, evidence) && (intent.kind
|
|
19286
|
+
const lifecycleCommandCheck = (intent, expectedOrganizationId) => async (request, evidence) => await matchesIntent(intent)(request, evidence) && (intent.kind === "retry" ? expectedOrganizationId === void 0 ? evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "account" : evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "organization" && evidence.sourceCommand.actor.orgId === expectedOrganizationId : evidence.preparedCommand?.executionRoute.kind === (intent.kind === "claim" || expectedOrganizationId === void 0 ? "direct" : "zodiac_role"));
|
|
19237
19287
|
/** The prepared command pays exactly the request the payer confirmed. */
|
|
19238
19288
|
const requestPaymentCheck = (confirmation) => async (request) => "kind" in request && request.kind === "payment_request" && canonicalJson(request.confirmation) === canonicalJson(confirmation);
|
|
19239
19289
|
function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganizationId, onPrepared) {
|
|
@@ -20123,17 +20173,21 @@ function paymentExecutionIntent(input) {
|
|
|
20123
20173
|
const to = normalizeTargetForBackend(input.to, "to");
|
|
20124
20174
|
if (!to.ok) return to;
|
|
20125
20175
|
try {
|
|
20126
|
-
|
|
20176
|
+
const decoded = Schema.decodeResult(PaymentExecutionIntentSchema, { onExcessProperty: "error" })({
|
|
20177
|
+
to: to.value,
|
|
20178
|
+
amount: input.amount,
|
|
20179
|
+
...input.externalAddressAcknowledged === void 0 ? {} : { externalAddressAcknowledged: input.externalAddressAcknowledged },
|
|
20180
|
+
...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
|
|
20181
|
+
...input.document === void 0 ? {} : { document: input.document },
|
|
20182
|
+
...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
|
|
20183
|
+
...input.timing === void 0 ? {} : { timing: input.timing }
|
|
20184
|
+
});
|
|
20185
|
+
return Result.isSuccess(decoded) ? {
|
|
20127
20186
|
ok: true,
|
|
20128
|
-
value:
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
|
|
20133
|
-
...input.document === void 0 ? {} : { document: input.document },
|
|
20134
|
-
...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
|
|
20135
|
-
...input.timing === void 0 ? {} : { timing: input.timing }
|
|
20136
|
-
})
|
|
20187
|
+
value: decoded.success
|
|
20188
|
+
} : {
|
|
20189
|
+
ok: false,
|
|
20190
|
+
error: invalidPaymentField(decoded.failure.issue)
|
|
20137
20191
|
};
|
|
20138
20192
|
} catch {
|
|
20139
20193
|
return {
|
|
@@ -20142,6 +20196,13 @@ function paymentExecutionIntent(input) {
|
|
|
20142
20196
|
};
|
|
20143
20197
|
}
|
|
20144
20198
|
}
|
|
20199
|
+
const formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();
|
|
20200
|
+
/** The first failing field and why, e.g. `amount.value`, as the backend's row decode names it (#2381). */
|
|
20201
|
+
function invalidPaymentField(issue) {
|
|
20202
|
+
const first = formatIssue(issue).issues[0];
|
|
20203
|
+
const field = first?.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
|
|
20204
|
+
return Errors.invalidInput(field === void 0 || field.length === 0 ? "payment" : field, first?.message ?? "invalid personal payment input");
|
|
20205
|
+
}
|
|
20145
20206
|
function normalizePaymentTiming(payment) {
|
|
20146
20207
|
const { release: _release, ...rest } = payment;
|
|
20147
20208
|
return {
|
|
@@ -20958,6 +21019,27 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
20958
21019
|
flights: /* @__PURE__ */ new Map()
|
|
20959
21020
|
};
|
|
20960
21021
|
const execute = (input, signal, operation = "organization-pay-batch", controls, onPrepared) => executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared);
|
|
21022
|
+
/** One Owner-signed cancel or redirect, answering the one Payment it acted on (#2299). */
|
|
21023
|
+
const ownerLifecycle = (intent, controls, operation) => runCapxulEffect(Effect.gen(function* () {
|
|
21024
|
+
const captured = carryInvocationParentSpan(controls, yield* Effect.currentSpan.pipe(Effect.orDie));
|
|
21025
|
+
return yield* awaitCapxulResult(async () => {
|
|
21026
|
+
const execution = executionDependencies$1(deps);
|
|
21027
|
+
if (execution === null) return {
|
|
21028
|
+
ok: false,
|
|
21029
|
+
error: Errors.notImplemented("organizationPayments", "executionComposition")
|
|
21030
|
+
};
|
|
21031
|
+
const result = await executePaymentLifecycle(execution, intent, controls?.signal, captured, orgId);
|
|
21032
|
+
if (!result.ok) return result;
|
|
21033
|
+
const [payment, ...rest] = result.value.payments;
|
|
21034
|
+
return payment === void 0 || rest.length !== 0 ? {
|
|
21035
|
+
ok: false,
|
|
21036
|
+
error: Errors.unknown()
|
|
21037
|
+
} : {
|
|
21038
|
+
ok: true,
|
|
21039
|
+
value: normalizePaymentTiming(payment)
|
|
21040
|
+
};
|
|
21041
|
+
});
|
|
21042
|
+
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
20961
21043
|
return {
|
|
20962
21044
|
...makeOrganizationPaymentReadMethods(deps, orgId),
|
|
20963
21045
|
retry: (paymentId, options) => {
|
|
@@ -21016,6 +21098,19 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
21016
21098
|
});
|
|
21017
21099
|
}), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.pay, deps.runPromise);
|
|
21018
21100
|
},
|
|
21101
|
+
cancel: (paymentId, options) => ownerLifecycle({
|
|
21102
|
+
kind: "cancel",
|
|
21103
|
+
paymentId
|
|
21104
|
+
}, deps.invocationControls?.(options) ?? options, CAPXUL_OPERATIONS.organizationPayments.cancel),
|
|
21105
|
+
redirect: (input, options) => {
|
|
21106
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
21107
|
+
const recipient = normalizeTargetForBackend(input.to, "to");
|
|
21108
|
+
return recipient.ok ? ownerLifecycle({
|
|
21109
|
+
kind: "redirect",
|
|
21110
|
+
paymentId: input.paymentId,
|
|
21111
|
+
recipient: recipient.value
|
|
21112
|
+
}, controls, CAPXUL_OPERATIONS.organizationPayments.redirect) : runCapxulEffect(Effect.fail(recipient.error), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.redirect, deps.runPromise);
|
|
21113
|
+
},
|
|
21019
21114
|
payBatch: (input, options) => {
|
|
21020
21115
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
21021
21116
|
return runCapxulEffect(Effect.gen(function* () {
|
|
@@ -25305,6 +25400,40 @@ function executeIdentityProductObservation(telemetry, record, state) {
|
|
|
25305
25400
|
return ignoreTransportFailure(() => invocationTelemetry.emit(mapped), "emit", mapped.name).pipe(Effect.andThen(ignoreTransportFailure(after, mapped.name === "auth_signed_out" ? "reset" : "identify", mapped.name)));
|
|
25306
25401
|
}
|
|
25307
25402
|
//#endregion
|
|
25403
|
+
//#region ../../packages/sdk/src/money/format.ts
|
|
25404
|
+
function formatMoney(money, options = {}) {
|
|
25405
|
+
if (!Number.isInteger(money.decimals) || money.decimals < 0) throw Errors.invalidInput("money.decimals", "must be a non-negative integer");
|
|
25406
|
+
const symbol = currencySymbolFor(toCurrencyCode(money.currency));
|
|
25407
|
+
const maximumFractionDigits = options.grammar === "code" ? 4 : 2;
|
|
25408
|
+
const rounded = roundDecimal(money.value, money.decimals, maximumFractionDigits);
|
|
25409
|
+
const grouped = rounded.integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
25410
|
+
const signed = `${rounded.negative ? "-" : ""}${grouped}`;
|
|
25411
|
+
if (options.grammar === "code") {
|
|
25412
|
+
if (!rounded.hadFraction) return `${signed} ${money.currency}`;
|
|
25413
|
+
let displayedFraction = rounded.fraction;
|
|
25414
|
+
while (displayedFraction.length > 2 && displayedFraction.endsWith("0")) displayedFraction = displayedFraction.slice(0, -1);
|
|
25415
|
+
return `${signed}.${displayedFraction} ${money.currency}`;
|
|
25416
|
+
}
|
|
25417
|
+
return `${symbol}${signed}.${rounded.fraction}`;
|
|
25418
|
+
}
|
|
25419
|
+
function roundDecimal(value, decimals, maximumFractionDigits) {
|
|
25420
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?$/.exec(value);
|
|
25421
|
+
if (match === null) throw Errors.invalidInput("money.value", "must be a base-10 decimal string");
|
|
25422
|
+
const integer = match[2] ?? "0";
|
|
25423
|
+
const sourceFraction = match[3] ?? "";
|
|
25424
|
+
if (sourceFraction.length > decimals) throw Errors.invalidInput("money.value", "fraction exceeds money.decimals");
|
|
25425
|
+
const scale = 10n ** BigInt(maximumFractionDigits);
|
|
25426
|
+
const keptFraction = sourceFraction.slice(0, maximumFractionDigits).padEnd(maximumFractionDigits, "0");
|
|
25427
|
+
let scaled = BigInt(integer) * scale + BigInt(keptFraction || "0");
|
|
25428
|
+
if ((sourceFraction[maximumFractionDigits] ?? "0") >= "5") scaled += 1n;
|
|
25429
|
+
return {
|
|
25430
|
+
negative: match[1] === "-",
|
|
25431
|
+
integer: (scaled / scale).toString(),
|
|
25432
|
+
fraction: (scaled % scale).toString().padStart(maximumFractionDigits, "0"),
|
|
25433
|
+
hadFraction: /[1-9]/.test(sourceFraction)
|
|
25434
|
+
};
|
|
25435
|
+
}
|
|
25436
|
+
//#endregion
|
|
25308
25437
|
//#region ../../packages/sdk/src/permissions/command-decoding.ts
|
|
25309
25438
|
/** The caller's input as a command: ids branded and the intent decoded, or invalid input thrown. */
|
|
25310
25439
|
const buildCommand = {
|
|
@@ -34966,13 +35095,15 @@ function normalizeOrgBudgetPolicy(policy) {
|
|
|
34966
35095
|
if (actionSet.has(action)) throw Errors.invalidInput("policy.actions", `duplicate Budget action: ${action}`);
|
|
34967
35096
|
actionSet.add(action);
|
|
34968
35097
|
}
|
|
35098
|
+
if (policy.refill === "monthly" && limit.kind === "unlimited") throw Errors.invalidInput("policy.refill", "only a capped Budget can refill");
|
|
34969
35099
|
return {
|
|
34970
35100
|
assetId,
|
|
34971
35101
|
tokenAddress,
|
|
34972
35102
|
decimals: policy.asset.decimals,
|
|
34973
35103
|
limit,
|
|
34974
35104
|
recipients,
|
|
34975
|
-
actions: ORG_BUDGET_ACTIONS.filter((action) => actionSet.has(action))
|
|
35105
|
+
actions: ORG_BUDGET_ACTIONS.filter((action) => actionSet.has(action)),
|
|
35106
|
+
refillPeriod: policy.refill === "monthly" ? MONTHLY_REFILL_PERIOD_SECONDS : 0n
|
|
34976
35107
|
};
|
|
34977
35108
|
}
|
|
34978
35109
|
function resolvedBudgetApprovalPermission(tokenAddress) {
|
|
@@ -35047,8 +35178,8 @@ function buildOrgBudgetCallsWithEncoder(input, encodeRoles) {
|
|
|
35047
35178
|
allowanceKey,
|
|
35048
35179
|
BigInt(policy.limit.rawUnits),
|
|
35049
35180
|
BigInt(policy.limit.rawUnits),
|
|
35050
|
-
0n,
|
|
35051
|
-
|
|
35181
|
+
policy.refillPeriod === 0n ? 0n : BigInt(policy.limit.rawUnits),
|
|
35182
|
+
policy.refillPeriod,
|
|
35052
35183
|
0n
|
|
35053
35184
|
]
|
|
35054
35185
|
}),
|
|
@@ -35242,13 +35373,20 @@ function buildExplicitBootstrapRoleCalls(input, encodeRoles) {
|
|
|
35242
35373
|
roles: [{
|
|
35243
35374
|
key: managementRoleKey,
|
|
35244
35375
|
members: [input.ownerAddress],
|
|
35245
|
-
permissions: [
|
|
35246
|
-
|
|
35247
|
-
|
|
35248
|
-
|
|
35249
|
-
|
|
35250
|
-
signature
|
|
35251
|
-
|
|
35376
|
+
permissions: [
|
|
35377
|
+
{
|
|
35378
|
+
targetAddress: input.rolesModuleAddress,
|
|
35379
|
+
signature: "assignRoles(address,bytes32[],bool[])"
|
|
35380
|
+
},
|
|
35381
|
+
...ROLE_MANAGEMENT_SIGNATURES.map((signature) => ({
|
|
35382
|
+
targetAddress: input.rolesModuleAddress,
|
|
35383
|
+
signature
|
|
35384
|
+
})),
|
|
35385
|
+
...OWNER_COMMITMENT_SIGNATURES.map((signature) => ({
|
|
35386
|
+
targetAddress: CAPXUL_PAYMENTS_V2_ADDRESS,
|
|
35387
|
+
signature
|
|
35388
|
+
}))
|
|
35389
|
+
]
|
|
35252
35390
|
}]
|
|
35253
35391
|
}).map((call) => ({
|
|
35254
35392
|
to: toLowerHex(call.to, "setUpRoles.call.to"),
|
|
@@ -35716,7 +35854,7 @@ function matchesCurrentPolicy(intent, policy) {
|
|
|
35716
35854
|
asset: policy.asset,
|
|
35717
35855
|
value: policy.limit
|
|
35718
35856
|
}, CONFIGURED_MONEY_ASSETS).minorUnits;
|
|
35719
|
-
return policy.asset === intent.asset && raw === recorded && (intent.scope === void 0 || canonicalJson(intent.scope) === canonicalJson(policy.scope));
|
|
35857
|
+
return policy.asset === intent.asset && raw === recorded && intent.refill === "monthly" === (policy.refill === "monthly") && (intent.scope === void 0 || canonicalJson(intent.scope) === canonicalJson(policy.scope));
|
|
35720
35858
|
}
|
|
35721
35859
|
function previousPolicyForChange(effect, command) {
|
|
35722
35860
|
if (effect.previousIntent.type === "budget" && (effect.previousIntent.scope === void 0 || effect.previousRecipientBindings === void 0)) throw new EvidenceMismatch("Prior complete Budget proof is unavailable");
|
|
@@ -35734,8 +35872,9 @@ function policyCalls(evidence, context, command) {
|
|
|
35734
35872
|
const roleKey = effect.operation === "change" ? effect.roleKey : roleKeyForPermission(permissionId);
|
|
35735
35873
|
const revision = effect.operation === "change" ? effect.revision : 0;
|
|
35736
35874
|
if (effect.operation !== command.operation || effect.operation === "change" && effect.permissionId !== permissionId || evidence.expectedRevision !== revision) throw new EvidenceMismatch("Permission policy binding mismatch");
|
|
35875
|
+
const intent = effect.operation === "change" ? keepRefillOnChange(command.intent, effect.previousCurrentPolicy) : command.intent;
|
|
35737
35876
|
const value = evidence.value;
|
|
35738
|
-
if (Array.isArray(value) || !("type" in value) || !matchesCurrentPolicy(
|
|
35877
|
+
if (Array.isArray(value) || !("type" in value) || !matchesCurrentPolicy(intent, value.currentPolicy) || !permissionMatches(value, context.orgId, permissionId, command.operation === "create" ? 0 : revision + 1, intent, roleKey)) throw new EvidenceMismatch("Permission value mismatch");
|
|
35739
35878
|
const planned = compilePermissionPolicyCallsWithEncoder({
|
|
35740
35879
|
permissionId,
|
|
35741
35880
|
roleKey,
|
|
@@ -35743,7 +35882,7 @@ function policyCalls(evidence, context, command) {
|
|
|
35743
35882
|
actorSafe: evidence.userOpSenderSafe,
|
|
35744
35883
|
rolesModule: context.rolesModule,
|
|
35745
35884
|
managerRoleKey: context.managerRoleKey,
|
|
35746
|
-
nextIntent: encodingIntent(
|
|
35885
|
+
nextIntent: encodingIntent(intent, effect.recipientBindings),
|
|
35747
35886
|
...previous === void 0 ? {} : { previousIntent: previous },
|
|
35748
35887
|
...effect.operation !== "change" || effect.previousAllowanceKey === null ? {} : { previousAllowanceKey: effect.previousAllowanceKey }
|
|
35749
35888
|
}, encodeRoles);
|
|
@@ -35795,7 +35934,8 @@ function encodingIntent(intent, bindings) {
|
|
|
35795
35934
|
"send",
|
|
35796
35935
|
"sendBatch",
|
|
35797
35936
|
"createCommitment"
|
|
35798
|
-
].filter((action) => action === "createCommitment" ? intent.scope.actions.includes("commitments") : intent.scope.actions.includes("pay"))
|
|
35937
|
+
].filter((action) => action === "createCommitment" ? intent.scope.actions.includes("commitments") : intent.scope.actions.includes("pay")),
|
|
35938
|
+
...intent.refill === "monthly" ? { refill: intent.refill } : {}
|
|
35799
35939
|
}
|
|
35800
35940
|
};
|
|
35801
35941
|
}
|
|
@@ -60190,6 +60330,22 @@ function normalizeRuntimeUrl(field, raw) {
|
|
|
60190
60330
|
}
|
|
60191
60331
|
//#endregion
|
|
60192
60332
|
//#region ../../packages/sdk/src/applications/bootstrap-adapter.ts
|
|
60333
|
+
/** A cached envelope this close to its expiry is fetched again, so it cannot lapse mid-command. */
|
|
60334
|
+
const CACHE_EXPIRY_MARGIN_MS = 3e4;
|
|
60335
|
+
/**
|
|
60336
|
+
* The cached envelope, when it is still inside its own lifetime. It is decoded
|
|
60337
|
+
* as a fresh body is; anything that does not decode, or a store that fails,
|
|
60338
|
+
* is a miss.
|
|
60339
|
+
*/
|
|
60340
|
+
async function cachedEnvelope(cache, now) {
|
|
60341
|
+
try {
|
|
60342
|
+
const cached = decodeBootstrapBody(await cache.load());
|
|
60343
|
+
const expiresAt = cached.issuedAt + cached.expiresIn - CACHE_EXPIRY_MARGIN_MS;
|
|
60344
|
+
return cached.issuedAt <= now && now < expiresAt ? cached : null;
|
|
60345
|
+
} catch {
|
|
60346
|
+
return null;
|
|
60347
|
+
}
|
|
60348
|
+
}
|
|
60193
60349
|
const DEFAULT_RETRY = {
|
|
60194
60350
|
attempts: 2,
|
|
60195
60351
|
baseDelayMs: 400
|
|
@@ -60230,19 +60386,24 @@ var HttpBootstrapAdapter = class {
|
|
|
60230
60386
|
fetchImpl;
|
|
60231
60387
|
observation;
|
|
60232
60388
|
retry;
|
|
60389
|
+
cache;
|
|
60233
60390
|
constructor(deps) {
|
|
60234
60391
|
this.bootstrapBaseUrl = deps.bootstrapBaseUrl.replace(/\/$/, "");
|
|
60235
60392
|
this.bootstrapHost = hostOf(this.bootstrapBaseUrl);
|
|
60236
60393
|
this.observation = deps.observation;
|
|
60237
60394
|
this.retry = deps.retry ?? DEFAULT_RETRY;
|
|
60395
|
+
this.cache = deps.cache;
|
|
60238
60396
|
this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
60239
60397
|
}
|
|
60240
60398
|
resolve(input) {
|
|
60241
|
-
|
|
60399
|
+
const fetched = this.attempt(input).pipe(Effect.retry({
|
|
60242
60400
|
times: this.retry.attempts,
|
|
60243
60401
|
while: isTransientBootstrapFailure,
|
|
60244
60402
|
schedule: Schedule.exponential(Duration.millis(this.retry.baseDelayMs))
|
|
60245
60403
|
}));
|
|
60404
|
+
const cache = this.cache;
|
|
60405
|
+
if (cache === void 0) return fetched;
|
|
60406
|
+
return Effect.promise(() => cachedEnvelope(cache, Date.now())).pipe(Effect.filterOrElse((cached) => cached !== null, () => fetched));
|
|
60246
60407
|
}
|
|
60247
60408
|
attempt(input) {
|
|
60248
60409
|
return Effect.tryPromise({
|
|
@@ -60267,7 +60428,13 @@ var HttpBootstrapAdapter = class {
|
|
|
60267
60428
|
}
|
|
60268
60429
|
mapResponse(res, input) {
|
|
60269
60430
|
if (res.ok) return Effect.tryPromise({
|
|
60270
|
-
try: async () =>
|
|
60431
|
+
try: async () => {
|
|
60432
|
+
const body = await res.json();
|
|
60433
|
+
const resolution = decodeBootstrapBody(body);
|
|
60434
|
+
const cache = this.cache;
|
|
60435
|
+
if (cache !== void 0) await Promise.resolve().then(() => cache.save(body)).catch(() => void 0);
|
|
60436
|
+
return resolution;
|
|
60437
|
+
},
|
|
60271
60438
|
catch: (cause) => {
|
|
60272
60439
|
if (cause instanceof CapxulError && cause.code === "INVALID_INPUT") return bootstrapErrorFromCapxul("invalidInput", cause);
|
|
60273
60440
|
return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
|
|
@@ -61934,7 +62101,9 @@ function makeNotImplementedOrganizationPaymentsMethods() {
|
|
|
61934
62101
|
subscribe: organizationPaymentExecutionUnavailable,
|
|
61935
62102
|
pay: organizationPaymentExecutionUnavailable,
|
|
61936
62103
|
payBatch: organizationPaymentExecutionUnavailable,
|
|
61937
|
-
retry: organizationPaymentExecutionUnavailable
|
|
62104
|
+
retry: organizationPaymentExecutionUnavailable,
|
|
62105
|
+
cancel: organizationPaymentExecutionUnavailable,
|
|
62106
|
+
redirect: organizationPaymentExecutionUnavailable
|
|
61938
62107
|
};
|
|
61939
62108
|
}
|
|
61940
62109
|
const orgMeUnavailable = () => Promise.resolve({
|
|
@@ -66127,6 +66296,23 @@ function subscribeConvexQuery(client, fn, args, callback, options) {
|
|
|
66127
66296
|
* ever needs a different one.
|
|
66128
66297
|
*/
|
|
66129
66298
|
const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
|
|
66299
|
+
/**
|
|
66300
|
+
* The fetcher the Convex client's auth handshake gets. Convex first asks for
|
|
66301
|
+
* "a possibly cached token" (`forceRefreshToken: false`) and, as soon as the
|
|
66302
|
+
* server accepts it, for a fresh one (`true`): the token whose `exp` it
|
|
66303
|
+
* schedules the next refresh from. The Better Auth clients keep no token: each
|
|
66304
|
+
* ask mints one over the network, so the cached ask was a second token fetch
|
|
66305
|
+
* and a second server verification (a JWKS read) on every session restore. It
|
|
66306
|
+
* answers "none" instead, and Convex makes its one fetch forced, as a fresh
|
|
66307
|
+
* token. Every forced ask still reaches the auth client: that first fetch, the
|
|
66308
|
+
* refresh before `exp`, and the retry after the server rejects a token.
|
|
66309
|
+
* ponytail: the OAuth bearer client can hold a cached exchange; it now pays one
|
|
66310
|
+
* exchange for the first authentication instead of reusing it (never more
|
|
66311
|
+
* fetches than before). Pass the cache through if that start ever matters.
|
|
66312
|
+
*/
|
|
66313
|
+
function freshTokenHandshake(fetchToken) {
|
|
66314
|
+
return async (args) => args.forceRefreshToken ? fetchToken(args) : null;
|
|
66315
|
+
}
|
|
66130
66316
|
/** Run a Convex call under the current trace parent, when there is one. */
|
|
66131
66317
|
const withTraceparent = (run) => Effect.flatMap(Effect.serviceOption(Tracer.ParentSpan), (parent) => run(parent._tag === "Some" ? formatTraceparent(parent.value) : void 0));
|
|
66132
66318
|
var ConvexCallAdapter = class {
|
|
@@ -66151,10 +66337,10 @@ var ConvexCallAdapter = class {
|
|
|
66151
66337
|
this.#tokenProvider = deps.tokenProvider;
|
|
66152
66338
|
this.#applicationId = deps.applicationId;
|
|
66153
66339
|
this.#observation = deps.observation;
|
|
66154
|
-
|
|
66340
|
+
this.refreshAuth();
|
|
66155
66341
|
}
|
|
66156
66342
|
refreshAuth() {
|
|
66157
|
-
if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
|
|
66343
|
+
if (this.#tokenProvider) this.#client.setAuth(freshTokenHandshake(this.#tokenProvider));
|
|
66158
66344
|
}
|
|
66159
66345
|
observeConnection(observer) {
|
|
66160
66346
|
return this.#connectionMonitor.observe(this.#client, observer);
|
|
@@ -67821,7 +68007,8 @@ async function createProductionAdapters(input) {
|
|
|
67821
68007
|
const bootstrapLayer = HttpBootstrapLayer({
|
|
67822
68008
|
bootstrapBaseUrl: resolvedInput.value.bootstrapBaseUrl,
|
|
67823
68009
|
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
67824
|
-
...invocationObservation === void 0 ? {} : { observation: invocationObservation }
|
|
68010
|
+
...invocationObservation === void 0 ? {} : { observation: invocationObservation },
|
|
68011
|
+
...input.bootstrapCache === void 0 ? {} : { cache: input.bootstrapCache }
|
|
67825
68012
|
});
|
|
67826
68013
|
try {
|
|
67827
68014
|
const bootstrapContext = await Effect.runPromise(Layer.buildWithScope(bootstrapLayer, scope));
|
|
@@ -68877,253 +69064,6 @@ function checkoutUrl(frontendOrigin, reference) {
|
|
|
68877
69064
|
return result.href;
|
|
68878
69065
|
}
|
|
68879
69066
|
//#endregion
|
|
68880
|
-
//#region src/observability/projection.ts
|
|
68881
|
-
const codes = [
|
|
68882
|
-
...CAPXUL_ERROR_CODES,
|
|
68883
|
-
"CLI_USAGE",
|
|
68884
|
-
"CLI_CONFIG",
|
|
68885
|
-
"CLI_STORAGE",
|
|
68886
|
-
"CLI_DEFECT",
|
|
68887
|
-
"CLI_DEADLINE"
|
|
68888
|
-
];
|
|
68889
|
-
/** A known Capxul or CLI error code, never a free-form string from a source. */
|
|
68890
|
-
const safeCode = (value) => codes.find((code) => code === value);
|
|
68891
|
-
/** Read one own data property without running a getter on a hostile value. */
|
|
68892
|
-
const own = (value, key) => {
|
|
68893
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
68894
|
-
try {
|
|
68895
|
-
return Object.getOwnPropertyDescriptor(value, key)?.value;
|
|
68896
|
-
} catch {
|
|
68897
|
-
return;
|
|
68898
|
-
}
|
|
68899
|
-
};
|
|
68900
|
-
/** The numeric and enumerated SDK failure facts a diagnostic log may keep. */
|
|
68901
|
-
function projectSdkProperties(properties) {
|
|
68902
|
-
const output = {};
|
|
68903
|
-
for (const key of [
|
|
68904
|
-
"chainId",
|
|
68905
|
-
"chain_id",
|
|
68906
|
-
"durationMs",
|
|
68907
|
-
"duration_ms",
|
|
68908
|
-
"http_status",
|
|
68909
|
-
"retry_count"
|
|
68910
|
-
]) {
|
|
68911
|
-
const value = own(properties, key);
|
|
68912
|
-
if (typeof value === "number" && Number.isFinite(value) && value >= 0) output[key] = value;
|
|
68913
|
-
}
|
|
68914
|
-
for (const key of [
|
|
68915
|
-
"error_kind",
|
|
68916
|
-
"error_code",
|
|
68917
|
-
"reason_code"
|
|
68918
|
-
]) {
|
|
68919
|
-
const value = safeCode(own(properties, key));
|
|
68920
|
-
if (value !== void 0) output[key] = value;
|
|
68921
|
-
}
|
|
68922
|
-
const operation = own(properties, "operation");
|
|
68923
|
-
if (isCapxulOperation(operation)) output.operation = operation;
|
|
68924
|
-
const failureMode = FAILURE_MODES$1.find((mode) => mode === own(properties, "failure_mode"));
|
|
68925
|
-
if (failureMode !== void 0) output.failure_mode = failureMode;
|
|
68926
|
-
return output;
|
|
68927
|
-
}
|
|
68928
|
-
/** A source-only `$exception`: basenames, function names and lines, never messages or paths. */
|
|
68929
|
-
function projectException(properties, command, kind = "CLI_DEFECT") {
|
|
68930
|
-
const sanitized = sanitizePostHogBrowserEvent({
|
|
68931
|
-
event: "$exception",
|
|
68932
|
-
properties: { ...properties }
|
|
68933
|
-
}).properties;
|
|
68934
|
-
const exceptions = own(sanitized, "$exception_list");
|
|
68935
|
-
const stacktrace = own(Array.isArray(exceptions) ? exceptions[0] : void 0, "stacktrace");
|
|
68936
|
-
const rawFrames = own(stacktrace, "frames");
|
|
68937
|
-
const frames = (Array.isArray(rawFrames) ? rawFrames : []).slice(0, 12).map((frame) => {
|
|
68938
|
-
const filename = own(frame, "filename");
|
|
68939
|
-
const basename = typeof filename === "string" ? filename.split(/[\\/]/u).at(-1)?.split(/[?#]/u)[0] : void 0;
|
|
68940
|
-
const name = own(frame, "function");
|
|
68941
|
-
return {
|
|
68942
|
-
platform: "node:javascript",
|
|
68943
|
-
filename: `capxul-cli:///${basename !== void 0 && /^[A-Za-z0-9._-]{1,96}$/u.test(basename) ? basename : "boundary"}`,
|
|
68944
|
-
function: typeof name === "string" && /^[A-Za-z_$<>][A-Za-z0-9_$.<>-]{0,95}$/u.test(name) ? name : "<anonymous>",
|
|
68945
|
-
lineno: boundedLine(own(frame, "lineno")),
|
|
68946
|
-
colno: boundedLine(own(frame, "colno"))
|
|
68947
|
-
};
|
|
68948
|
-
});
|
|
68949
|
-
return {
|
|
68950
|
-
$exception_type: kind,
|
|
68951
|
-
$exception_message: kind === "CLI_DEFECT" ? "CLI command failed" : "SDK operation failed",
|
|
68952
|
-
$exception_level: "error",
|
|
68953
|
-
$exception_fingerprint: `cli.${command}.${kind}:${frames[0]?.filename ?? "boundary"}:${frames[0]?.function ?? "unknown"}`,
|
|
68954
|
-
$issue_name: `${kind === "CLI_DEFECT" ? "CLI" : "SDK"} ${command} defect`,
|
|
68955
|
-
$exception_list: [{
|
|
68956
|
-
type: kind,
|
|
68957
|
-
value: kind === "CLI_DEFECT" ? "CLI command failed" : "SDK operation failed",
|
|
68958
|
-
mechanism: {
|
|
68959
|
-
handled: true,
|
|
68960
|
-
type: kind === "CLI_DEFECT" ? "capxul_cli_boundary" : "capxul_cli_sdk_boundary"
|
|
68961
|
-
},
|
|
68962
|
-
stacktrace: {
|
|
68963
|
-
type: "raw",
|
|
68964
|
-
frames
|
|
68965
|
-
}
|
|
68966
|
-
}]
|
|
68967
|
-
};
|
|
68968
|
-
}
|
|
68969
|
-
const boundedLine = (value) => typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= 1e7 ? value : null;
|
|
68970
|
-
//#endregion
|
|
68971
|
-
//#region src/observability/capture.ts
|
|
68972
|
-
const isTelemetryEventName = (event) => Object.hasOwn(TELEMETRY_EVENT_SCHEMAS, event);
|
|
68973
|
-
const CAPTURE_BYTES = 8192;
|
|
68974
|
-
const textEncoder = new TextEncoder();
|
|
68975
|
-
const IDENTITY_KEYS = /* @__PURE__ */ new Set([
|
|
68976
|
-
"actor_id",
|
|
68977
|
-
"actor_kind",
|
|
68978
|
-
"auth_user_id",
|
|
68979
|
-
"anonymous_id",
|
|
68980
|
-
"cli_identity_kind",
|
|
68981
|
-
"distinct_id",
|
|
68982
|
-
"$groups"
|
|
68983
|
-
]);
|
|
68984
|
-
/** The bounded PostHog capture queue writers for one invocation. Nothing here sends. */
|
|
68985
|
-
const makeCaptures = (options) => {
|
|
68986
|
-
const { input, token, envelope, enabled, clock, queue, state } = options;
|
|
68987
|
-
const enqueue = (body) => {
|
|
68988
|
-
if (textEncoder.encode(body).byteLength > CAPTURE_BYTES || !Queue.offerUnsafe(queue, body)) state.dropped += 1;
|
|
68989
|
-
};
|
|
68990
|
-
const capture = (event, properties, identity) => {
|
|
68991
|
-
if (!enabled()) {
|
|
68992
|
-
state.dropped += 1;
|
|
68993
|
-
return;
|
|
68994
|
-
}
|
|
68995
|
-
const occurredAt = identity?.observedAt ?? clock.currentTimeMillisUnsafe();
|
|
68996
|
-
const record = sanitizePostHogBrowserEvent({
|
|
68997
|
-
event,
|
|
68998
|
-
properties: {
|
|
68999
|
-
...properties,
|
|
69000
|
-
...envelope,
|
|
69001
|
-
occurred_at: occurredAt,
|
|
69002
|
-
...state.nativeContext
|
|
69003
|
-
}
|
|
69004
|
-
});
|
|
69005
|
-
if (event === "access_recovery_completed") {
|
|
69006
|
-
const checked = Schema.decodeUnknownOption(TELEMETRY_EVENT_SCHEMAS.access_recovery_completed)({
|
|
69007
|
-
name: event,
|
|
69008
|
-
props: properties
|
|
69009
|
-
});
|
|
69010
|
-
if (Option.isSome(checked) && checked.value.name === "access_recovery_completed") record.properties.recovery = checked.value.props.recovery;
|
|
69011
|
-
}
|
|
69012
|
-
enqueue(JSON.stringify({
|
|
69013
|
-
api_key: token,
|
|
69014
|
-
...record,
|
|
69015
|
-
uuid: identity?.eventId ?? crypto.randomUUID(),
|
|
69016
|
-
timestamp: new Date(occurredAt).toISOString(),
|
|
69017
|
-
properties: {
|
|
69018
|
-
...record.properties,
|
|
69019
|
-
distinct_id: input.anonymousIdentity?.id ?? input.invocationId,
|
|
69020
|
-
$process_person_profile: false
|
|
69021
|
-
}
|
|
69022
|
-
}));
|
|
69023
|
-
};
|
|
69024
|
-
/**
|
|
69025
|
-
* Join this epoch's anonymous history to the person AUTH verified.
|
|
69026
|
-
*
|
|
69027
|
-
* This is a PostHog lifecycle event, not a catalog event: it carries the
|
|
69028
|
-
* person as `distinct_id` and the epoch as `$anon_distinct_id`, and
|
|
69029
|
-
* deliberately omits `$process_person_profile` so the merge is processed —
|
|
69030
|
-
* every catalog event here sets it false, which is what keeps those events off
|
|
69031
|
-
* the person until this record exists.
|
|
69032
|
-
*
|
|
69033
|
-
* The binding's own stored `eventId` is the record UUID, so PostHog
|
|
69034
|
-
* deduplicates a replayed merge. The same idiom `cli_first_used` uses.
|
|
69035
|
-
*
|
|
69036
|
-
* No `$set` traits: identify traits bypass the per-event redactor, and this
|
|
69037
|
-
* module has no pre-minimized person fact to send. The merge is the whole
|
|
69038
|
-
* payload.
|
|
69039
|
-
*/
|
|
69040
|
-
const captureIdentify = (authUserId, anonymousId, eventId, occurredAt) => {
|
|
69041
|
-
if (!enabled()) {
|
|
69042
|
-
state.dropped += 1;
|
|
69043
|
-
return;
|
|
69044
|
-
}
|
|
69045
|
-
enqueue(JSON.stringify({
|
|
69046
|
-
api_key: token,
|
|
69047
|
-
event: "$identify",
|
|
69048
|
-
uuid: eventId,
|
|
69049
|
-
timestamp: new Date(occurredAt).toISOString(),
|
|
69050
|
-
properties: {
|
|
69051
|
-
capxul_env: input.environment,
|
|
69052
|
-
distinct_id: authUserId,
|
|
69053
|
-
$anon_distinct_id: anonymousId
|
|
69054
|
-
}
|
|
69055
|
-
}));
|
|
69056
|
-
};
|
|
69057
|
-
const captureSdkDiagnostic = (event, properties) => {
|
|
69058
|
-
const code = safeCode(own(properties, "error_kind")) ?? (event === "$exception" ? "UNKNOWN" : void 0);
|
|
69059
|
-
const operation = own(properties, "operation");
|
|
69060
|
-
if (code === void 0 || state.sdkDiagnostics.length >= 32) {
|
|
69061
|
-
state.dropped += 1;
|
|
69062
|
-
return;
|
|
69063
|
-
}
|
|
69064
|
-
state.sdkDiagnostics.push({
|
|
69065
|
-
log: {
|
|
69066
|
-
...projectSdkProperties(properties),
|
|
69067
|
-
error_code: code,
|
|
69068
|
-
...isCapxulOperation(operation) ? { operation } : {}
|
|
69069
|
-
},
|
|
69070
|
-
...event === "$exception" ? { exception: projectException(properties, input.command, "SDK_DEFECT") } : {}
|
|
69071
|
-
});
|
|
69072
|
-
};
|
|
69073
|
-
return {
|
|
69074
|
-
capture,
|
|
69075
|
-
captureIdentify,
|
|
69076
|
-
client: { capture: (event, properties) => {
|
|
69077
|
-
if (!enabled()) return;
|
|
69078
|
-
if (event === "$exception" || event === "capxul_sdk_expected_outcome") {
|
|
69079
|
-
captureSdkDiagnostic(event, properties);
|
|
69080
|
-
return;
|
|
69081
|
-
}
|
|
69082
|
-
const catalog = isTelemetryEventName(event) ? TELEMETRY_EVENT_SCHEMAS[event] : void 0;
|
|
69083
|
-
if (catalog === void 0) {
|
|
69084
|
-
state.dropped += 1;
|
|
69085
|
-
return;
|
|
69086
|
-
}
|
|
69087
|
-
const props = {};
|
|
69088
|
-
const shape = catalog.fields.props;
|
|
69089
|
-
const members = "fields" in shape ? [shape] : shape.members;
|
|
69090
|
-
for (const member of members) for (const key of Object.keys(member.fields)) {
|
|
69091
|
-
if (IDENTITY_KEYS.has(key)) continue;
|
|
69092
|
-
const value = own(properties, key);
|
|
69093
|
-
if (value !== void 0) props[key] = value;
|
|
69094
|
-
}
|
|
69095
|
-
const decoded = Schema.decodeUnknownOption(catalog)({
|
|
69096
|
-
name: event,
|
|
69097
|
-
props: {
|
|
69098
|
-
...props,
|
|
69099
|
-
...envelope
|
|
69100
|
-
}
|
|
69101
|
-
});
|
|
69102
|
-
if (Option.isSome(decoded) && Queue.sizeUnsafe(queue) < 29) capture(event, { ...decoded.value.props });
|
|
69103
|
-
else state.dropped += 1;
|
|
69104
|
-
} }
|
|
69105
|
-
};
|
|
69106
|
-
};
|
|
69107
|
-
/** The SDK's host observability for this invocation, joined to the command's trace. */
|
|
69108
|
-
const sdkObservation = (options) => {
|
|
69109
|
-
const { input, client, enabled, deliveryBudget, traceparent } = options;
|
|
69110
|
-
return postHogObservability(client, {
|
|
69111
|
-
enabled,
|
|
69112
|
-
engineeringEnabled: enabled,
|
|
69113
|
-
engineeringPrivacy: "minimal",
|
|
69114
|
-
engineeringDeliveryBudget: deliveryBudget,
|
|
69115
|
-
capxulEnv: input.environment,
|
|
69116
|
-
context: {
|
|
69117
|
-
application: "capxul-cli",
|
|
69118
|
-
...input.anonymousIdentity === void 0 ? {} : { anonymousId: input.anonymousIdentity.id },
|
|
69119
|
-
release: input.cliVersion,
|
|
69120
|
-
correlationId: input.invocationId,
|
|
69121
|
-
journeyId: input.invocationId,
|
|
69122
|
-
traceparent
|
|
69123
|
-
}
|
|
69124
|
-
});
|
|
69125
|
-
};
|
|
69126
|
-
//#endregion
|
|
69127
69067
|
//#region src/platform/state-faults.ts
|
|
69128
69068
|
var StateFileError = class extends Data.TaggedError("StateFileError") {
|
|
69129
69069
|
get message() {
|
|
@@ -69412,6 +69352,108 @@ function walletDetails(error) {
|
|
|
69412
69352
|
};
|
|
69413
69353
|
}
|
|
69414
69354
|
//#endregion
|
|
69355
|
+
//#region src/runtime/amount.ts
|
|
69356
|
+
/** Money as people read it, with at least two decimals: `550.00 USDC`. */
|
|
69357
|
+
const shownAmount = (amount) => {
|
|
69358
|
+
const [number = "", symbol = ""] = formatAssetAmount(amount).split(" ");
|
|
69359
|
+
const [integer = "0", fraction = ""] = number.split(".");
|
|
69360
|
+
return `${integer}.${fraction.padEnd(2, "0")} ${symbol}`;
|
|
69361
|
+
};
|
|
69362
|
+
/** The number of `shownAmount`, for a table whose column names the asset: `1,000.00`. */
|
|
69363
|
+
const shownNumber = (amount) => {
|
|
69364
|
+
try {
|
|
69365
|
+
const shown = shownAmount(amount);
|
|
69366
|
+
return shown.slice(0, shown.lastIndexOf(" "));
|
|
69367
|
+
} catch {
|
|
69368
|
+
return amount.value;
|
|
69369
|
+
}
|
|
69370
|
+
};
|
|
69371
|
+
/** `USDC on Base Sepolia`, as `balance` names it. An asset this CLI does not know keeps its id. */
|
|
69372
|
+
const assetName = (asset) => {
|
|
69373
|
+
try {
|
|
69374
|
+
const { symbol, chainId } = requireAsset(toAssetId(asset));
|
|
69375
|
+
return `${symbol} on ${networkName(chainId)}`;
|
|
69376
|
+
} catch {
|
|
69377
|
+
return asset;
|
|
69378
|
+
}
|
|
69379
|
+
};
|
|
69380
|
+
/** A quantity in an asset's smallest units as a decimal string: `998230000` is `998.23`. */
|
|
69381
|
+
const fromBaseUnits = (raw, { decimals }) => {
|
|
69382
|
+
const digits = raw.padStart(decimals + 1, "0");
|
|
69383
|
+
const whole = digits.slice(0, digits.length - decimals);
|
|
69384
|
+
const fraction = digits.slice(digits.length - decimals).replace(/0+$/u, "");
|
|
69385
|
+
return fraction === "" ? whole : `${whole}.${fraction}`;
|
|
69386
|
+
};
|
|
69387
|
+
/** A signed raw-unit quantity as `shownNumber` shows it, or undefined for an asset this CLI does not know. */
|
|
69388
|
+
const shownBaseUnits = (asset, raw) => {
|
|
69389
|
+
try {
|
|
69390
|
+
const id = toAssetId(asset);
|
|
69391
|
+
const sign = raw.startsWith("-") ? "-" : "";
|
|
69392
|
+
const value = fromBaseUnits(raw.slice(sign.length), requireAsset(id));
|
|
69393
|
+
return `${sign}${shownNumber({
|
|
69394
|
+
asset: id,
|
|
69395
|
+
value
|
|
69396
|
+
})}`;
|
|
69397
|
+
} catch {
|
|
69398
|
+
return;
|
|
69399
|
+
}
|
|
69400
|
+
};
|
|
69401
|
+
//#endregion
|
|
69402
|
+
//#region src/runtime/money-refusal.ts
|
|
69403
|
+
/** The asset a refusal names, by symbol or token address, among the ones this CLI knows. */
|
|
69404
|
+
const namedAsset = (details) => Object.values(TOKENS.BASE_SEPOLIA).map((id) => requireAsset(id)).find((asset) => asset.symbol === details.asset || typeof details.token === "string" && asset.tokenAddress.toLowerCase() === details.token.toLowerCase());
|
|
69405
|
+
/** INSUFFICIENT_BALANCE carries base units; a person reads `998.23 USDC`. */
|
|
69406
|
+
const insufficientBalance = (details) => {
|
|
69407
|
+
const asset = details === void 0 ? void 0 : namedAsset(details);
|
|
69408
|
+
const { available, required } = details ?? {};
|
|
69409
|
+
if (asset === void 0 || typeof available !== "string" || typeof required !== "string" || !/^\d+$/u.test(available) || !/^\d+$/u.test(required)) return "The balance is too low for this payment.";
|
|
69410
|
+
const shown = (raw) => shownAmount({
|
|
69411
|
+
asset: asset.assetId,
|
|
69412
|
+
value: fromBaseUnits(raw, asset)
|
|
69413
|
+
});
|
|
69414
|
+
return `You have ${shown(available)}; this needs ${shown(required)}.`;
|
|
69415
|
+
};
|
|
69416
|
+
const invalidRecipient = (reason) => typeof reason === "string" && /^[\w .,'@-]{1,120}$/u.test(reason) ? `The recipient was refused: ${reason.replace(/\.$/u, "")}.` : "The recipient was refused.";
|
|
69417
|
+
const requestKey = (reason) => /already used/u.test(String(reason)) ? "This request key was used for a different payment. Use a new --request-key, or send the same payment again to replay it." : "The request key was refused. Use a different --request-key.";
|
|
69418
|
+
/** An amount in major units, as a person reads it; `undefined` for anything else. */
|
|
69419
|
+
const heldAmount = (value) => {
|
|
69420
|
+
if (!isRecord(value) || typeof value.asset !== "string" || typeof value.value !== "string") return void 0;
|
|
69421
|
+
try {
|
|
69422
|
+
return shownAmount({
|
|
69423
|
+
asset: toAssetId(value.asset),
|
|
69424
|
+
value: value.value
|
|
69425
|
+
});
|
|
69426
|
+
} catch {
|
|
69427
|
+
return;
|
|
69428
|
+
}
|
|
69429
|
+
};
|
|
69430
|
+
/**
|
|
69431
|
+
* Why an Organization's Budget refused a spend. `not_member` and
|
|
69432
|
+
* `condition_violation` are left out: permission commands raise them too, with
|
|
69433
|
+
* no more cause than the label, so a Budget sentence would mislead there.
|
|
69434
|
+
*/
|
|
69435
|
+
const BUDGET_REFUSAL = {
|
|
69436
|
+
over_cap: "This payment is over the per-payment cap of your Budget.",
|
|
69437
|
+
daily_cap: "This payment would go over your Budget's daily allowance.",
|
|
69438
|
+
disallowed_recipient: "Your Budget does not allow paying this recipient."
|
|
69439
|
+
};
|
|
69440
|
+
const budgetRefusal = (details) => {
|
|
69441
|
+
if (details.reason === "budget_exhausted") {
|
|
69442
|
+
const left = heldAmount(details.remaining);
|
|
69443
|
+
const needs = heldAmount(details.required);
|
|
69444
|
+
return left === void 0 || needs === void 0 ? "Your Budget has run out for this period." : `Your Budget has ${left} left; this needs ${needs}.`;
|
|
69445
|
+
}
|
|
69446
|
+
return typeof details.reason === "string" ? BUDGET_REFUSAL[details.reason] : void 0;
|
|
69447
|
+
};
|
|
69448
|
+
/** The sentence for a payment refusal the backend explains in `details`, or nothing for any other. */
|
|
69449
|
+
const moneyRefusalMessage = (error) => {
|
|
69450
|
+
if (error.code === "ROLE_PERMISSION_DENIED") return error.details === void 0 ? void 0 : budgetRefusal(error.details);
|
|
69451
|
+
if (error.code === "INSUFFICIENT_BALANCE") return insufficientBalance(error.details);
|
|
69452
|
+
if (error.code === "INVALID_RECIPIENT") return invalidRecipient(error.details?.reason);
|
|
69453
|
+
if (error.code === "SUPERSEDED") return "Another attempt at this payment went first, so this one was not sent.";
|
|
69454
|
+
if (error.code === "INVALID_INPUT" && error.details?.field === "requestKey") return requestKey(error.details.reason);
|
|
69455
|
+
};
|
|
69456
|
+
//#endregion
|
|
69415
69457
|
//#region src/runtime/usage-failure.ts
|
|
69416
69458
|
/** A command or flag name as the CLI declares them. */
|
|
69417
69459
|
const NAME = /^[a-z][a-z0-9-]{0,63}$/u;
|
|
@@ -69457,23 +69499,65 @@ function helpFor(failures, command) {
|
|
|
69457
69499
|
] : ["capxul", "--help"];
|
|
69458
69500
|
}
|
|
69459
69501
|
/**
|
|
69460
|
-
*
|
|
69461
|
-
*
|
|
69462
|
-
*
|
|
69502
|
+
* Commands #2294 removed, by the path a person types, and what replaced them.
|
|
69503
|
+
* The closest name by spelling would suggest a command that does something else
|
|
69504
|
+
* (`org use` is near `org me`), so these are named by hand. `takes` is an
|
|
69505
|
+
* argument the replacement needs that the old command also took.
|
|
69506
|
+
*/
|
|
69507
|
+
const REPLACED = {
|
|
69508
|
+
"org use": {
|
|
69509
|
+
to: ["use", "org"],
|
|
69510
|
+
takes: "<handle>"
|
|
69511
|
+
},
|
|
69512
|
+
"org get": { to: ["org", "show"] },
|
|
69513
|
+
"org status": { to: ["org", "show"] },
|
|
69514
|
+
"account status": { to: ["account", "show"] },
|
|
69515
|
+
telemetry: { to: ["config", "telemetry"] }
|
|
69516
|
+
};
|
|
69517
|
+
/**
|
|
69518
|
+
* The command line with a mistyped or removed command corrected: the typed
|
|
69519
|
+
* path up to the typo, the command to run instead, then the command words typed
|
|
69520
|
+
* after it that the tree still has, then the `--org` and `--env` typed (without
|
|
69521
|
+
* them `org payroll run` refuses bare). Other IDs, handles, flags and values are
|
|
69522
|
+
* left out, so nothing typed by mistake is echoed, and the line never carries a
|
|
69523
|
+
* value into a place that refuses it.
|
|
69463
69524
|
*/
|
|
69464
|
-
function correction(failures, args) {
|
|
69525
|
+
function correction(failures, args, kind) {
|
|
69465
69526
|
const unknown = failures.find((failure) => failure instanceof CliError.UnknownSubcommand);
|
|
69466
|
-
const suggested = unknown?.suggestions.find((candidate) => NAME.test(candidate));
|
|
69467
69527
|
const parent = unknown?.parent ?? ["capxul"];
|
|
69468
|
-
if (
|
|
69528
|
+
if (unknown === void 0 || !parent.every((part) => NAME.test(part))) return void 0;
|
|
69529
|
+
const replaced = REPLACED[[...parent.slice(1), unknown.subcommand].join(" ")];
|
|
69530
|
+
const suggested = unknown.suggestions.find((candidate) => NAME.test(candidate));
|
|
69531
|
+
const base = replaced === void 0 ? [...parent, suggested] : ["capxul", ...replaced.to];
|
|
69532
|
+
if (!base.every((part) => part !== void 0)) return void 0;
|
|
69469
69533
|
const at = args.indexOf(unknown.subcommand);
|
|
69470
|
-
const after = at === -1 ? [] : args.slice(at + 1);
|
|
69534
|
+
const after = at === -1 || replaced?.takes !== void 0 ? [] : args.slice(at + 1);
|
|
69471
69535
|
const stop = after.findIndex((arg) => !NAME.test(arg));
|
|
69472
|
-
|
|
69473
|
-
|
|
69474
|
-
|
|
69475
|
-
|
|
69476
|
-
|
|
69536
|
+
const carried = [];
|
|
69537
|
+
for (const word of stop === -1 ? after : after.slice(0, stop)) {
|
|
69538
|
+
if (kind !== void 0 && kind([
|
|
69539
|
+
...base.slice(1),
|
|
69540
|
+
...carried,
|
|
69541
|
+
word
|
|
69542
|
+
]) === void 0) break;
|
|
69543
|
+
carried.push(word);
|
|
69544
|
+
}
|
|
69545
|
+
const argv = [...base, ...carried];
|
|
69546
|
+
const scope = scopeArgs(args);
|
|
69547
|
+
const hint = `Did you mean: ${[
|
|
69548
|
+
...argv,
|
|
69549
|
+
...replaced?.takes === void 0 ? [] : [replaced.takes],
|
|
69550
|
+
...scope
|
|
69551
|
+
].join(" ")}`;
|
|
69552
|
+
const bare = replaced?.takes !== void 0 || kind?.(argv.slice(1)) === "group";
|
|
69553
|
+
return {
|
|
69554
|
+
argv: [
|
|
69555
|
+
...argv,
|
|
69556
|
+
...scope,
|
|
69557
|
+
...bare ? ["--help"] : []
|
|
69558
|
+
],
|
|
69559
|
+
hint
|
|
69560
|
+
};
|
|
69477
69561
|
}
|
|
69478
69562
|
/** The flag or argument a refusal names. */
|
|
69479
69563
|
const fieldOf = (failures) => {
|
|
@@ -69483,18 +69567,18 @@ const fieldOf = (failures) => {
|
|
|
69483
69567
|
if (failure instanceof CliError.MissingArgument && NAME.test(failure.argument)) return failure.argument;
|
|
69484
69568
|
}
|
|
69485
69569
|
};
|
|
69486
|
-
function usageFailure(error, command, args = []) {
|
|
69570
|
+
function usageFailure(error, command, args = [], kind) {
|
|
69487
69571
|
const failures = error instanceof CliError.ShowHelp ? error.errors : [error];
|
|
69488
69572
|
const missing = failures.flatMap((failure) => failure instanceof CliError.MissingOption && NAME.test(failure.option) ? [`--${failure.option}`] : []);
|
|
69489
69573
|
const sentences = [...missing.length === 0 ? [] : [`Missing required flag${missing.length === 1 ? "" : "s"}: ${[...new Set(missing)].join(", ")}.`], ...new Set(failures.flatMap((failure) => refusal(failure) ?? []))];
|
|
69490
|
-
const corrected = correction(failures, args);
|
|
69574
|
+
const corrected = correction(failures, args, kind);
|
|
69491
69575
|
const field = fieldOf(failures);
|
|
69492
69576
|
return {
|
|
69493
69577
|
code: "CLI_USAGE",
|
|
69494
69578
|
message: sentences.length === 0 ? "Invalid command or input." : sentences.join(" "),
|
|
69495
69579
|
...corrected === void 0 ? { next: helpFor(failures, command) } : {
|
|
69496
|
-
hint:
|
|
69497
|
-
next: corrected
|
|
69580
|
+
hint: corrected.hint,
|
|
69581
|
+
next: corrected.argv
|
|
69498
69582
|
},
|
|
69499
69583
|
...field === void 0 ? {} : { field },
|
|
69500
69584
|
exitCode: 2,
|
|
@@ -69561,6 +69645,7 @@ const applicationMessage = (error, command, wallet) => {
|
|
|
69561
69645
|
if (error.code === "WRONG_STATE") return wrongStateMessage(error.details?.currentState, command);
|
|
69562
69646
|
if (error.code === "INVALID_INPUT" || error.code === "OTP_EXPIRED") return authCommand(command) ? authInputMessage(error) : "The input was refused. Check the command arguments.";
|
|
69563
69647
|
if (error.code === "CAPABILITY_UNAVAILABLE" && error.details?.reason === "backend-function-missing") return backendVersionMessage(error.details.path);
|
|
69648
|
+
if (error.code === "UNKNOWN" && error.details?.reason === "backend-error-redacted") return redactedBackendMessage(error.details.requestId);
|
|
69564
69649
|
if (error.code === "RATE_LIMITED" && typeof error.details?.retryAfterMs === "number") return `The Capxul request was refused: RATE_LIMITED. Try again in ${Math.ceil(error.details.retryAfterMs / 1e3)} seconds.`;
|
|
69565
69650
|
if (isExpectedOutcome(error.code)) return `The Capxul request was refused: ${error.code}.`;
|
|
69566
69651
|
return `The Capxul request failed: ${error.code}.`;
|
|
@@ -69569,6 +69654,10 @@ const applicationMessage = (error, command, wallet) => {
|
|
|
69569
69654
|
const backendVersionMessage = (path) => {
|
|
69570
69655
|
return `The backend does not have this command yet${typeof path === "string" && /^[a-z][A-Za-z0-9/_]*:[A-Za-z0-9_]+$/u.test(path) ? ` (no function ${path})` : ""}: it runs a different version from this CLI. Update the backend, or point CAPXUL_BOOTSTRAP_URL at a deployment that matches this CLI.`;
|
|
69571
69656
|
};
|
|
69657
|
+
/** A deployed backend redacts its own errors (#2304): only its request id is left to quote. */
|
|
69658
|
+
const redactedBackendMessage = (requestId) => {
|
|
69659
|
+
return `The backend refused the request and gave no detail.${typeof requestId === "string" && /^[A-Za-z0-9]{1,64}$/u.test(requestId) ? ` Quote request ${requestId} to Capxul support.` : ""} If the backend is newer than this CLI, updating the CLI may fix it.`;
|
|
69660
|
+
};
|
|
69572
69661
|
/**
|
|
69573
69662
|
* Exit codes by class: 1 a CLI defect, 2 refused input or state, 3 not signed
|
|
69574
69663
|
* in, 4 not permitted, 5 failed, 6 any other refusal the operation names as an
|
|
@@ -69607,7 +69696,7 @@ const configurationFailure = (message) => new CliFailure({
|
|
|
69607
69696
|
exitCode: 2,
|
|
69608
69697
|
outcome: "refused"
|
|
69609
69698
|
});
|
|
69610
|
-
function classifyFailure(cause, command, args) {
|
|
69699
|
+
function classifyFailure(cause, command, args, kind) {
|
|
69611
69700
|
const error = Cause.squash(cause);
|
|
69612
69701
|
if (Cause.hasInterruptsOnly(cause) || Terminal.isQuitError(error)) return {
|
|
69613
69702
|
code: "CANCELLED",
|
|
@@ -69617,7 +69706,7 @@ function classifyFailure(cause, command, args) {
|
|
|
69617
69706
|
};
|
|
69618
69707
|
if (error instanceof CliFailure) return error;
|
|
69619
69708
|
if (error instanceof StateFileError) return storageFailure(error);
|
|
69620
|
-
if (CliError.isCliError(error)) return usageFailure(error, command, args);
|
|
69709
|
+
if (CliError.isCliError(error)) return usageFailure(error, command, args, kind);
|
|
69621
69710
|
if (isCapxulError(error)) {
|
|
69622
69711
|
const wallet = walletDetails(error);
|
|
69623
69712
|
const details = {
|
|
@@ -69627,7 +69716,7 @@ function classifyFailure(cause, command, args) {
|
|
|
69627
69716
|
const signIn = error.code === "NOT_AUTHENTICATED" && wallet === void 0 && error.details?.field !== "publishableKey";
|
|
69628
69717
|
return {
|
|
69629
69718
|
code: error.code,
|
|
69630
|
-
message: applicationMessage(error, command, wallet),
|
|
69719
|
+
message: moneyRefusalMessage(error) ?? applicationMessage(error, command, wallet),
|
|
69631
69720
|
...signIn ? { next: SIGN_IN$1 } : {},
|
|
69632
69721
|
...details.field === void 0 ? {} : { field: details.field },
|
|
69633
69722
|
...error.mode === void 0 ? {} : { mode: error.mode },
|
|
@@ -69686,6 +69775,358 @@ function parsedCommand(cause) {
|
|
|
69686
69775
|
return CliError.isCliError(error) && error._tag === "ShowHelp" ? error.commandPath.slice(1).join(".") || "capxul" : "capxul";
|
|
69687
69776
|
}
|
|
69688
69777
|
//#endregion
|
|
69778
|
+
//#region src/runtime/command-metadata.ts
|
|
69779
|
+
var CliCommandMetadata = class extends Context.Service()("capxul-cli/CommandMetadata") {};
|
|
69780
|
+
const collectionControl = (name) => name === "config" || name === "config.telemetry" || name.startsWith("config.telemetry.");
|
|
69781
|
+
/** Resolve only a complete path supplied by the native parser. */
|
|
69782
|
+
function commandAtPath(root, path) {
|
|
69783
|
+
if (path[0] !== root.name) return void 0;
|
|
69784
|
+
let selected = root;
|
|
69785
|
+
for (const part of path.slice(1)) {
|
|
69786
|
+
const child = selected.subcommands.flatMap((group) => group.commands).find((candidate) => candidate.name === part || candidate.alias === part);
|
|
69787
|
+
if (child === void 0) return void 0;
|
|
69788
|
+
selected = child;
|
|
69789
|
+
}
|
|
69790
|
+
return selected;
|
|
69791
|
+
}
|
|
69792
|
+
/** What the tree has at a path of command words (without the root's name). */
|
|
69793
|
+
const pathKind = (root) => (path) => {
|
|
69794
|
+
const found = commandAtPath(root, [root.name, ...path]);
|
|
69795
|
+
return found === void 0 ? void 0 : found.subcommands.length > 0 ? "group" : "command";
|
|
69796
|
+
};
|
|
69797
|
+
const validateNativeScope = (input) => {
|
|
69798
|
+
if (typeof input === "object" && input !== null && "orgs" in input && Array.isArray(input.orgs) && input.orgs.every((id) => typeof id === "string") && new Set(input.orgs).size > 1) return Effect.fail(new CliError.InvalidValue({
|
|
69799
|
+
option: "org",
|
|
69800
|
+
value: "multiple IDs",
|
|
69801
|
+
expected: "one exact Organization ID",
|
|
69802
|
+
kind: "flag"
|
|
69803
|
+
}));
|
|
69804
|
+
return Effect.void;
|
|
69805
|
+
};
|
|
69806
|
+
/** Keep the native grammar and validate scope without business actions. */
|
|
69807
|
+
function nativeInputTree(node) {
|
|
69808
|
+
const input = Command.withHandler(node, validateNativeScope);
|
|
69809
|
+
const children = node.subcommands.flatMap((group) => {
|
|
69810
|
+
const commands = group.commands.map(nativeInputTree);
|
|
69811
|
+
return group.group === void 0 ? commands : [{
|
|
69812
|
+
group: group.group,
|
|
69813
|
+
commands
|
|
69814
|
+
}];
|
|
69815
|
+
});
|
|
69816
|
+
return Command.withSubcommands(input, children);
|
|
69817
|
+
}
|
|
69818
|
+
/** The command whose metadata is named `name`, searched from `root`. */
|
|
69819
|
+
const commandNamed = (root, name) => {
|
|
69820
|
+
const metadata = Context.getOption(root.annotations, CliCommandMetadata);
|
|
69821
|
+
if (Option.isSome(metadata) && metadata.value.name === name && root.subcommands.length === 0) return root;
|
|
69822
|
+
for (const child of root.subcommands.flatMap((group) => group.commands)) {
|
|
69823
|
+
const found = commandNamed(child, name);
|
|
69824
|
+
if (found !== void 0) return found;
|
|
69825
|
+
}
|
|
69826
|
+
};
|
|
69827
|
+
/** The flag inside `param` when it takes one value: nothing in it repeats, and it is no switch. */
|
|
69828
|
+
const oneValue = (param) => {
|
|
69829
|
+
if (Param.isSingle(param)) return param.primitiveType._tag === "Boolean" ? void 0 : param;
|
|
69830
|
+
if (param._tag === "Variadic") return void 0;
|
|
69831
|
+
const inner = readField(param, "param");
|
|
69832
|
+
return Param.isParam(inner) ? oneValue(inner) : void 0;
|
|
69833
|
+
};
|
|
69834
|
+
/**
|
|
69835
|
+
* The names of `node`'s flags that take one value, as typed (`--name`, `-n`).
|
|
69836
|
+
* The parser keeps the first of a repeated one and drops the rest without a
|
|
69837
|
+
* word, so the caller refuses a repeat. `config` is the command's own definition.
|
|
69838
|
+
*/
|
|
69839
|
+
const singleValueFlags = (node) => {
|
|
69840
|
+
const flags = readField(readField(node, "config"), "flags");
|
|
69841
|
+
return (Array.isArray(flags) ? flags : []).flatMap((flag) => {
|
|
69842
|
+
const single = Param.isParam(flag) ? oneValue(flag) : void 0;
|
|
69843
|
+
return single === void 0 ? [] : [`--${single.name}`, ...single.aliases.map((alias) => alias.length === 1 ? `-${alias}` : `--${alias}`)];
|
|
69844
|
+
});
|
|
69845
|
+
};
|
|
69846
|
+
/**
|
|
69847
|
+
* A refusal naming the first flag that takes one value and was typed more than
|
|
69848
|
+
* once: the parser keeps the first `--timeout-seconds` and `--fields` keeps the
|
|
69849
|
+
* last, both silently. `--fields` and `--env` are global flags.
|
|
69850
|
+
*/
|
|
69851
|
+
const repeatedFlag = (root, name, args) => {
|
|
69852
|
+
const node = root === void 0 ? void 0 : commandNamed(root, name);
|
|
69853
|
+
const end = args.indexOf("--");
|
|
69854
|
+
const typed = end < 0 ? args : args.slice(0, end);
|
|
69855
|
+
const repeated = [
|
|
69856
|
+
...node === void 0 ? [] : singleValueFlags(node),
|
|
69857
|
+
"--fields",
|
|
69858
|
+
"--env"
|
|
69859
|
+
].find((flag) => typed.filter((arg) => arg === flag || arg.startsWith(`${flag}=`)).length > 1);
|
|
69860
|
+
return repeated === void 0 ? void 0 : new CliFailure({
|
|
69861
|
+
code: "CLI_USAGE",
|
|
69862
|
+
message: `Provide ${repeated} once.`,
|
|
69863
|
+
field: repeated.replace(/^-+/u, ""),
|
|
69864
|
+
exitCode: 2,
|
|
69865
|
+
outcome: "refused"
|
|
69866
|
+
});
|
|
69867
|
+
};
|
|
69868
|
+
function metadataAtPath(root, path) {
|
|
69869
|
+
let selected = root;
|
|
69870
|
+
for (const part of path.slice(1)) {
|
|
69871
|
+
const child = selected.subcommands.flatMap((group) => group.commands).find((command) => command.name === part || command.alias === part);
|
|
69872
|
+
if (child === void 0) break;
|
|
69873
|
+
selected = child;
|
|
69874
|
+
}
|
|
69875
|
+
const metadata = Context.getOption(selected.annotations, CliCommandMetadata);
|
|
69876
|
+
return Option.isSome(metadata) && CLI_COMMAND_NAMES.includes(metadata.value.name) ? metadata.value : {
|
|
69877
|
+
name: "unknown",
|
|
69878
|
+
capability: "cli",
|
|
69879
|
+
observation: "command"
|
|
69880
|
+
};
|
|
69881
|
+
}
|
|
69882
|
+
//#endregion
|
|
69883
|
+
//#region src/observability/projection.ts
|
|
69884
|
+
const codes = [
|
|
69885
|
+
...CAPXUL_ERROR_CODES,
|
|
69886
|
+
"CLI_USAGE",
|
|
69887
|
+
"CLI_CONFIG",
|
|
69888
|
+
"CLI_STORAGE",
|
|
69889
|
+
"CLI_DEFECT",
|
|
69890
|
+
"CLI_DEADLINE"
|
|
69891
|
+
];
|
|
69892
|
+
/** A known Capxul or CLI error code, never a free-form string from a source. */
|
|
69893
|
+
const safeCode = (value) => codes.find((code) => code === value);
|
|
69894
|
+
/** Read one own data property without running a getter on a hostile value. */
|
|
69895
|
+
const own = (value, key) => {
|
|
69896
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
69897
|
+
try {
|
|
69898
|
+
return Object.getOwnPropertyDescriptor(value, key)?.value;
|
|
69899
|
+
} catch {
|
|
69900
|
+
return;
|
|
69901
|
+
}
|
|
69902
|
+
};
|
|
69903
|
+
/** The numeric and enumerated SDK failure facts a diagnostic log may keep. */
|
|
69904
|
+
function projectSdkProperties(properties) {
|
|
69905
|
+
const output = {};
|
|
69906
|
+
for (const key of [
|
|
69907
|
+
"chainId",
|
|
69908
|
+
"chain_id",
|
|
69909
|
+
"durationMs",
|
|
69910
|
+
"duration_ms",
|
|
69911
|
+
"http_status",
|
|
69912
|
+
"retry_count"
|
|
69913
|
+
]) {
|
|
69914
|
+
const value = own(properties, key);
|
|
69915
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) output[key] = value;
|
|
69916
|
+
}
|
|
69917
|
+
for (const key of [
|
|
69918
|
+
"error_kind",
|
|
69919
|
+
"error_code",
|
|
69920
|
+
"reason_code"
|
|
69921
|
+
]) {
|
|
69922
|
+
const value = safeCode(own(properties, key));
|
|
69923
|
+
if (value !== void 0) output[key] = value;
|
|
69924
|
+
}
|
|
69925
|
+
const operation = own(properties, "operation");
|
|
69926
|
+
if (isCapxulOperation(operation)) output.operation = operation;
|
|
69927
|
+
const failureMode = FAILURE_MODES$1.find((mode) => mode === own(properties, "failure_mode"));
|
|
69928
|
+
if (failureMode !== void 0) output.failure_mode = failureMode;
|
|
69929
|
+
return output;
|
|
69930
|
+
}
|
|
69931
|
+
/** A source-only `$exception`: basenames, function names and lines, never messages or paths. */
|
|
69932
|
+
function projectException(properties, command, kind = "CLI_DEFECT") {
|
|
69933
|
+
const sanitized = sanitizePostHogBrowserEvent({
|
|
69934
|
+
event: "$exception",
|
|
69935
|
+
properties: { ...properties }
|
|
69936
|
+
}).properties;
|
|
69937
|
+
const exceptions = own(sanitized, "$exception_list");
|
|
69938
|
+
const stacktrace = own(Array.isArray(exceptions) ? exceptions[0] : void 0, "stacktrace");
|
|
69939
|
+
const rawFrames = own(stacktrace, "frames");
|
|
69940
|
+
const frames = (Array.isArray(rawFrames) ? rawFrames : []).slice(0, 12).map((frame) => {
|
|
69941
|
+
const filename = own(frame, "filename");
|
|
69942
|
+
const basename = typeof filename === "string" ? filename.split(/[\\/]/u).at(-1)?.split(/[?#]/u)[0] : void 0;
|
|
69943
|
+
const name = own(frame, "function");
|
|
69944
|
+
return {
|
|
69945
|
+
platform: "node:javascript",
|
|
69946
|
+
filename: `capxul-cli:///${basename !== void 0 && /^[A-Za-z0-9._-]{1,96}$/u.test(basename) ? basename : "boundary"}`,
|
|
69947
|
+
function: typeof name === "string" && /^[A-Za-z_$<>][A-Za-z0-9_$.<>-]{0,95}$/u.test(name) ? name : "<anonymous>",
|
|
69948
|
+
lineno: boundedLine(own(frame, "lineno")),
|
|
69949
|
+
colno: boundedLine(own(frame, "colno"))
|
|
69950
|
+
};
|
|
69951
|
+
});
|
|
69952
|
+
return {
|
|
69953
|
+
$exception_type: kind,
|
|
69954
|
+
$exception_message: kind === "CLI_DEFECT" ? "CLI command failed" : "SDK operation failed",
|
|
69955
|
+
$exception_level: "error",
|
|
69956
|
+
$exception_fingerprint: `cli.${command}.${kind}:${frames[0]?.filename ?? "boundary"}:${frames[0]?.function ?? "unknown"}`,
|
|
69957
|
+
$issue_name: `${kind === "CLI_DEFECT" ? "CLI" : "SDK"} ${command} defect`,
|
|
69958
|
+
$exception_list: [{
|
|
69959
|
+
type: kind,
|
|
69960
|
+
value: kind === "CLI_DEFECT" ? "CLI command failed" : "SDK operation failed",
|
|
69961
|
+
mechanism: {
|
|
69962
|
+
handled: true,
|
|
69963
|
+
type: kind === "CLI_DEFECT" ? "capxul_cli_boundary" : "capxul_cli_sdk_boundary"
|
|
69964
|
+
},
|
|
69965
|
+
stacktrace: {
|
|
69966
|
+
type: "raw",
|
|
69967
|
+
frames
|
|
69968
|
+
}
|
|
69969
|
+
}]
|
|
69970
|
+
};
|
|
69971
|
+
}
|
|
69972
|
+
const boundedLine = (value) => typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= 1e7 ? value : null;
|
|
69973
|
+
//#endregion
|
|
69974
|
+
//#region src/observability/capture.ts
|
|
69975
|
+
const isTelemetryEventName = (event) => Object.hasOwn(TELEMETRY_EVENT_SCHEMAS, event);
|
|
69976
|
+
const CAPTURE_BYTES = 8192;
|
|
69977
|
+
const textEncoder = new TextEncoder();
|
|
69978
|
+
const IDENTITY_KEYS = /* @__PURE__ */ new Set([
|
|
69979
|
+
"actor_id",
|
|
69980
|
+
"actor_kind",
|
|
69981
|
+
"auth_user_id",
|
|
69982
|
+
"anonymous_id",
|
|
69983
|
+
"cli_identity_kind",
|
|
69984
|
+
"distinct_id",
|
|
69985
|
+
"$groups"
|
|
69986
|
+
]);
|
|
69987
|
+
/** The bounded PostHog capture queue writers for one invocation. Nothing here sends. */
|
|
69988
|
+
const makeCaptures = (options) => {
|
|
69989
|
+
const { input, token, envelope, enabled, clock, queue, state } = options;
|
|
69990
|
+
const enqueue = (body) => {
|
|
69991
|
+
if (textEncoder.encode(body).byteLength > CAPTURE_BYTES || !Queue.offerUnsafe(queue, body)) state.dropped += 1;
|
|
69992
|
+
};
|
|
69993
|
+
const capture = (event, properties, identity) => {
|
|
69994
|
+
if (!enabled()) {
|
|
69995
|
+
state.dropped += 1;
|
|
69996
|
+
return;
|
|
69997
|
+
}
|
|
69998
|
+
const occurredAt = identity?.observedAt ?? clock.currentTimeMillisUnsafe();
|
|
69999
|
+
const record = sanitizePostHogBrowserEvent({
|
|
70000
|
+
event,
|
|
70001
|
+
properties: {
|
|
70002
|
+
...properties,
|
|
70003
|
+
...envelope,
|
|
70004
|
+
occurred_at: occurredAt,
|
|
70005
|
+
...state.nativeContext
|
|
70006
|
+
}
|
|
70007
|
+
});
|
|
70008
|
+
if (event === "access_recovery_completed") {
|
|
70009
|
+
const checked = Schema.decodeUnknownOption(TELEMETRY_EVENT_SCHEMAS.access_recovery_completed)({
|
|
70010
|
+
name: event,
|
|
70011
|
+
props: properties
|
|
70012
|
+
});
|
|
70013
|
+
if (Option.isSome(checked) && checked.value.name === "access_recovery_completed") record.properties.recovery = checked.value.props.recovery;
|
|
70014
|
+
}
|
|
70015
|
+
enqueue(JSON.stringify({
|
|
70016
|
+
api_key: token,
|
|
70017
|
+
...record,
|
|
70018
|
+
uuid: identity?.eventId ?? crypto.randomUUID(),
|
|
70019
|
+
timestamp: new Date(occurredAt).toISOString(),
|
|
70020
|
+
properties: {
|
|
70021
|
+
...record.properties,
|
|
70022
|
+
distinct_id: input.anonymousIdentity?.id ?? input.invocationId,
|
|
70023
|
+
$process_person_profile: false
|
|
70024
|
+
}
|
|
70025
|
+
}));
|
|
70026
|
+
};
|
|
70027
|
+
/**
|
|
70028
|
+
* Join this epoch's anonymous history to the person AUTH verified.
|
|
70029
|
+
*
|
|
70030
|
+
* This is a PostHog lifecycle event, not a catalog event: it carries the
|
|
70031
|
+
* person as `distinct_id` and the epoch as `$anon_distinct_id`, and
|
|
70032
|
+
* deliberately omits `$process_person_profile` so the merge is processed —
|
|
70033
|
+
* every catalog event here sets it false, which is what keeps those events off
|
|
70034
|
+
* the person until this record exists.
|
|
70035
|
+
*
|
|
70036
|
+
* The binding's own stored `eventId` is the record UUID, so PostHog
|
|
70037
|
+
* deduplicates a replayed merge. The same idiom `cli_first_used` uses.
|
|
70038
|
+
*
|
|
70039
|
+
* No `$set` traits: identify traits bypass the per-event redactor, and this
|
|
70040
|
+
* module has no pre-minimized person fact to send. The merge is the whole
|
|
70041
|
+
* payload.
|
|
70042
|
+
*/
|
|
70043
|
+
const captureIdentify = (authUserId, anonymousId, eventId, occurredAt) => {
|
|
70044
|
+
if (!enabled()) {
|
|
70045
|
+
state.dropped += 1;
|
|
70046
|
+
return;
|
|
70047
|
+
}
|
|
70048
|
+
enqueue(JSON.stringify({
|
|
70049
|
+
api_key: token,
|
|
70050
|
+
event: "$identify",
|
|
70051
|
+
uuid: eventId,
|
|
70052
|
+
timestamp: new Date(occurredAt).toISOString(),
|
|
70053
|
+
properties: {
|
|
70054
|
+
capxul_env: input.environment,
|
|
70055
|
+
distinct_id: authUserId,
|
|
70056
|
+
$anon_distinct_id: anonymousId
|
|
70057
|
+
}
|
|
70058
|
+
}));
|
|
70059
|
+
};
|
|
70060
|
+
const captureSdkDiagnostic = (event, properties) => {
|
|
70061
|
+
const code = safeCode(own(properties, "error_kind")) ?? (event === "$exception" ? "UNKNOWN" : void 0);
|
|
70062
|
+
const operation = own(properties, "operation");
|
|
70063
|
+
if (code === void 0 || state.sdkDiagnostics.length >= 32) {
|
|
70064
|
+
state.dropped += 1;
|
|
70065
|
+
return;
|
|
70066
|
+
}
|
|
70067
|
+
state.sdkDiagnostics.push({
|
|
70068
|
+
log: {
|
|
70069
|
+
...projectSdkProperties(properties),
|
|
70070
|
+
error_code: code,
|
|
70071
|
+
...isCapxulOperation(operation) ? { operation } : {}
|
|
70072
|
+
},
|
|
70073
|
+
...event === "$exception" ? { exception: projectException(properties, input.command, "SDK_DEFECT") } : {}
|
|
70074
|
+
});
|
|
70075
|
+
};
|
|
70076
|
+
return {
|
|
70077
|
+
capture,
|
|
70078
|
+
captureIdentify,
|
|
70079
|
+
client: { capture: (event, properties) => {
|
|
70080
|
+
if (!enabled()) return;
|
|
70081
|
+
if (event === "$exception" || event === CAPXUL_SDK_EXPECTED_OUTCOME_EVENT) {
|
|
70082
|
+
captureSdkDiagnostic(event, properties);
|
|
70083
|
+
return;
|
|
70084
|
+
}
|
|
70085
|
+
const catalog = isTelemetryEventName(event) ? TELEMETRY_EVENT_SCHEMAS[event] : void 0;
|
|
70086
|
+
if (catalog === void 0) {
|
|
70087
|
+
state.dropped += 1;
|
|
70088
|
+
return;
|
|
70089
|
+
}
|
|
70090
|
+
const props = {};
|
|
70091
|
+
const shape = catalog.fields.props;
|
|
70092
|
+
const members = "fields" in shape ? [shape] : shape.members;
|
|
70093
|
+
for (const member of members) for (const key of Object.keys(member.fields)) {
|
|
70094
|
+
if (IDENTITY_KEYS.has(key)) continue;
|
|
70095
|
+
const value = own(properties, key);
|
|
70096
|
+
if (value !== void 0) props[key] = value;
|
|
70097
|
+
}
|
|
70098
|
+
const decoded = Schema.decodeUnknownOption(catalog)({
|
|
70099
|
+
name: event,
|
|
70100
|
+
props: {
|
|
70101
|
+
...props,
|
|
70102
|
+
...envelope
|
|
70103
|
+
}
|
|
70104
|
+
});
|
|
70105
|
+
if (Option.isSome(decoded) && Queue.sizeUnsafe(queue) < 29) capture(event, { ...decoded.value.props });
|
|
70106
|
+
else state.dropped += 1;
|
|
70107
|
+
} }
|
|
70108
|
+
};
|
|
70109
|
+
};
|
|
70110
|
+
/** The SDK's host observability for this invocation, joined to the command's trace. */
|
|
70111
|
+
const sdkObservation = (options) => {
|
|
70112
|
+
const { input, client, enabled, deliveryBudget, traceparent } = options;
|
|
70113
|
+
return postHogObservability(client, {
|
|
70114
|
+
enabled,
|
|
70115
|
+
engineeringEnabled: enabled,
|
|
70116
|
+
engineeringPrivacy: "minimal",
|
|
70117
|
+
engineeringDeliveryBudget: deliveryBudget,
|
|
70118
|
+
capxulEnv: input.environment,
|
|
70119
|
+
context: {
|
|
70120
|
+
application: "capxul-cli",
|
|
70121
|
+
...input.anonymousIdentity === void 0 ? {} : { anonymousId: input.anonymousIdentity.id },
|
|
70122
|
+
release: input.cliVersion,
|
|
70123
|
+
correlationId: input.invocationId,
|
|
70124
|
+
journeyId: input.invocationId,
|
|
70125
|
+
traceparent
|
|
70126
|
+
}
|
|
70127
|
+
});
|
|
70128
|
+
};
|
|
70129
|
+
//#endregion
|
|
69689
70130
|
//#region src/platform/state-files.ts
|
|
69690
70131
|
const Settings = Schema.Struct({
|
|
69691
70132
|
version: Schema.Literal(1),
|
|
@@ -70518,17 +70959,24 @@ function observeInvocation(input, work) {
|
|
|
70518
70959
|
//#region src/runtime/output.ts
|
|
70519
70960
|
const writeStdout = (text) => Effect.flatMap(Stdio.Stdio, (stdio) => Stream.run(Stream.make(text), stdio.stdout()));
|
|
70520
70961
|
const writeStderr = (text) => Effect.flatMap(Stdio.Stdio, (stdio) => Stream.run(Stream.make(text), stdio.stderr()));
|
|
70962
|
+
const showsProgress = (reader) => Effect.gen(function* () {
|
|
70963
|
+
if (reader.mode === "json") return true;
|
|
70964
|
+
return reader.stderrTty === true && (yield* (yield* Stdio.Stdio).stdoutIsTerminal);
|
|
70965
|
+
});
|
|
70966
|
+
/** A state or recovery line while the work has not failed, under `withWaiting`'s rule. */
|
|
70967
|
+
const writeProgress = (reader, text) => Effect.flatMap(showsProgress(reader), (shown) => shown ? writeStderr(text) : Effect.void);
|
|
70521
70968
|
/** Wrap pending work only. Keep terminal prompts outside this scope. */
|
|
70522
70969
|
const withWaiting = (work, options) => Effect.scoped(Effect.gen(function* () {
|
|
70523
70970
|
const stdio = yield* Stdio.Stdio;
|
|
70524
|
-
|
|
70525
|
-
if (options.mode === "human" && !
|
|
70526
|
-
|
|
70527
|
-
|
|
70528
|
-
|
|
70529
|
-
|
|
70530
|
-
|
|
70531
|
-
|
|
70971
|
+
if (!(yield* showsProgress(options))) return yield* work;
|
|
70972
|
+
if (!(options.mode === "human" && options.tty && !options.ci)) {
|
|
70973
|
+
yield* writeStderr(options.mode === "json" ? `${JSON.stringify({
|
|
70974
|
+
type: "cli.waiting",
|
|
70975
|
+
label: options.label,
|
|
70976
|
+
status: "waiting"
|
|
70977
|
+
})}\n` : `Waiting: ${terminalText(options.label)}\n`);
|
|
70978
|
+
return yield* work;
|
|
70979
|
+
}
|
|
70532
70980
|
const clear = "\r\x1B[2K";
|
|
70533
70981
|
const frames = [
|
|
70534
70982
|
"⠋",
|
|
@@ -70554,20 +71002,27 @@ const withWaiting = (work, options) => Effect.scoped(Effect.gen(function* () {
|
|
|
70554
71002
|
function renderResult(mode, envelope, lines = [], rawHuman) {
|
|
70555
71003
|
const pretty = process.stdout.isTTY;
|
|
70556
71004
|
if (mode === "json") return writeStdout(`${JSON.stringify(envelope, null, pretty ? 2 : void 0)}\n`);
|
|
70557
|
-
return envelope.outcome === "succeeded" ? writeStdout(rawHuman ?? `${(pretty ? lines.map(readableLine) : lines).join("\n")}\n`) : Effect.flatMap(Config.String("NO_COLOR").pipe(Config.withDefault(""), Effect.orElseSucceed(() => "")), (noColor) => writeStderr(errorText(envelope.error, process.stderr.isTTY && noColor === "")));
|
|
71005
|
+
return envelope.outcome === "succeeded" ? writeStdout(rawHuman ?? `${(pretty ? lines.map(readableLine) : lines).join("\n")}\n`) : Effect.flatMap(Config.String("NO_COLOR").pipe(Config.withDefault(""), Effect.orElseSucceed(() => "")), (noColor) => writeStderr(errorText(envelope.error, process.stderr.isTTY && noColor === "", errorRef(envelope.error.details, envelope.invocationId))));
|
|
70558
71006
|
}
|
|
70559
71007
|
/** A shell word, quoted only when the shell would read it differently. */
|
|
70560
71008
|
const shellWord = (value) => /^[\w@%+=:,./-]+$/u.test(value) ? value : shellArgument(value);
|
|
70561
71009
|
/**
|
|
71010
|
+
* The id a person quotes to support: the backend's request id, else its
|
|
71011
|
+
* correlation id, else this run's invocation ID. Each is validated upstream.
|
|
71012
|
+
*/
|
|
71013
|
+
const errorRef = (details, invocationId) => details?.requestId ?? details?.correlationId ?? invocationId ?? void 0;
|
|
71014
|
+
/**
|
|
70562
71015
|
* A refusal for a person: one line (red on a terminal) for what happened, and
|
|
70563
|
-
* one for what fixes it. They share a line when both fit in 80 columns.
|
|
71016
|
+
* one for what fixes it. They share a line when both fit in 80 columns. The
|
|
71017
|
+
* last line is the dim `ref <id>`, unless the message already quotes that id.
|
|
70564
71018
|
*/
|
|
70565
|
-
const errorText = (error, color) => {
|
|
71019
|
+
const errorText = (error, color, ref) => {
|
|
70566
71020
|
const what = `✗ ${error.message}`;
|
|
70567
71021
|
const fix = error.hint ?? (error.next === void 0 ? void 0 : `Try: ${error.next.argv.map(shellWord).join(" ")}`);
|
|
70568
71022
|
const painted = color ? `\u001b[31m${what}\u001b[0m` : what;
|
|
70569
|
-
|
|
70570
|
-
|
|
71023
|
+
const body = fix === void 0 ? `${painted}\n` : !what.includes("\n") && what.length + fix.length < 80 ? `${painted} ${fix}\n` : `${painted}\n${fix}\n`;
|
|
71024
|
+
if (ref === void 0 || error.message.includes(ref)) return body;
|
|
71025
|
+
return `${body}${color ? `\u001b[2mref ${ref}\u001b[0m` : `ref ${ref}`}\n`;
|
|
70571
71026
|
};
|
|
70572
71027
|
const readableLine = (line) => {
|
|
70573
71028
|
try {
|
|
@@ -70580,8 +71035,8 @@ const terminalText = (value) => value.replace(/[\p{Cc}\p{Cf}]/gu, (character) =>
|
|
|
70580
71035
|
const shellArgument = (value) => `'${value.replaceAll("'", "'\"'\"'")}'`;
|
|
70581
71036
|
//#endregion
|
|
70582
71037
|
//#region src/runtime/version.ts
|
|
70583
|
-
const cliVersion = "4.20.0-beta.
|
|
70584
|
-
const sdkVersion = "4.20.0-beta.
|
|
71038
|
+
const cliVersion = "4.20.0-beta.28";
|
|
71039
|
+
const sdkVersion = "4.20.0-beta.28";
|
|
70585
71040
|
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"));
|
|
70586
71041
|
const parts = (version) => {
|
|
70587
71042
|
const [core = "", ...prerelease] = version.split("+")[0].split("-");
|
|
@@ -70788,6 +71243,29 @@ const readFields = (value) => {
|
|
|
70788
71243
|
const fields = value.split(",").map((field) => field.trim()).filter((field) => field !== "");
|
|
70789
71244
|
return fields.length === 0 || fields.some((field) => !/^[\w-]+(?:\.[\w-]+)*$/u.test(field)) ? void 0 : fields;
|
|
70790
71245
|
};
|
|
71246
|
+
/**
|
|
71247
|
+
* What `--json` returns from a command that moves money: the open approval,
|
|
71248
|
+
* before anyone approves it. A test holds this to `handOver`'s real result.
|
|
71249
|
+
*/
|
|
71250
|
+
const APPROVAL$1 = {
|
|
71251
|
+
status: null,
|
|
71252
|
+
approvalId: null,
|
|
71253
|
+
approvalUrl: null,
|
|
71254
|
+
paymentIds: null,
|
|
71255
|
+
expiresAt: null,
|
|
71256
|
+
next: { argv: null }
|
|
71257
|
+
};
|
|
71258
|
+
/**
|
|
71259
|
+
* The commands that move money, each with the result it declares. A typo in
|
|
71260
|
+
* `--fields` must not cost an approval, so these check `--fields` against the
|
|
71261
|
+
* declared result before they run. `capxul schema` marks the same four.
|
|
71262
|
+
*/
|
|
71263
|
+
const MONEY_RESULTS = {
|
|
71264
|
+
"payment.send": APPROVAL$1,
|
|
71265
|
+
"payment.retry": APPROVAL$1,
|
|
71266
|
+
"inbox.pay": APPROVAL$1,
|
|
71267
|
+
"org.payroll.run": APPROVAL$1
|
|
71268
|
+
};
|
|
70791
71269
|
const fieldsRefusal = (message) => new CliFailure({
|
|
70792
71270
|
code: "CLI_USAGE",
|
|
70793
71271
|
message,
|
|
@@ -70795,6 +71273,64 @@ const fieldsRefusal = (message) => new CliFailure({
|
|
|
70795
71273
|
exitCode: 2,
|
|
70796
71274
|
outcome: "refused"
|
|
70797
71275
|
});
|
|
71276
|
+
/** A page is the list shape, `{ items, nextCursor }`; any other `items` is an ordinary field. */
|
|
71277
|
+
const isPage = (data) => isRecord(data) && Array.isArray(data.items) && Object.hasOwn(data, "nextCursor");
|
|
71278
|
+
/** The records `--fields` picks from: each item of a page or list, else the data itself. */
|
|
71279
|
+
const recordsOf = (data) => (Array.isArray(data) ? data : isPage(data) ? data.items : [data]).filter(isRecord);
|
|
71280
|
+
const has = (value, [key, ...rest]) => key !== void 0 && isRecord(value) && Object.hasOwn(value, key) && (rest.length === 0 || has(value[key], rest));
|
|
71281
|
+
/** Edit distance, for "did you mean". */
|
|
71282
|
+
const distance = (a, b) => {
|
|
71283
|
+
let row = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
71284
|
+
for (let i = 0; i < a.length; i++) {
|
|
71285
|
+
const next = [i + 1];
|
|
71286
|
+
for (let j = 0; j < b.length; j++) next.push(Math.min(row[j + 1] + 1, next[j] + 1, row[j] + (a[i] === b[j] ? 0 : 1)));
|
|
71287
|
+
row = next;
|
|
71288
|
+
}
|
|
71289
|
+
return row[b.length];
|
|
71290
|
+
};
|
|
71291
|
+
/** The fields at the deepest level `path` reaches in any record, and those a letter or two off it. */
|
|
71292
|
+
const nearby = (records, path) => {
|
|
71293
|
+
let depth = path.length - 1;
|
|
71294
|
+
while (depth > 0 && !records.some((record) => has(record, path.slice(0, depth)))) depth--;
|
|
71295
|
+
const parent = path.slice(0, depth);
|
|
71296
|
+
const names = [...new Set(records.flatMap((record) => {
|
|
71297
|
+
const inner = parent.reduce((value, key) => isRecord(value) ? value[key] : void 0, record);
|
|
71298
|
+
return isRecord(inner) ? Object.keys(inner) : [];
|
|
71299
|
+
}))].filter((name) => /^[\w-]+$/u.test(name));
|
|
71300
|
+
const typed = path.slice(depth).join(".").toLowerCase();
|
|
71301
|
+
const close = names.filter((name) => {
|
|
71302
|
+
const apart = distance(name.toLowerCase(), typed);
|
|
71303
|
+
return apart <= 2 && apart * 2 < Math.max(name.length, typed.length);
|
|
71304
|
+
});
|
|
71305
|
+
const named = (name) => [...parent, name].join(".");
|
|
71306
|
+
return {
|
|
71307
|
+
names: names.map(named),
|
|
71308
|
+
close: close.map(named)
|
|
71309
|
+
};
|
|
71310
|
+
};
|
|
71311
|
+
/**
|
|
71312
|
+
* The first requested field the result cannot give, as a refusal that names it,
|
|
71313
|
+
* the closest real field and the fields there are. Against a command's declared
|
|
71314
|
+
* result (`declared`), any other name is unknown. Against the result itself, a
|
|
71315
|
+
* name no record has is refused only as a near miss of one it does have
|
|
71316
|
+
* (`stauts`): an optional field this result lacks (`execution` on a Payment
|
|
71317
|
+
* that has none) is left out, as `jq` would. A field some records lack, a
|
|
71318
|
+
* page's own fields and an empty page pass.
|
|
71319
|
+
*/
|
|
71320
|
+
const unknownFieldRefusal = (data, fields, declared = false) => {
|
|
71321
|
+
const records = recordsOf(data);
|
|
71322
|
+
if (records.length === 0) return void 0;
|
|
71323
|
+
const pageKeys = isPage(data) ? Object.keys(data) : [];
|
|
71324
|
+
for (const path of fields.map((field) => field.split("."))) {
|
|
71325
|
+
if (records.some((record) => has(record, path))) continue;
|
|
71326
|
+
if (path.length === 1 && pageKeys.includes(path[0])) continue;
|
|
71327
|
+
const { names, close } = nearby(records, path);
|
|
71328
|
+
if (!declared && close.length === 0) continue;
|
|
71329
|
+
const suggestion = close.length === 0 ? "" : ` Did you mean ${close.map((name) => `"${name}"`).join(" or ")}?`;
|
|
71330
|
+
const list = `${names.slice(0, 30).join(", ")}${names.length > 30 ? ", …" : ""}.`;
|
|
71331
|
+
return fieldsRefusal(!declared ? `Field "${path.join(".")}" is not in this result.${suggestion} Fields in this result: ${list}` : `Unknown field "${path.join(".")}".${suggestion} ${names.length === 0 ? "This result has no fields to pick." : `Valid fields: ${list}`}`);
|
|
71332
|
+
}
|
|
71333
|
+
};
|
|
70798
71334
|
const copyPath = (from, to, [key, ...rest]) => {
|
|
70799
71335
|
if (key === void 0 || !Object.hasOwn(from, key)) return;
|
|
70800
71336
|
const value = from[key];
|
|
@@ -70808,14 +71344,14 @@ const copyPath = (from, to, [key, ...rest]) => {
|
|
|
70808
71344
|
copyPath(value, inner, rest);
|
|
70809
71345
|
};
|
|
70810
71346
|
/**
|
|
70811
|
-
* Keep the named fields; `amount.value` reaches inside `amount`. A
|
|
70812
|
-
* its
|
|
71347
|
+
* Keep the named fields; `amount.value` reaches inside `amount`. A page keeps
|
|
71348
|
+
* its own fields (`nextCursor`) and picks from each item. A field the data
|
|
70813
71349
|
* lacks is left out, as `jq` would.
|
|
70814
71350
|
*/
|
|
70815
71351
|
const pickFields = (data, fields) => {
|
|
70816
71352
|
if (Array.isArray(data)) return data.map((item) => pickFields(item, fields));
|
|
70817
71353
|
if (!isRecord(data)) return data;
|
|
70818
|
-
if (
|
|
71354
|
+
if (isPage(data)) return {
|
|
70819
71355
|
...data,
|
|
70820
71356
|
items: data.items.map((item) => pickFields(item, fields))
|
|
70821
71357
|
};
|
|
@@ -70907,44 +71443,14 @@ const withoutOrganization = (saved, origin) => {
|
|
|
70907
71443
|
};
|
|
70908
71444
|
};
|
|
70909
71445
|
//#endregion
|
|
70910
|
-
//#region src/runtime/
|
|
70911
|
-
|
|
70912
|
-
|
|
70913
|
-
/**
|
|
70914
|
-
|
|
70915
|
-
|
|
70916
|
-
const FieldsFlag = GlobalFlag.Setting("fields")({ flag: Flag.String("fields").pipe(Flag.withMetavar("<a,b>"), Flag.withDescription("choose which JSON fields come back"), Flag.optional) });
|
|
70917
|
-
/** `--env <name>` for one command. The runner reads it with the other environment sources. */
|
|
70918
|
-
const EnvironmentFlag = GlobalFlag.Setting("env")({ flag: Flag.String("env").pipe(Flag.withMetavar("<name>"), Flag.withDescription("use this environment for one command"), Flag.optional) });
|
|
70919
|
-
/** Local diagnostics require an explicit debug level. */
|
|
70920
|
-
const localDiagnosticsEnabled = (args) => {
|
|
70921
|
-
const terminator = args.indexOf("--");
|
|
70922
|
-
const flags = terminator < 0 ? args : args.slice(0, terminator);
|
|
70923
|
-
let level;
|
|
70924
|
-
for (const [index, arg] of flags.entries()) if (arg.startsWith("--log-level=")) level = arg.slice(12);
|
|
70925
|
-
else if (arg.startsWith("--logLevel=")) level = arg.slice(11);
|
|
70926
|
-
else if (arg === "--log-level" || arg === "--logLevel") level = flags[index + 1];
|
|
70927
|
-
return level === "debug";
|
|
70928
|
-
};
|
|
70929
|
-
/** Negotiate only the global format before native CLI error rendering.
|
|
70930
|
-
* Native Flag decoding remains the value authority; the command runner parses everything else.
|
|
71446
|
+
//#region src/runtime/root-command.ts
|
|
71447
|
+
/** The command tree `capxul` runs, for the commands that describe it (`capxul schema`). */
|
|
71448
|
+
var RootCommand = class extends Context.Service()("capxul-cli/RootCommand") {};
|
|
71449
|
+
/**
|
|
71450
|
+
* The tree, when the entry provided it. Reading it as optional keeps it out of
|
|
71451
|
+
* every command's requirements; only `schema` asks, and the entry always gives it.
|
|
70931
71452
|
*/
|
|
70932
|
-
const
|
|
70933
|
-
const terminator = args.indexOf("--");
|
|
70934
|
-
const flags = terminator < 0 ? args : args.slice(0, terminator);
|
|
70935
|
-
const values = [];
|
|
70936
|
-
for (const [index, arg] of flags.entries()) if (arg === "--no-json") values.push("false");
|
|
70937
|
-
else if (arg.startsWith("--json=")) values.push(arg.slice(7));
|
|
70938
|
-
else if (arg === "--json") {
|
|
70939
|
-
const next = flags[index + 1];
|
|
70940
|
-
const value = next === void 0 ? "true" : yield* Primitive.Boolean.parse(next).pipe(Effect.as(next), Effect.orElseSucceed(() => "true"));
|
|
70941
|
-
values.push(value);
|
|
70942
|
-
}
|
|
70943
|
-
return yield* jsonFlag.parse({
|
|
70944
|
-
flags: { json: values },
|
|
70945
|
-
arguments: []
|
|
70946
|
-
}).pipe(Effect.map(([, json]) => json ? "json" : "human"), Effect.orElseSucceed(() => values.includes("true") ? "json" : "human"));
|
|
70947
|
-
});
|
|
71453
|
+
const rootCommand = Effect.flatMap(Effect.serviceOption(RootCommand), (root) => Option.isSome(root) ? Effect.succeed(root.value) : Effect.die("RootCommand is not provided"));
|
|
70948
71454
|
//#endregion
|
|
70949
71455
|
//#region src/runtime/run-command.ts
|
|
70950
71456
|
var CliRuntime = class extends Context.Service()("capxul-cli/Runtime") {};
|
|
@@ -71017,14 +71523,28 @@ const runInvocation = (runtime, metadata, kind, work, network = false) => Effect
|
|
|
71017
71523
|
return notice === "" ? Effect.void : writeStderr(notice).pipe(Effect.ignoreCause);
|
|
71018
71524
|
}));
|
|
71019
71525
|
});
|
|
71020
|
-
/**
|
|
71021
|
-
|
|
71526
|
+
/**
|
|
71527
|
+
* The flags, checked before the command runs: a flag that takes one value is
|
|
71528
|
+
* typed once. `--fields` is a JSON choice that names at least one field. A
|
|
71529
|
+
* command that moves money also checks the names against its declared
|
|
71530
|
+
* result, so a typo is refused before anything is prepared.
|
|
71531
|
+
*/
|
|
71532
|
+
const requestedFields = (runtime, name) => Effect.gen(function* () {
|
|
71533
|
+
const root = Option.getOrUndefined(yield* Effect.serviceOption(RootCommand));
|
|
71534
|
+
const repeated = repeatedFlag(root, name, runtime.args ?? []);
|
|
71535
|
+
if (repeated !== void 0) return yield* repeated;
|
|
71022
71536
|
const value = flagValue(runtime.args ?? [], "fields");
|
|
71023
71537
|
if (value === void 0) return void 0;
|
|
71024
71538
|
if (runtime.mode !== "json") return yield* fieldsRefusal("--fields chooses JSON fields. Add --json.");
|
|
71025
71539
|
const fields = readFields(value);
|
|
71026
71540
|
if (fields === void 0) return yield* fieldsRefusal("Provide --fields as names separated by commas, like id,status.");
|
|
71027
|
-
|
|
71541
|
+
const declared = MONEY_RESULTS[name];
|
|
71542
|
+
const unknown = declared === void 0 ? void 0 : unknownFieldRefusal(declared, fields, true);
|
|
71543
|
+
if (unknown !== void 0) return yield* unknown;
|
|
71544
|
+
return {
|
|
71545
|
+
fields,
|
|
71546
|
+
declared: declared !== void 0
|
|
71547
|
+
};
|
|
71028
71548
|
});
|
|
71029
71549
|
/** Commands that print where you are themselves, or change who you are. */
|
|
71030
71550
|
const selfLocating = (name) => name === "whoami" || name === "use" || name.startsWith("use.") || name.startsWith("auth.");
|
|
@@ -71040,10 +71560,13 @@ const writeHeader = (config, runtime, scope = "context") => Effect.gen(function*
|
|
|
71040
71560
|
const runCommand = (options, work) => Effect.gen(function* () {
|
|
71041
71561
|
const runtime = yield* CliRuntime;
|
|
71042
71562
|
return yield* runInvocation(runtime, options, "command", (host, onFinalize) => Effect.gen(function* () {
|
|
71043
|
-
const
|
|
71563
|
+
const requested = yield* requestedFields(runtime, options.name);
|
|
71564
|
+
const fields = requested?.fields;
|
|
71044
71565
|
const config = yield* loadConfigWith(options.environment ?? flagValue(runtime.args ?? [], "env"));
|
|
71045
71566
|
if (runtime.mode === "human" && !selfLocating(options.name)) yield* writeHeader(config, runtime, actorScope$1(options.name));
|
|
71046
71567
|
const result = yield* work(config, host, onFinalize);
|
|
71568
|
+
const unknown = fields === void 0 || requested?.declared === true ? void 0 : unknownFieldRefusal(result.data, fields);
|
|
71569
|
+
if (unknown !== void 0) return yield* unknown;
|
|
71047
71570
|
yield* renderResult(runtime.mode, {
|
|
71048
71571
|
version: 1,
|
|
71049
71572
|
command: options.name,
|
|
@@ -71056,7 +71579,7 @@ const runCommand = (options, work) => Effect.gen(function* () {
|
|
|
71056
71579
|
});
|
|
71057
71580
|
//#endregion
|
|
71058
71581
|
//#region src/commands/account/subcommands/shared.ts
|
|
71059
|
-
const emailFlag$10 = Flag.String("email").pipe(Flag.optional);
|
|
71582
|
+
const emailFlag$10 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
71060
71583
|
const names$8 = {
|
|
71061
71584
|
root: "account",
|
|
71062
71585
|
show: "account.show",
|
|
@@ -71234,6 +71757,22 @@ const openSessionRecord = (config, email) => Effect.gen(function* () {
|
|
|
71234
71757
|
};
|
|
71235
71758
|
});
|
|
71236
71759
|
//#endregion
|
|
71760
|
+
//#region src/auth/bootstrap-cache.ts
|
|
71761
|
+
const bootstrapCache = (config, run) => {
|
|
71762
|
+
const record = privateRecordScope(config, "bootstrap", "application");
|
|
71763
|
+
return {
|
|
71764
|
+
load: () => run(Effect.gen(function* () {
|
|
71765
|
+
const { directory, scope } = yield* record;
|
|
71766
|
+
const value = yield* readPrivateRecord(directory, scope);
|
|
71767
|
+
return value === null ? null : JSON.parse(value);
|
|
71768
|
+
})),
|
|
71769
|
+
save: (body) => run(Effect.gen(function* () {
|
|
71770
|
+
const { directory, scope } = yield* record;
|
|
71771
|
+
yield* writePrivateRecord(directory, scope, JSON.stringify(body));
|
|
71772
|
+
}))
|
|
71773
|
+
};
|
|
71774
|
+
};
|
|
71775
|
+
//#endregion
|
|
71237
71776
|
//#region src/auth/current-session.ts
|
|
71238
71777
|
const CurrentSession = Schema.Struct({
|
|
71239
71778
|
version: Schema.Literal(1),
|
|
@@ -71277,12 +71816,23 @@ const forgetCurrentSession = (config, email, stillCleared) => Effect.gen(functio
|
|
|
71277
71816
|
});
|
|
71278
71817
|
//#endregion
|
|
71279
71818
|
//#region src/auth/input.ts
|
|
71280
|
-
|
|
71819
|
+
/** A refusal of the input: `field` is the flag or argument refused, `next` the command that fixes it. */
|
|
71820
|
+
const inputRequired = (message, fix = {}) => new CliFailure({
|
|
71281
71821
|
code: "CLI_USAGE",
|
|
71282
71822
|
message,
|
|
71823
|
+
...fix,
|
|
71283
71824
|
exitCode: 2,
|
|
71284
71825
|
outcome: "refused"
|
|
71285
71826
|
});
|
|
71827
|
+
/** An Organization command with none chosen: `org list` finds the handle to choose. */
|
|
71828
|
+
const organizationRequired = () => inputRequired("Provide --org, or choose one with: capxul use org <handle>", {
|
|
71829
|
+
field: "org",
|
|
71830
|
+
next: [
|
|
71831
|
+
"capxul",
|
|
71832
|
+
"org",
|
|
71833
|
+
"list"
|
|
71834
|
+
]
|
|
71835
|
+
});
|
|
71286
71836
|
const readEmail = (email) => Effect.gen(function* () {
|
|
71287
71837
|
const runtime = yield* CliRuntime;
|
|
71288
71838
|
if (email === void 0) {
|
|
@@ -71418,7 +71968,7 @@ const withAccountSignerProgress = (client, work) => Effect.scoped(Effect.gen(fun
|
|
|
71418
71968
|
const observe = (status) => {
|
|
71419
71969
|
if (closed || status === previous) return;
|
|
71420
71970
|
previous = status;
|
|
71421
|
-
writes = writes.then(() => run(
|
|
71971
|
+
writes = writes.then(() => run(writeProgress(runtime, runtime.mode === "json" ? `${JSON.stringify({
|
|
71422
71972
|
type: "account.signer",
|
|
71423
71973
|
status
|
|
71424
71974
|
})}\n` : `Account signer: ${terminalText(status)}\n`)));
|
|
@@ -71445,6 +71995,7 @@ const clientFor = (config, store, options) => Effect.gen(function* () {
|
|
|
71445
71995
|
return yield* acquireClient({
|
|
71446
71996
|
publishableKey: Redacted.value(config.publishableKey),
|
|
71447
71997
|
bootstrapBaseUrl: config.bootstrapBaseUrl,
|
|
71998
|
+
bootstrapCache: bootstrapCache(config, run),
|
|
71448
71999
|
requirement: options.requirement ?? "none",
|
|
71449
72000
|
accountSetup: "explicit",
|
|
71450
72001
|
nodeSessionStorage: {
|
|
@@ -71724,15 +72275,48 @@ const lifecycleResult = (lifecycle, email) => ({
|
|
|
71724
72275
|
] : []
|
|
71725
72276
|
]
|
|
71726
72277
|
});
|
|
72278
|
+
/**
|
|
72279
|
+
* A valuation as currency with its code and two decimals (`3,998.22 USD`), as
|
|
72280
|
+
* every amount reads; a currency this CLI cannot format keeps its raw value.
|
|
72281
|
+
*/
|
|
72282
|
+
const shownValuation = (valuation) => {
|
|
72283
|
+
if (valuation === null) return "unavailable";
|
|
72284
|
+
try {
|
|
72285
|
+
const symbolic = formatMoney(valuation, { grammar: "symbol" });
|
|
72286
|
+
return terminalText(`${symbolic.replace(/^[^\d-]+/u, "")} ${valuation.currency}`);
|
|
72287
|
+
} catch {
|
|
72288
|
+
return `${terminalText(valuation.value)} ${terminalText(valuation.currency)}`;
|
|
72289
|
+
}
|
|
72290
|
+
};
|
|
72291
|
+
/** Each asset on a row, with the amounts formatted as the home screen formats them. */
|
|
72292
|
+
const positionTable = (positions) => {
|
|
72293
|
+
const rows = [[
|
|
72294
|
+
"ASSET",
|
|
72295
|
+
"BALANCE",
|
|
72296
|
+
"AVAILABLE"
|
|
72297
|
+
], ...positions.map((position) => [
|
|
72298
|
+
terminalText(assetName(position.asset)),
|
|
72299
|
+
shownNumber({
|
|
72300
|
+
asset: position.asset,
|
|
72301
|
+
value: position.balance
|
|
72302
|
+
}),
|
|
72303
|
+
shownNumber({
|
|
72304
|
+
asset: position.asset,
|
|
72305
|
+
value: position.available
|
|
72306
|
+
})
|
|
72307
|
+
])];
|
|
72308
|
+
const widths = [
|
|
72309
|
+
0,
|
|
72310
|
+
1,
|
|
72311
|
+
2
|
|
72312
|
+
].map((column) => Math.max(...rows.map((row) => row[column].length)));
|
|
72313
|
+
return rows.map((row) => row.map((cell, column) => column === 0 ? cell.padEnd(widths[0]) : cell.padStart(widths[column])).join(" ").trimEnd());
|
|
72314
|
+
};
|
|
71727
72315
|
const balanceLines$1 = (account) => [
|
|
71728
72316
|
`Account ${terminalText(account.id)}`,
|
|
71729
|
-
...account.balances.length === 0 ? ["No asset balances returned."] : account.balances
|
|
71730
|
-
|
|
71731
|
-
|
|
71732
|
-
` Available: ${terminalText(position.available)}`
|
|
71733
|
-
]),
|
|
71734
|
-
`Balance valuation: ${account.balance === null ? "unavailable" : `${terminalText(account.balance.value)} ${terminalText(account.balance.currency)}`}`,
|
|
71735
|
-
`Available valuation: ${account.available === null ? "unavailable" : `${terminalText(account.available.value)} ${terminalText(account.available.currency)}`}`
|
|
72317
|
+
...account.balances.length === 0 ? ["No asset balances returned."] : positionTable(account.balances),
|
|
72318
|
+
`Balance valuation: ${shownValuation(account.balance)}`,
|
|
72319
|
+
`Available valuation: ${shownValuation(account.available)}`
|
|
71736
72320
|
];
|
|
71737
72321
|
const depositLines = (instructions) => [
|
|
71738
72322
|
`Deposit to ${terminalText(instructions.label)}`,
|
|
@@ -71780,7 +72364,14 @@ const organizationId = (config, value) => Effect.gen(function* () {
|
|
|
71780
72364
|
});
|
|
71781
72365
|
const handle = value.replace(/^@/u, "").toLowerCase();
|
|
71782
72366
|
const found = organizations.find((org) => org.handle.toLowerCase() === handle);
|
|
71783
|
-
if (found === void 0) return yield* inputRequired(`You are not a member of an Organization "${terminalText(value)}". See yours with: capxul org list
|
|
72367
|
+
if (found === void 0) return yield* inputRequired(`You are not a member of an Organization "${terminalText(value)}". See yours with: capxul org list`, {
|
|
72368
|
+
field: "org",
|
|
72369
|
+
next: [
|
|
72370
|
+
"capxul",
|
|
72371
|
+
"org",
|
|
72372
|
+
"list"
|
|
72373
|
+
]
|
|
72374
|
+
});
|
|
71784
72375
|
return found.id;
|
|
71785
72376
|
});
|
|
71786
72377
|
/**
|
|
@@ -71814,10 +72405,10 @@ const actorScope = (actor) => actor.kind === "personal" ? {
|
|
|
71814
72405
|
};
|
|
71815
72406
|
//#endregion
|
|
71816
72407
|
//#region src/commands/org/subcommands/shared.ts
|
|
71817
|
-
const emailFlag$9 = Flag.String("email").pipe(Flag.optional);
|
|
72408
|
+
const emailFlag$9 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
71818
72409
|
const scoped$1 = { email: emailFlag$9 };
|
|
71819
|
-
const confirmFlag$6 = Flag.Boolean("confirm").pipe(Flag.withDefault(false));
|
|
71820
|
-
const timeoutFlag$4 = Flag.Int("timeout-seconds").pipe(Flag.withDefault(120));
|
|
72410
|
+
const confirmFlag$6 = Flag.Boolean("confirm").pipe(Flag.withDescription(FLAG_HELP.confirm), Flag.withDefault(false));
|
|
72411
|
+
const timeoutFlag$4 = Flag.Int("timeout-seconds").pipe(Flag.withDescription(FLAG_HELP.timeout), Flag.withDefault(120));
|
|
71821
72412
|
const metadata$18 = (name) => ({
|
|
71822
72413
|
name,
|
|
71823
72414
|
capability: "organization",
|
|
@@ -72841,8 +73432,9 @@ const policyLines = (label, policy) => {
|
|
|
72841
73432
|
if (policy.type === "managePeople") return [`${label}: Manage people`];
|
|
72842
73433
|
return [
|
|
72843
73434
|
`${label}: Budget`,
|
|
72844
|
-
` Asset
|
|
73435
|
+
` Asset: ${terminalText(assetName(policy.asset))}`,
|
|
72845
73436
|
` Per-payment cap: ${policy.limit === null ? "no cap" : terminalText(policy.limit)}`,
|
|
73437
|
+
` Refill: ${policy.refill === "monthly" ? "Every 30 days" : "None"}`,
|
|
72846
73438
|
` Recipients: ${policy.scope.recipients.type === "anyone" ? "Anyone" : policy.scope.recipients.accounts.map(terminalText).join(", ")}`,
|
|
72847
73439
|
` Actions: ${policy.scope.actions.map((action) => action === "pay" ? "Payments (single and batch)" : "Commitments").join(", ")}`
|
|
72848
73440
|
];
|
|
@@ -72909,7 +73501,7 @@ const organizationMeLines = (member) => [
|
|
|
72909
73501
|
` Name: ${terminalText(budget.label)}`,
|
|
72910
73502
|
` Budget ID: ${terminalText(budget.id)}`,
|
|
72911
73503
|
` Per-payment cap: ${budget.limit === null ? "no cap" : terminalText(budget.limit.value)} ${terminalText(budget.asset.symbol)}`,
|
|
72912
|
-
` Asset
|
|
73504
|
+
` Asset: ${terminalText(assetName(budget.asset.assetId))}`
|
|
72913
73505
|
])
|
|
72914
73506
|
];
|
|
72915
73507
|
//#endregion
|
|
@@ -73090,7 +73682,7 @@ const readOrganization = (config, command, input, handler) => Effect.gen(functio
|
|
|
73090
73682
|
}
|
|
73091
73683
|
organizationId = resolved;
|
|
73092
73684
|
if (organizationId === null) {
|
|
73093
|
-
if (!interactive || organizations.length === 0) return yield*
|
|
73685
|
+
if (!interactive || organizations.length === 0) return yield* organizationRequired();
|
|
73094
73686
|
organizationId = yield* Prompt.Select({
|
|
73095
73687
|
message: "Organization",
|
|
73096
73688
|
choices: organizations.map((org) => ({
|
|
@@ -73507,10 +74099,10 @@ const command$79 = Command.make("retry", {
|
|
|
73507
74099
|
const account = Command.make("account").pipe(annotate$8("root"), Command.withDescription("your Capxul account and its setup"), Command.withSubcommands([command$80, command$79]));
|
|
73508
74100
|
//#endregion
|
|
73509
74101
|
//#region src/commands/activity/subcommands/shared.ts
|
|
73510
|
-
const emailFlag$8 = Flag.String("email").pipe(Flag.optional);
|
|
73511
|
-
const fromFlag = Flag.Int("from").pipe(Flag.optional);
|
|
73512
|
-
const toFlag = Flag.Int("to").pipe(Flag.optional);
|
|
73513
|
-
const confirmFlag$5 = Flag.Boolean("confirm").pipe(Flag.withDefault(false));
|
|
74102
|
+
const emailFlag$8 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
74103
|
+
const fromFlag = Flag.Int("from").pipe(Flag.withDescription("only activity from this time on, in epoch milliseconds"), Flag.optional);
|
|
74104
|
+
const toFlag = Flag.Int("to").pipe(Flag.withDescription("only activity before this time, in epoch milliseconds"), Flag.optional);
|
|
74105
|
+
const confirmFlag$5 = Flag.Boolean("confirm").pipe(Flag.withDescription(FLAG_HELP.confirm), Flag.withDefault(false));
|
|
73514
74106
|
const names$7 = {
|
|
73515
74107
|
root: "activity",
|
|
73516
74108
|
list: "activity.list",
|
|
@@ -73646,19 +74238,23 @@ const readAnnotation = (source) => Effect.gen(function* () {
|
|
|
73646
74238
|
}).pipe(Effect.mapError((error) => source === void 0 || error instanceof CliFailure ? error : annotationError()));
|
|
73647
74239
|
//#endregion
|
|
73648
74240
|
//#region src/commands/payment/internal/shared.ts
|
|
73649
|
-
/** Money as people read it, with at least two decimals: `550.00 USDC`. */
|
|
73650
|
-
const shownAmount = (amount) => {
|
|
73651
|
-
const [number = "", symbol = ""] = formatAssetAmount(amount).split(" ");
|
|
73652
|
-
const [integer = "0", fraction = ""] = number.split(".");
|
|
73653
|
-
return `${integer}.${fraction.padEnd(2, "0")} ${symbol}`;
|
|
73654
|
-
};
|
|
73655
74241
|
const paymentIdPattern = /^payment_[0-9A-Za-z]+$/u;
|
|
73656
|
-
const readPaymentId = (paymentId) => paymentIdPattern.test(paymentId) ? Effect.succeed(paymentId) : Effect.
|
|
73657
|
-
|
|
73658
|
-
|
|
73659
|
-
|
|
73660
|
-
|
|
73661
|
-
|
|
74242
|
+
const readPaymentId = (paymentId) => paymentIdPattern.test(paymentId) ? Effect.succeed(paymentId) : Effect.gen(function* () {
|
|
74243
|
+
const runtime = yield* CliRuntime;
|
|
74244
|
+
return yield* new CliFailure({
|
|
74245
|
+
code: "CLI_USAGE",
|
|
74246
|
+
message: "Provide the Payment ID: payment_ followed by letters or digits.",
|
|
74247
|
+
field: "id",
|
|
74248
|
+
next: [
|
|
74249
|
+
"capxul",
|
|
74250
|
+
"payment",
|
|
74251
|
+
"list",
|
|
74252
|
+
...scopeArgs(runtime.args ?? [])
|
|
74253
|
+
],
|
|
74254
|
+
exitCode: 2,
|
|
74255
|
+
outcome: "refused"
|
|
74256
|
+
});
|
|
74257
|
+
});
|
|
73662
74258
|
const paymentLines = (payment) => [
|
|
73663
74259
|
`Payment ${terminalText(payment.id)}`,
|
|
73664
74260
|
` Status: ${payment.status}`,
|
|
@@ -73680,8 +74276,8 @@ const incomplete = (warning) => warning === void 0 ? [] : ["Activity is incomple
|
|
|
73680
74276
|
const pageLines = (page) => [
|
|
73681
74277
|
...incomplete(page.readWarning),
|
|
73682
74278
|
`Activity: ${page.items.length} rows`,
|
|
73683
|
-
...page.items.map((item) => `${item.kind === "movement" ? "Movement" : "Payment"} ${terminalText(item.id)}: ${terminalText(item.amount.value)} ${terminalText(item.amount.asset)}`),
|
|
73684
|
-
page.nextCursor === null ? "End of activity." : `More: the same command with --after ${terminalText(page.nextCursor)}`,
|
|
74279
|
+
...page.items.map((item) => `${item.kind === "movement" ? "Movement" : "Payment"} ${terminalText(item.id)}: ${terminalText(item.amount.value)} ${terminalText(assetName(item.amount.asset))}`),
|
|
74280
|
+
page.nextCursor === null ? "End of activity." : `More: the same command with --after ${terminalText(shellArgument(page.nextCursor))}`,
|
|
73685
74281
|
`Checkpoint: ${page.checkpoint}`,
|
|
73686
74282
|
`Observed at: ${page.observedAt}`
|
|
73687
74283
|
];
|
|
@@ -73690,7 +74286,11 @@ const summaryLines = (summary, window) => [
|
|
|
73690
74286
|
`From: ${window?.from === void 0 ? "unbounded" : window.from}`,
|
|
73691
74287
|
`To: ${window?.to === void 0 ? "unbounded" : window.to}`,
|
|
73692
74288
|
`Activity: ${summary.count} rows`,
|
|
73693
|
-
...summary.totals.map((total) =>
|
|
74289
|
+
...summary.totals.map((total) => {
|
|
74290
|
+
const known = shownBaseUnits(total.asset, "0") !== void 0;
|
|
74291
|
+
const show = (raw) => terminalText(shownBaseUnits(total.asset, raw) ?? raw);
|
|
74292
|
+
return `${terminalText(assetName(total.asset))}: in ${show(total.in)}, out ${show(total.out)}, net ${show(total.net)}, pending in ${show(total.pendingIn)}${known ? "" : " raw units"}`;
|
|
74293
|
+
}),
|
|
73694
74294
|
`Observed at: ${summary.observedAt}`
|
|
73695
74295
|
];
|
|
73696
74296
|
const detailLines = (activity, id) => [
|
|
@@ -73726,11 +74326,11 @@ const execute$90 = (config, input) => Effect.gen(function* () {
|
|
|
73726
74326
|
//#region src/commands/activity/subcommands/list/command.ts
|
|
73727
74327
|
const command$78 = Command.make("list", {
|
|
73728
74328
|
email: emailFlag$8,
|
|
73729
|
-
limit: Flag.Int("limit").pipe(Flag.withDefault(25)),
|
|
74329
|
+
limit: Flag.Int("limit").pipe(Flag.withDescription("how many rows to return, 1 to 100"), Flag.withDefault(25)),
|
|
73730
74330
|
cursor: Flag.String("after").pipe(Flag.withDescription("the next page's cursor"), Flag.optional),
|
|
73731
|
-
activityKind: Flag.String("kind").pipe(Flag.optional),
|
|
73732
|
-
direction: Flag.String("direction").pipe(Flag.optional),
|
|
73733
|
-
statuses: Flag.String("status").pipe(Flag.atLeast(0)),
|
|
74331
|
+
activityKind: Flag.String("kind").pipe(Flag.withDescription("only payments or only movements"), Flag.optional),
|
|
74332
|
+
direction: Flag.String("direction").pipe(Flag.withDescription("only in, out or self"), Flag.optional),
|
|
74333
|
+
statuses: Flag.String("status").pipe(Flag.withDescription("only payments in this status; repeat it for several"), Flag.atLeast(0)),
|
|
73734
74334
|
from: fromFlag,
|
|
73735
74335
|
to: toFlag
|
|
73736
74336
|
}, ({ email, limit, cursor, activityKind, direction, statuses, from, to }) => dispatch$7("list", (config, orgId) => execute$90(config, {
|
|
@@ -73854,48 +74454,17 @@ const saveDocumentPdf = (config, document, outputDirectory) => Effect.gen(functi
|
|
|
73854
74454
|
byteLength: rendered.bytes.byteLength
|
|
73855
74455
|
};
|
|
73856
74456
|
});
|
|
73857
|
-
/** Document output cannot change a Payment result or start another Payment. */
|
|
73858
|
-
const savePaymentDocuments = (config, client, payment, outputDirectory) => Effect.gen(function* () {
|
|
73859
|
-
const refs = [...new Map(payment.documents.map((ref) => [`${ref.documentHash}:${ref.contentHash}`, ref])).values()];
|
|
73860
|
-
const outputs = yield* Effect.forEach(refs, (ref) => Effect.gen(function* () {
|
|
73861
|
-
const document = yield* Effect.tryPromise({
|
|
73862
|
-
try: (signal) => client.paymentDocuments.render({
|
|
73863
|
-
documentHash: ref.documentHash,
|
|
73864
|
-
contentHash: ref.contentHash
|
|
73865
|
-
}, { signal }),
|
|
73866
|
-
catch: () => failure("CLI_DEFECT", "The payment document could not be read.")
|
|
73867
|
-
}).pipe(Effect.flatMap(resultValue));
|
|
73868
|
-
if (document.documentHash.toLowerCase() !== ref.documentHash.toLowerCase() || document.contentHash.toLowerCase() !== ref.contentHash.toLowerCase()) return yield* failure("CLI_DEFECT", "The payment document hashes do not match.");
|
|
73869
|
-
return {
|
|
73870
|
-
state: "saved",
|
|
73871
|
-
...yield* saveDocumentPdf(config, document, outputDirectory)
|
|
73872
|
-
};
|
|
73873
|
-
}).pipe(Effect.catch((error) => {
|
|
73874
|
-
const classified = classifyFailure(Cause.fail(error));
|
|
73875
|
-
return Effect.succeed({
|
|
73876
|
-
state: "failed",
|
|
73877
|
-
documentHash: ref.documentHash,
|
|
73878
|
-
contentHash: ref.contentHash,
|
|
73879
|
-
error: {
|
|
73880
|
-
code: classified.code,
|
|
73881
|
-
message: classified.message
|
|
73882
|
-
}
|
|
73883
|
-
});
|
|
73884
|
-
})));
|
|
73885
|
-
return {
|
|
73886
|
-
state: outputs.some((output) => output.state === "failed") ? "failed" : payment.documentState === "unavailable" ? "unavailable" : payment.documentState === "pending" || refs.length === 0 ? "pending" : "ready",
|
|
73887
|
-
outputs
|
|
73888
|
-
};
|
|
73889
|
-
});
|
|
73890
|
-
const documentOutputLines = (output) => [` Document output: ${output.state}`, ...output.outputs.map((document) => document.state === "saved" ? ` PDF: ${terminalText(document.path)}` : ` Document ${terminalText(document.documentHash)}: ${terminalText(document.error.message)}`)];
|
|
73891
74457
|
/** The command that saves one document as PDF. Reads never write files themselves. */
|
|
73892
74458
|
const renderCommand = (ref) => `capxul document render --document-hash ${ref.documentHash} --content-hash ${ref.contentHash}`;
|
|
74459
|
+
const uniqueRefs = (payment) => [...new Map(payment.documents.map((ref) => [`${ref.documentHash}:${ref.contentHash}`, ref])).values()];
|
|
73893
74460
|
/** A read's documents: where they stand, and the command that saves each one as PDF. */
|
|
73894
74461
|
const documentReferenceLines = (payment) => {
|
|
73895
|
-
const refs =
|
|
74462
|
+
const refs = uniqueRefs(payment);
|
|
73896
74463
|
if (refs.length === 0) return payment.documentState === "pending" ? [" Documents: being prepared"] : [];
|
|
73897
74464
|
return refs.map((ref) => ` ${ref.kind}: ${terminalText(renderCommand(ref))}`);
|
|
73898
74465
|
};
|
|
74466
|
+
/** A write saves no file: this names the command that saves each document as PDF. */
|
|
74467
|
+
const saveDocumentLines = (payment) => uniqueRefs(payment).map((ref) => `Save the ${ref.kind}: ${terminalText(renderCommand(ref))}`);
|
|
73899
74468
|
//#endregion
|
|
73900
74469
|
//#region src/commands/activity/subcommands/get/execute.ts
|
|
73901
74470
|
const execute$89 = (config, input) => Effect.gen(function* () {
|
|
@@ -73920,8 +74489,8 @@ const execute$89 = (config, input) => Effect.gen(function* () {
|
|
|
73920
74489
|
//#region src/commands/activity/subcommands/get/command.ts
|
|
73921
74490
|
const command$77 = Command.make("get", {
|
|
73922
74491
|
email: emailFlag$8,
|
|
73923
|
-
activityKind: Flag.String("kind"),
|
|
73924
|
-
id: Flag.String("id")
|
|
74492
|
+
activityKind: Flag.String("kind").pipe(Flag.withDescription("payment or movement")),
|
|
74493
|
+
id: Flag.String("id").pipe(Flag.withDescription("the Payment or movement ID"))
|
|
73925
74494
|
}, ({ email, activityKind, id }) => dispatch$7("get", (config, orgId) => execute$89(config, {
|
|
73926
74495
|
kind: "get",
|
|
73927
74496
|
email: Option.getOrUndefined(email),
|
|
@@ -73984,7 +74553,7 @@ const execute$87 = (config, input) => Effect.gen(function* () {
|
|
|
73984
74553
|
//#region src/commands/activity/subcommands/annotate/command.ts
|
|
73985
74554
|
const command$75 = Command.make("annotate", {
|
|
73986
74555
|
email: emailFlag$8,
|
|
73987
|
-
source: Flag.String("input").pipe(Flag.optional),
|
|
74556
|
+
source: Flag.String("input").pipe(Flag.withDescription("the annotation JSON, from a file or - for stdin"), Flag.optional),
|
|
73988
74557
|
confirm: confirmFlag$5
|
|
73989
74558
|
}, ({ email, source, confirm }) => dispatch$7("annotate", (config, orgId) => execute$87(config, {
|
|
73990
74559
|
kind: "annotate",
|
|
@@ -74044,7 +74613,7 @@ const metadata$16 = {
|
|
|
74044
74613
|
capability: "account",
|
|
74045
74614
|
observation: "command"
|
|
74046
74615
|
};
|
|
74047
|
-
const address = Command.make("address", { email: Flag.String("email").pipe(Flag.optional) }, ({ email }) => runCommand({
|
|
74616
|
+
const address = Command.make("address", { email: Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional) }, ({ email }) => runCommand({
|
|
74048
74617
|
...metadata$16,
|
|
74049
74618
|
network: true
|
|
74050
74619
|
}, (config) => execute$84(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$16), Command.withDescription("where to send crypto to this account"), Command.withExamples([{ command: "capxul address" }]));
|
|
@@ -74125,11 +74694,14 @@ const command$74 = Command.make("login", {
|
|
|
74125
74694
|
*/
|
|
74126
74695
|
const execute$82 = (config, input) => Effect.gen(function* () {
|
|
74127
74696
|
const pending = (yield* readWhere(config)).origins?.[config.bootstrapBaseUrl]?.pending;
|
|
74128
|
-
if (input.email === void 0 && pending === void 0) return yield* inputRequired("No sign-in is waiting for a code. Start one with: capxul auth login");
|
|
74697
|
+
if (input.email === void 0 && pending === void 0) return yield* inputRequired("No sign-in is waiting for a code. Start one with: capxul auth login", { next: SIGN_IN$1 });
|
|
74129
74698
|
const email = yield* readEmail(input.email ?? pending);
|
|
74130
74699
|
const store = yield* openSessionRecord(config, email);
|
|
74131
74700
|
const flow = store.current().flow;
|
|
74132
|
-
if (flow === null || flow.email !== email) return yield* inputRequired("No sign-in is waiting for a code for that email. Start one with: capxul auth login"
|
|
74701
|
+
if (flow === null || flow.email !== email) return yield* inputRequired("No sign-in is waiting for a code for that email. Start one with: capxul auth login", {
|
|
74702
|
+
field: "email",
|
|
74703
|
+
next: SIGN_IN$1
|
|
74704
|
+
});
|
|
74133
74705
|
const client = yield* clientFor(config, store, { restore: false });
|
|
74134
74706
|
yield* call((signal) => client.auth.getSession({ signal }));
|
|
74135
74707
|
yield* call((signal) => client.auth.resumeOtp({
|
|
@@ -74349,7 +74921,7 @@ const metadata$14 = {
|
|
|
74349
74921
|
capability: "account",
|
|
74350
74922
|
observation: "command"
|
|
74351
74923
|
};
|
|
74352
|
-
const balance = Command.make("balance", { email: Flag.String("email").pipe(Flag.optional) }, ({ email }) => runCommand({
|
|
74924
|
+
const balance = Command.make("balance", { email: Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional) }, ({ email }) => runCommand({
|
|
74353
74925
|
...metadata$14,
|
|
74354
74926
|
network: true
|
|
74355
74927
|
}, (config) => execute$77(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$14), Command.withDescription("balances for whoever you're acting as"), Command.withExamples([{ command: "capxul balance" }, {
|
|
@@ -74415,12 +74987,12 @@ const config = Command.make("config").pipe(Command.annotate(CliCommandMetadata,
|
|
|
74415
74987
|
}), Command.withDescription("this CLI's own settings"), Command.withSubcommands([telemetry]));
|
|
74416
74988
|
//#endregion
|
|
74417
74989
|
//#region src/commands/contact/subcommands/shared.ts
|
|
74418
|
-
const emailFlag$6 = Flag.String("email").pipe(Flag.optional);
|
|
74419
|
-
const includeHiddenFlag = Flag.Boolean("include-hidden").pipe(Flag.withDefault(false));
|
|
74990
|
+
const emailFlag$6 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
74991
|
+
const includeHiddenFlag = Flag.Boolean("include-hidden").pipe(Flag.withDescription("also list the contacts you hid"), Flag.withDefault(false));
|
|
74420
74992
|
const entryIdArgument = Argument.String("id").pipe(Argument.withDescription("The contact's PartyId"), Argument.optional);
|
|
74421
|
-
const labelFlag = Flag.String("label");
|
|
74422
|
-
const inputFlag = Flag.String("input").pipe(Flag.optional);
|
|
74423
|
-
const confirmFlag$4 = Flag.Boolean("confirm").pipe(Flag.withDefault(false));
|
|
74993
|
+
const labelFlag = Flag.String("label").pipe(Flag.withDescription("the new label to show for this contact"));
|
|
74994
|
+
const inputFlag = Flag.String("input").pipe(Flag.withDescription("the contact JSON, from a file or - for stdin"), Flag.optional);
|
|
74995
|
+
const confirmFlag$4 = Flag.Boolean("confirm").pipe(Flag.withDescription(FLAG_HELP.confirm), Flag.withDefault(false));
|
|
74424
74996
|
const names$6 = {
|
|
74425
74997
|
root: "contact",
|
|
74426
74998
|
list: "contact.list",
|
|
@@ -74635,7 +75207,15 @@ const selectContactParty = (book, supplied, orgs) => Effect.gen(function* () {
|
|
|
74635
75207
|
const optionalPartyId = (value) => Effect.gen(function* () {
|
|
74636
75208
|
if (value !== void 0) return yield* partyId(value);
|
|
74637
75209
|
const runtime = yield* CliRuntime;
|
|
74638
|
-
if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* inputRequired("Provide the contact ID."
|
|
75210
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* inputRequired("Provide the contact ID.", {
|
|
75211
|
+
field: "id",
|
|
75212
|
+
next: [
|
|
75213
|
+
"capxul",
|
|
75214
|
+
"contact",
|
|
75215
|
+
"list",
|
|
75216
|
+
...scopeArgs(runtime.args ?? [])
|
|
75217
|
+
]
|
|
75218
|
+
});
|
|
74639
75219
|
return null;
|
|
74640
75220
|
});
|
|
74641
75221
|
const actorLine = (orgs) => orgs[0] === void 0 ? "Actor: personal" : `Actor: Organization ${JSON.stringify(orgs[0])}`;
|
|
@@ -74918,26 +75498,41 @@ const readRecipient$1 = (value) => Effect.gen(function* () {
|
|
|
74918
75498
|
kind: "email",
|
|
74919
75499
|
email: yield* readEmail(text)
|
|
74920
75500
|
} };
|
|
74921
|
-
return yield* Effect.try({
|
|
75501
|
+
if (!/^0x[0-9a-f]*$/iu.test(text)) return yield* Effect.try({
|
|
74922
75502
|
try: () => ({ to: {
|
|
74923
75503
|
kind: "handle",
|
|
74924
75504
|
handle: String(toHandle(text.replace(/^@/u, "")))
|
|
74925
75505
|
} }),
|
|
74926
|
-
catch: () =>
|
|
75506
|
+
catch: () => notARecipient(text)
|
|
74927
75507
|
});
|
|
75508
|
+
return yield* notARecipient(text);
|
|
74928
75509
|
});
|
|
75510
|
+
/**
|
|
75511
|
+
* The backend refuses a handle nobody has with only the field and a reason, so
|
|
75512
|
+
* the sentence that names the recipient is written here, where it is known.
|
|
75513
|
+
*/
|
|
75514
|
+
const namingRecipient = (to) => (error) => to.kind === "handle" && error.code === "INVALID_INPUT" && error.details?.field === "handle" ? new CliFailure({
|
|
75515
|
+
...classifyFailure(Cause.fail(error)),
|
|
75516
|
+
message: `No one has the handle @${terminalText(to.handle)}. Send to an email, an existing @handle or an 0x address.`
|
|
75517
|
+
}) : error;
|
|
75518
|
+
/** What a recipient can be, naming the one that was typed. */
|
|
75519
|
+
const notARecipient = (text) => inputRequired(`"${terminalText(text)}" is not a valid email, @handle or 0x address.`, { field: "to" });
|
|
74929
75520
|
/** `<amount>` of `--asset` (a symbol the account holds, or an asset ID). */
|
|
74930
75521
|
const readAmount = (value, asset, account) => Effect.gen(function* () {
|
|
74931
75522
|
const wanted = asset.trim().toUpperCase();
|
|
74932
75523
|
const position = account.balances.find((held) => held.symbol.toUpperCase() === wanted || held.asset.toUpperCase() === wanted);
|
|
74933
75524
|
if (position === void 0) return yield* inputRequired(`Your account holds no ${terminalText(asset)}. Choose one it holds with --asset: ${account.balances.map((held) => held.symbol).join(", ")}.`);
|
|
74934
|
-
const
|
|
74935
|
-
|
|
75525
|
+
const metadata = requireAsset(position.asset);
|
|
75526
|
+
const amount = parseAssetAmount(value.trim(), metadata);
|
|
75527
|
+
if (isMoneyParseError(amount)) return yield* inputRequired(amountRefusal(amount.reason, metadata), { field: "amount" });
|
|
75528
|
+
if (/^0*(?:\.0*)?$/u.test(amount.value)) return yield* inputRequired(amountRefusal("non-positive", metadata), { field: "amount" });
|
|
74936
75529
|
return {
|
|
74937
75530
|
amount,
|
|
74938
75531
|
position
|
|
74939
75532
|
};
|
|
74940
75533
|
});
|
|
75534
|
+
/** Why an amount was refused, as a sentence that names the cause. */
|
|
75535
|
+
const amountRefusal = (reason, asset) => reason === "too-many-decimals" ? `${asset.symbol} has ${asset.decimals} decimals: use at most ${asset.decimals} decimal places.` : "Provide a positive amount, like 50 or 12.5.";
|
|
74941
75536
|
/** The recipient and amount, asked on a terminal when the command line has none. */
|
|
74942
75537
|
const readSendWords = (to, amount) => Effect.gen(function* () {
|
|
74943
75538
|
if (to !== void 0 && amount !== void 0) return {
|
|
@@ -75079,7 +75674,7 @@ const keyCommand = Command.make("key", {
|
|
|
75079
75674
|
const fundCommand = Command.make("fund", {
|
|
75080
75675
|
amount: Argument.String("amount").pipe(Argument.withDescription("how much test money, like 100"), Argument.withDefault("100")),
|
|
75081
75676
|
asset: Flag.String("asset").pipe(Flag.withDescription("which test asset: a symbol your account holds"), Flag.withDefault("USDC")),
|
|
75082
|
-
email: Flag.String("email").pipe(Flag.optional)
|
|
75677
|
+
email: Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional)
|
|
75083
75678
|
}, ({ amount, asset, email }) => runCommand({
|
|
75084
75679
|
...metadata$11("dev.fund"),
|
|
75085
75680
|
network: true
|
|
@@ -75213,16 +75808,13 @@ const balanceLines = (positions) => {
|
|
|
75213
75808
|
if (positions === null) return ["Balances", " unavailable right now"];
|
|
75214
75809
|
const held = positions.filter((position) => !/^0*(?:\.0*)?$/u.test(position.balance));
|
|
75215
75810
|
if (held.length === 0) return ["Balances", " nothing yet"];
|
|
75216
|
-
const amounts = held.map((position) => {
|
|
75217
|
-
|
|
75811
|
+
const amounts = held.map((position) => ({
|
|
75812
|
+
symbol: position.symbol,
|
|
75813
|
+
value: shownNumber({
|
|
75218
75814
|
asset: position.asset,
|
|
75219
75815
|
value: position.balance
|
|
75220
|
-
})
|
|
75221
|
-
|
|
75222
|
-
symbol: position.symbol,
|
|
75223
|
-
value: shown.slice(0, shown.lastIndexOf(" "))
|
|
75224
|
-
};
|
|
75225
|
-
});
|
|
75816
|
+
})
|
|
75817
|
+
}));
|
|
75226
75818
|
const symbolWidth = Math.max(...amounts.map((entry) => entry.symbol.length));
|
|
75227
75819
|
const valueWidth = Math.max(...amounts.map((entry) => entry.value.length));
|
|
75228
75820
|
return ["Balances", ...amounts.map((entry) => ` ${terminalText(entry.symbol).padEnd(symbolWidth)} ${entry.value.padStart(valueWidth)}`)];
|
|
@@ -75298,7 +75890,7 @@ const readEach = (client, orgId, scope) => {
|
|
|
75298
75890
|
needs: inbox === null || paid === null ? null : needsFrom({
|
|
75299
75891
|
requestsToPay: inbox.items.filter((item) => item.status === "open").map((item) => item.id),
|
|
75300
75892
|
payrollToApprove: (payroll?.items ?? []).filter((run) => run.status === "draft").map((run) => run.id),
|
|
75301
|
-
paymentsToRetry: paid.items.filter((payment) => payment.execution?.retryRequired === true).map((payment) => payment.id)
|
|
75893
|
+
paymentsToRetry: paid.items.filter((payment) => payment.execution?.retryRequired === true && payment.direction !== "in").map((payment) => payment.id)
|
|
75302
75894
|
}, scope)
|
|
75303
75895
|
})));
|
|
75304
75896
|
};
|
|
@@ -75332,7 +75924,10 @@ const execute$64 = (config) => Effect.gen(function* () {
|
|
|
75332
75924
|
if (client === null) return notSignedIn;
|
|
75333
75925
|
const named = flagValue(runtime.args ?? [], "org");
|
|
75334
75926
|
const scope = named === void 0 ? [] : ["--org", named];
|
|
75335
|
-
const { balances, needs } = yield* readHome(client, actor.kind === "personal" ? null : actor.id, scope)
|
|
75927
|
+
const { balances, needs } = yield* withWaiting(readHome(client, actor.kind === "personal" ? null : actor.id, scope), {
|
|
75928
|
+
...runtime,
|
|
75929
|
+
label: "Loading balances and what needs you"
|
|
75930
|
+
});
|
|
75336
75931
|
const tryArgv = [
|
|
75337
75932
|
[
|
|
75338
75933
|
"capxul",
|
|
@@ -75386,10 +75981,10 @@ const home = Command.make("home", {}, () => runCommand({
|
|
|
75386
75981
|
}, execute$64)).pipe(Command.annotate(CliCommandMetadata, metadata$9), Command.withDescription("home screen: where you are, what needs you"), Command.unlisted);
|
|
75387
75982
|
//#endregion
|
|
75388
75983
|
//#region src/commands/document/subcommands/shared.ts
|
|
75389
|
-
const documentHashFlag = Flag.String("document-hash");
|
|
75390
|
-
const contentHashFlag = Flag.String("content-hash");
|
|
75391
|
-
const emailFlag$5 = Flag.String("email").pipe(Flag.optional);
|
|
75392
|
-
const outFlag = Flag.String("out");
|
|
75984
|
+
const documentHashFlag = Flag.String("document-hash").pipe(Flag.withDescription("the document's hash"));
|
|
75985
|
+
const contentHashFlag = Flag.String("content-hash").pipe(Flag.withDescription("the hash of its content"));
|
|
75986
|
+
const emailFlag$5 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
75987
|
+
const outFlag = Flag.String("out").pipe(Flag.withDescription("the new file to write the original bytes to"));
|
|
75393
75988
|
const names$5 = {
|
|
75394
75989
|
root: "document",
|
|
75395
75990
|
get: "document.get",
|
|
@@ -75508,7 +76103,7 @@ const command$59 = Command.make("render", {
|
|
|
75508
76103
|
documentHash: documentHashFlag,
|
|
75509
76104
|
contentHash: contentHashFlag,
|
|
75510
76105
|
email: emailFlag$5,
|
|
75511
|
-
outputDirectory: Flag.String("output-dir").pipe(Flag.optional)
|
|
76106
|
+
outputDirectory: Flag.String("output-dir").pipe(Flag.withDescription("where to save the PDF and HTML (default: the documents folder next to CLI settings)"), Flag.optional)
|
|
75512
76107
|
}, ({ documentHash, contentHash, email, outputDirectory }) => dispatch$4("render", (config) => execute$61(config, {
|
|
75513
76108
|
kind: "render",
|
|
75514
76109
|
documentHash,
|
|
@@ -75946,8 +76541,9 @@ const invitationGrantLines = (grant) => {
|
|
|
75946
76541
|
return [
|
|
75947
76542
|
` ${grant.type === "new_budget" ? "New Budget" : "Budget"}: ${terminalText(grant.label)}`,
|
|
75948
76543
|
...grant.type === "budget" ? [` Budget ID: ${terminalText(grant.budgetId)}`] : [],
|
|
75949
|
-
` Asset: ${terminalText(grant.policy.asset)}`,
|
|
75950
|
-
` Per-payment cap: ${grant.policy.limit === null ? "no cap" : terminalText(grant.policy.limit)}`,
|
|
76544
|
+
` Asset: ${terminalText(assetName(grant.policy.asset))}`,
|
|
76545
|
+
` Per-payment cap: ${grant.policy.limit === null ? "no cap" : `${terminalText(grant.policy.limit)} ${terminalText(assetName(grant.policy.asset))}`}`,
|
|
76546
|
+
` Refill: ${grant.policy.refill === "monthly" ? "Every 30 days" : "None"}`,
|
|
75951
76547
|
` Recipients: ${grant.policy.scope.recipients.type === "anyone" ? "anyone" : grant.policy.scope.recipients.accounts.map(terminalText).join(", ")}`,
|
|
75952
76548
|
` Actions: ${grant.policy.scope.actions.join(", ")}`
|
|
75953
76549
|
];
|
|
@@ -76089,10 +76685,10 @@ const previewLines$1 = (preview, recipient) => [
|
|
|
76089
76685
|
];
|
|
76090
76686
|
//#endregion
|
|
76091
76687
|
//#region src/commands/org-invite/subcommands/shared.ts
|
|
76092
|
-
const emailFlag$4 = Flag.String("email").pipe(Flag.optional);
|
|
76688
|
+
const emailFlag$4 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
76093
76689
|
const invitationIdArgument = Argument.String("id").pipe(Argument.withDescription("The invitation ID"), Argument.optional);
|
|
76094
|
-
const timeoutFlag$3 = Flag.Int("timeout-seconds").pipe(Flag.withDefault(120));
|
|
76095
|
-
const confirmFlag$3 = Flag.Boolean("confirm").pipe(Flag.withDefault(false));
|
|
76690
|
+
const timeoutFlag$3 = Flag.Int("timeout-seconds").pipe(Flag.withDescription(FLAG_HELP.timeout), Flag.withDefault(120));
|
|
76691
|
+
const confirmFlag$3 = Flag.Boolean("confirm").pipe(Flag.withDescription(FLAG_HELP.confirm), Flag.withDefault(false));
|
|
76096
76692
|
const target = {
|
|
76097
76693
|
invitationId: invitationIdArgument,
|
|
76098
76694
|
email: emailFlag$4
|
|
@@ -76129,8 +76725,8 @@ const command$57 = Command.make("invite", {
|
|
|
76129
76725
|
permissionIds: Flag.String("permission-id").pipe(Flag.withDescription("Manage-people Permission ID"), Flag.atLeast(0)),
|
|
76130
76726
|
...newBudgetFlags,
|
|
76131
76727
|
email: emailFlag$4,
|
|
76132
|
-
requestKey: Flag.String("request-key").pipe(Flag.optional),
|
|
76133
|
-
preview: Flag.Boolean("preview").pipe(Flag.withDefault(false)),
|
|
76728
|
+
requestKey: Flag.String("request-key").pipe(Flag.withDescription("a key that makes repeating the same invitation safe"), Flag.optional),
|
|
76729
|
+
preview: Flag.Boolean("preview").pipe(Flag.withDescription("show what would be authorized and write nothing"), Flag.withDefault(false)),
|
|
76134
76730
|
confirm: confirmFlag$3,
|
|
76135
76731
|
timeoutSeconds: timeoutFlag$3
|
|
76136
76732
|
}, ({ email, toEmail, accountId, budgetIds, permissionIds, newBudgetName, asset, limit, unlimited, recipientAccounts, anyRecipient, actions, requestKey, preview, confirm, timeoutSeconds }) => Effect.gen(function* () {
|
|
@@ -76396,8 +76992,8 @@ const execute$58 = (config, input) => Effect.gen(function* () {
|
|
|
76396
76992
|
/** --expected-offer is a compatibility spelling; conflicting digests refuse. */
|
|
76397
76993
|
const command$56 = Command.make("accept", {
|
|
76398
76994
|
...target,
|
|
76399
|
-
offerDigest: Flag.String("offer-digest").pipe(Flag.optional),
|
|
76400
|
-
expectedOffer: Flag.String("expected-offer").pipe(Flag.optional),
|
|
76995
|
+
offerDigest: Flag.String("offer-digest").pipe(Flag.withDescription("the digest of the offer you reviewed; scripts must pass it"), Flag.optional),
|
|
76996
|
+
expectedOffer: Flag.String("expected-offer").pipe(Flag.withDescription("the same as --offer-digest, under its older name"), Flag.optional),
|
|
76401
76997
|
confirm: confirmFlag$3,
|
|
76402
76998
|
timeoutSeconds: timeoutFlag$3
|
|
76403
76999
|
}, ({ email, invitationId, offerDigest, expectedOffer, confirm, timeoutSeconds }) => dispatch$3("invite.accept", "own", (config, orgs) => execute$58(config, {
|
|
@@ -76527,9 +77123,9 @@ const make$1 = (side) => {
|
|
|
76527
77123
|
const name = side === "own" ? "invite.list" : "org.invite.list";
|
|
76528
77124
|
return Command.make("list", {
|
|
76529
77125
|
email: emailFlag$4,
|
|
76530
|
-
limit: Flag.Int("limit").pipe(Flag.optional),
|
|
77126
|
+
limit: Flag.Int("limit").pipe(Flag.withDescription("how many invitations to return"), Flag.optional),
|
|
76531
77127
|
cursor: Flag.String("after").pipe(Flag.withDescription("the next page's cursor"), Flag.optional),
|
|
76532
|
-
phases: Flag.String("phase").pipe(Flag.atLeast(0))
|
|
77128
|
+
phases: Flag.String("phase").pipe(Flag.withDescription("only invitations in this phase; repeat it for several"), Flag.atLeast(0))
|
|
76533
77129
|
}, ({ email, limit, cursor, phases }) => dispatch$3(name, side, (config, orgs) => side === "org" && orgs.length === 0 ? Effect.fail(inputRequired("Provide --org.")) : execute$54(config, {
|
|
76534
77130
|
email: Option.getOrUndefined(email),
|
|
76535
77131
|
orgs,
|
|
@@ -76623,7 +77219,7 @@ const execute$50 = (config, input) => Effect.gen(function* () {
|
|
|
76623
77219
|
const key = JSON.stringify(progress);
|
|
76624
77220
|
if (key === previous) return;
|
|
76625
77221
|
previous = key;
|
|
76626
|
-
await run(
|
|
77222
|
+
await run(writeProgress(runtime, runtime.mode === "json" ? `${key}\n` : `Invitation ${terminalText(invitation.id)}: ${terminalText(invitation.phase)}; delivery: ${terminalText(invitation.delivery)}${invitation.recovery === null ? "" : `; recovery: ${terminalText(invitation.recovery.action)} (${terminalText(invitation.recovery.reason)})`}\n`), { signal });
|
|
76627
77223
|
});
|
|
76628
77224
|
}).pipe(Effect.onInterrupt(() => writeInvitationReadRecovery(runtime.mode, resolvedOrg, invitationId, session.email).pipe(Effect.ignoreCause))), {
|
|
76629
77225
|
...runtime,
|
|
@@ -76675,7 +77271,7 @@ const organizationInvite = Command.make("invite").pipe(annotate$3(CAPXUL_OPERATI
|
|
|
76675
77271
|
//#endregion
|
|
76676
77272
|
//#region src/commands/offer/subcommands/shared.ts
|
|
76677
77273
|
const idArgument$1 = Argument.String("id").pipe(Argument.withDescription("The offer ID"));
|
|
76678
|
-
const revisionFlag = Flag.Int("expected-revision");
|
|
77274
|
+
const revisionFlag = Flag.Int("expected-revision").pipe(Flag.withDescription("the revision you last read, so a change made since is not overwritten"));
|
|
76679
77275
|
const names$4 = {
|
|
76680
77276
|
root: "offer",
|
|
76681
77277
|
list: "offer.list",
|
|
@@ -76699,9 +77295,9 @@ const makeContext$1 = () => ({
|
|
|
76699
77295
|
});
|
|
76700
77296
|
//#endregion
|
|
76701
77297
|
//#region src/commands/request/subcommands/shared.ts
|
|
76702
|
-
const emailFlag$3 = Flag.String("email").pipe(Flag.optional);
|
|
77298
|
+
const emailFlag$3 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
76703
77299
|
const idArgument = Argument.String("id").pipe(Argument.withDescription("The request ID"));
|
|
76704
|
-
const confirmFlag$2 = Flag.Boolean("confirm").pipe(Flag.withDefault(false));
|
|
77300
|
+
const confirmFlag$2 = Flag.Boolean("confirm").pipe(Flag.withDescription(FLAG_HELP.confirm), Flag.withDefault(false));
|
|
76705
77301
|
const names$3 = {
|
|
76706
77302
|
"issued.root": "request",
|
|
76707
77303
|
"issued.issue": "request.issue",
|
|
@@ -76920,8 +77516,8 @@ const execute$47 = (config, input) => Effect.gen(function* () {
|
|
|
76920
77516
|
//#region src/commands/offer/subcommands/create/command.ts
|
|
76921
77517
|
const command$45 = (context) => Command.make("create", {
|
|
76922
77518
|
email: emailFlag$3,
|
|
76923
|
-
source: Flag.String("input"),
|
|
76924
|
-
requestKey: Flag.String("request-key"),
|
|
77519
|
+
source: Flag.String("input").pipe(Flag.withDescription("the offer JSON, from a file or - for stdin")),
|
|
77520
|
+
requestKey: Flag.String("request-key").pipe(Flag.withDescription("a key that makes repeating the same create safe")),
|
|
76925
77521
|
confirm: confirmFlag$2
|
|
76926
77522
|
}, ({ email, source, requestKey, confirm }) => context.dispatch("create", (scope) => ({
|
|
76927
77523
|
kind: "create",
|
|
@@ -76960,7 +77556,7 @@ const command$44 = (context) => Command.make("revise", {
|
|
|
76960
77556
|
email: emailFlag$3,
|
|
76961
77557
|
id: idArgument$1,
|
|
76962
77558
|
revision: revisionFlag,
|
|
76963
|
-
source: Flag.String("input"),
|
|
77559
|
+
source: Flag.String("input").pipe(Flag.withDescription("the offer JSON, from a file or - for stdin")),
|
|
76964
77560
|
confirm: confirmFlag$2
|
|
76965
77561
|
}, ({ email, id, revision, source, confirm }) => context.dispatch("revise", (scope) => ({
|
|
76966
77562
|
kind: "revise",
|
|
@@ -77022,15 +77618,16 @@ const makeCommand = () => {
|
|
|
77022
77618
|
const offer = makeCommand();
|
|
77023
77619
|
//#endregion
|
|
77024
77620
|
//#region src/commands/org/subcommands/permission/subcommands/shared.ts
|
|
77025
|
-
const emailFlag$2 = Flag.String("email").pipe(Flag.optional);
|
|
77621
|
+
const emailFlag$2 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
77026
77622
|
const permissionIdArgument = Argument.String("id").pipe(Argument.withDescription("The Permission ID"));
|
|
77027
|
-
const sourceFlag$2 = Flag.String("input").pipe(Flag.optional);
|
|
77028
|
-
const accountIdFlag = Flag.String("account-id").pipe(Flag.optional);
|
|
77623
|
+
const sourceFlag$2 = Flag.String("input").pipe(Flag.withDescription("the Permission JSON, from a file or - for stdin"), Flag.optional);
|
|
77624
|
+
const accountIdFlag = Flag.String("account-id").pipe(Flag.withDescription("the member's Account ID"), Flag.optional);
|
|
77029
77625
|
const assignmentIdArgument = Argument.String("id").pipe(Argument.withDescription("The Permission assignment ID"), Argument.optional);
|
|
77030
|
-
const expectedMemberEmailFlag = Flag.String("expected-member-email").pipe(Flag.optional);
|
|
77031
|
-
const requestKeyFlag$2 = Flag.String("request-key").pipe(Flag.between(0, 1), Flag.map((values) => Option.fromUndefinedOr(values[0])));
|
|
77032
|
-
const confirmFlag$1 = Flag.Boolean("confirm").pipe(Flag.withDefault(false));
|
|
77033
|
-
const
|
|
77626
|
+
const expectedMemberEmailFlag = Flag.String("expected-member-email").pipe(Flag.withDescription("the member's email, checked before anything changes"), Flag.optional);
|
|
77627
|
+
const requestKeyFlag$2 = Flag.String("request-key").pipe(Flag.withDescription(FLAG_HELP.requestKey), Flag.between(0, 1), Flag.map((values) => Option.fromUndefinedOr(values[0])));
|
|
77628
|
+
const confirmFlag$1 = Flag.Boolean("confirm").pipe(Flag.withDescription(FLAG_HELP.confirm), Flag.withDefault(false));
|
|
77629
|
+
const waitFlag = Flag.Boolean("wait").pipe(Flag.withDescription("after submitting, follow the Permission command until it is applied, failed or needs action"), Flag.withDefault(false));
|
|
77630
|
+
const timeoutFlag$2 = Flag.Int("timeout-seconds").pipe(Flag.withDescription(FLAG_HELP.timeout), Flag.withDefault(120));
|
|
77034
77631
|
const names$2 = {
|
|
77035
77632
|
root: "org.permission",
|
|
77036
77633
|
list: "org.permission.list",
|
|
@@ -77057,7 +77654,7 @@ const dispatch$2 = (kind, execute) => runCommand({
|
|
|
77057
77654
|
//#endregion
|
|
77058
77655
|
//#region src/commands/org/subcommands/permission/internal/write-input.ts
|
|
77059
77656
|
const LIMIT$2 = 65536;
|
|
77060
|
-
const invalid$5 = () => inputRequired("Provide a complete Permission policy with an exact asset, positive cap, recipient rule, and
|
|
77657
|
+
const invalid$5 = () => inputRequired("Provide a complete Permission policy with an exact asset, positive cap, recipient rule, supported actions, and an optional refill (\"monthly\": every 30 days, or \"none\").");
|
|
77061
77658
|
/** File, stdin, and guided answers use the SDK's complete-policy decoder. */
|
|
77062
77659
|
const parsePermissionInput = (input) => Effect.try({
|
|
77063
77660
|
try: () => decodePermissionIntent(input),
|
|
@@ -77101,6 +77698,18 @@ const guidePermissionInput = (before) => Effect.gen(function* () {
|
|
|
77101
77698
|
message: existing === null ? "Per-payment cap (major units)" : `Per-payment cap (major units; asset fixed: ${asset})`,
|
|
77102
77699
|
...existing?.limit == null ? {} : { default: existing.limit }
|
|
77103
77700
|
});
|
|
77701
|
+
const refill = yield* Prompt.Select({
|
|
77702
|
+
message: "Refill",
|
|
77703
|
+
choices: [{
|
|
77704
|
+
title: "Refills every 30 days",
|
|
77705
|
+
value: "monthly",
|
|
77706
|
+
selected: existing === null || existing.refill === "monthly"
|
|
77707
|
+
}, {
|
|
77708
|
+
title: "Never refills (the cap is one pot)",
|
|
77709
|
+
value: "none",
|
|
77710
|
+
selected: existing !== null && existing.refill !== "monthly"
|
|
77711
|
+
}]
|
|
77712
|
+
});
|
|
77104
77713
|
const recipients = yield* Prompt.Select({
|
|
77105
77714
|
message: "Recipient rule",
|
|
77106
77715
|
choices: [{
|
|
@@ -77128,6 +77737,7 @@ const guidePermissionInput = (before) => Effect.gen(function* () {
|
|
|
77128
77737
|
asset,
|
|
77129
77738
|
value
|
|
77130
77739
|
},
|
|
77740
|
+
refill,
|
|
77131
77741
|
scope: {
|
|
77132
77742
|
recipients: recipients === "anyone" ? { type: "anyone" } : {
|
|
77133
77743
|
type: "allowlist",
|
|
@@ -77312,7 +77922,7 @@ const validateWriteInput = (input) => Effect.gen(function* () {
|
|
|
77312
77922
|
const selectPolicy = (input, read, orgId, suppliedId, suppliedIntent, interactive) => Effect.gen(function* () {
|
|
77313
77923
|
const permission = input.kind === "change" ? yield* selectPermission(read?.items ?? [], orgId, suppliedId, interactive) : null;
|
|
77314
77924
|
const before = permission?.currentPolicy ?? null;
|
|
77315
|
-
const intent = suppliedIntent ?? (yield* guidePermissionInput(before));
|
|
77925
|
+
const intent = keepRefillOnChange(suppliedIntent ?? (yield* guidePermissionInput(before)), before);
|
|
77316
77926
|
if (before?.type === "budget" && intent.type === "budget" && before.asset !== intent.asset) return yield* inputRequired("A Budget change must retain its exact asset.");
|
|
77317
77927
|
const after = intentPolicy(intent);
|
|
77318
77928
|
const reset = allowanceReset(before, after);
|
|
@@ -77346,7 +77956,7 @@ const samePolicy = (left, right) => {
|
|
|
77346
77956
|
if (left === null || right === null) return left === right;
|
|
77347
77957
|
if (left.type !== right.type) return false;
|
|
77348
77958
|
if (left.type === "managePeople" || right.type === "managePeople") return true;
|
|
77349
|
-
return left.asset === right.asset && cap$1(left.limit) === cap$1(right.limit) && recipients(left) === recipients(right) && [...left.scope.actions].sort().join(",") === [...right.scope.actions].sort().join(",");
|
|
77959
|
+
return left.asset === right.asset && cap$1(left.limit) === cap$1(right.limit) && left.refill === "monthly" === (right.refill === "monthly") && recipients(left) === recipients(right) && [...left.scope.actions].sort().join(",") === [...right.scope.actions].sort().join(",");
|
|
77350
77960
|
};
|
|
77351
77961
|
const allowanceReset = (before, after) => before?.type === "budget" && before.limit !== null || before !== null && after.type === "budget" && after.limit !== null;
|
|
77352
77962
|
const writeFailureMessage = (error, fallback) => {
|
|
@@ -77372,14 +77982,14 @@ const selectionPreview = (selection) => {
|
|
|
77372
77982
|
} : { assignmentId: selection.assignmentId }
|
|
77373
77983
|
};
|
|
77374
77984
|
};
|
|
77985
|
+
/** The first line after a write is submitted. Submission does not prove application. */
|
|
77986
|
+
const submittedLine = (kind) => kind === "assign" ? "Submitted. Assignment is pending application." : kind === "revoke" ? "Submitted. Assignment removal is pending application." : kind === "replace" ? "Submitted. Module replacement is pending application." : "Submitted. The Permission is pending application.";
|
|
77375
77987
|
//#endregion
|
|
77376
77988
|
//#region src/commands/org/subcommands/permission/internal/presentation.ts
|
|
77377
77989
|
const cap = (permission) => {
|
|
77378
77990
|
if (permission.limitRaw === void 0) return "no cap";
|
|
77379
77991
|
if (permission.decimals === void 0) return `${terminalText(permission.limitRaw)} raw units (decimals unavailable)`;
|
|
77380
|
-
|
|
77381
|
-
const raw = String(permission.limitRaw).padStart(decimals + 1, "0");
|
|
77382
|
-
return decimals === 0 ? raw : `${raw.slice(0, -decimals)}.${raw.slice(-decimals)}`;
|
|
77992
|
+
return fromBaseUnits(String(permission.limitRaw), { decimals: permission.decimals });
|
|
77383
77993
|
};
|
|
77384
77994
|
const permissionLines = (permission) => [
|
|
77385
77995
|
`Permission ${terminalText(permission.permissionId)}`,
|
|
@@ -77387,7 +77997,7 @@ const permissionLines = (permission) => [
|
|
|
77387
77997
|
` Type: ${permission.type === "budget" ? "Budget" : "Manage people"}`,
|
|
77388
77998
|
` State: ${permission.state}`,
|
|
77389
77999
|
` Revision: ${permission.revision}`,
|
|
77390
|
-
...permission.type === "budget" ? [` Asset
|
|
78000
|
+
...permission.type === "budget" ? [` Asset: ${permission.assetId === void 0 ? "unavailable" : terminalText(assetName(permission.assetId))}`, ` Per-payment cap: ${cap(permission)}`] : [],
|
|
77391
78001
|
...permission.currentPolicy === void 0 ? [] : policyLines(" Current policy", permission.currentPolicy)
|
|
77392
78002
|
];
|
|
77393
78003
|
const permissionListLines = (organizationId, result, name) => [
|
|
@@ -77429,7 +78039,7 @@ const resolvePermissionScope = (config, email, explicit) => Effect.gen(function*
|
|
|
77429
78039
|
});
|
|
77430
78040
|
let orgId = explicit ?? (yield* readOrganizationSelection(config, session.authUserId));
|
|
77431
78041
|
if (orgId === null) {
|
|
77432
|
-
if (!runtime.tty || runtime.ci || runtime.mode === "json" || organizations.length === 0) return yield*
|
|
78042
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json" || organizations.length === 0) return yield* organizationRequired();
|
|
77433
78043
|
orgId = yield* Prompt.Select({
|
|
77434
78044
|
message: "Organization",
|
|
77435
78045
|
choices: organizations.map((org) => ({
|
|
@@ -77525,6 +78135,112 @@ const restoreWriteClient = (config, opened, signer, confirmed, refusal) => Effec
|
|
|
77525
78135
|
return write;
|
|
77526
78136
|
});
|
|
77527
78137
|
//#endregion
|
|
78138
|
+
//#region src/platform/observe-snapshots.ts
|
|
78139
|
+
/**
|
|
78140
|
+
* Follow an SDK subscription until `onValue` finishes it. An abort, a failed
|
|
78141
|
+
* registration, an error snapshot or a failed print settles it too; every
|
|
78142
|
+
* progress line is written before it resolves, and teardown runs once.
|
|
78143
|
+
*/
|
|
78144
|
+
const observeSnapshots = (register, operation, onValue) => Effect.gen(function* () {
|
|
78145
|
+
const runtime = yield* CliRuntime;
|
|
78146
|
+
const run = Effect.runPromiseWith(yield* Effect.context());
|
|
78147
|
+
let output = Promise.resolve();
|
|
78148
|
+
return yield* Effect.promise((signal) => new Promise((resolve) => {
|
|
78149
|
+
let closed = false;
|
|
78150
|
+
let unsubscribe;
|
|
78151
|
+
let previous;
|
|
78152
|
+
const finish = (result) => {
|
|
78153
|
+
if (closed) return;
|
|
78154
|
+
closed = true;
|
|
78155
|
+
signal.removeEventListener("abort", abort);
|
|
78156
|
+
unsubscribe?.();
|
|
78157
|
+
output.then(() => resolve(result), (cause) => resolve({
|
|
78158
|
+
ok: false,
|
|
78159
|
+
error: Errors.unknown(cause)
|
|
78160
|
+
}));
|
|
78161
|
+
};
|
|
78162
|
+
const abort = () => finish({
|
|
78163
|
+
ok: false,
|
|
78164
|
+
error: Errors.cancelled({ operation })
|
|
78165
|
+
});
|
|
78166
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
78167
|
+
const report = (progress, human) => {
|
|
78168
|
+
const semantic = JSON.stringify(progress);
|
|
78169
|
+
if (semantic === previous) return;
|
|
78170
|
+
previous = semantic;
|
|
78171
|
+
const text = runtime.mode === "json" ? semantic : human();
|
|
78172
|
+
output = output.then(() => signal.aborted ? void 0 : run(writeProgress(runtime, `${text}\n`), { signal }));
|
|
78173
|
+
output.catch((cause) => finish({
|
|
78174
|
+
ok: false,
|
|
78175
|
+
error: Errors.unknown(cause)
|
|
78176
|
+
}));
|
|
78177
|
+
};
|
|
78178
|
+
const listener = (snapshot) => {
|
|
78179
|
+
if (closed || snapshot.status === "loading") return;
|
|
78180
|
+
if (snapshot.status === "error") return finish({
|
|
78181
|
+
ok: false,
|
|
78182
|
+
error: snapshot.error
|
|
78183
|
+
});
|
|
78184
|
+
onValue(snapshot.value, {
|
|
78185
|
+
finish,
|
|
78186
|
+
report
|
|
78187
|
+
});
|
|
78188
|
+
};
|
|
78189
|
+
register(listener, signal).then((result) => {
|
|
78190
|
+
if (!result.ok) return finish(result);
|
|
78191
|
+
unsubscribe = result.value;
|
|
78192
|
+
if (closed) unsubscribe();
|
|
78193
|
+
}).catch((cause) => finish({
|
|
78194
|
+
ok: false,
|
|
78195
|
+
error: Errors.unknown(cause)
|
|
78196
|
+
}));
|
|
78197
|
+
if (signal.aborted) abort();
|
|
78198
|
+
})).pipe(Effect.onInterrupt(() => Effect.promise(() => output).pipe(Effect.ignoreCause)));
|
|
78199
|
+
});
|
|
78200
|
+
//#endregion
|
|
78201
|
+
//#region src/commands/org/subcommands/permission/internal/observation-input.ts
|
|
78202
|
+
const missing = () => new CliFailure({
|
|
78203
|
+
code: "INVALID_INPUT",
|
|
78204
|
+
outcome: "refused",
|
|
78205
|
+
exitCode: 2,
|
|
78206
|
+
message: "The owned Permission command was not found. Check --org and --request-key."
|
|
78207
|
+
});
|
|
78208
|
+
const matches$1 = (value, orgId, key) => value.orgId === orgId && value.requestKey === key;
|
|
78209
|
+
const mismatch$1 = () => Errors.invalidInput("command", "Permission command response scope does not match");
|
|
78210
|
+
const validateObservationInput = (input) => Effect.gen(function* () {
|
|
78211
|
+
const timeout = yield* readWriteTimeout(input.timeoutSeconds);
|
|
78212
|
+
const email = input.email === void 0 ? void 0 : yield* readEmail(input.email);
|
|
78213
|
+
let rawKey = input.requestKey;
|
|
78214
|
+
if (rawKey !== void 0) {
|
|
78215
|
+
const key = yield* readRequestKey(rawKey);
|
|
78216
|
+
if (rawKey !== key) return yield* inputRequired("Provide one exact --request-key without surrounding whitespace.");
|
|
78217
|
+
}
|
|
78218
|
+
let rawOrg = input.orgs[0];
|
|
78219
|
+
if (input.orgs.length > 1 || rawOrg !== void 0 && /[\p{Cc}\p{Cf}]/u.test(rawOrg)) return yield* inputRequired("Provide one exact --org ID.");
|
|
78220
|
+
if (rawOrg !== void 0) yield* Effect.try({
|
|
78221
|
+
try: () => toOrgId(rawOrg),
|
|
78222
|
+
catch: () => inputRequired("Provide a valid --org ID.")
|
|
78223
|
+
});
|
|
78224
|
+
if (rawOrg === void 0 || rawKey === void 0) {
|
|
78225
|
+
const runtime = yield* CliRuntime;
|
|
78226
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* inputRequired(rawOrg === void 0 ? "Provide one exact --org ID." : "Provide --request-key.");
|
|
78227
|
+
if (rawOrg === void 0) rawOrg = yield* Prompt.String({ message: "Exact Organization ID (--org)" });
|
|
78228
|
+
}
|
|
78229
|
+
const orgId = yield* Effect.try({
|
|
78230
|
+
try: () => toOrgId(rawOrg),
|
|
78231
|
+
catch: () => inputRequired("Provide a valid --org ID.")
|
|
78232
|
+
});
|
|
78233
|
+
if (rawKey === void 0) rawKey = yield* Prompt.String({ message: "Original request key (--request-key)" });
|
|
78234
|
+
const key = yield* readRequestKey(rawKey);
|
|
78235
|
+
if (rawKey !== key) return yield* inputRequired("Provide one exact --request-key without surrounding whitespace.");
|
|
78236
|
+
return {
|
|
78237
|
+
orgId,
|
|
78238
|
+
key,
|
|
78239
|
+
timeout,
|
|
78240
|
+
email
|
|
78241
|
+
};
|
|
78242
|
+
});
|
|
78243
|
+
//#endregion
|
|
77528
78244
|
//#region src/commands/org/subcommands/permission/internal/command-recovery.ts
|
|
77529
78245
|
const commandRecovery = (orgId, key, email, commandId) => {
|
|
77530
78246
|
const args = [
|
|
@@ -77573,6 +78289,112 @@ const commandLines = (view) => [
|
|
|
77573
78289
|
"Verified receipts",
|
|
77574
78290
|
...view.verifiedReceipts.length === 0 ? [" None recorded."] : view.verifiedReceipts.map((receipt) => ` ${terminalText(receipt.transactionHash)}; block ${receipt.blockNumber}; log ${receipt.logIndex}`)
|
|
77575
78291
|
];
|
|
78292
|
+
/** Where `--wait` ended: only an applied command changed the Permission. */
|
|
78293
|
+
const endedLine = (view) => view.status === "applied" ? "Applied." : view.status === "failed" ? "Not applied: the command failed." : view.status === "actionable" ? "Not applied: the command needs action." : "Still pending.";
|
|
78294
|
+
//#endregion
|
|
78295
|
+
//#region src/commands/org/subcommands/permission/internal/command-observation.ts
|
|
78296
|
+
/** The command's state as one progress record: its executions and verified receipts. */
|
|
78297
|
+
const commandProgress = (orgId, key, value) => ({
|
|
78298
|
+
type: "permission.command.state",
|
|
78299
|
+
organizationId: orgId,
|
|
78300
|
+
requestKey: key,
|
|
78301
|
+
commandId: value.commandId,
|
|
78302
|
+
status: value.status,
|
|
78303
|
+
state: value.state,
|
|
78304
|
+
executions: value.executions.map((execution) => ({
|
|
78305
|
+
executionId: execution.executionId,
|
|
78306
|
+
state: execution.state,
|
|
78307
|
+
...execution.userOpHash === void 0 ? {} : { userOpHash: execution.userOpHash }
|
|
78308
|
+
})),
|
|
78309
|
+
verifiedReceipts: value.verifiedReceipts.map((receipt) => ({
|
|
78310
|
+
transactionHash: receipt.transactionHash,
|
|
78311
|
+
blockNumber: receipt.blockNumber,
|
|
78312
|
+
logIndex: receipt.logIndex
|
|
78313
|
+
}))
|
|
78314
|
+
});
|
|
78315
|
+
const commandProgressLines = (value, progress) => [
|
|
78316
|
+
`Permission command ${terminalText(value.commandId)}: ${value.status}; recorded state ${value.state}`,
|
|
78317
|
+
...progress.executions.map((execution) => ` Execution ${terminalText(execution.executionId)}: ${execution.state}${execution.userOpHash === void 0 ? "" : `; user operation ${terminalText(execution.userOpHash)}`}`),
|
|
78318
|
+
...progress.verifiedReceipts.map((receipt) => ` Verified receipt: ${terminalText(receipt.transactionHash)}; block ${receipt.blockNumber}; log ${receipt.logIndex}`)
|
|
78319
|
+
].join("\n");
|
|
78320
|
+
/** The SDK supplies fresh authorized snapshots; one finish path owns teardown. */
|
|
78321
|
+
const observeCommand = (register, orgId, key, seen) => Effect.suspend(() => {
|
|
78322
|
+
let observedId;
|
|
78323
|
+
return observeSnapshots(register, CAPXUL_OPERATIONS.org.permissions.subscribeCommand, (value, { finish, report }) => {
|
|
78324
|
+
if (value === null) return finish({
|
|
78325
|
+
ok: true,
|
|
78326
|
+
value: null
|
|
78327
|
+
});
|
|
78328
|
+
if (!matches$1(value, orgId, key) || observedId !== void 0 && observedId !== value.commandId) return finish({
|
|
78329
|
+
ok: false,
|
|
78330
|
+
error: mismatch$1()
|
|
78331
|
+
});
|
|
78332
|
+
observedId = value.commandId;
|
|
78333
|
+
seen(value);
|
|
78334
|
+
const progress = commandProgress(orgId, key, value);
|
|
78335
|
+
report(progress, () => commandProgressLines(value, progress));
|
|
78336
|
+
if (value.status === "applied" || value.status === "failed" || value.status === "actionable") finish({
|
|
78337
|
+
ok: true,
|
|
78338
|
+
value
|
|
78339
|
+
});
|
|
78340
|
+
});
|
|
78341
|
+
}).pipe(Effect.flatMap(resultValue));
|
|
78342
|
+
/** Follow the command with fresh SDK snapshots until it is applied, failed or actionable. */
|
|
78343
|
+
const followCommand = (methods, orgId, key, seen) => observeCommand((listener, signal) => methods.subscribeCommand(key, listener, { signal }), orgId, key, seen);
|
|
78344
|
+
/** Historical reads never select current Organization access or compose a signer. */
|
|
78345
|
+
const observeValidatedCommand = (config, input, validated, read) => Effect.gen(function* () {
|
|
78346
|
+
const { orgId, key, timeout } = validated;
|
|
78347
|
+
let email = validated.email;
|
|
78348
|
+
let commandId;
|
|
78349
|
+
const runtime = yield* CliRuntime;
|
|
78350
|
+
const guarded = (work) => work.pipe(Effect.mapError((error) => {
|
|
78351
|
+
const failure = classifyFailure(Cause.fail(error), input.kind === "commandGet" ? "org.permission.command.get" : "org.permission.command.wait");
|
|
78352
|
+
const known = commandRecovery(orgId, key, email, commandId);
|
|
78353
|
+
return new CliFailure({
|
|
78354
|
+
...failure,
|
|
78355
|
+
details: {
|
|
78356
|
+
...failure.details,
|
|
78357
|
+
...known.details
|
|
78358
|
+
},
|
|
78359
|
+
message: `${isCapxulError(error) && error.details?.field === "command" ? "The Permission command response could not be verified." : failure.message}\n${known.lines}`
|
|
78360
|
+
});
|
|
78361
|
+
}), Effect.onInterrupt(() => {
|
|
78362
|
+
const known = commandRecovery(orgId, key, email, commandId);
|
|
78363
|
+
return writeStderr(runtime.mode === "json" ? `${JSON.stringify({
|
|
78364
|
+
type: "permission.command.recovery",
|
|
78365
|
+
...known.details
|
|
78366
|
+
})}\n` : `${known.lines}\n`).pipe(Effect.ignoreCause);
|
|
78367
|
+
}));
|
|
78368
|
+
const restored = yield* guarded(restoreAuthenticatedClient(config, email, { requirement: "none" }));
|
|
78369
|
+
email = restored.session.email;
|
|
78370
|
+
const result = yield* guarded(Effect.gen(function* () {
|
|
78371
|
+
const reading = read(restored.client.org(orgId).permissions, orgId, key, (value) => {
|
|
78372
|
+
commandId = value.commandId;
|
|
78373
|
+
});
|
|
78374
|
+
const view = yield* input.kind === "commandWait" ? withWaiting(reading, {
|
|
78375
|
+
...runtime,
|
|
78376
|
+
label: "Observe Permission command state"
|
|
78377
|
+
}) : reading;
|
|
78378
|
+
if (view === null) return yield* missing();
|
|
78379
|
+
if (!matches$1(view, orgId, key)) return yield* Effect.fail(mismatch$1());
|
|
78380
|
+
commandId = view.commandId;
|
|
78381
|
+
return view;
|
|
78382
|
+
})).pipe(Effect.timeoutOption(timeout * 1e3));
|
|
78383
|
+
if (Option.isNone(result)) {
|
|
78384
|
+
const known = commandRecovery(orgId, key, email, commandId);
|
|
78385
|
+
return yield* new CliFailure({
|
|
78386
|
+
code: "CLI_DEADLINE",
|
|
78387
|
+
outcome: "failed",
|
|
78388
|
+
exitCode: 5,
|
|
78389
|
+
details: known.details,
|
|
78390
|
+
message: `The CLI stopped waiting. Permission application is not confirmed.\n${known.lines}`
|
|
78391
|
+
});
|
|
78392
|
+
}
|
|
78393
|
+
return {
|
|
78394
|
+
data: { command: result.value },
|
|
78395
|
+
lines: commandLines(result.value)
|
|
78396
|
+
};
|
|
78397
|
+
});
|
|
77576
78398
|
//#endregion
|
|
77577
78399
|
//#region src/commands/org/subcommands/permission/internal/write-target.ts
|
|
77578
78400
|
/** Read what the confirmed write targets: the replacement preview, the assignment, or the policy. */
|
|
@@ -77634,7 +78456,7 @@ const writeValidatedPermission = (config, input, validated, submit) => Effect.ge
|
|
|
77634
78456
|
lines,
|
|
77635
78457
|
question: selection.kind === "replace" ? "Replace this module with this complete authority scope?" : selection.kind === "assign" ? "Assign this Permission to this Account?" : selection.kind === "revoke" ? "Revoke only this assignment?" : "Confirm this complete policy?"
|
|
77636
78458
|
});
|
|
77637
|
-
else if (runtime.mode !== "json") yield*
|
|
78459
|
+
else if (runtime.mode !== "json") yield* writeProgress(runtime, `${lines.join("\n")}\n`);
|
|
77638
78460
|
else yield* writeStderr(`${JSON.stringify({
|
|
77639
78461
|
type: "permission.preview",
|
|
77640
78462
|
organizationId: orgId,
|
|
@@ -77706,19 +78528,40 @@ const writeValidatedPermission = (config, input, validated, submit) => Effect.ge
|
|
|
77706
78528
|
details: recovery(),
|
|
77707
78529
|
message: `The CLI stopped waiting. Permission application is not confirmed.\n${recoveryText()}`
|
|
77708
78530
|
});
|
|
77709
|
-
|
|
78531
|
+
const execution = result.value;
|
|
78532
|
+
if (input.wait !== true) return {
|
|
77710
78533
|
data: {
|
|
77711
|
-
execution
|
|
78534
|
+
execution,
|
|
77712
78535
|
requestKey: key
|
|
77713
78536
|
},
|
|
77714
78537
|
lines: [
|
|
77715
|
-
selection.kind
|
|
77716
|
-
`Command: ${terminalText(
|
|
77717
|
-
`Execution: ${terminalText(
|
|
78538
|
+
submittedLine(selection.kind),
|
|
78539
|
+
`Command: ${terminalText(execution.commandId)}`,
|
|
78540
|
+
`Execution: ${terminalText(execution.executionId)}`,
|
|
77718
78541
|
`Request key: ${key}`,
|
|
77719
78542
|
commandRecovery(orgId, key, preview.session.email).lines
|
|
77720
78543
|
]
|
|
77721
78544
|
};
|
|
78545
|
+
const followed = yield* observeValidatedCommand(config, {
|
|
78546
|
+
kind: "commandWait",
|
|
78547
|
+
orgs: [orgId],
|
|
78548
|
+
email: preview.session.email,
|
|
78549
|
+
requestKey: key,
|
|
78550
|
+
timeoutSeconds: input.timeoutSeconds
|
|
78551
|
+
}, {
|
|
78552
|
+
orgId,
|
|
78553
|
+
key,
|
|
78554
|
+
timeout,
|
|
78555
|
+
email: preview.session.email
|
|
78556
|
+
}, followCommand);
|
|
78557
|
+
return {
|
|
78558
|
+
data: {
|
|
78559
|
+
execution,
|
|
78560
|
+
requestKey: key,
|
|
78561
|
+
command: followed.data.command
|
|
78562
|
+
},
|
|
78563
|
+
lines: [endedLine(followed.data.command), ...commandLines(followed.data.command)]
|
|
78564
|
+
};
|
|
77722
78565
|
});
|
|
77723
78566
|
//#endregion
|
|
77724
78567
|
//#region src/commands/org/subcommands/permission/subcommands/create/execute.ts
|
|
@@ -77742,14 +78585,16 @@ const command$40 = Command.make("create", {
|
|
|
77742
78585
|
input: sourceFlag$2,
|
|
77743
78586
|
requestKey: requestKeyFlag$2,
|
|
77744
78587
|
confirm: confirmFlag$1,
|
|
78588
|
+
wait: waitFlag,
|
|
77745
78589
|
timeoutSeconds: timeoutFlag$2
|
|
77746
|
-
}, ({ email, input, requestKey, confirm, timeoutSeconds }) => dispatch$2("create", (config, orgs) => execute$42(config, {
|
|
78590
|
+
}, ({ email, input, requestKey, confirm, wait, timeoutSeconds }) => dispatch$2("create", (config, orgs) => execute$42(config, {
|
|
77747
78591
|
kind: "create",
|
|
77748
78592
|
orgs,
|
|
77749
78593
|
email: Option.getOrUndefined(email),
|
|
77750
78594
|
source: Option.getOrUndefined(input),
|
|
77751
78595
|
requestKey: Option.getOrUndefined(requestKey),
|
|
77752
78596
|
confirm,
|
|
78597
|
+
wait,
|
|
77753
78598
|
timeoutSeconds
|
|
77754
78599
|
}))).pipe(annotate$2("create"), Command.withDescription("Create a Permission from its complete policy"));
|
|
77755
78600
|
//#endregion
|
|
@@ -77776,8 +78621,9 @@ const command$39 = Command.make("change", {
|
|
|
77776
78621
|
input: sourceFlag$2,
|
|
77777
78622
|
requestKey: requestKeyFlag$2,
|
|
77778
78623
|
confirm: confirmFlag$1,
|
|
78624
|
+
wait: waitFlag,
|
|
77779
78625
|
timeoutSeconds: timeoutFlag$2
|
|
77780
|
-
}, ({ email, permissionId, input, requestKey, confirm, timeoutSeconds }) => dispatch$2("change", (config, orgs) => execute$41(config, {
|
|
78626
|
+
}, ({ email, permissionId, input, requestKey, confirm, wait, timeoutSeconds }) => dispatch$2("change", (config, orgs) => execute$41(config, {
|
|
77781
78627
|
kind: "change",
|
|
77782
78628
|
orgs,
|
|
77783
78629
|
email: Option.getOrUndefined(email),
|
|
@@ -77785,6 +78631,7 @@ const command$39 = Command.make("change", {
|
|
|
77785
78631
|
source: Option.getOrUndefined(input),
|
|
77786
78632
|
requestKey: Option.getOrUndefined(requestKey),
|
|
77787
78633
|
confirm,
|
|
78634
|
+
wait,
|
|
77788
78635
|
timeoutSeconds
|
|
77789
78636
|
}))).pipe(annotate$2("change"), Command.withDescription("Replace one active Permission's complete policy"));
|
|
77790
78637
|
//#endregion
|
|
@@ -77813,8 +78660,9 @@ const command$38 = Command.make("assign", {
|
|
|
77813
78660
|
expectedMemberEmail: expectedMemberEmailFlag,
|
|
77814
78661
|
requestKey: requestKeyFlag$2,
|
|
77815
78662
|
confirm: confirmFlag$1,
|
|
78663
|
+
wait: waitFlag,
|
|
77816
78664
|
timeoutSeconds: timeoutFlag$2
|
|
77817
|
-
}, ({ email, permissionId, accountId, expectedMemberEmail, requestKey, confirm, timeoutSeconds }) => dispatch$2("assign", (config, orgs) => execute$40(config, {
|
|
78665
|
+
}, ({ email, permissionId, accountId, expectedMemberEmail, requestKey, confirm, wait, timeoutSeconds }) => dispatch$2("assign", (config, orgs) => execute$40(config, {
|
|
77818
78666
|
kind: "assign",
|
|
77819
78667
|
orgs,
|
|
77820
78668
|
email: Option.getOrUndefined(email),
|
|
@@ -77823,6 +78671,7 @@ const command$38 = Command.make("assign", {
|
|
|
77823
78671
|
expectedMemberEmail: Option.getOrUndefined(expectedMemberEmail),
|
|
77824
78672
|
requestKey: Option.getOrUndefined(requestKey),
|
|
77825
78673
|
confirm,
|
|
78674
|
+
wait,
|
|
77826
78675
|
timeoutSeconds
|
|
77827
78676
|
}))).pipe(annotate$2("assign"), Command.withDescription("Assign one active Permission to one exact Account"));
|
|
77828
78677
|
//#endregion
|
|
@@ -77847,14 +78696,16 @@ const command$37 = Command.make("revoke", {
|
|
|
77847
78696
|
assignmentId: assignmentIdArgument,
|
|
77848
78697
|
requestKey: requestKeyFlag$2,
|
|
77849
78698
|
confirm: confirmFlag$1,
|
|
78699
|
+
wait: waitFlag,
|
|
77850
78700
|
timeoutSeconds: timeoutFlag$2
|
|
77851
|
-
}, ({ email, assignmentId, requestKey, confirm, timeoutSeconds }) => dispatch$2("revoke", (config, orgs) => execute$39(config, {
|
|
78701
|
+
}, ({ email, assignmentId, requestKey, confirm, wait, timeoutSeconds }) => dispatch$2("revoke", (config, orgs) => execute$39(config, {
|
|
77852
78702
|
kind: "revoke",
|
|
77853
78703
|
orgs,
|
|
77854
78704
|
email: Option.getOrUndefined(email),
|
|
77855
78705
|
assignmentId: Option.getOrUndefined(assignmentId),
|
|
77856
78706
|
requestKey: Option.getOrUndefined(requestKey),
|
|
77857
78707
|
confirm,
|
|
78708
|
+
wait,
|
|
77858
78709
|
timeoutSeconds
|
|
77859
78710
|
}))).pipe(annotate$2("revoke"), Command.withDescription("Revoke one exact active Permission assignment"));
|
|
77860
78711
|
//#endregion
|
|
@@ -77878,224 +78729,18 @@ const command$36 = Command.make("replace", {
|
|
|
77878
78729
|
email: emailFlag$2,
|
|
77879
78730
|
requestKey: requestKeyFlag$2,
|
|
77880
78731
|
confirm: confirmFlag$1,
|
|
78732
|
+
wait: waitFlag,
|
|
77881
78733
|
timeoutSeconds: timeoutFlag$2
|
|
77882
|
-
}, ({ email, requestKey, confirm, timeoutSeconds }) => dispatch$2("replace", (config, orgs) => execute$38(config, {
|
|
78734
|
+
}, ({ email, requestKey, confirm, wait, timeoutSeconds }) => dispatch$2("replace", (config, orgs) => execute$38(config, {
|
|
77883
78735
|
kind: "replace",
|
|
77884
78736
|
orgs,
|
|
77885
78737
|
email: Option.getOrUndefined(email),
|
|
77886
78738
|
requestKey: Option.getOrUndefined(requestKey),
|
|
77887
78739
|
confirm,
|
|
78740
|
+
wait,
|
|
77888
78741
|
timeoutSeconds
|
|
77889
78742
|
}))).pipe(annotate$2("replace"), Command.withDescription("Replace the current Roles module with its complete authority scope"));
|
|
77890
78743
|
//#endregion
|
|
77891
|
-
//#region src/platform/observe-snapshots.ts
|
|
77892
|
-
/**
|
|
77893
|
-
* Follow an SDK subscription until `onValue` finishes it. An abort, a failed
|
|
77894
|
-
* registration, an error snapshot or a failed print settles it too; every
|
|
77895
|
-
* progress line is written before it resolves, and teardown runs once.
|
|
77896
|
-
*/
|
|
77897
|
-
const observeSnapshots = (register, operation, onValue) => Effect.gen(function* () {
|
|
77898
|
-
const runtime = yield* CliRuntime;
|
|
77899
|
-
const run = Effect.runPromiseWith(yield* Effect.context());
|
|
77900
|
-
let output = Promise.resolve();
|
|
77901
|
-
return yield* Effect.promise((signal) => new Promise((resolve) => {
|
|
77902
|
-
let closed = false;
|
|
77903
|
-
let unsubscribe;
|
|
77904
|
-
let previous;
|
|
77905
|
-
const finish = (result) => {
|
|
77906
|
-
if (closed) return;
|
|
77907
|
-
closed = true;
|
|
77908
|
-
signal.removeEventListener("abort", abort);
|
|
77909
|
-
unsubscribe?.();
|
|
77910
|
-
output.then(() => resolve(result), (cause) => resolve({
|
|
77911
|
-
ok: false,
|
|
77912
|
-
error: Errors.unknown(cause)
|
|
77913
|
-
}));
|
|
77914
|
-
};
|
|
77915
|
-
const abort = () => finish({
|
|
77916
|
-
ok: false,
|
|
77917
|
-
error: Errors.cancelled({ operation })
|
|
77918
|
-
});
|
|
77919
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
77920
|
-
const report = (progress, human) => {
|
|
77921
|
-
const semantic = JSON.stringify(progress);
|
|
77922
|
-
if (semantic === previous) return;
|
|
77923
|
-
previous = semantic;
|
|
77924
|
-
const text = runtime.mode === "json" ? semantic : human();
|
|
77925
|
-
output = output.then(() => signal.aborted ? void 0 : run(writeStderr(`${text}\n`), { signal }));
|
|
77926
|
-
output.catch((cause) => finish({
|
|
77927
|
-
ok: false,
|
|
77928
|
-
error: Errors.unknown(cause)
|
|
77929
|
-
}));
|
|
77930
|
-
};
|
|
77931
|
-
const listener = (snapshot) => {
|
|
77932
|
-
if (closed || snapshot.status === "loading") return;
|
|
77933
|
-
if (snapshot.status === "error") return finish({
|
|
77934
|
-
ok: false,
|
|
77935
|
-
error: snapshot.error
|
|
77936
|
-
});
|
|
77937
|
-
onValue(snapshot.value, {
|
|
77938
|
-
finish,
|
|
77939
|
-
report
|
|
77940
|
-
});
|
|
77941
|
-
};
|
|
77942
|
-
register(listener, signal).then((result) => {
|
|
77943
|
-
if (!result.ok) return finish(result);
|
|
77944
|
-
unsubscribe = result.value;
|
|
77945
|
-
if (closed) unsubscribe();
|
|
77946
|
-
}).catch((cause) => finish({
|
|
77947
|
-
ok: false,
|
|
77948
|
-
error: Errors.unknown(cause)
|
|
77949
|
-
}));
|
|
77950
|
-
if (signal.aborted) abort();
|
|
77951
|
-
})).pipe(Effect.onInterrupt(() => Effect.promise(() => output).pipe(Effect.ignoreCause)));
|
|
77952
|
-
});
|
|
77953
|
-
//#endregion
|
|
77954
|
-
//#region src/commands/org/subcommands/permission/internal/observation-input.ts
|
|
77955
|
-
const missing = () => new CliFailure({
|
|
77956
|
-
code: "INVALID_INPUT",
|
|
77957
|
-
outcome: "refused",
|
|
77958
|
-
exitCode: 2,
|
|
77959
|
-
message: "The owned Permission command was not found. Check --org and --request-key."
|
|
77960
|
-
});
|
|
77961
|
-
const matches$1 = (value, orgId, key) => value.orgId === orgId && value.requestKey === key;
|
|
77962
|
-
const mismatch$1 = () => Errors.invalidInput("command", "Permission command response scope does not match");
|
|
77963
|
-
const validateObservationInput = (input) => Effect.gen(function* () {
|
|
77964
|
-
const timeout = yield* readWriteTimeout(input.timeoutSeconds);
|
|
77965
|
-
const email = input.email === void 0 ? void 0 : yield* readEmail(input.email);
|
|
77966
|
-
let rawKey = input.requestKey;
|
|
77967
|
-
if (rawKey !== void 0) {
|
|
77968
|
-
const key = yield* readRequestKey(rawKey);
|
|
77969
|
-
if (rawKey !== key) return yield* inputRequired("Provide one exact --request-key without surrounding whitespace.");
|
|
77970
|
-
}
|
|
77971
|
-
let rawOrg = input.orgs[0];
|
|
77972
|
-
if (input.orgs.length > 1 || rawOrg !== void 0 && /[\p{Cc}\p{Cf}]/u.test(rawOrg)) return yield* inputRequired("Provide one exact --org ID.");
|
|
77973
|
-
if (rawOrg !== void 0) yield* Effect.try({
|
|
77974
|
-
try: () => toOrgId(rawOrg),
|
|
77975
|
-
catch: () => inputRequired("Provide a valid --org ID.")
|
|
77976
|
-
});
|
|
77977
|
-
if (rawOrg === void 0 || rawKey === void 0) {
|
|
77978
|
-
const runtime = yield* CliRuntime;
|
|
77979
|
-
if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* inputRequired(rawOrg === void 0 ? "Provide one exact --org ID." : "Provide --request-key.");
|
|
77980
|
-
if (rawOrg === void 0) rawOrg = yield* Prompt.String({ message: "Exact Organization ID (--org)" });
|
|
77981
|
-
}
|
|
77982
|
-
const orgId = yield* Effect.try({
|
|
77983
|
-
try: () => toOrgId(rawOrg),
|
|
77984
|
-
catch: () => inputRequired("Provide a valid --org ID.")
|
|
77985
|
-
});
|
|
77986
|
-
if (rawKey === void 0) rawKey = yield* Prompt.String({ message: "Original request key (--request-key)" });
|
|
77987
|
-
const key = yield* readRequestKey(rawKey);
|
|
77988
|
-
if (rawKey !== key) return yield* inputRequired("Provide one exact --request-key without surrounding whitespace.");
|
|
77989
|
-
return {
|
|
77990
|
-
orgId,
|
|
77991
|
-
key,
|
|
77992
|
-
timeout,
|
|
77993
|
-
email
|
|
77994
|
-
};
|
|
77995
|
-
});
|
|
77996
|
-
//#endregion
|
|
77997
|
-
//#region src/commands/org/subcommands/permission/internal/command-observation.ts
|
|
77998
|
-
/** The command's state as one progress record: its executions and verified receipts. */
|
|
77999
|
-
const commandProgress = (orgId, key, value) => ({
|
|
78000
|
-
type: "permission.command.state",
|
|
78001
|
-
organizationId: orgId,
|
|
78002
|
-
requestKey: key,
|
|
78003
|
-
commandId: value.commandId,
|
|
78004
|
-
status: value.status,
|
|
78005
|
-
state: value.state,
|
|
78006
|
-
executions: value.executions.map((execution) => ({
|
|
78007
|
-
executionId: execution.executionId,
|
|
78008
|
-
state: execution.state,
|
|
78009
|
-
...execution.userOpHash === void 0 ? {} : { userOpHash: execution.userOpHash }
|
|
78010
|
-
})),
|
|
78011
|
-
verifiedReceipts: value.verifiedReceipts.map((receipt) => ({
|
|
78012
|
-
transactionHash: receipt.transactionHash,
|
|
78013
|
-
blockNumber: receipt.blockNumber,
|
|
78014
|
-
logIndex: receipt.logIndex
|
|
78015
|
-
}))
|
|
78016
|
-
});
|
|
78017
|
-
const commandProgressLines = (value, progress) => [
|
|
78018
|
-
`Permission command ${terminalText(value.commandId)}: ${value.status}; recorded state ${value.state}`,
|
|
78019
|
-
...progress.executions.map((execution) => ` Execution ${terminalText(execution.executionId)}: ${execution.state}${execution.userOpHash === void 0 ? "" : `; user operation ${terminalText(execution.userOpHash)}`}`),
|
|
78020
|
-
...progress.verifiedReceipts.map((receipt) => ` Verified receipt: ${terminalText(receipt.transactionHash)}; block ${receipt.blockNumber}; log ${receipt.logIndex}`)
|
|
78021
|
-
].join("\n");
|
|
78022
|
-
/** The SDK supplies fresh authorized snapshots; one finish path owns teardown. */
|
|
78023
|
-
const observeCommand = (register, orgId, key, seen) => Effect.suspend(() => {
|
|
78024
|
-
let observedId;
|
|
78025
|
-
return observeSnapshots(register, CAPXUL_OPERATIONS.org.permissions.subscribeCommand, (value, { finish, report }) => {
|
|
78026
|
-
if (value === null) return finish({
|
|
78027
|
-
ok: true,
|
|
78028
|
-
value: null
|
|
78029
|
-
});
|
|
78030
|
-
if (!matches$1(value, orgId, key) || observedId !== void 0 && observedId !== value.commandId) return finish({
|
|
78031
|
-
ok: false,
|
|
78032
|
-
error: mismatch$1()
|
|
78033
|
-
});
|
|
78034
|
-
observedId = value.commandId;
|
|
78035
|
-
seen(value);
|
|
78036
|
-
const progress = commandProgress(orgId, key, value);
|
|
78037
|
-
report(progress, () => commandProgressLines(value, progress));
|
|
78038
|
-
if (value.status === "applied" || value.status === "failed" || value.status === "actionable") finish({
|
|
78039
|
-
ok: true,
|
|
78040
|
-
value
|
|
78041
|
-
});
|
|
78042
|
-
});
|
|
78043
|
-
}).pipe(Effect.flatMap(resultValue));
|
|
78044
|
-
/** Historical reads never select current Organization access or compose a signer. */
|
|
78045
|
-
const observeValidatedCommand = (config, input, validated, read) => Effect.gen(function* () {
|
|
78046
|
-
const { orgId, key, timeout } = validated;
|
|
78047
|
-
let email = validated.email;
|
|
78048
|
-
let commandId;
|
|
78049
|
-
const runtime = yield* CliRuntime;
|
|
78050
|
-
const result = yield* Effect.gen(function* () {
|
|
78051
|
-
const restored = yield* restoreAuthenticatedClient(config, email, { requirement: "none" });
|
|
78052
|
-
email = restored.session.email;
|
|
78053
|
-
const methods = restored.client.org(orgId).permissions;
|
|
78054
|
-
const reading = read(methods, orgId, key, (value) => {
|
|
78055
|
-
commandId = value.commandId;
|
|
78056
|
-
});
|
|
78057
|
-
const view = yield* input.kind === "commandWait" ? withWaiting(reading, {
|
|
78058
|
-
...runtime,
|
|
78059
|
-
label: "Observe Permission command state"
|
|
78060
|
-
}) : reading;
|
|
78061
|
-
if (view === null) return yield* missing();
|
|
78062
|
-
if (!matches$1(view, orgId, key)) return yield* Effect.fail(mismatch$1());
|
|
78063
|
-
commandId = view.commandId;
|
|
78064
|
-
return view;
|
|
78065
|
-
}).pipe(Effect.mapError((error) => {
|
|
78066
|
-
const failure = classifyFailure(Cause.fail(error), input.kind === "commandGet" ? "org.permission.command.get" : "org.permission.command.wait");
|
|
78067
|
-
const known = commandRecovery(orgId, key, email, commandId);
|
|
78068
|
-
return new CliFailure({
|
|
78069
|
-
...failure,
|
|
78070
|
-
details: {
|
|
78071
|
-
...failure.details,
|
|
78072
|
-
...known.details
|
|
78073
|
-
},
|
|
78074
|
-
message: `${isCapxulError(error) && error.details?.field === "command" ? "The Permission command response could not be verified." : failure.message}\n${known.lines}`
|
|
78075
|
-
});
|
|
78076
|
-
}), Effect.onInterrupt(() => {
|
|
78077
|
-
const known = commandRecovery(orgId, key, email, commandId);
|
|
78078
|
-
return writeStderr(runtime.mode === "json" ? `${JSON.stringify({
|
|
78079
|
-
type: "permission.command.recovery",
|
|
78080
|
-
...known.details
|
|
78081
|
-
})}\n` : `${known.lines}\n`).pipe(Effect.ignoreCause);
|
|
78082
|
-
}), Effect.timeoutOption(timeout * 1e3));
|
|
78083
|
-
if (Option.isNone(result)) {
|
|
78084
|
-
const known = commandRecovery(orgId, key, email, commandId);
|
|
78085
|
-
return yield* new CliFailure({
|
|
78086
|
-
code: "CLI_DEADLINE",
|
|
78087
|
-
outcome: "failed",
|
|
78088
|
-
exitCode: 5,
|
|
78089
|
-
details: known.details,
|
|
78090
|
-
message: `The CLI stopped waiting. Permission application is not confirmed.\n${known.lines}`
|
|
78091
|
-
});
|
|
78092
|
-
}
|
|
78093
|
-
return {
|
|
78094
|
-
data: { command: result.value },
|
|
78095
|
-
lines: commandLines(result.value)
|
|
78096
|
-
};
|
|
78097
|
-
});
|
|
78098
|
-
//#endregion
|
|
78099
78744
|
//#region src/commands/org/subcommands/permission/subcommands/command/subcommands/get/execute.ts
|
|
78100
78745
|
const execute$37 = (config, input) => Effect.gen(function* () {
|
|
78101
78746
|
const validated = yield* validateObservationInput(input);
|
|
@@ -78118,7 +78763,7 @@ const command$35 = Command.make("get", {
|
|
|
78118
78763
|
//#region src/commands/org/subcommands/permission/subcommands/command/subcommands/wait/execute.ts
|
|
78119
78764
|
const execute$36 = (config, input) => Effect.gen(function* () {
|
|
78120
78765
|
const validated = yield* validateObservationInput(input);
|
|
78121
|
-
return yield* observeValidatedCommand(config, input, validated,
|
|
78766
|
+
return yield* observeValidatedCommand(config, input, validated, followCommand);
|
|
78122
78767
|
});
|
|
78123
78768
|
//#endregion
|
|
78124
78769
|
//#region src/commands/org/subcommands/permission/subcommands/command/subcommands/wait/command.ts
|
|
@@ -78150,14 +78795,14 @@ const permission = Command.make("permission").pipe(annotate$2("root"), Command.w
|
|
|
78150
78795
|
]));
|
|
78151
78796
|
//#endregion
|
|
78152
78797
|
//#region src/commands/org/subcommands/payroll/subcommands/shared.ts
|
|
78153
|
-
const emailFlag$1 = Flag.String("email").pipe(Flag.optional);
|
|
78154
|
-
const sourceFlag$1 = Flag.String("input").pipe(Flag.atLeast(0));
|
|
78798
|
+
const emailFlag$1 = Flag.String("email").pipe(Flag.withDescription(FLAG_HELP.email), Flag.optional);
|
|
78799
|
+
const sourceFlag$1 = Flag.String("input").pipe(Flag.withDescription(FLAG_HELP.input), Flag.atLeast(0));
|
|
78155
78800
|
const groupIdArgument = Argument.String("id").pipe(Argument.withDescription("The payroll group ID"), Argument.optional, Argument.map(Option.toArray));
|
|
78156
78801
|
const runIdArgument = Argument.String("id").pipe(Argument.withDescription("The Payroll run ID"), Argument.optional, Argument.map(Option.toArray));
|
|
78157
|
-
const requestKeyFlag$1 = Flag.String("request-key").pipe(Flag.atLeast(0));
|
|
78158
|
-
const resumeFlag = Flag.String("resume").pipe(Flag.atLeast(0));
|
|
78159
|
-
const confirmFlag = Flag.Boolean("confirm").pipe(Flag.withDefault(false));
|
|
78160
|
-
const timeoutFlag$1 = Flag.Int("timeout-seconds").pipe(Flag.withDefault(120));
|
|
78802
|
+
const requestKeyFlag$1 = Flag.String("request-key").pipe(Flag.withDescription("a key that makes repeating the same run safe"), Flag.atLeast(0));
|
|
78803
|
+
const resumeFlag = Flag.String("resume").pipe(Flag.withDescription("prepare a saved run again, by its request key"), Flag.atLeast(0));
|
|
78804
|
+
const confirmFlag = Flag.Boolean("confirm").pipe(Flag.withDescription(FLAG_HELP.confirm), Flag.withDefault(false));
|
|
78805
|
+
const timeoutFlag$1 = Flag.Int("timeout-seconds").pipe(Flag.withDescription(FLAG_HELP.timeout), Flag.withDefault(120));
|
|
78161
78806
|
const names$1 = {
|
|
78162
78807
|
root: "org.payroll",
|
|
78163
78808
|
groups: "org.payroll.groups",
|
|
@@ -78196,7 +78841,7 @@ const selectOrganization = (organizations, selected) => Effect.gen(function* ()
|
|
|
78196
78841
|
let orgId = selected;
|
|
78197
78842
|
if (orgId === null) {
|
|
78198
78843
|
const runtime = yield* CliRuntime;
|
|
78199
|
-
if (!runtime.tty || runtime.ci || runtime.mode === "json" || organizations.length === 0) return yield*
|
|
78844
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json" || organizations.length === 0) return yield* organizationRequired();
|
|
78200
78845
|
orgId = yield* Prompt.Select({
|
|
78201
78846
|
message: "Organization",
|
|
78202
78847
|
choices: organizations.map((org) => ({
|
|
@@ -78382,10 +79027,10 @@ const runLines$1 = (value) => [
|
|
|
78382
79027
|
...value.items.length === 0 ? ["No Payroll runs found."] : value.items.flatMap((run) => [
|
|
78383
79028
|
`Run: ${terminalText(run.id)} (${terminalText(run.status)})`,
|
|
78384
79029
|
` Period (epoch ms): ${run.periodStart} to ${run.periodEnd}; recipients: ${run.recipientCount}`,
|
|
78385
|
-
` Total: ${terminalText(run.total
|
|
79030
|
+
` Total: ${terminalText(shownNumber(run.total))} ${terminalText(assetName(run.total.asset))}`
|
|
78386
79031
|
]),
|
|
78387
79032
|
"Settled this month (backend totals)",
|
|
78388
|
-
...value.settledThisMonth.length === 0 ? [" No settled asset totals."] : value.settledThisMonth.map((total) => ` ${terminalText(total
|
|
79033
|
+
...value.settledThisMonth.length === 0 ? [" No settled asset totals."] : value.settledThisMonth.map((total) => ` ${terminalText(shownNumber(total))} ${terminalText(assetName(total.asset))}`)
|
|
78389
79034
|
];
|
|
78390
79035
|
//#endregion
|
|
78391
79036
|
//#region src/commands/org/subcommands/payroll/subcommands/groups/subcommands/list/execute.ts
|
|
@@ -78756,7 +79401,7 @@ const guideRunInput = (budgets, groups, terms, contacts) => Effect.gen(function*
|
|
|
78756
79401
|
const asset = yield* Prompt.Select({
|
|
78757
79402
|
message: "Settlement asset",
|
|
78758
79403
|
choices: assets.map((value) => ({
|
|
78759
|
-
title:
|
|
79404
|
+
title: terminalText(assetName(value)),
|
|
78760
79405
|
value
|
|
78761
79406
|
}))
|
|
78762
79407
|
});
|
|
@@ -78843,6 +79488,47 @@ const guideRunInput = (budgets, groups, terms, contacts) => Effect.gen(function*
|
|
|
78843
79488
|
});
|
|
78844
79489
|
});
|
|
78845
79490
|
//#endregion
|
|
79491
|
+
//#region src/commands/org/subcommands/payroll/internal/run-lookup.ts
|
|
79492
|
+
const isPayrollRunId = (value) => /^payroll_run_[0-9a-f]{64}$/u.test(value);
|
|
79493
|
+
const exactRunId = (value) => isPayrollRunId(value) ? Effect.succeed(value) : Effect.fail(inputRequired("Provide the Payroll run ID: payroll_run_ followed by a 64-character digest."));
|
|
79494
|
+
const validateRunInput = (input) => Effect.gen(function* () {
|
|
79495
|
+
const timeout = yield* readWriteTimeout(input.timeoutSeconds);
|
|
79496
|
+
const email = input.email === void 0 ? void 0 : yield* readEmail(input.email);
|
|
79497
|
+
if (input.orgs.length > 1) return yield* inputRequired("Provide one exact --org ID.");
|
|
79498
|
+
const orgId = yield* explicitOrganization(input.orgs);
|
|
79499
|
+
if (input.runIds.length > 1) return yield* inputRequired("Provide one exact Payroll run ID.");
|
|
79500
|
+
let runId = input.runIds[0];
|
|
79501
|
+
if (runId !== void 0) yield* exactRunId(runId);
|
|
79502
|
+
const runtime = yield* CliRuntime;
|
|
79503
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json") {
|
|
79504
|
+
if (orgId === null) return yield* inputRequired("Provide one exact --org ID.");
|
|
79505
|
+
if (runId === void 0) return yield* inputRequired("Provide the Payroll run ID.");
|
|
79506
|
+
}
|
|
79507
|
+
if (runId === void 0) runId = yield* Prompt.String({ message: "Payroll run ID" });
|
|
79508
|
+
return {
|
|
79509
|
+
orgId,
|
|
79510
|
+
runId: yield* exactRunId(runId),
|
|
79511
|
+
timeout,
|
|
79512
|
+
email
|
|
79513
|
+
};
|
|
79514
|
+
});
|
|
79515
|
+
const missingRun = () => new CliFailure({
|
|
79516
|
+
code: "INVALID_INPUT",
|
|
79517
|
+
outcome: "refused",
|
|
79518
|
+
exitCode: 2,
|
|
79519
|
+
message: "The Payroll run was not found or is unavailable to this session. Check --org and the Payroll run ID."
|
|
79520
|
+
});
|
|
79521
|
+
const matches = (detail, orgId, runId) => detail.orgId === orgId && detail.run.id === runId;
|
|
79522
|
+
const mismatch = () => Errors.invalidInput("runId", "Payroll run response scope does not match");
|
|
79523
|
+
const finishedRun = (detail) => detail.items.length > 0 && detail.items.every((item) => paymentPhase(item.payment.status).outcome === "good") || detail.items.some((item) => {
|
|
79524
|
+
const phase = paymentPhase(item.payment.status);
|
|
79525
|
+
return phase.terminal && phase.outcome !== "good" || phase.needsAction || item.payment.execution?.retryRequired === true;
|
|
79526
|
+
}) || [
|
|
79527
|
+
"failed",
|
|
79528
|
+
"mismatch",
|
|
79529
|
+
"unresolved"
|
|
79530
|
+
].includes(detail.command.verificationState) || detail.command.completionOutcome === "failed" || detail.command.terminalOutcome !== void 0 && detail.command.terminalOutcome !== "settled";
|
|
79531
|
+
//#endregion
|
|
78846
79532
|
//#region src/commands/org/subcommands/payroll/internal/run-preview.ts
|
|
78847
79533
|
/** The recovery a Payroll run prints: resume the saved input, or read and wait for the run. */
|
|
78848
79534
|
const runCommandRecovery = (input) => {
|
|
@@ -78896,6 +79582,19 @@ const runCommandRecovery = (input) => {
|
|
|
78896
79582
|
]
|
|
78897
79583
|
};
|
|
78898
79584
|
};
|
|
79585
|
+
/**
|
|
79586
|
+
* The backend's refusal for a request key whose run already settled or failed
|
|
79587
|
+
* (#2383): the run to read. Resuming that key returns the same refusal.
|
|
79588
|
+
*/
|
|
79589
|
+
const completedRun = (error, key) => {
|
|
79590
|
+
if (!isCapxulError(error) || error.code !== "WRONG_STATE") return void 0;
|
|
79591
|
+
const runId = error.details?.runId;
|
|
79592
|
+
const resolution = error.details?.payrollResolution;
|
|
79593
|
+
return typeof runId === "string" && isPayrollRunId(runId) && error.details?.requestKey === key && (resolution === "settled" || resolution === "failed") ? {
|
|
79594
|
+
runId,
|
|
79595
|
+
resolution
|
|
79596
|
+
} : void 0;
|
|
79597
|
+
};
|
|
78899
79598
|
/** Each Payroll recipient resolves to its asserted known Party and Safe, never an external address; each item comes back with its target. */
|
|
78900
79599
|
const resolveRunTargets = (client, items) => Effect.forEach(items, (item) => Effect.promise((signal) => client.targets.resolve(item.to, { signal })).pipe(Effect.flatMap(resultValue), Effect.flatMap((target) => {
|
|
78901
79600
|
if (target.kind === "external_address" || target.requiresAcknowledgement === true) return Effect.fail(inputRequired("External-address Payroll requires an explicit acknowledgement before sending."));
|
|
@@ -78913,14 +79612,14 @@ const runPreviewLines = (input) => {
|
|
|
78913
79612
|
`Acting as: ${terminalText(preview.session.email)} (${terminalText(preview.session.authUserId)})`,
|
|
78914
79613
|
`Organization: ${terminalText(selectedOrg)}`,
|
|
78915
79614
|
`Budget: ${terminalText(budget.label)} (${terminalText(budget.id)})`,
|
|
78916
|
-
`Per-payment cap: ${budget.limit === null ? "unlimited" : terminalText(budget.limit.value)} ${terminalText(requested.asset)}`,
|
|
78917
|
-
`Settlement asset: ${terminalText(requested.asset)}`,
|
|
79615
|
+
`Per-payment cap: ${budget.limit === null ? "unlimited" : terminalText(budget.limit.value)} ${terminalText(assetName(requested.asset))}`,
|
|
79616
|
+
`Settlement asset: ${terminalText(assetName(requested.asset))}`,
|
|
78918
79617
|
`Period (epoch ms): ${requested.period.start} to ${requested.period.end}`,
|
|
78919
79618
|
`Request key: ${terminalText(key)}`,
|
|
78920
79619
|
...employees.flatMap(({ item, target }, index) => [
|
|
78921
79620
|
`Employee ${index + 1}: ${terminalText(target.label)} (${terminalText(item.partyId)})`,
|
|
78922
79621
|
` Recipient: ${terminalText(JSON.stringify(item.to))}; Safe: ${terminalText(target.safeAddress)}`,
|
|
78923
|
-
` Payout quantity: ${terminalText(item.amount.value)} ${terminalText(item.amount.asset)}`,
|
|
79622
|
+
` Payout quantity: ${terminalText(item.amount.value)} ${terminalText(assetName(item.amount.asset))}`,
|
|
78924
79623
|
` Gross: ${terminalText(item.gross)} raw units; net: ${terminalText(item.net)} raw units`,
|
|
78925
79624
|
...item.adjustments.map((row) => ` Adjustment: ${terminalText(row.label)}; ${terminalText(row.amount)} raw units`)
|
|
78926
79625
|
])
|
|
@@ -79085,21 +79784,34 @@ const followApproval = (client, approvalId, options) => Effect.gen(function* ()
|
|
|
79085
79784
|
onNone: () => Effect.fail(stillWaiting(approvalId, options)),
|
|
79086
79785
|
onSome: Effect.succeed
|
|
79087
79786
|
})));
|
|
79088
|
-
/** `Paid 50.00 USDC to Rex · 0x5b2e…91c4`,
|
|
79787
|
+
/** `Paid 50.00 USDC to Rex · 0x5b2e…91c4`, where the receipt is, and how to save it as PDF. */
|
|
79089
79788
|
const paidLines = (outcome, scope = {}) => outcome.payments.flatMap((payment) => {
|
|
79090
79789
|
const hash = payment.receipt?.transactionHash;
|
|
79091
|
-
return [
|
|
79092
|
-
"
|
|
79093
|
-
|
|
79094
|
-
|
|
79095
|
-
|
|
79096
|
-
|
|
79097
|
-
|
|
79790
|
+
return [
|
|
79791
|
+
`Paid ${shownAmount(payment.amount)} to ${terminalText(payment.counterpartyLabel)}${hash === void 0 ? "" : ` · ${hash.slice(0, 6)}…${hash.slice(-4)}`}`,
|
|
79792
|
+
`receipt: ${terminalText([
|
|
79793
|
+
"capxul",
|
|
79794
|
+
"payment",
|
|
79795
|
+
"get",
|
|
79796
|
+
payment.id,
|
|
79797
|
+
...scoped(scope)
|
|
79798
|
+
].join(" "))}`,
|
|
79799
|
+
...saveDocumentLines(payment)
|
|
79800
|
+
];
|
|
79098
79801
|
});
|
|
79099
|
-
/** A progress line for a person, on stderr. */
|
|
79100
|
-
const progress = (line) =>
|
|
79802
|
+
/** A progress line for a person, on stderr at a terminal. */
|
|
79803
|
+
const progress = (reader, line) => writeProgress(reader, `${line}\n`);
|
|
79804
|
+
/** What the person must do: shown even when output is piped, like a prompt. */
|
|
79805
|
+
const instruction = (line) => writeStderr(`${line}\n`);
|
|
79101
79806
|
/** After the approval lapses, the time a send and its settlement may still take. */
|
|
79102
79807
|
const SETTLE_MS = 12e4;
|
|
79808
|
+
/** How long to follow an approval that lapses at `expiresAt`: until then, plus the settlement. */
|
|
79809
|
+
const untilLapseMs = (expiresAt, now) => Math.max(expiresAt - now, 0) + SETTLE_MS;
|
|
79810
|
+
/** The default wait for an approval ID: as long as `payment send` follows it. */
|
|
79811
|
+
const followUntilLapseMs = (client, approvalId) => Effect.gen(function* () {
|
|
79812
|
+
const approval = yield* read(client, approvalId);
|
|
79813
|
+
return untilLapseMs(approval.expiresAt, yield* Clock.currentTimeMillis);
|
|
79814
|
+
});
|
|
79103
79815
|
/**
|
|
79104
79816
|
* Hand a prepared approval over. An agent gets the link and the wait command
|
|
79105
79817
|
* back at once. A person sees the preview, the approval page opens, and the
|
|
@@ -79112,18 +79824,18 @@ const handOver = (client, approval, options) => Effect.gen(function* () {
|
|
|
79112
79824
|
data: awaitingApproval(approval, options),
|
|
79113
79825
|
lines: []
|
|
79114
79826
|
};
|
|
79115
|
-
for (const line of previewLines(approval, options)) yield* progress(line);
|
|
79116
|
-
yield* progress("✓ Prepared");
|
|
79827
|
+
for (const line of previewLines(approval, options)) yield* progress(runtime, line);
|
|
79828
|
+
yield* progress(runtime, "✓ Prepared");
|
|
79117
79829
|
const opened = runtime.tty && !runtime.ci ? yield* openUrl(approval.url) : false;
|
|
79118
|
-
yield*
|
|
79119
|
-
yield*
|
|
79830
|
+
yield* instruction(`… Approve in your browser${opened ? " (opened)" : ""}`);
|
|
79831
|
+
yield* instruction(` ${approval.url}`);
|
|
79120
79832
|
const now = yield* Clock.currentTimeMillis;
|
|
79121
79833
|
const outcome = yield* followApproval(client, approval.approvalId, {
|
|
79122
79834
|
organizationId: options.organizationId,
|
|
79123
|
-
deadlineMs: options.timeoutSeconds === void 0 ?
|
|
79124
|
-
onSigned: progress("✓ Signed")
|
|
79125
|
-
}).pipe(Effect.onInterrupt(() =>
|
|
79126
|
-
yield* progress("✓ Confirmed");
|
|
79835
|
+
deadlineMs: options.timeoutSeconds === void 0 ? untilLapseMs(approval.expiresAt, now) : options.timeoutSeconds * 1e3,
|
|
79836
|
+
onSigned: progress(runtime, "✓ Signed")
|
|
79837
|
+
}).pipe(Effect.onInterrupt(() => instruction(`Nothing is sent until you approve. Follow it with: ${waitArgv(approval.approvalId, options).join(" ")}`).pipe(Effect.ignore)));
|
|
79838
|
+
yield* progress(runtime, "✓ Confirmed");
|
|
79127
79839
|
return {
|
|
79128
79840
|
data: outcome,
|
|
79129
79841
|
lines: paidLines(outcome, options)
|
|
@@ -79146,13 +79858,18 @@ const execute$30 = (config, input) => Effect.gen(function* () {
|
|
|
79146
79858
|
runId: void 0,
|
|
79147
79859
|
canResume: saved
|
|
79148
79860
|
});
|
|
79149
|
-
const
|
|
79861
|
+
const noticeText = (type) => {
|
|
79150
79862
|
const known = recovery();
|
|
79151
|
-
return
|
|
79863
|
+
return runtime.mode === "json" ? `${JSON.stringify({
|
|
79152
79864
|
type,
|
|
79153
79865
|
...known.details
|
|
79154
|
-
})}\n` : `${known.lines.join("\n")}\n
|
|
79866
|
+
})}\n` : `${known.lines.join("\n")}\n`;
|
|
79155
79867
|
};
|
|
79868
|
+
const keyNotice = (type) => runtime.mode === "json" ? writeProgress(runtime, `${JSON.stringify({
|
|
79869
|
+
type,
|
|
79870
|
+
organizationId: orgId,
|
|
79871
|
+
requestKey: key
|
|
79872
|
+
})}\n`) : Effect.void;
|
|
79156
79873
|
const within = (work) => boundedNetwork(work, timeout);
|
|
79157
79874
|
return yield* Effect.gen(function* () {
|
|
79158
79875
|
const opened = yield* openAuthenticatedSession(config, email);
|
|
@@ -79185,18 +79902,18 @@ const execute$30 = (config, input) => Effect.gen(function* () {
|
|
|
79185
79902
|
employees,
|
|
79186
79903
|
key
|
|
79187
79904
|
});
|
|
79188
|
-
yield*
|
|
79905
|
+
yield* writeProgress(runtime, runtime.mode === "json" ? `${JSON.stringify({
|
|
79189
79906
|
type: "org.payroll.preview",
|
|
79190
79907
|
organizationId: selectedOrg,
|
|
79191
79908
|
requestKey: key,
|
|
79192
79909
|
input: requested,
|
|
79193
79910
|
targets
|
|
79194
79911
|
})}\n` : `${lines.join("\n")}\n`);
|
|
79195
|
-
yield*
|
|
79912
|
+
yield* keyNotice("org.payroll.requestKey");
|
|
79196
79913
|
const draft = stored ?? (yield* saveCommandDraft(config, selector, requested, preview.stillAuthenticated));
|
|
79197
79914
|
saved = true;
|
|
79198
79915
|
const admitted = yield* parseRunInput(draft.input);
|
|
79199
|
-
yield*
|
|
79916
|
+
yield* keyNotice("org.payroll.saved");
|
|
79200
79917
|
const approval = yield* within(withWaiting(Effect.promise((signal) => preview.client.approvals.prepare({
|
|
79201
79918
|
organizationId: selectedOrg,
|
|
79202
79919
|
payrollRun: {
|
|
@@ -79214,16 +79931,24 @@ const execute$30 = (config, input) => Effect.gen(function* () {
|
|
|
79214
79931
|
}).pipe(Effect.mapError((error) => {
|
|
79215
79932
|
const scoped = orgId === null ? error : normalizeOrganizationReadFailure(error, orgId);
|
|
79216
79933
|
const failure = classifyFailure(Cause.fail(scoped), operation);
|
|
79217
|
-
const
|
|
79934
|
+
const done = completedRun(error, key);
|
|
79935
|
+
const known = done === void 0 ? recovery() : runCommandRecovery({
|
|
79936
|
+
orgId,
|
|
79937
|
+
actorEmail,
|
|
79938
|
+
key,
|
|
79939
|
+
runId: done.runId,
|
|
79940
|
+
canResume: false
|
|
79941
|
+
});
|
|
79942
|
+
const message = done === void 0 ? failure.message : `This request key's Payroll run already ${done.resolution}. Read it below; a new run needs a new --request-key.`;
|
|
79218
79943
|
return new CliFailure({
|
|
79219
79944
|
...failure,
|
|
79220
79945
|
details: {
|
|
79221
79946
|
...failure.details,
|
|
79222
79947
|
...known.details
|
|
79223
79948
|
},
|
|
79224
|
-
message: `${
|
|
79949
|
+
message: `${message}${known.lines.length === 0 ? "" : `\n${known.lines.join("\n")}`}`
|
|
79225
79950
|
});
|
|
79226
|
-
}), Effect.onInterrupt(() =>
|
|
79951
|
+
}), Effect.onInterrupt(() => writeStderr(noticeText("org.payroll.recovery")).pipe(Effect.ignoreCause)));
|
|
79227
79952
|
});
|
|
79228
79953
|
//#endregion
|
|
79229
79954
|
//#region src/commands/org/subcommands/payroll/subcommands/run/command.ts
|
|
@@ -79243,47 +79968,6 @@ const command$27 = Command.make("run", {
|
|
|
79243
79968
|
timeoutSeconds: timeout
|
|
79244
79969
|
}))).pipe(annotate$1("run"), Command.withDescription("run payroll from a Budget; you approve it in the browser"));
|
|
79245
79970
|
//#endregion
|
|
79246
|
-
//#region src/commands/org/subcommands/payroll/internal/run-lookup.ts
|
|
79247
|
-
const isPayrollRunId = (value) => /^payroll_run_[0-9a-f]{64}$/u.test(value);
|
|
79248
|
-
const exactRunId = (value) => isPayrollRunId(value) ? Effect.succeed(value) : Effect.fail(inputRequired("Provide the Payroll run ID: payroll_run_ followed by a 64-character digest."));
|
|
79249
|
-
const validateRunInput = (input) => Effect.gen(function* () {
|
|
79250
|
-
const timeout = yield* readWriteTimeout(input.timeoutSeconds);
|
|
79251
|
-
const email = input.email === void 0 ? void 0 : yield* readEmail(input.email);
|
|
79252
|
-
if (input.orgs.length > 1) return yield* inputRequired("Provide one exact --org ID.");
|
|
79253
|
-
const orgId = yield* explicitOrganization(input.orgs);
|
|
79254
|
-
if (input.runIds.length > 1) return yield* inputRequired("Provide one exact Payroll run ID.");
|
|
79255
|
-
let runId = input.runIds[0];
|
|
79256
|
-
if (runId !== void 0) yield* exactRunId(runId);
|
|
79257
|
-
const runtime = yield* CliRuntime;
|
|
79258
|
-
if (!runtime.tty || runtime.ci || runtime.mode === "json") {
|
|
79259
|
-
if (orgId === null) return yield* inputRequired("Provide one exact --org ID.");
|
|
79260
|
-
if (runId === void 0) return yield* inputRequired("Provide the Payroll run ID.");
|
|
79261
|
-
}
|
|
79262
|
-
if (runId === void 0) runId = yield* Prompt.String({ message: "Payroll run ID" });
|
|
79263
|
-
return {
|
|
79264
|
-
orgId,
|
|
79265
|
-
runId: yield* exactRunId(runId),
|
|
79266
|
-
timeout,
|
|
79267
|
-
email
|
|
79268
|
-
};
|
|
79269
|
-
});
|
|
79270
|
-
const missingRun = () => new CliFailure({
|
|
79271
|
-
code: "INVALID_INPUT",
|
|
79272
|
-
outcome: "refused",
|
|
79273
|
-
exitCode: 2,
|
|
79274
|
-
message: "The Payroll run was not found or is unavailable to this session. Check --org and the Payroll run ID."
|
|
79275
|
-
});
|
|
79276
|
-
const matches = (detail, orgId, runId) => detail.orgId === orgId && detail.run.id === runId;
|
|
79277
|
-
const mismatch = () => Errors.invalidInput("runId", "Payroll run response scope does not match");
|
|
79278
|
-
const finishedRun = (detail) => detail.items.length > 0 && detail.items.every((item) => paymentPhase(item.payment.status).outcome === "good") || detail.items.some((item) => {
|
|
79279
|
-
const phase = paymentPhase(item.payment.status);
|
|
79280
|
-
return phase.terminal && phase.outcome !== "good" || phase.needsAction || item.payment.execution?.retryRequired === true;
|
|
79281
|
-
}) || [
|
|
79282
|
-
"failed",
|
|
79283
|
-
"mismatch",
|
|
79284
|
-
"unresolved"
|
|
79285
|
-
].includes(detail.command.verificationState) || detail.command.completionOutcome === "failed" || detail.command.terminalOutcome !== void 0 && detail.command.terminalOutcome !== "settled";
|
|
79286
|
-
//#endregion
|
|
79287
79971
|
//#region src/commands/org/subcommands/payroll/internal/run-lines.ts
|
|
79288
79972
|
const runRecovery = (orgId, runId, email, command) => {
|
|
79289
79973
|
const args = [
|
|
@@ -79320,7 +80004,7 @@ const runDetailLines = (detail) => [
|
|
|
79320
80004
|
`Payroll run ${terminalText(detail.run.id)}: recorded status ${detail.run.status}`,
|
|
79321
80005
|
` Organization: ${terminalText(detail.orgId)}`,
|
|
79322
80006
|
` Period: ${detail.run.periodStart} to ${detail.run.periodEnd}`,
|
|
79323
|
-
` Total: ${terminalText(
|
|
80007
|
+
` Total: ${terminalText(shownNumber(detail.run.total))} ${terminalText(assetName(detail.run.total.asset))}; recipients: ${detail.run.recipientCount}`,
|
|
79324
80008
|
` Request key: ${terminalText(detail.command.requestKey)}`,
|
|
79325
80009
|
` Command: ${terminalText(detail.command.commandId)}; state ${detail.command.state}; verification ${detail.command.verificationState}`,
|
|
79326
80010
|
...detail.command.completionOutcome === void 0 ? [] : [` Recorded completion: ${detail.command.completionOutcome}`],
|
|
@@ -79332,7 +80016,7 @@ const runDetailLines = (detail) => [
|
|
|
79332
80016
|
` Item ${item.itemIndex}: employee ${terminalText(item.employeeRef)}; engagement ${terminalText(item.engagementEdgeId)}`,
|
|
79333
80017
|
` Gross: ${terminalText(item.gross)} raw units; net: ${terminalText(item.net)} raw units`,
|
|
79334
80018
|
...item.adjustments.map((adjustment) => ` Adjustment: ${terminalText(adjustment.label)}; ${terminalText(adjustment.amount)} raw units`),
|
|
79335
|
-
` Quantity: ${terminalText(item.amount.value)}; asset: ${terminalText(item.amount.asset)}`,
|
|
80019
|
+
` Quantity: ${terminalText(item.amount.value)}; asset: ${terminalText(assetName(item.amount.asset))}`,
|
|
79336
80020
|
` Payment ${terminalText(item.payment.id)}: ${item.payment.status}`,
|
|
79337
80021
|
` Counterparty: ${terminalText(item.payment.counterpartyLabel)}`,
|
|
79338
80022
|
` Receipt: ${item.payment.receipt === null ? "none recorded" : `${terminalText(item.payment.receipt.transactionHash)} (chain ${item.payment.receipt.chainId})`}`,
|
|
@@ -79350,28 +80034,7 @@ const readValidatedRun = (config, input, validated, read) => Effect.gen(function
|
|
|
79350
80034
|
let command;
|
|
79351
80035
|
const runtime = yield* CliRuntime;
|
|
79352
80036
|
const known = () => orgId === null ? null : runRecovery(orgId, validated.runId, email, command);
|
|
79353
|
-
const
|
|
79354
|
-
const restored = yield* restoreAuthenticatedClient(config, email, { requirement: "none" });
|
|
79355
|
-
email = restored.session.email;
|
|
79356
|
-
const organizations = yield* Effect.promise((signal) => restored.client.orgs({ signal })).pipe(Effect.flatMap(resultItems));
|
|
79357
|
-
orgId = (yield* selectOrganization(organizations, orgId)).id;
|
|
79358
|
-
if (!(yield* restored.stillAuthenticated)) return yield* Effect.fail(Errors.notAuthenticated());
|
|
79359
|
-
const reading = read(restored.client.org(orgId).payroll, orgId, validated.runId, (value) => {
|
|
79360
|
-
command = value.command;
|
|
79361
|
-
});
|
|
79362
|
-
const detail = yield* input.kind === "wait" ? withWaiting(reading, {
|
|
79363
|
-
...runtime,
|
|
79364
|
-
label: "Observe Payroll run state"
|
|
79365
|
-
}) : reading;
|
|
79366
|
-
if (detail === null) return yield* missingRun();
|
|
79367
|
-
if (!matches(detail, orgId, validated.runId)) return yield* Effect.fail(mismatch());
|
|
79368
|
-
if (!(yield* restored.stillAuthenticated)) return yield* Effect.fail(Errors.notAuthenticated());
|
|
79369
|
-
command = detail.command;
|
|
79370
|
-
return {
|
|
79371
|
-
detail,
|
|
79372
|
-
client: restored.client
|
|
79373
|
-
};
|
|
79374
|
-
}).pipe(Effect.mapError((error) => {
|
|
80037
|
+
const guarded = (work) => work.pipe(Effect.mapError((error) => {
|
|
79375
80038
|
const failure = classifyFailure(Cause.fail(orgId === null ? error : normalizeOrganizationReadFailure(error, orgId)), `org.payroll.${input.kind}`);
|
|
79376
80039
|
const recovery = known();
|
|
79377
80040
|
return new CliFailure({
|
|
@@ -79391,7 +80054,33 @@ const readValidatedRun = (config, input, validated, read) => Effect.gen(function
|
|
|
79391
80054
|
...recovery.details,
|
|
79392
80055
|
message: recovery.lines
|
|
79393
80056
|
})}\n` : `${recovery.lines}\n`).pipe(Effect.ignoreCause);
|
|
79394
|
-
})
|
|
80057
|
+
}));
|
|
80058
|
+
const { restored, selectedOrg } = yield* guarded(Effect.gen(function* () {
|
|
80059
|
+
const session = yield* restoreAuthenticatedClient(config, email, { requirement: "none" });
|
|
80060
|
+
email = session.session.email;
|
|
80061
|
+
const organizations = yield* Effect.promise((signal) => session.client.orgs({ signal })).pipe(Effect.flatMap(resultItems));
|
|
80062
|
+
const organization = yield* selectOrganization(organizations, orgId);
|
|
80063
|
+
orgId = organization.id;
|
|
80064
|
+
if (!(yield* session.stillAuthenticated)) return yield* Effect.fail(Errors.notAuthenticated());
|
|
80065
|
+
return {
|
|
80066
|
+
restored: session,
|
|
80067
|
+
selectedOrg: organization.id
|
|
80068
|
+
};
|
|
80069
|
+
}));
|
|
80070
|
+
const observed = yield* guarded(Effect.gen(function* () {
|
|
80071
|
+
const reading = read(restored.client.org(selectedOrg).payroll, selectedOrg, validated.runId, (value) => {
|
|
80072
|
+
command = value.command;
|
|
80073
|
+
});
|
|
80074
|
+
const detail = yield* input.kind === "wait" ? withWaiting(reading, {
|
|
80075
|
+
...runtime,
|
|
80076
|
+
label: "Observe Payroll run state"
|
|
80077
|
+
}) : reading;
|
|
80078
|
+
if (detail === null) return yield* missingRun();
|
|
80079
|
+
if (!matches(detail, selectedOrg, validated.runId)) return yield* Effect.fail(mismatch());
|
|
80080
|
+
if (!(yield* restored.stillAuthenticated)) return yield* Effect.fail(Errors.notAuthenticated());
|
|
80081
|
+
command = detail.command;
|
|
80082
|
+
return { detail };
|
|
80083
|
+
})).pipe(Effect.timeoutOption(validated.timeout * 1e3));
|
|
79395
80084
|
if (Option.isNone(observed)) {
|
|
79396
80085
|
const recovery = known();
|
|
79397
80086
|
return yield* new CliFailure({
|
|
@@ -79528,6 +80217,11 @@ const continueRecoverySetup = (scoped, signal, onCurrent) => Effect.gen(function
|
|
|
79528
80217
|
});
|
|
79529
80218
|
//#endregion
|
|
79530
80219
|
//#region src/commands/org/internal/progress.ts
|
|
80220
|
+
/** An epoch-ms time as a person reads it; a value no date can hold keeps its number. */
|
|
80221
|
+
const checkTime = (at) => {
|
|
80222
|
+
const date = new Date(at);
|
|
80223
|
+
return Number.isNaN(date.valueOf()) ? `${at} (Unix ms)` : date.toISOString();
|
|
80224
|
+
};
|
|
79531
80225
|
/**
|
|
79532
80226
|
* Organization lifecycle progress owned by one write command.
|
|
79533
80227
|
*
|
|
@@ -79539,7 +80233,7 @@ const continueRecoverySetup = (scoped, signal, onCurrent) => Effect.gen(function
|
|
|
79539
80233
|
* the signature. An interruption retains an ID that was already saved.
|
|
79540
80234
|
*/
|
|
79541
80235
|
const asProgressError = (cause) => cause instanceof Error ? cause : new Error("Organization progress failed.", { cause });
|
|
79542
|
-
const makeOrganizationProgress = (
|
|
80236
|
+
const makeOrganizationProgress = (reader, run, persist) => {
|
|
79543
80237
|
const queued = [];
|
|
79544
80238
|
const held = [];
|
|
79545
80239
|
const heldSet = /* @__PURE__ */ new Set();
|
|
@@ -79554,7 +80248,7 @@ const makeOrganizationProgress = (mode, run, persist) => {
|
|
|
79554
80248
|
draining = draining.then(async () => {
|
|
79555
80249
|
while (queued.length > 0) {
|
|
79556
80250
|
const text = queued[0];
|
|
79557
|
-
await run(
|
|
80251
|
+
await run(writeProgress(reader, text));
|
|
79558
80252
|
queued.shift();
|
|
79559
80253
|
}
|
|
79560
80254
|
if (committed !== null && persisted !== committed) {
|
|
@@ -79603,14 +80297,14 @@ const makeOrganizationProgress = (mode, run, persist) => {
|
|
|
79603
80297
|
lastObserved = state;
|
|
79604
80298
|
const status = state.status;
|
|
79605
80299
|
return {
|
|
79606
|
-
text: `${mode === "json" ? JSON.stringify({
|
|
80300
|
+
text: `${reader.mode === "json" ? JSON.stringify({
|
|
79607
80301
|
type: "organization.lifecycle",
|
|
79608
80302
|
organizationId: committed,
|
|
79609
80303
|
status,
|
|
79610
80304
|
recovery: state.recovery,
|
|
79611
80305
|
reason: state.issue?.reason ?? null,
|
|
79612
80306
|
nextCheckAt: state.nextCheckAt
|
|
79613
|
-
}) : `Organization ${JSON.stringify(committed)}: ${terminalText(status)}; recovery: ${terminalText(state.recovery)}${state.issue === null ? "" : `; reason: ${terminalText(state.issue.reason)}`}${state.nextCheckAt === null ? "" : `; next check: ${state.nextCheckAt}`}`}\n`,
|
|
80307
|
+
}) : `Organization ${JSON.stringify(committed)}: ${terminalText(status)}; recovery: ${terminalText(state.recovery)}${state.issue === null ? "" : `; reason: ${terminalText(state.issue.reason)}`}${state.nextCheckAt === null ? "" : `; next check: ${checkTime(state.nextCheckAt)}`}`}\n`,
|
|
79614
80308
|
ready: status === "ready"
|
|
79615
80309
|
};
|
|
79616
80310
|
};
|
|
@@ -79709,7 +80403,7 @@ const execute$27 = (config, input) => Effect.gen(function* () {
|
|
|
79709
80403
|
handle: draft.handle,
|
|
79710
80404
|
organizationId: toOrgId(organizationId)
|
|
79711
80405
|
});
|
|
79712
|
-
const progress = makeOrganizationProgress(runtime
|
|
80406
|
+
const progress = makeOrganizationProgress(runtime, run, (organizationId) => run(persistRecovery(organizationId)));
|
|
79713
80407
|
yield* Effect.addFinalizer(() => Effect.promise(progress.flush).pipe(Effect.ignoreCause));
|
|
79714
80408
|
yield* Ref.set(progressRef, progress);
|
|
79715
80409
|
const flushProgress = Effect.gen(function* () {
|
|
@@ -79807,11 +80501,11 @@ const execute$27 = (config, input) => Effect.gen(function* () {
|
|
|
79807
80501
|
//#endregion
|
|
79808
80502
|
//#region src/commands/org/subcommands/create/command.ts
|
|
79809
80503
|
const command$24 = Command.make("create", {
|
|
79810
|
-
name: Flag.String("name"),
|
|
79811
|
-
handle: Flag.String("handle"),
|
|
79812
|
-
country: Flag.String("country"),
|
|
79813
|
-
bio: Flag.String("bio").pipe(Flag.optional),
|
|
79814
|
-
size: Flag.String("size").pipe(Flag.optional),
|
|
80504
|
+
name: Flag.String("name").pipe(Flag.withDescription("the Organization's name, as people see it")),
|
|
80505
|
+
handle: Flag.String("handle").pipe(Flag.withDescription("its @handle, written without the @ sign")),
|
|
80506
|
+
country: Flag.String("country").pipe(Flag.withDescription("its country, as a two-letter code like GB")),
|
|
80507
|
+
bio: Flag.String("bio").pipe(Flag.withDescription("a short description of the Organization"), Flag.optional),
|
|
80508
|
+
size: Flag.String("size").pipe(Flag.withDescription("how big the Organization is, in your own words"), Flag.optional),
|
|
79815
80509
|
email: emailFlag$9,
|
|
79816
80510
|
confirm: confirmFlag$6,
|
|
79817
80511
|
timeoutSeconds: timeoutFlag$4
|
|
@@ -79989,7 +80683,7 @@ const execute$22 = (config, input) => Effect.gen(function* () {
|
|
|
79989
80683
|
return yield* withWaiting(Effect.gen(function* () {
|
|
79990
80684
|
const context = yield* Effect.context();
|
|
79991
80685
|
const run = Effect.runPromiseWith(context);
|
|
79992
|
-
const progress = makeOrganizationProgress(waitingRuntime
|
|
80686
|
+
const progress = makeOrganizationProgress(waitingRuntime, run, async () => void 0);
|
|
79993
80687
|
yield* Effect.addFinalizer(() => Effect.promise(progress.flush).pipe(Effect.ignoreCause));
|
|
79994
80688
|
const deadline = new AbortController();
|
|
79995
80689
|
const openfortSignerFactory = yield* organizationWriteSigner(config, opened, run, async () => {
|
|
@@ -80094,7 +80788,7 @@ const follow = (client, scoped, organizationId, email, observed) => Effect.gen(f
|
|
|
80094
80788
|
const runtime = yield* CliRuntime;
|
|
80095
80789
|
return yield* withWaiting(Effect.gen(function* () {
|
|
80096
80790
|
const run = Effect.runPromiseWith(yield* Effect.context());
|
|
80097
|
-
const progress = makeOrganizationProgress(runtime
|
|
80791
|
+
const progress = makeOrganizationProgress(runtime, run, async () => void 0);
|
|
80098
80792
|
yield* Effect.addFinalizer(() => Effect.promise(progress.flush).pipe(Effect.ignoreCause));
|
|
80099
80793
|
const lifecycle = yield* observeOrganizationSetup((listener, signal) => scoped.setup.subscribe(listener, { signal }), organizationId, null, async (value) => {
|
|
80100
80794
|
observed(value);
|
|
@@ -80147,8 +80841,10 @@ const org = Command.make("org").pipe(Command.annotate(CliCommandMetadata, metada
|
|
|
80147
80841
|
//#endregion
|
|
80148
80842
|
//#region src/commands/payment/subcommands/shared.ts
|
|
80149
80843
|
const emailFlag = Flag.String("email").pipe(Flag.withDescription("the signed-in person to use, when this machine has several"), Flag.optional);
|
|
80150
|
-
const paymentIdArgument = Argument.String("id").pipe(Argument.withDescription("The Payment ID
|
|
80151
|
-
|
|
80844
|
+
const paymentIdArgument = Argument.String("id").pipe(Argument.withDescription("The Payment ID"));
|
|
80845
|
+
/** `payment wait` also follows the approval `payment send --json` returns. */
|
|
80846
|
+
const paymentOrApprovalIdArgument = Argument.String("id").pipe(Argument.withDescription("The Payment ID, or the approval ID from payment send"));
|
|
80847
|
+
const timeoutFlag = Flag.Int("timeout-seconds").pipe(Flag.withDescription("stop following after this many seconds (default: follow until it ends)"), Flag.optional);
|
|
80152
80848
|
const sourceFlag = Flag.String("input").pipe(Flag.withDescription("the SDK payment JSON, from a file or - for stdin"), Flag.optional);
|
|
80153
80849
|
const requestKeyFlag = Flag.String("request-key").pipe(Flag.withDescription("a key that makes repeating the same payment safe"), Flag.optional);
|
|
80154
80850
|
const names = {
|
|
@@ -80362,7 +81058,7 @@ const timeoutFailure = (paymentId, email, organizationId) => {
|
|
|
80362
81058
|
code: "NETWORK_ERROR",
|
|
80363
81059
|
outcome: "failed",
|
|
80364
81060
|
exitCode: 5,
|
|
80365
|
-
message:
|
|
81061
|
+
message: `Payment settlement is not confirmed. The command stopped waiting. This Payment may still settle.\n${known.lines}`,
|
|
80366
81062
|
details: known.details
|
|
80367
81063
|
});
|
|
80368
81064
|
};
|
|
@@ -80439,31 +81135,29 @@ const observePayment = (register, paymentId, organizationId) => observeSnapshots
|
|
|
80439
81135
|
value: payment
|
|
80440
81136
|
});
|
|
80441
81137
|
}).pipe(Effect.flatMap(resultValue), Effect.filterOrFail((payment) => payment !== null, paymentNotFound));
|
|
81138
|
+
/** `Option.none()` when `seconds` pass first. With no bound the wait follows the Payment until it ends. */
|
|
81139
|
+
const within = (seconds) => (work) => seconds === void 0 ? Effect.asSome(work) : Effect.timeoutOption(work, seconds * 1e3);
|
|
80442
81140
|
const waitForPayment = (config, input, register, organizationId) => Effect.gen(function* () {
|
|
80443
81141
|
const paymentId = yield* readPaymentId(input.paymentId);
|
|
80444
|
-
const timeoutSeconds = yield* readWriteTimeout(input.timeoutSeconds);
|
|
81142
|
+
const timeoutSeconds = input.timeoutSeconds === void 0 ? void 0 : yield* readWriteTimeout(input.timeoutSeconds);
|
|
80445
81143
|
let email;
|
|
80446
|
-
const
|
|
80447
|
-
const restored = yield* restoreAuthenticatedClient(config, input.email);
|
|
80448
|
-
email = restored.session.email;
|
|
80449
|
-
const runtime = yield* CliRuntime;
|
|
80450
|
-
return {
|
|
80451
|
-
payment: yield* withWaiting(observePayment((listener, signal) => register(restored.client, paymentId, listener, signal), paymentId, organizationId), {
|
|
80452
|
-
...runtime,
|
|
80453
|
-
label: "Observe Payment state"
|
|
80454
|
-
}),
|
|
80455
|
-
client: restored.client
|
|
80456
|
-
};
|
|
80457
|
-
}).pipe(Effect.mapError((error) => recoverFailure(error, paymentId, email, organizationId)), Effect.onInterrupt(() => Effect.gen(function* () {
|
|
81144
|
+
const guarded = (work) => work.pipe(Effect.mapError((error) => recoverFailure(error, paymentId, email, organizationId)), Effect.onInterrupt(() => Effect.gen(function* () {
|
|
80458
81145
|
const runtime = yield* CliRuntime;
|
|
80459
81146
|
const known = recovery(paymentId, email, organizationId);
|
|
80460
81147
|
yield* writeStderr(runtime.mode === "json" ? `${JSON.stringify({
|
|
80461
81148
|
type: "payment.recovery",
|
|
80462
81149
|
...known.details
|
|
80463
81150
|
})}\n` : `${known.lines}\n`);
|
|
80464
|
-
}).pipe(Effect.ignoreCause))
|
|
81151
|
+
}).pipe(Effect.ignoreCause)));
|
|
81152
|
+
const restored = yield* guarded(restoreAuthenticatedClient(config, input.email));
|
|
81153
|
+
email = restored.session.email;
|
|
81154
|
+
const runtime = yield* CliRuntime;
|
|
81155
|
+
const observed = yield* guarded(withWaiting(observePayment((listener, signal) => register(restored.client, paymentId, listener, signal), paymentId, organizationId), {
|
|
81156
|
+
...runtime,
|
|
81157
|
+
label: "Observe Payment state"
|
|
81158
|
+
})).pipe(within(timeoutSeconds));
|
|
80465
81159
|
if (Option.isNone(observed)) return yield* timeoutFailure(paymentId, email, organizationId);
|
|
80466
|
-
const
|
|
81160
|
+
const payment = observed.value;
|
|
80467
81161
|
return {
|
|
80468
81162
|
data: payment,
|
|
80469
81163
|
lines: [...paymentLines(payment), ...documentReferenceLines(payment)]
|
|
@@ -80473,7 +81167,7 @@ const waitForPayment = (config, input, register, organizationId) => Effect.gen(f
|
|
|
80473
81167
|
//#region src/commands/org/subcommands/payment/subcommands/wait/execute.ts
|
|
80474
81168
|
const execute$15 = (config, input) => Effect.gen(function* () {
|
|
80475
81169
|
const paymentId = yield* readPaymentId(input.paymentId);
|
|
80476
|
-
const timeoutSeconds = yield* readWriteTimeout(input.timeoutSeconds);
|
|
81170
|
+
const timeoutSeconds = input.timeoutSeconds === void 0 ? void 0 : yield* readWriteTimeout(input.timeoutSeconds);
|
|
80477
81171
|
const email = input.email === void 0 ? void 0 : yield* readEmail(input.email);
|
|
80478
81172
|
const organizationId = yield* readOrganizationId(input.orgs);
|
|
80479
81173
|
return yield* waitForPayment(config, {
|
|
@@ -80495,16 +81189,16 @@ const isApprovalId = (id) => id.startsWith("approval_");
|
|
|
80495
81189
|
*/
|
|
80496
81190
|
const waitForApproval = (config, input) => Effect.gen(function* () {
|
|
80497
81191
|
if (!APPROVAL_ID.test(input.approvalId)) return yield* inputRequired("Provide the approval ID: approval_ followed by 26 letters or digits.");
|
|
80498
|
-
const timeoutSeconds = yield* readWriteTimeout(input.timeoutSeconds);
|
|
81192
|
+
const timeoutSeconds = input.timeoutSeconds === void 0 ? void 0 : yield* readWriteTimeout(input.timeoutSeconds);
|
|
80499
81193
|
const runtime = yield* CliRuntime;
|
|
80500
81194
|
const { client } = yield* restoreAuthenticatedClient(config, input.email);
|
|
80501
81195
|
const human = runtime.mode === "human";
|
|
80502
81196
|
const outcome = yield* followApproval(client, input.approvalId, {
|
|
80503
81197
|
organizationId: input.organizationId,
|
|
80504
|
-
deadlineMs: timeoutSeconds * 1e3,
|
|
80505
|
-
onSigned: human ? progress("✓ Signed") : Effect.void
|
|
81198
|
+
deadlineMs: timeoutSeconds === void 0 ? yield* followUntilLapseMs(client, input.approvalId) : timeoutSeconds * 1e3,
|
|
81199
|
+
onSigned: human ? progress(runtime, "✓ Signed") : Effect.void
|
|
80506
81200
|
});
|
|
80507
|
-
if (human) yield* progress("✓ Confirmed");
|
|
81201
|
+
if (human) yield* progress(runtime, "✓ Confirmed");
|
|
80508
81202
|
return {
|
|
80509
81203
|
data: outcome,
|
|
80510
81204
|
lines: paidLines(outcome, input)
|
|
@@ -80516,33 +81210,38 @@ const execute$14 = (config, input) => waitForPayment(config, input, (client, pay
|
|
|
80516
81210
|
//#endregion
|
|
80517
81211
|
//#region src/commands/payment/subcommands/wait/command.ts
|
|
80518
81212
|
const command$13 = Command.make("wait", {
|
|
80519
|
-
paymentId:
|
|
81213
|
+
paymentId: paymentOrApprovalIdArgument,
|
|
80520
81214
|
email: emailFlag,
|
|
80521
81215
|
timeoutSeconds: timeoutFlag
|
|
80522
|
-
}, (
|
|
80523
|
-
|
|
80524
|
-
|
|
80525
|
-
|
|
80526
|
-
|
|
80527
|
-
|
|
80528
|
-
|
|
80529
|
-
|
|
80530
|
-
|
|
80531
|
-
|
|
80532
|
-
|
|
80533
|
-
|
|
80534
|
-
|
|
80535
|
-
|
|
80536
|
-
|
|
80537
|
-
|
|
80538
|
-
|
|
80539
|
-
|
|
80540
|
-
|
|
80541
|
-
|
|
80542
|
-
|
|
80543
|
-
|
|
80544
|
-
|
|
80545
|
-
|
|
81216
|
+
}, (flags) => {
|
|
81217
|
+
const { paymentId } = flags;
|
|
81218
|
+
const email = Option.getOrUndefined(flags.email);
|
|
81219
|
+
const timeoutSeconds = Option.getOrUndefined(flags.timeoutSeconds);
|
|
81220
|
+
return dispatch("wait", {
|
|
81221
|
+
personal: (config) => isApprovalId(paymentId) ? waitForApproval(config, {
|
|
81222
|
+
approvalId: paymentId,
|
|
81223
|
+
email,
|
|
81224
|
+
timeoutSeconds
|
|
81225
|
+
}) : execute$14(config, {
|
|
81226
|
+
kind: "wait",
|
|
81227
|
+
paymentId,
|
|
81228
|
+
email,
|
|
81229
|
+
timeoutSeconds
|
|
81230
|
+
}),
|
|
81231
|
+
organization: (config, orgId) => isApprovalId(paymentId) ? waitForApproval(config, {
|
|
81232
|
+
approvalId: paymentId,
|
|
81233
|
+
email,
|
|
81234
|
+
timeoutSeconds,
|
|
81235
|
+
organizationId: orgId
|
|
81236
|
+
}) : execute$15(config, {
|
|
81237
|
+
kind: "wait",
|
|
81238
|
+
orgs: [orgId],
|
|
81239
|
+
paymentId,
|
|
81240
|
+
email,
|
|
81241
|
+
timeoutSeconds
|
|
81242
|
+
})
|
|
81243
|
+
});
|
|
81244
|
+
}).pipe(annotate("wait"), Command.withDescription("Follow a Payment, or an approval ID from payment send, until it ends"));
|
|
80546
81245
|
//#endregion
|
|
80547
81246
|
//#region src/commands/payment/internal/send-input.ts
|
|
80548
81247
|
const LIMIT$1 = 65536;
|
|
@@ -80596,8 +81295,9 @@ const parsePaymentInput = (value) => Effect.gen(function* () {
|
|
|
80596
81295
|
if (typeof rawValue !== "string") return yield* invalid$2();
|
|
80597
81296
|
const assetId = Object.values(TOKENS.BASE_SEPOLIA).find((id) => id === amountInput.asset);
|
|
80598
81297
|
if (assetId === void 0) return yield* inputRequired("Provide an admitted Base Sepolia asset ID.");
|
|
80599
|
-
const
|
|
80600
|
-
|
|
81298
|
+
const metadata = requireAsset(assetId);
|
|
81299
|
+
const amount = parseAssetAmount(rawValue, metadata);
|
|
81300
|
+
if (isMoneyParseError(amount)) return yield* inputRequired(amountRefusal(amount.reason, metadata), { field: "amount" });
|
|
80601
81301
|
if (value.paymentType !== void 0 && value.paymentType !== "unspecified" && value.paymentType !== "invoice" && value.paymentType !== "reimbursement") return yield* inputRequired("Provide a supported --payment-type.");
|
|
80602
81302
|
if (value.timing !== void 0 && (!record$1(value.timing) || !keysOnly(value.timing, ["kind"]) || value.timing.kind !== "instant")) return yield* inputRequired("Only instant Payment timing is supported.");
|
|
80603
81303
|
return {
|
|
@@ -80712,6 +81412,7 @@ const readPaymentInput = (source) => Effect.gen(function* () {
|
|
|
80712
81412
|
/** The checks every send makes before a client: one source of the payment, the key and the follow time. */
|
|
80713
81413
|
const readSendFlags = (input) => Effect.gen(function* () {
|
|
80714
81414
|
if (input.source !== void 0 && (input.to !== void 0 || input.amount !== void 0)) return yield* inputRequired("Use <to> <amount> or --input, not both.");
|
|
81415
|
+
if (input.to !== void 0) yield* readRecipient$1(input.to);
|
|
80715
81416
|
return {
|
|
80716
81417
|
requestKey: input.requestKey === void 0 ? void 0 : yield* readRequestKey(input.requestKey),
|
|
80717
81418
|
timeoutSeconds: input.timeoutSeconds === void 0 ? void 0 : yield* readWriteTimeout(input.timeoutSeconds)
|
|
@@ -80739,7 +81440,7 @@ const execute$13 = (config, input) => Effect.gen(function* () {
|
|
|
80739
81440
|
const approval = yield* withWaiting(Effect.promise((signal) => client.approvals.prepare({
|
|
80740
81441
|
...payment,
|
|
80741
81442
|
...flags.requestKey === void 0 ? {} : { requestKey: flags.requestKey }
|
|
80742
|
-
}, { signal })).pipe(Effect.flatMap(resultValue)), {
|
|
81443
|
+
}, { signal })).pipe(Effect.flatMap(resultValue), Effect.mapError(namingRecipient(payment.to))), {
|
|
80743
81444
|
...runtime,
|
|
80744
81445
|
label: "Preparing the payment"
|
|
80745
81446
|
});
|
|
@@ -80868,7 +81569,7 @@ const execute$12 = (config, orgId, input) => Effect.gen(function* () {
|
|
|
80868
81569
|
organizationId: orgId,
|
|
80869
81570
|
permissionId: String(budget.id),
|
|
80870
81571
|
...flags.requestKey === void 0 ? {} : { requestKey: flags.requestKey }
|
|
80871
|
-
}, { signal })).pipe(Effect.flatMap(resultValue)), {
|
|
81572
|
+
}, { signal })).pipe(Effect.flatMap(resultValue), Effect.mapError(namingRecipient(payment.to))), {
|
|
80872
81573
|
...runtime,
|
|
80873
81574
|
label: "Preparing the payment"
|
|
80874
81575
|
});
|
|
@@ -81231,33 +81932,29 @@ const execute$6 = (config, input) => Effect.gen(function* () {
|
|
|
81231
81932
|
const actor = orgId === null ? client.account : client.org(orgId);
|
|
81232
81933
|
const stored = yield* Effect.promise((signal) => actor.requests.issue(draft, { signal })).pipe(Effect.flatMap(resultValue), Effect.mapError((error) => requestWriteFailure(error, orgId, "issued", null, session.email)));
|
|
81233
81934
|
const item = requestCheckout(stored, config.checkoutFrontendOrigin);
|
|
81234
|
-
const documents = yield* savePaymentDocuments(config, client, item, input.outputDirectory);
|
|
81235
81935
|
return {
|
|
81236
81936
|
data: {
|
|
81237
81937
|
scope: scopeValue(orgId),
|
|
81238
81938
|
direction: input.direction,
|
|
81239
|
-
item
|
|
81240
|
-
documents
|
|
81939
|
+
item
|
|
81241
81940
|
},
|
|
81242
|
-
lines:
|
|
81941
|
+
lines: requestLines(item, session.email, orgId)
|
|
81243
81942
|
};
|
|
81244
81943
|
});
|
|
81245
81944
|
//#endregion
|
|
81246
81945
|
//#region src/commands/request/subcommands/issue/command.ts
|
|
81247
81946
|
const command$6 = (context) => Command.make("issue", {
|
|
81248
81947
|
email: emailFlag$3,
|
|
81249
|
-
source: Flag.String("input"),
|
|
81250
|
-
confirm: confirmFlag$2
|
|
81251
|
-
|
|
81252
|
-
}, ({ email, source, confirm, outputDirectory }) => context.dispatch("issue", (scope) => ({
|
|
81948
|
+
source: Flag.String("input").pipe(Flag.withDescription("the request JSON, from a file or - for stdin")),
|
|
81949
|
+
confirm: confirmFlag$2
|
|
81950
|
+
}, ({ email, source, confirm }) => context.dispatch("issue", (scope) => ({
|
|
81253
81951
|
kind: "issue",
|
|
81254
81952
|
...scope,
|
|
81255
81953
|
direction: context.direction,
|
|
81256
81954
|
source,
|
|
81257
81955
|
confirm,
|
|
81258
|
-
email: Option.getOrUndefined(email)
|
|
81259
|
-
|
|
81260
|
-
}), execute$6)).pipe(context.annotate("issue"), Command.withDescription("Issue a payment request and save its available PDFs"));
|
|
81956
|
+
email: Option.getOrUndefined(email)
|
|
81957
|
+
}), execute$6)).pipe(context.annotate("issue"), Command.withDescription("Issue a payment request"));
|
|
81261
81958
|
//#endregion
|
|
81262
81959
|
//#region src/commands/request/subcommands/pay/execute.ts
|
|
81263
81960
|
const command$5 = (args) => args.map(shellArgument).join(" ");
|
|
@@ -81363,12 +82060,7 @@ const inbox = Command.make("inbox").pipe(received.annotate("root"), Command.with
|
|
|
81363
82060
|
//#endregion
|
|
81364
82061
|
//#region src/commands/schema/internal/contract.ts
|
|
81365
82062
|
/** Commands that move money. Each one hands over to the approval page. */
|
|
81366
|
-
const MONEY =
|
|
81367
|
-
"payment.send",
|
|
81368
|
-
"payment.retry",
|
|
81369
|
-
"inbox.pay",
|
|
81370
|
-
"org.payroll.run"
|
|
81371
|
-
]);
|
|
82063
|
+
const MONEY = new Set(Object.keys(MONEY_RESULTS));
|
|
81372
82064
|
/** Commands that answer from this machine alone. */
|
|
81373
82065
|
const LOCAL = /* @__PURE__ */ new Set([
|
|
81374
82066
|
"whoami",
|
|
@@ -81380,7 +82072,9 @@ const LOCAL = /* @__PURE__ */ new Set([
|
|
|
81380
82072
|
"schema"
|
|
81381
82073
|
]);
|
|
81382
82074
|
/** A money command's `data`: the approval link for an agent, the outcome for a person. */
|
|
81383
|
-
const APPROVAL = "--json: { status: 'awaiting_approval', approvalId, approvalUrl, paymentIds, expiresAt, next: { argv } }; a person: ApprovalOutcome { approval, payments }";
|
|
82075
|
+
const APPROVAL = "--json: { status: 'awaiting_approval', approvalId, approvalUrl, paymentIds, expiresAt, next: { argv } }; a person: ApprovalOutcome { approval, payments }. It saves no PDF: `capxul document render` does";
|
|
82076
|
+
/** `data` of an Organization Permission write: what was submitted, and with `--wait` where the command ended. */
|
|
82077
|
+
const PERMISSION_WRITE = "{ execution, requestKey }; with --wait also command: PermissionCommandView (status applied, failed or actionable)";
|
|
81384
82078
|
/** `data` of the commands an agent reaches for most; other lists are pages, others the SDK value. */
|
|
81385
82079
|
const OUTPUT = {
|
|
81386
82080
|
home: "{ where, signedIn, balances: Account | null, needsYou: NeedsYou[] | null, try }",
|
|
@@ -81394,6 +82088,12 @@ const OUTPUT = {
|
|
|
81394
82088
|
"payment.retry": APPROVAL,
|
|
81395
82089
|
"inbox.pay": APPROVAL,
|
|
81396
82090
|
"org.payroll.run": APPROVAL,
|
|
82091
|
+
"request.issue": "{ scope, direction, item: ActorRequest & { checkoutUrl } }. It saves no PDF: `capxul document render` does",
|
|
82092
|
+
"org.permission.create": PERMISSION_WRITE,
|
|
82093
|
+
"org.permission.change": PERMISSION_WRITE,
|
|
82094
|
+
"org.permission.assign": PERMISSION_WRITE,
|
|
82095
|
+
"org.permission.revoke": PERMISSION_WRITE,
|
|
82096
|
+
"org.permission.replace": PERMISSION_WRITE,
|
|
81397
82097
|
[CAPXUL_OPERATIONS.inbox.list]: "Page<InboxItem>",
|
|
81398
82098
|
"inbox.get": "InboxItem",
|
|
81399
82099
|
"request.list": "Page<ActorRequest>",
|
|
@@ -81456,15 +82156,6 @@ const EXIT_CODES = {
|
|
|
81456
82156
|
130: "cancelled: interrupted, or rejected on the approval page"
|
|
81457
82157
|
};
|
|
81458
82158
|
//#endregion
|
|
81459
|
-
//#region src/runtime/root-command.ts
|
|
81460
|
-
/** The command tree `capxul` runs, for the commands that describe it (`capxul schema`). */
|
|
81461
|
-
var RootCommand = class extends Context.Service()("capxul-cli/RootCommand") {};
|
|
81462
|
-
/**
|
|
81463
|
-
* The tree, when the entry provided it. Reading it as optional keeps it out of
|
|
81464
|
-
* every command's requirements; only `schema` asks, and the entry always gives it.
|
|
81465
|
-
*/
|
|
81466
|
-
const rootCommand = Effect.flatMap(Effect.serviceOption(RootCommand), (root) => Option.isSome(root) ? Effect.succeed(root.value) : Effect.die("RootCommand is not provided"));
|
|
81467
|
-
//#endregion
|
|
81468
82159
|
//#region src/commands/schema/internal/describe.ts
|
|
81469
82160
|
const flagOf = (flag) => ({
|
|
81470
82161
|
name: `--${flag.name}`,
|
|
@@ -81644,7 +82335,14 @@ const execute$2 = (config, wanted) => Effect.gen(function* () {
|
|
|
81644
82335
|
});
|
|
81645
82336
|
const handle = wanted.replace(/^@/u, "").toLowerCase();
|
|
81646
82337
|
const organization = organizations.find((candidate) => candidate.handle.toLowerCase() === handle || candidate.id === wanted);
|
|
81647
|
-
if (organization === void 0) return yield* inputRequired(`You are not a member of an Organization "${terminalText(wanted)}". See yours with: capxul org list
|
|
82338
|
+
if (organization === void 0) return yield* inputRequired(`You are not a member of an Organization "${terminalText(wanted)}". See yours with: capxul org list`, {
|
|
82339
|
+
field: "handle",
|
|
82340
|
+
next: [
|
|
82341
|
+
"capxul",
|
|
82342
|
+
"org",
|
|
82343
|
+
"list"
|
|
82344
|
+
]
|
|
82345
|
+
});
|
|
81648
82346
|
const saved = {
|
|
81649
82347
|
id: organization.id,
|
|
81650
82348
|
handle: organization.handle,
|
|
@@ -82285,6 +82983,15 @@ const completionFailure = (error) => {
|
|
|
82285
82983
|
if (error._tag === "ShowHelp") return error.errors.some(completionFailure);
|
|
82286
82984
|
return (error._tag === "InvalidValue" || error._tag === "MissingOption" || error._tag === "DuplicateOption") && (error.option === "completions" || error.option === "--completions");
|
|
82287
82985
|
};
|
|
82986
|
+
const helpFlag = (arg) => arg === "--help" || arg === "-h";
|
|
82987
|
+
const unknownSubcommand = (error) => error._tag === "UnknownSubcommand" || error._tag === "ShowHelp" && error.errors.some((cause) => cause._tag === "UnknownSubcommand");
|
|
82988
|
+
/**
|
|
82989
|
+
* The refusal for an unknown command in a line that asks for help: `capxul
|
|
82990
|
+
* nonsense --help` would print the parent's help and exit 0. The line is
|
|
82991
|
+
* parsed without its help flag, and an unknown command refuses as it does
|
|
82992
|
+
* without one.
|
|
82993
|
+
*/
|
|
82994
|
+
const unknownCommandUnderHelp = (args, parse) => args.some(helpFlag) ? Effect.map(parse(args.filter((arg) => !helpFlag(arg))), (parsed) => Result.isFailure(parsed) && unknownSubcommand(parsed.failure) ? parsed.failure : void 0) : Effect.undefined;
|
|
82288
82995
|
/** Help shown only because required options or arguments were missing. */
|
|
82289
82996
|
const missingParameters = (error) => CliError.isCliError(error) && error._tag === "ShowHelp" && error.errors.length > 0 && error.errors.every((cause) => cause._tag === "MissingOption" || cause._tag === "MissingArgument");
|
|
82290
82997
|
/**
|
|
@@ -82333,7 +83040,7 @@ const reportNativeFailure = (input) => Effect.gen(function* () {
|
|
|
82333
83040
|
}
|
|
82334
83041
|
}
|
|
82335
83042
|
const invocation = yield* Ref.get(current);
|
|
82336
|
-
const failure = classifyFailure(cause, invocation?.command ?? parsedCommand(cause), runtime.args);
|
|
83043
|
+
const failure = classifyFailure(cause, invocation?.command ?? parsedCommand(cause), runtime.args, pathKind(command));
|
|
82337
83044
|
yield* renderResult(mode, failureEnvelope(failure, invocation?.command ?? parsedCommand(cause), invocation?.kind === "command" ? invocation.invocationId : null));
|
|
82338
83045
|
return failure.exitCode;
|
|
82339
83046
|
});
|
|
@@ -82456,7 +83163,8 @@ const runCli = (platform) => Effect.uninterruptibleMask((restore) => Effect.gen(
|
|
|
82456
83163
|
})))
|
|
82457
83164
|
});
|
|
82458
83165
|
const builtIns = cliConfig.builtIns.filter((flag) => interactive && !ci && mode === "human" || flag !== GlobalFlag.Wizard).map((flag) => flag === GlobalFlag.Help ? decorate(GlobalFlag.Help, "help") : flag === GlobalFlag.Version ? decorate(GlobalFlag.Version, "version") : flag === GlobalFlag.Completions ? decorate(GlobalFlag.Completions, "completion") : flag === GlobalFlag.Wizard ? wizard : flag);
|
|
82459
|
-
const
|
|
83166
|
+
const unknownCommand = yield* unknownCommandUnderHelp(args, (rest) => parseProbe(homeInvocation(rest)).pipe(Effect.provideService(Console.Console, captureConsole)));
|
|
83167
|
+
const nativeExit = yield* restore((unknownCommand ? Effect.fail(unknownCommand) : runNative(homeInvocation(args))).pipe(Effect.catch((error) => Effect.gen(function* () {
|
|
82460
83168
|
if (!interactive || ci || mode !== "human" || !missingParameters(error) || (yield* Ref.get(current)) !== null) return yield* Effect.fail(error);
|
|
82461
83169
|
const leaf = commandAtPath(command, error.commandPath);
|
|
82462
83170
|
if (leaf === void 0 || leaf.subcommands.length > 0) return yield* error;
|
|
@@ -82488,4 +83196,4 @@ const runCli = (platform) => Effect.uninterruptibleMask((restore) => Effect.gen(
|
|
|
82488
83196
|
//#endregion
|
|
82489
83197
|
export { runCli };
|
|
82490
83198
|
|
|
82491
|
-
//# sourceMappingURL=entry-
|
|
83199
|
+
//# sourceMappingURL=entry-DXu0dr0S.mjs.map
|