@capxul/cli 4.20.0-beta.7 → 4.20.0-beta.9
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 +103 -14
- package/dist/browser/signer.js.map +1 -1
- package/dist/main.mjs +695 -196
- package/dist/main.mjs.map +1 -1
- package/package.json +3 -3
package/dist/main.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import process$1, { arch, exit, platform, stderr, versions } from "node:process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { Cause, Clock, Config, Console, Context, Crypto, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, FileSystem, Layer, LogLevel, Logger, Metric, Option, Path, Queue, Redacted, Ref, References, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stdio, Stream, Tracer } from "effect";
|
|
5
|
+
import { Cause, Clock, Config, Console, Context, Crypto, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, FileSystem, Layer, LogLevel, Logger, Metric, Option, Path, Queue, Redacted, Ref, References, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stdio, Stream, Terminal, Tracer } from "effect";
|
|
6
6
|
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
7
7
|
import { CliConfig, CliError, Command, Flag, GlobalFlag, Primitive, Prompt } from "effect/unstable/cli";
|
|
8
8
|
import * as nc from "node:crypto";
|
|
@@ -4174,11 +4174,11 @@ function encodePacked(types, values) {
|
|
|
4174
4174
|
for (let i = 0; i < types.length; i++) {
|
|
4175
4175
|
const type = types[i];
|
|
4176
4176
|
const value = values[i];
|
|
4177
|
-
data.push(encode$
|
|
4177
|
+
data.push(encode$3(type, value));
|
|
4178
4178
|
}
|
|
4179
4179
|
return concatHex(data);
|
|
4180
4180
|
}
|
|
4181
|
-
function encode$
|
|
4181
|
+
function encode$3(type, value, isArray = false) {
|
|
4182
4182
|
if (type === "address") {
|
|
4183
4183
|
const address = value;
|
|
4184
4184
|
if (!isAddress(address)) throw new InvalidAddressError({ address });
|
|
@@ -4212,7 +4212,7 @@ function encode$2(type, value, isArray = false) {
|
|
|
4212
4212
|
if (arrayMatch && Array.isArray(value)) {
|
|
4213
4213
|
const [_type, childType] = arrayMatch;
|
|
4214
4214
|
const data = [];
|
|
4215
|
-
for (let i = 0; i < value.length; i++) data.push(encode$
|
|
4215
|
+
for (let i = 0; i < value.length; i++) data.push(encode$3(childType, value[i], true));
|
|
4216
4216
|
if (data.length === 0) return "0x";
|
|
4217
4217
|
return concatHex(data);
|
|
4218
4218
|
}
|
|
@@ -11001,6 +11001,18 @@ function withInvocationParentSpan(effect, controls) {
|
|
|
11001
11001
|
const parent = controls?.[INVOCATION_PARENT_SPAN];
|
|
11002
11002
|
return parent === void 0 ? effect : effect.pipe(Effect.withParentSpan(parent));
|
|
11003
11003
|
}
|
|
11004
|
+
/**
|
|
11005
|
+
* The input field a refused request named.
|
|
11006
|
+
*
|
|
11007
|
+
* The surfaces that call the port directly already carry `details.field` for an
|
|
11008
|
+
* `INVALID_INPUT` refusal. The actor keeps that one allowlisted detail instead of
|
|
11009
|
+
* dropping it, so a caller sees the same refusal either way.
|
|
11010
|
+
*/
|
|
11011
|
+
const refusedInputField = (failure) => {
|
|
11012
|
+
if (failure.code !== "INVALID_INPUT") return void 0;
|
|
11013
|
+
const field = failure.error?.details?.field;
|
|
11014
|
+
return typeof field === "string" && field.length > 0 ? { field } : void 0;
|
|
11015
|
+
};
|
|
11004
11016
|
var ActorFailure = class extends Error {
|
|
11005
11017
|
reason;
|
|
11006
11018
|
machine;
|
|
@@ -11166,7 +11178,7 @@ const boot = (spec, options = {}) => Effect.gen(function* () {
|
|
|
11166
11178
|
const event = { _tag: env.origin.event };
|
|
11167
11179
|
const failedState = exit.failure === timeoutFailure || exit.failure === cancelledFailure ? yield* recoverFailure(state, env.origin, exit.failure) : state;
|
|
11168
11180
|
yield* emit(nonApplied(failedState, event, exit.failure.code, outcome, env, exit.failure));
|
|
11169
|
-
const details = exit.failure === timeoutFailure ? { reason: "timeout" } :
|
|
11181
|
+
const details = exit.failure === timeoutFailure ? { reason: "timeout" } : refusedInputField(exit.failure);
|
|
11170
11182
|
yield* failReply(held.reply, makeFailure(failedState, event, exit.failure.code, details, exit.failure.error ?? exit.failure));
|
|
11171
11183
|
return;
|
|
11172
11184
|
}
|
|
@@ -13225,7 +13237,7 @@ function subscribeActivity(deps, actor, controls, onChange) {
|
|
|
13225
13237
|
}
|
|
13226
13238
|
//#endregion
|
|
13227
13239
|
//#region ../../packages/sdk/package.json
|
|
13228
|
-
var version$1 = "4.20.0-beta.
|
|
13240
|
+
var version$1 = "4.20.0-beta.9";
|
|
13229
13241
|
//#endregion
|
|
13230
13242
|
//#region ../../packages/sdk/src/telemetry/exception-projection.ts
|
|
13231
13243
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -25087,7 +25099,7 @@ function toFormData$1(obj, formData, options) {
|
|
|
25087
25099
|
*
|
|
25088
25100
|
* @returns {string} The encoded string.
|
|
25089
25101
|
*/
|
|
25090
|
-
function encode$
|
|
25102
|
+
function encode$2(str) {
|
|
25091
25103
|
const charMap = {
|
|
25092
25104
|
"!": "%21",
|
|
25093
25105
|
"'": "%27",
|
|
@@ -25118,8 +25130,8 @@ prototype.append = function append(name, value) {
|
|
|
25118
25130
|
};
|
|
25119
25131
|
prototype.toString = function toString(encoder) {
|
|
25120
25132
|
const _encode = encoder ? function(value) {
|
|
25121
|
-
return encoder.call(this, value, encode$
|
|
25122
|
-
} : encode$
|
|
25133
|
+
return encoder.call(this, value, encode$2);
|
|
25134
|
+
} : encode$2;
|
|
25123
25135
|
return this._pairs.map(function each(pair) {
|
|
25124
25136
|
return _encode(pair[0]) + "=" + _encode(pair[1]);
|
|
25125
25137
|
}, "").join("&");
|
|
@@ -25134,7 +25146,7 @@ prototype.toString = function toString(encoder) {
|
|
|
25134
25146
|
*
|
|
25135
25147
|
* @returns {string} The encoded value.
|
|
25136
25148
|
*/
|
|
25137
|
-
function encode(val) {
|
|
25149
|
+
function encode$1(val) {
|
|
25138
25150
|
return encodeURIComponent(val).replace(/%3A/gi, ":").replace(/%24/g, "$").replace(/%2C/gi, ",").replace(/%20/g, "+");
|
|
25139
25151
|
}
|
|
25140
25152
|
/**
|
|
@@ -25148,7 +25160,7 @@ function encode(val) {
|
|
|
25148
25160
|
*/
|
|
25149
25161
|
function buildURL(url, params, options) {
|
|
25150
25162
|
if (!params) return url;
|
|
25151
|
-
const _encode = options && options.encode || encode;
|
|
25163
|
+
const _encode = options && options.encode || encode$1;
|
|
25152
25164
|
const _options = utils_default.isFunction(options) ? { serialize: options } : options;
|
|
25153
25165
|
const serializeFn = _options && _options.serialize;
|
|
25154
25166
|
let serializedParams;
|
|
@@ -41029,6 +41041,7 @@ function mapBetterAuthError(operation, body) {
|
|
|
41029
41041
|
const code = typeof errBody.code === "string" ? errBody.code : "";
|
|
41030
41042
|
if (code === "OTP_EXPIRED") return Errors.otpExpired();
|
|
41031
41043
|
if (code === "INVALID_OTP") return Errors.invalidInput("otp", errBody.message ?? "invalid OTP");
|
|
41044
|
+
if (code === "TOO_MANY_ATTEMPTS") return Errors.rateLimited({ resource: "better-auth/verifyOtp" });
|
|
41032
41045
|
if (code === "VALIDATION_ERROR" || code === "INVALID_EMAIL") return Errors.invalidInput("email", errBody.message ?? "invalid email");
|
|
41033
41046
|
}
|
|
41034
41047
|
return Errors.providerError("better-auth", operation, new Error(String(body)));
|
|
@@ -41646,6 +41659,10 @@ var BetterAuthNodeAdapter = class {
|
|
|
41646
41659
|
ok: false,
|
|
41647
41660
|
error: Errors.invalidInput("otp", errBody.message ?? "invalid OTP")
|
|
41648
41661
|
};
|
|
41662
|
+
if (errBody.code === "TOO_MANY_ATTEMPTS") return {
|
|
41663
|
+
ok: false,
|
|
41664
|
+
error: Errors.rateLimited({ resource: "better-auth/verifyOtp" })
|
|
41665
|
+
};
|
|
41649
41666
|
}
|
|
41650
41667
|
}
|
|
41651
41668
|
return {
|
|
@@ -45158,6 +45175,12 @@ const stderrConvexLogger = {
|
|
|
45158
45175
|
error: writeConvexDiagnostic,
|
|
45159
45176
|
logVerbose: () => void 0
|
|
45160
45177
|
};
|
|
45178
|
+
const silentConvexLogger = {
|
|
45179
|
+
log: () => void 0,
|
|
45180
|
+
warn: () => void 0,
|
|
45181
|
+
error: () => void 0,
|
|
45182
|
+
logVerbose: () => void 0
|
|
45183
|
+
};
|
|
45161
45184
|
/** Exact floor-first allowlist; every additional handler must migrate its validator first. */
|
|
45162
45185
|
const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
45163
45186
|
"system:healthObserved",
|
|
@@ -45246,7 +45269,7 @@ var ConvexCallAdapter = class {
|
|
|
45246
45269
|
const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
|
|
45247
45270
|
this.#client = deps.client ?? new ConvexClient(deps.convexUrl, {
|
|
45248
45271
|
...webSocketConstructor === void 0 ? {} : { webSocketConstructor },
|
|
45249
|
-
|
|
45272
|
+
logger: deps.localDiagnostics === true ? stderrConvexLogger : silentConvexLogger
|
|
45250
45273
|
});
|
|
45251
45274
|
this.#tokenProvider = deps.tokenProvider;
|
|
45252
45275
|
this.#applicationId = deps.applicationId;
|
|
@@ -51040,7 +51063,7 @@ function productionAuthCacheLayer(input) {
|
|
|
51040
51063
|
function productionConvexCallLayer(input, refreshConvexAuthRef) {
|
|
51041
51064
|
return Layer.unwrap(Effect.map(AuthClientPortTag, (authClient) => ConvexCallLayer({
|
|
51042
51065
|
convexUrl: input.runtimeUrls.convexUrl,
|
|
51043
|
-
|
|
51066
|
+
localDiagnostics: input.localDiagnostics,
|
|
51044
51067
|
...input.applicationId === void 0 ? {} : { applicationId: input.applicationId },
|
|
51045
51068
|
...input.observation === void 0 ? {} : { observation: input.observation },
|
|
51046
51069
|
...input.convexClient === void 0 ? {} : { client: input.convexClient },
|
|
@@ -51079,6 +51102,7 @@ function resolveProductionObservability(input) {
|
|
|
51079
51102
|
};
|
|
51080
51103
|
}
|
|
51081
51104
|
async function createProductionAdapters(input) {
|
|
51105
|
+
const localDiagnostics = productionLocalDiagnostics(input);
|
|
51082
51106
|
const { observation, telemetry, invocationObservation, invocationTelemetry } = resolveProductionObservability(input);
|
|
51083
51107
|
const resolvedInput = resolveInput(input);
|
|
51084
51108
|
if (!resolvedInput.ok) return resolvedInput;
|
|
@@ -51149,6 +51173,7 @@ async function createProductionAdapters(input) {
|
|
|
51149
51173
|
const portsLayer = mergeProductionAdapterLayers(makeProductionAdapterLayerEntries({
|
|
51150
51174
|
bootstrap,
|
|
51151
51175
|
runtime: resolvedInput.value.runtime,
|
|
51176
|
+
localDiagnostics,
|
|
51152
51177
|
...resolvedInput.value.origin === void 0 ? {} : { origin: resolvedInput.value.origin },
|
|
51153
51178
|
runtimeUrls: runtimeUrls.value,
|
|
51154
51179
|
chainId: bootstrapResult.value.chainId,
|
|
@@ -51163,7 +51188,7 @@ async function createProductionAdapters(input) {
|
|
|
51163
51188
|
...input.signal === void 0 ? {} : { signal: input.signal },
|
|
51164
51189
|
...input.resetSignerSession === void 0 ? {} : { resetSignerSession: input.resetSignerSession }
|
|
51165
51190
|
}));
|
|
51166
|
-
const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime, hostEngineeringPolicy(input.observability))).pipe(Layer.provideMerge(resolvedInput.value.runtime
|
|
51191
|
+
const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime, hostEngineeringPolicy(input.observability))).pipe(Layer.provideMerge(localDiagnosticsLayer(localDiagnostics, resolvedInput.value.runtime)));
|
|
51167
51192
|
const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
|
|
51168
51193
|
const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
|
|
51169
51194
|
const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
|
|
@@ -51227,6 +51252,13 @@ function optionalEngineeringTelemetryLayer(policy, runtime, hostPolicy) {
|
|
|
51227
51252
|
return Layer.empty;
|
|
51228
51253
|
}
|
|
51229
51254
|
}
|
|
51255
|
+
function localDiagnosticsLayer(enabled, runtime) {
|
|
51256
|
+
if (!enabled) return Logger.layer([]);
|
|
51257
|
+
return runtime === "node" ? Layer.succeed(Logger.LogToStderr, true) : Layer.empty;
|
|
51258
|
+
}
|
|
51259
|
+
function productionLocalDiagnostics(input) {
|
|
51260
|
+
return input.localDiagnostics !== false;
|
|
51261
|
+
}
|
|
51230
51262
|
function refreshConvexAuthOnSession(authClient, refresh) {
|
|
51231
51263
|
return {
|
|
51232
51264
|
...authClient,
|
|
@@ -51291,7 +51323,7 @@ async function createCapxulClientWithSignerControls(input, controls) {
|
|
|
51291
51323
|
runtime,
|
|
51292
51324
|
authBaseUrl: resolvedAuthBaseUrl,
|
|
51293
51325
|
createBrowserSigner: createOpenfortBrowserSignerFromBootstrap,
|
|
51294
|
-
diagnostic: new EffectLogDiagnosticAdapter(adapters.value.context, new ConsoleDiagnosticAdapter()),
|
|
51326
|
+
diagnostic: new EffectLogDiagnosticAdapter(adapters.value.context, productionLocalDiagnostics(input) ? new ConsoleDiagnosticAdapter() : void 0),
|
|
51295
51327
|
signer: input.signer,
|
|
51296
51328
|
factory: input.openfortSignerFactory,
|
|
51297
51329
|
finalSign: controls.signUserOpHash,
|
|
@@ -51663,8 +51695,8 @@ function captureException(telemetry, error, context) {
|
|
|
51663
51695
|
}
|
|
51664
51696
|
//#endregion
|
|
51665
51697
|
//#region src/version.ts
|
|
51666
|
-
const cliVersion = "4.20.0-beta.
|
|
51667
|
-
const sdkVersion = "4.20.0-beta.
|
|
51698
|
+
const cliVersion = "4.20.0-beta.9";
|
|
51699
|
+
const sdkVersion = "4.20.0-beta.9";
|
|
51668
51700
|
const ReleaseVersion = Schema.String.check(Schema.isMaxLength(256), Schema.isPattern(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?$/u)).pipe(Schema.brand("CliReleaseVersion"));
|
|
51669
51701
|
const parts = (version) => {
|
|
51670
51702
|
const [core = "", ...prerelease] = version.split("+")[0].split("-");
|
|
@@ -52193,7 +52225,7 @@ const finishUpdateCheck = Effect.fn(function* (directory, attempt, target) {
|
|
|
52193
52225
|
});
|
|
52194
52226
|
//#endregion
|
|
52195
52227
|
//#region src/platform/collection-policy.ts
|
|
52196
|
-
const decode$
|
|
52228
|
+
const decode$3 = Schema.decodeUnknownOption(Schema.fromJsonString(Settings), { onExcessProperty: "error" });
|
|
52197
52229
|
const missing = (error) => error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
52198
52230
|
const resolveProtectedDirectory = (directory, uid) => {
|
|
52199
52231
|
const stateDirectory = lstatSync(directory, { throwIfNoEntry: false });
|
|
@@ -52237,7 +52269,7 @@ const collectionPolicy = (settingsPath, forcedDisabled) => () => {
|
|
|
52237
52269
|
length += count;
|
|
52238
52270
|
}
|
|
52239
52271
|
if (length > 65536) return false;
|
|
52240
|
-
const settings = decode$
|
|
52272
|
+
const settings = decode$3(bytes.toString("utf8", 0, length));
|
|
52241
52273
|
return Option.isSome(settings) && settings.value.collection.enabled;
|
|
52242
52274
|
} catch {
|
|
52243
52275
|
return false;
|
|
@@ -52258,9 +52290,31 @@ var CliFailure = class extends Data.TaggedError("CliFailure") {};
|
|
|
52258
52290
|
*/
|
|
52259
52291
|
const WRONG_STATE_MESSAGE = "The command cannot run from the current state.";
|
|
52260
52292
|
const ORGANIZATION_WRONG_STATE_MESSAGE = "The Organization cannot run this command from its current state. Resume that exact Organization with capxul org retry --org instead of creating a replacement.";
|
|
52293
|
+
const PERSONAL_ACCOUNT_WRONG_STATE_MESSAGE = "The personal Account cannot run this command from its current state. Resume the same Account and Profile with capxul auth signup instead of creating a replacement.";
|
|
52261
52294
|
const INPUT_MESSAGE = "The input was refused. Check the email and verification code, or request a new code.";
|
|
52295
|
+
const PROFILE_INPUT_MESSAGE = "The Profile input was refused. Correct the display name, country, or handle, then run capxul auth signup again.";
|
|
52296
|
+
const HANDLE_INPUT_MESSAGE = "That handle is not available. Choose another handle, then run capxul auth signup again.";
|
|
52262
52297
|
const organizationCommand = (command) => command === "org" || command?.startsWith("org.") === true;
|
|
52263
52298
|
/**
|
|
52299
|
+
* The authentication journey family. A Profile or personal Account refusal is
|
|
52300
|
+
* not an email or verification-code problem, so it never reuses OTP copy.
|
|
52301
|
+
*/
|
|
52302
|
+
const authCommand = (command) => command === "auth" || command?.startsWith("auth.") === true;
|
|
52303
|
+
/** The Profile fields a personal signup states. */
|
|
52304
|
+
const PROFILE_FIELDS = /* @__PURE__ */ new Set([
|
|
52305
|
+
"profile",
|
|
52306
|
+
"displayName",
|
|
52307
|
+
"country",
|
|
52308
|
+
"countryCode",
|
|
52309
|
+
"handle"
|
|
52310
|
+
]);
|
|
52311
|
+
const authInputMessage = (error) => {
|
|
52312
|
+
const field = typeof error.details?.field === "string" ? error.details.field : "";
|
|
52313
|
+
if (field === "handle") return HANDLE_INPUT_MESSAGE;
|
|
52314
|
+
if (PROFILE_FIELDS.has(field)) return PROFILE_INPUT_MESSAGE;
|
|
52315
|
+
return INPUT_MESSAGE;
|
|
52316
|
+
};
|
|
52317
|
+
/**
|
|
52264
52318
|
* The one allowlist. Only a WRONG_STATE whose SDK details carry an Organization
|
|
52265
52319
|
* ID the public constructor accepts emits that ID, and it never enters message
|
|
52266
52320
|
* text or valid states. No other error detail reaches terminal output, and a
|
|
@@ -52282,13 +52336,13 @@ const configurationFailure = (message) => new CliFailure({
|
|
|
52282
52336
|
outcome: "refused"
|
|
52283
52337
|
});
|
|
52284
52338
|
function classifyFailure(cause, command) {
|
|
52285
|
-
|
|
52339
|
+
const error = Cause.squash(cause);
|
|
52340
|
+
if (Cause.hasInterruptsOnly(cause) || Terminal.isQuitError(error)) return {
|
|
52286
52341
|
code: "CANCELLED",
|
|
52287
52342
|
message: "The command was interrupted.",
|
|
52288
52343
|
exitCode: 130,
|
|
52289
52344
|
outcome: "cancelled"
|
|
52290
52345
|
};
|
|
52291
|
-
const error = Cause.squash(cause);
|
|
52292
52346
|
if (error instanceof CliFailure) return error;
|
|
52293
52347
|
if (error instanceof StateFileError) return {
|
|
52294
52348
|
code: "CLI_STORAGE",
|
|
@@ -52310,7 +52364,7 @@ function classifyFailure(cause, command) {
|
|
|
52310
52364
|
const details = allowlistedDetails(error);
|
|
52311
52365
|
return {
|
|
52312
52366
|
code: error.code,
|
|
52313
|
-
message: auth ? "Authentication is required." : denied ? "This action is not permitted." : wrongState ? organizationCommand(command) ? ORGANIZATION_WRONG_STATE_MESSAGE : WRONG_STATE_MESSAGE : input ? INPUT_MESSAGE : "The Capxul request failed.",
|
|
52367
|
+
message: auth ? "Authentication is required." : denied ? "This action is not permitted." : wrongState ? organizationCommand(command) ? ORGANIZATION_WRONG_STATE_MESSAGE : authCommand(command) ? PERSONAL_ACCOUNT_WRONG_STATE_MESSAGE : WRONG_STATE_MESSAGE : input ? authCommand(command) ? authInputMessage(error) : INPUT_MESSAGE : "The Capxul request failed.",
|
|
52314
52368
|
exitCode: auth ? 3 : denied ? 4 : input || wrongState ? 2 : 5,
|
|
52315
52369
|
outcome: auth || denied || input || wrongState ? "refused" : "failed",
|
|
52316
52370
|
...details === void 0 ? {} : { details }
|
|
@@ -52330,7 +52384,7 @@ function parsedCommand(cause) {
|
|
|
52330
52384
|
//#endregion
|
|
52331
52385
|
//#region src/runtime/config.ts
|
|
52332
52386
|
const bundledKey = Redacted.make("cap_pk_test_DXF9C0HPKB9GPQX5C6RDP6NXKZG1B0D7");
|
|
52333
|
-
const bundledOrigin = "https://
|
|
52387
|
+
const bundledOrigin = "https://api.staging.capxul.com";
|
|
52334
52388
|
const defaultHost = "https://us.i.posthog.com";
|
|
52335
52389
|
const defaultProjectToken = Redacted.make("phc_BZuAVofrkNXzwpJkuDG7Rdfurir45fj2dudQvJZk847e");
|
|
52336
52390
|
const optionalText$1 = (name) => Config.option(Config.string(name));
|
|
@@ -52728,10 +52782,10 @@ function observeInvocation(input, work) {
|
|
|
52728
52782
|
});
|
|
52729
52783
|
yield* Effect.annotateCurrentSpan(completion);
|
|
52730
52784
|
yield* logEngineeringInfo("cli.command.completed", completion);
|
|
52731
|
-
for (const diagnostic of sdkDiagnostics) yield*
|
|
52785
|
+
for (const diagnostic of sdkDiagnostics) yield* logEngineeringInfo("cli.sdk.operation.failed", {
|
|
52732
52786
|
...engineeringEnvelope,
|
|
52733
52787
|
...diagnostic
|
|
52734
|
-
})
|
|
52788
|
+
});
|
|
52735
52789
|
const metricAttributes = {
|
|
52736
52790
|
command: input.command,
|
|
52737
52791
|
capability: input.capability,
|
|
@@ -52921,6 +52975,39 @@ function metadataAtPath(root, path) {
|
|
|
52921
52975
|
};
|
|
52922
52976
|
}
|
|
52923
52977
|
//#endregion
|
|
52978
|
+
//#region src/runtime/format.ts
|
|
52979
|
+
const jsonFlag = Flag.boolean("json").pipe(Flag.withDefault(false), Flag.withDescription("Print a versioned JSON result"));
|
|
52980
|
+
const JsonOutput = GlobalFlag.setting("json")({ flag: jsonFlag });
|
|
52981
|
+
/** Local diagnostics require an explicit debug level. */
|
|
52982
|
+
const localDiagnosticsEnabled = (args) => {
|
|
52983
|
+
const terminator = args.indexOf("--");
|
|
52984
|
+
const flags = terminator < 0 ? args : args.slice(0, terminator);
|
|
52985
|
+
let level;
|
|
52986
|
+
for (const [index, arg] of flags.entries()) if (arg.startsWith("--log-level=")) level = arg.slice(12);
|
|
52987
|
+
else if (arg.startsWith("--logLevel=")) level = arg.slice(11);
|
|
52988
|
+
else if (arg === "--log-level" || arg === "--logLevel") level = flags[index + 1];
|
|
52989
|
+
return level === "debug";
|
|
52990
|
+
};
|
|
52991
|
+
/** Negotiate only the global format before native CLI error rendering.
|
|
52992
|
+
* Native Flag decoding remains the value authority; the command runner parses everything else.
|
|
52993
|
+
*/
|
|
52994
|
+
const outputMode = (args) => Effect.gen(function* () {
|
|
52995
|
+
const terminator = args.indexOf("--");
|
|
52996
|
+
const flags = terminator < 0 ? args : args.slice(0, terminator);
|
|
52997
|
+
const values = [];
|
|
52998
|
+
for (const [index, arg] of flags.entries()) if (arg === "--no-json") values.push("false");
|
|
52999
|
+
else if (arg.startsWith("--json=")) values.push(arg.slice(7));
|
|
53000
|
+
else if (arg === "--json") {
|
|
53001
|
+
const next = flags[index + 1];
|
|
53002
|
+
const value = next === void 0 ? "true" : yield* Primitive.boolean.parse(next).pipe(Effect.as(next), Effect.catch(() => Effect.succeed("true")));
|
|
53003
|
+
values.push(value);
|
|
53004
|
+
}
|
|
53005
|
+
return yield* jsonFlag.parse({
|
|
53006
|
+
flags: { json: values },
|
|
53007
|
+
arguments: []
|
|
53008
|
+
}).pipe(Effect.map(([, json]) => json ? "json" : "human"), Effect.catch(() => Effect.succeed(values.includes("true") ? "json" : "human")));
|
|
53009
|
+
});
|
|
53010
|
+
//#endregion
|
|
52924
53011
|
//#region src/runtime/run-command.ts
|
|
52925
53012
|
var CliRuntime = class extends Context.Service()("capxul-cli/Runtime") {};
|
|
52926
53013
|
const acquireInvocationIdentity = (config, eligible) => Effect.gen(function* () {
|
|
@@ -52942,9 +53029,10 @@ const runInvocation = (runtime, metadata, kind, work, network = false) => Effect
|
|
|
52942
53029
|
yield* Ref.set(runtime.current, invocation);
|
|
52943
53030
|
const config = runtime.observation;
|
|
52944
53031
|
const identity = yield* acquireInvocationIdentity(config, !silent);
|
|
53032
|
+
const debug = localDiagnosticsEnabled(runtime.args ?? []);
|
|
52945
53033
|
let dropped = 0;
|
|
52946
53034
|
let engineeringDropped = 0;
|
|
52947
|
-
|
|
53035
|
+
const observed = observeInvocation({
|
|
52948
53036
|
command: metadata.name,
|
|
52949
53037
|
capability: metadata.capability,
|
|
52950
53038
|
kind: invocationKind,
|
|
@@ -52984,7 +53072,9 @@ const runInvocation = (runtime, metadata, kind, work, network = false) => Effect
|
|
|
52984
53072
|
onFinalize
|
|
52985
53073
|
});
|
|
52986
53074
|
return yield* work(host, onFinalize);
|
|
52987
|
-
}))
|
|
53075
|
+
}));
|
|
53076
|
+
return yield* (debug ? observed : observed.pipe(Effect.provideService(References.MinimumLogLevel, "None"))).pipe(Effect.onExit(() => {
|
|
53077
|
+
if (!debug) return Effect.void;
|
|
52988
53078
|
const notice = (dropped > 0 ? `Telemetry dropped ${dropped} records.\n` : "") + (engineeringDropped > 0 ? `Engineering telemetry dropped ${engineeringDropped} items.\n` : "");
|
|
52989
53079
|
return notice === "" ? Effect.void : writeStderr(notice).pipe(Effect.catchCause(() => Effect.void));
|
|
52990
53080
|
}));
|
|
@@ -53006,8 +53096,12 @@ const runCommand = (options, work) => Effect.gen(function* () {
|
|
|
53006
53096
|
//#region src/platform/capxul.ts
|
|
53007
53097
|
const resultValue = (result) => result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
|
|
53008
53098
|
const acquireClient = (input, onFinalize) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
53099
|
+
const runtime = yield* CliRuntime;
|
|
53009
53100
|
const created = yield* restore(Effect.promise(async (signal) => {
|
|
53010
|
-
const result = await createCapxulClient(
|
|
53101
|
+
const result = await createCapxulClient({
|
|
53102
|
+
...input,
|
|
53103
|
+
localDiagnostics: localDiagnosticsEnabled(runtime.args ?? [])
|
|
53104
|
+
});
|
|
53011
53105
|
if (signal.aborted && result.ok) await result.value.close();
|
|
53012
53106
|
return result;
|
|
53013
53107
|
}));
|
|
@@ -53166,11 +53260,11 @@ const CurrentSession = Schema.Struct({
|
|
|
53166
53260
|
authUserId: Schema.NonEmptyString
|
|
53167
53261
|
}))
|
|
53168
53262
|
});
|
|
53169
|
-
const decode$
|
|
53263
|
+
const decode$2 = (value) => Schema.decodeUnknownEffect(Schema.fromJsonString(CurrentSession), { onExcessProperty: "error" })(value).pipe(Effect.mapError(() => new StateFileError({ reason: "Corrupt" })));
|
|
53170
53264
|
const readCurrentSession = (config) => Effect.gen(function* () {
|
|
53171
53265
|
const { directory, scope } = yield* privateRecordScope(config, "current-session", "current");
|
|
53172
53266
|
const value = yield* readPrivateRecord(directory, scope);
|
|
53173
|
-
return value === null ? null : (yield* decode$
|
|
53267
|
+
return value === null ? null : (yield* decode$2(value)).current;
|
|
53174
53268
|
});
|
|
53175
53269
|
const rememberCurrentSession = (config, session, stillAuthenticated) => Effect.gen(function* () {
|
|
53176
53270
|
const { directory, scope } = yield* privateRecordScope(config, "current-session", "current");
|
|
@@ -53183,7 +53277,7 @@ const rememberCurrentSession = (config, session, stillAuthenticated) => Effect.g
|
|
|
53183
53277
|
};
|
|
53184
53278
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
53185
53279
|
const expected = yield* readPrivateRecord(directory, scope);
|
|
53186
|
-
if (expected !== null) yield* decode$
|
|
53280
|
+
if (expected !== null) yield* decode$2(expected);
|
|
53187
53281
|
if (yield* compareAndSetPrivateRecord(directory, scope, expected, JSON.stringify(next), stillAuthenticated)) return;
|
|
53188
53282
|
if (!(yield* stillAuthenticated)) break;
|
|
53189
53283
|
}
|
|
@@ -53193,7 +53287,7 @@ const forgetCurrentSession = (config, email, stillCleared) => Effect.gen(functio
|
|
|
53193
53287
|
const { directory, scope } = yield* privateRecordScope(config, "current-session", "current");
|
|
53194
53288
|
const expected = yield* readPrivateRecordSnapshot(directory, scope);
|
|
53195
53289
|
if (expected === null) return false;
|
|
53196
|
-
if ((yield* decode$
|
|
53290
|
+
if ((yield* decode$2(expected)).current?.email !== email.toLowerCase()) return false;
|
|
53197
53291
|
return yield* compareAndSetPrivateRecord(directory, scope, expected, JSON.stringify({
|
|
53198
53292
|
version: 1,
|
|
53199
53293
|
current: null
|
|
@@ -53210,48 +53304,118 @@ const inputRequired = (message) => new CliFailure({
|
|
|
53210
53304
|
const readEmail = (email) => Effect.gen(function* () {
|
|
53211
53305
|
const runtime = yield* CliRuntime;
|
|
53212
53306
|
if (email === void 0) {
|
|
53213
|
-
if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired("Provide --email.
|
|
53307
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired("Provide --email."));
|
|
53214
53308
|
email = yield* Prompt.text({ message: "Email" });
|
|
53215
53309
|
}
|
|
53216
53310
|
const normalized = email.trim().toLowerCase();
|
|
53217
53311
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized)) return yield* Effect.fail(inputRequired("Provide a valid email."));
|
|
53218
53312
|
return normalized;
|
|
53219
53313
|
});
|
|
53314
|
+
const OTP_INPUT_ERROR = "OTP input must contain six digits only.";
|
|
53315
|
+
const OTP_BOUNDARY_MARKS = /^[\s\p{Cf}]+|[\s\p{Cf}]+$/gu;
|
|
53316
|
+
const normalizeOtpInput = (input) => {
|
|
53317
|
+
if (new TextEncoder().encode(input).byteLength > 64) return null;
|
|
53318
|
+
const normalized = input.replace(OTP_BOUNDARY_MARKS, "");
|
|
53319
|
+
return /^[0-9]{6}$/.test(normalized) ? normalized : null;
|
|
53320
|
+
};
|
|
53220
53321
|
const readOtp = (stdin) => Effect.gen(function* () {
|
|
53221
53322
|
const runtime = yield* CliRuntime;
|
|
53222
53323
|
if (!stdin) {
|
|
53223
53324
|
if (!runtime.tty || runtime.ci || runtime.mode === "json") return yield* Effect.fail(inputRequired("Provide the OTP on stdin with --otp-stdin; OTP arguments are not accepted."));
|
|
53224
|
-
return Redacted.value(yield* Prompt.
|
|
53325
|
+
return Redacted.value(yield* Prompt.password({
|
|
53326
|
+
message: "Email OTP",
|
|
53327
|
+
validate: (input) => {
|
|
53328
|
+
const normalized = normalizeOtpInput(input);
|
|
53329
|
+
return normalized === null ? Effect.fail(OTP_INPUT_ERROR) : Effect.succeed(normalized);
|
|
53330
|
+
}
|
|
53331
|
+
}));
|
|
53225
53332
|
}
|
|
53226
53333
|
const stdio = yield* Stdio.Stdio;
|
|
53227
53334
|
let length = 0;
|
|
53228
53335
|
const chunks = yield* stdio.stdin.pipe(Stream.mapEffect((chunk) => {
|
|
53229
53336
|
length += chunk.byteLength;
|
|
53230
|
-
return length > 64 ? Effect.fail(inputRequired(
|
|
53337
|
+
return length > 64 ? Effect.fail(inputRequired(OTP_INPUT_ERROR)) : Effect.succeed(chunk);
|
|
53231
53338
|
}), Stream.decodeText(), Stream.runCollect);
|
|
53232
|
-
const code = Array.from(chunks).join("")
|
|
53233
|
-
if (
|
|
53339
|
+
const code = normalizeOtpInput(Array.from(chunks).join(""));
|
|
53340
|
+
if (code === null) return yield* Effect.fail(inputRequired(OTP_INPUT_ERROR));
|
|
53234
53341
|
return code;
|
|
53235
53342
|
});
|
|
53236
|
-
const
|
|
53237
|
-
|
|
53238
|
-
|
|
53239
|
-
|
|
53240
|
-
|
|
53241
|
-
|
|
53343
|
+
const PROFILE_FLAGS = {
|
|
53344
|
+
displayName: "--display-name",
|
|
53345
|
+
country: "--country",
|
|
53346
|
+
handle: "--handle"
|
|
53347
|
+
};
|
|
53348
|
+
const PROFILE_REFUSALS = {
|
|
53349
|
+
displayName: "Provide --display-name with at least one visible character.",
|
|
53350
|
+
country: "Provide --country as a two-letter ISO country code, for example GH.",
|
|
53351
|
+
handle: "Provide --handle as 3 to 30 characters of a-z, 0-9, underscore, or hyphen."
|
|
53352
|
+
};
|
|
53353
|
+
/**
|
|
53354
|
+
* Accept one Profile value, or return the refusal that names its field.
|
|
53355
|
+
*
|
|
53356
|
+
* Country and handle use the public SDK constructors, so the CLI accepts
|
|
53357
|
+
* exactly the values the backend stores. A handle loses its leading `@` and
|
|
53358
|
+
* its case before the check, which is the canon spelling the owner persists.
|
|
53359
|
+
*/
|
|
53360
|
+
const acceptPersonField = (field, raw) => {
|
|
53361
|
+
const trimmed = raw.trim();
|
|
53362
|
+
if (trimmed.length === 0) return { refusal: inputRequired(PROFILE_REFUSALS[field]) };
|
|
53363
|
+
try {
|
|
53364
|
+
if (field === "country") return { value: toCountryCode(trimmed) };
|
|
53365
|
+
if (field === "handle") return { value: toHandle(trimmed.replace(/^@/u, "").toLowerCase()) };
|
|
53366
|
+
return { value: trimmed };
|
|
53367
|
+
} catch {
|
|
53368
|
+
return { refusal: inputRequired(PROFILE_REFUSALS[field]) };
|
|
53242
53369
|
}
|
|
53243
|
-
|
|
53244
|
-
|
|
53245
|
-
|
|
53370
|
+
};
|
|
53371
|
+
/**
|
|
53372
|
+
* Validate the Profile values a caller supplied, before any client work.
|
|
53373
|
+
*
|
|
53374
|
+
* `auth signup` runs this first, so a malformed value refuses locally and
|
|
53375
|
+
* consumes no one-time code. An absent field stays with `readPersonInput`,
|
|
53376
|
+
* which prompts on a terminal and refuses headlessly.
|
|
53377
|
+
*/
|
|
53378
|
+
const checkPersonFlags = (flags) => Effect.gen(function* () {
|
|
53379
|
+
for (const field of [
|
|
53380
|
+
"displayName",
|
|
53381
|
+
"country",
|
|
53382
|
+
"handle"
|
|
53383
|
+
]) {
|
|
53384
|
+
const supplied = flags[field];
|
|
53385
|
+
if (supplied === void 0) continue;
|
|
53386
|
+
const accepted = acceptPersonField(field, supplied);
|
|
53387
|
+
if ("refusal" in accepted) return yield* Effect.fail(accepted.refusal);
|
|
53388
|
+
}
|
|
53389
|
+
});
|
|
53390
|
+
/**
|
|
53391
|
+
* One required Profile field.
|
|
53392
|
+
*
|
|
53393
|
+
* A non-interactive run accepts only the explicit flag, so headless signup is
|
|
53394
|
+
* deterministic and never prompts. An interactive run prompts, and offers the
|
|
53395
|
+
* existing Profile value as the default so a continuation keeps it.
|
|
53396
|
+
*/
|
|
53397
|
+
const readRequired = (label, field, supplied, existing) => Effect.gen(function* () {
|
|
53398
|
+
const runtime = yield* CliRuntime;
|
|
53399
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json") {
|
|
53400
|
+
if (supplied === void 0) return yield* Effect.fail(inputRequired(`Provide ${PROFILE_FLAGS[field]}.`));
|
|
53401
|
+
const accepted = acceptPersonField(field, supplied);
|
|
53402
|
+
if ("refusal" in accepted) return yield* Effect.fail(accepted.refusal);
|
|
53403
|
+
return accepted.value;
|
|
53404
|
+
}
|
|
53405
|
+
const fallback = supplied ?? existing;
|
|
53406
|
+
const value = yield* Prompt.text({
|
|
53407
|
+
message: label,
|
|
53408
|
+
...fallback === void 0 || fallback.trim().length === 0 ? {} : { default: fallback }
|
|
53409
|
+
});
|
|
53410
|
+
const accepted = acceptPersonField(field, value);
|
|
53411
|
+
if ("refusal" in accepted) return yield* Effect.fail(accepted.refusal);
|
|
53412
|
+
return accepted.value;
|
|
53246
53413
|
});
|
|
53247
53414
|
const readPersonInput = (flags, profile) => Effect.gen(function* () {
|
|
53248
|
-
const displayName = yield* readRequired("Display name", "display-name", flags.displayName ?? profile?.displayName ?? void 0);
|
|
53249
|
-
const country = yield* readRequired("Country code", "country", flags.country ?? profile?.country ?? void 0);
|
|
53250
|
-
const handle = yield* readRequired("Handle", "handle", flags.handle ?? profile?.handle ?? void 0);
|
|
53251
53415
|
return {
|
|
53252
|
-
displayName,
|
|
53253
|
-
country: country.
|
|
53254
|
-
handle
|
|
53416
|
+
displayName: yield* readRequired("Display name", "displayName", flags.displayName, profile?.displayName ?? void 0),
|
|
53417
|
+
country: yield* readRequired("Country code", "country", flags.country, profile?.country ?? void 0),
|
|
53418
|
+
handle: yield* readRequired("Handle", "handle", flags.handle, profile?.handle ?? void 0)
|
|
53255
53419
|
};
|
|
53256
53420
|
});
|
|
53257
53421
|
//#endregion
|
|
@@ -53928,6 +54092,17 @@ const verifiedSession = (client, email) => Effect.gen(function* () {
|
|
|
53928
54092
|
if (session === null || session.email.toLowerCase() !== email) return yield* Effect.fail(Errors.notAuthenticated());
|
|
53929
54093
|
return session;
|
|
53930
54094
|
});
|
|
54095
|
+
const submitOtp = (otpStdin, verify) => Effect.gen(function* () {
|
|
54096
|
+
const submissionLimit = otpStdin ? 1 : 3;
|
|
54097
|
+
for (let submission = 1; submission <= submissionLimit; submission += 1) {
|
|
54098
|
+
const code = yield* readOtp(otpStdin);
|
|
54099
|
+
const result = yield* Effect.result(verify(code));
|
|
54100
|
+
if (Result.isSuccess(result)) return;
|
|
54101
|
+
const error = result.failure;
|
|
54102
|
+
if (!(error.code === "INVALID_INPUT" && error.details?.field === "otp") || submission === submissionLimit) return yield* Effect.fail(error);
|
|
54103
|
+
yield* writeStderr("Invalid OTP. Try again.\n");
|
|
54104
|
+
}
|
|
54105
|
+
});
|
|
53931
54106
|
/**
|
|
53932
54107
|
* Local authentication checks only: the current-session pointer, the explicit
|
|
53933
54108
|
* email, and a stored credential. No bootstrap, client, or network work. A
|
|
@@ -54005,6 +54180,60 @@ const flushWalletDiagnostics = (bridge, invocationId) => Effect.suspend(() => br
|
|
|
54005
54180
|
invocation_id: invocationId,
|
|
54006
54181
|
journey_id: invocationId
|
|
54007
54182
|
}), { discard: true }));
|
|
54183
|
+
const SIGNUP_OTP_INPUT_REQUIRED = "Use --otp-stdin for non-interactive signup. OTP arguments are not accepted.";
|
|
54184
|
+
/**
|
|
54185
|
+
* One account-setup continuation: profile input, then wallet and Account work.
|
|
54186
|
+
*
|
|
54187
|
+
* `signup` calls this after authentication. An interactive `login` calls it
|
|
54188
|
+
* only after the person accepts the setup offer. A headless `login` never calls
|
|
54189
|
+
* it, so sign-in cannot become signup.
|
|
54190
|
+
*
|
|
54191
|
+
* The step returns a ready report only. When the personal Account is not ready,
|
|
54192
|
+
* it returns a typed recoverable report: `setupState` names the state, the
|
|
54193
|
+
* lifecycle carries the failure code, and the copy names the exact resume
|
|
54194
|
+
* action. The step never starts wallet work for a ready Account, so a retry
|
|
54195
|
+
* reuses the one Profile, wallet, Smart Account, and Account.
|
|
54196
|
+
*/
|
|
54197
|
+
const completeAccountSetup = (config, store, client, session, suppliedProfile) => Effect.gen(function* () {
|
|
54198
|
+
const current = yield* call((signal) => client.identity.loadCurrent({ signal }));
|
|
54199
|
+
const before = yield* profileReport(client, session);
|
|
54200
|
+
if (before.data.setupState === "ready") return before;
|
|
54201
|
+
const profile = yield* readPersonInput(suppliedProfile, current);
|
|
54202
|
+
const runtime = yield* CliRuntime;
|
|
54203
|
+
const run = Effect.runPromiseWith(yield* Effect.context());
|
|
54204
|
+
let bridge;
|
|
54205
|
+
const invocation = yield* Ref.get(runtime.current);
|
|
54206
|
+
const openfortSignerFactory = (bootstrap) => {
|
|
54207
|
+
const created = createBrowserSignerBridge({
|
|
54208
|
+
bootstrap,
|
|
54209
|
+
environment: config.environment,
|
|
54210
|
+
handle: () => profile.handle,
|
|
54211
|
+
trustedAuthOrigin: new URL(config.bootstrapBaseUrl).origin,
|
|
54212
|
+
sessionToken: () => run(store.tokenIfCurrent())
|
|
54213
|
+
});
|
|
54214
|
+
bridge = created;
|
|
54215
|
+
invocation?.onFinalize?.(Effect.promise(() => created.close()).pipe(Effect.asVoid, Effect.catchCause(() => Effect.void)));
|
|
54216
|
+
return created.signer;
|
|
54217
|
+
};
|
|
54218
|
+
const walletClient = yield* clientFor(config, store, {
|
|
54219
|
+
restore: true,
|
|
54220
|
+
requirement: "deployed",
|
|
54221
|
+
openfortSignerFactory
|
|
54222
|
+
});
|
|
54223
|
+
const verified = yield* verifiedSession(walletClient, session.email);
|
|
54224
|
+
yield* writeStderr("Preparing your wallet in the browser. Keep that tab open.\n");
|
|
54225
|
+
const report = yield* Effect.gen(function* () {
|
|
54226
|
+
yield* call((signal) => walletClient.onboarding.completePerson(profile, { signal }));
|
|
54227
|
+
return yield* profileReport(walletClient, verified);
|
|
54228
|
+
}).pipe(Effect.ensuring(Effect.suspend(() => flushWalletDiagnostics(bridge, runtime.invocationId))));
|
|
54229
|
+
return {
|
|
54230
|
+
data: {
|
|
54231
|
+
...report.data,
|
|
54232
|
+
signerStatus: walletClient.signer.status()
|
|
54233
|
+
},
|
|
54234
|
+
lines: report.lines
|
|
54235
|
+
};
|
|
54236
|
+
});
|
|
54008
54237
|
const sendOtp = (config, suppliedEmail) => Effect.gen(function* () {
|
|
54009
54238
|
const email = yield* readEmail(suppliedEmail);
|
|
54010
54239
|
const store = yield* openSessionRecord(config, email);
|
|
@@ -54029,7 +54258,6 @@ const verifyOtp = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
|
|
|
54029
54258
|
const store = yield* openSessionRecord(config, email);
|
|
54030
54259
|
const flow = store.current().flow;
|
|
54031
54260
|
if (flow === null || flow.email !== email) return yield* Effect.fail(inputRequired("No pending OTP. Run auth send --email <email> first."));
|
|
54032
|
-
const code = yield* readOtp(otpStdin);
|
|
54033
54261
|
const client = yield* clientFor(config, store, { restore: false });
|
|
54034
54262
|
yield* call((signal) => client.auth.getSession({ signal }));
|
|
54035
54263
|
yield* call((signal) => client.auth.resumeOtp({
|
|
@@ -54039,13 +54267,13 @@ const verifyOtp = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
|
|
|
54039
54267
|
signal,
|
|
54040
54268
|
journey_id: flow.journeyId
|
|
54041
54269
|
}));
|
|
54042
|
-
yield* call((signal) => client.auth.verifyOtp({
|
|
54270
|
+
yield* submitOtp(otpStdin, (code) => call((signal) => client.auth.verifyOtp({
|
|
54043
54271
|
email,
|
|
54044
54272
|
code
|
|
54045
54273
|
}, {
|
|
54046
54274
|
signal,
|
|
54047
54275
|
journey_id: flow.journeyId
|
|
54048
|
-
}));
|
|
54276
|
+
})));
|
|
54049
54277
|
const session = yield* verifiedSession(client, email);
|
|
54050
54278
|
yield* bindPerson(config, store, session);
|
|
54051
54279
|
const report = yield* profileReport(client, session);
|
|
@@ -54060,95 +54288,64 @@ const verifyOtp = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
|
|
|
54060
54288
|
const login = (config, suppliedEmail, otpStdin) => Effect.gen(function* () {
|
|
54061
54289
|
const email = yield* readEmail(suppliedEmail);
|
|
54062
54290
|
const runtime = yield* CliRuntime;
|
|
54063
|
-
if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired("Use auth send, then auth verify
|
|
54291
|
+
if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired("Provide the OTP on stdin with --otp-stdin. Use capxul auth send, then capxul auth verify, to split sign-in across two commands."));
|
|
54064
54292
|
const store = yield* openSessionRecord(config, email);
|
|
54065
54293
|
const client = yield* clientFor(config, store, { restore: false });
|
|
54066
54294
|
yield* call((signal) => client.auth.getSession({ signal }));
|
|
54067
54295
|
yield* store.begin(yield* Clock.currentTimeMillis, runtime.invocationId);
|
|
54068
54296
|
yield* call((signal) => client.auth.signIn({ email }, { signal }));
|
|
54069
54297
|
yield* writeStderr("Verification email sent. Enter the code to continue.\n");
|
|
54070
|
-
|
|
54071
|
-
yield* call((signal) => client.auth.verifyOtp({
|
|
54298
|
+
yield* submitOtp(otpStdin, (code) => call((signal) => client.auth.verifyOtp({
|
|
54072
54299
|
email,
|
|
54073
54300
|
code
|
|
54074
54301
|
}, {
|
|
54075
54302
|
signal,
|
|
54076
54303
|
journey_id: runtime.invocationId
|
|
54077
|
-
}));
|
|
54304
|
+
})));
|
|
54078
54305
|
const session = yield* verifiedSession(client, email);
|
|
54079
54306
|
yield* bindPerson(config, store, session);
|
|
54080
|
-
|
|
54307
|
+
const report = yield* profileReport(client, session);
|
|
54308
|
+
if (report.data.setupState === "ready") return report;
|
|
54309
|
+
if (!runtime.tty || runtime.ci || runtime.mode === "json") return report;
|
|
54310
|
+
if (!(yield* Prompt.confirm({
|
|
54311
|
+
message: "Your Capxul account is not set up yet. Continue setup now?",
|
|
54312
|
+
initial: false
|
|
54313
|
+
}))) return report;
|
|
54314
|
+
return yield* completeAccountSetup(config, store, client, session, {});
|
|
54081
54315
|
});
|
|
54082
54316
|
const signup = (config, suppliedEmail, otpStdin, suppliedProfile) => Effect.gen(function* () {
|
|
54083
54317
|
const email = yield* readEmail(suppliedEmail);
|
|
54084
54318
|
const runtime = yield* CliRuntime;
|
|
54319
|
+
yield* checkPersonFlags(suppliedProfile);
|
|
54085
54320
|
const store = yield* openSessionRecord(config, email);
|
|
54086
|
-
const run = Effect.runPromiseWith(yield* Effect.context());
|
|
54087
54321
|
const restore = store.current().token !== null;
|
|
54088
|
-
|
|
54089
|
-
let bridge;
|
|
54322
|
+
if (!restore && !otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired(SIGNUP_OTP_INPUT_REQUIRED));
|
|
54090
54323
|
let client = yield* clientFor(config, store, { restore });
|
|
54091
54324
|
let session = restore ? yield* verifiedSession(client, email).pipe(Effect.catch((error) => error.code === "NOT_AUTHENTICATED" ? store.clear.pipe(Effect.as(null)) : Effect.fail(error))) : null;
|
|
54092
54325
|
if (session === null) {
|
|
54093
54326
|
if (restore) client = yield* clientFor(config, store, { restore: false });
|
|
54094
|
-
if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired(
|
|
54327
|
+
if (!otpStdin && (!runtime.tty || runtime.ci || runtime.mode === "json")) return yield* Effect.fail(inputRequired(SIGNUP_OTP_INPUT_REQUIRED));
|
|
54095
54328
|
yield* call((signal) => client.auth.getSession({ signal }));
|
|
54096
54329
|
yield* store.begin(yield* Clock.currentTimeMillis, runtime.invocationId);
|
|
54097
54330
|
yield* call((signal) => client.auth.signIn({ email }, { signal }));
|
|
54098
54331
|
yield* writeStderr("Verification email sent. Enter the code to continue.\n");
|
|
54099
|
-
|
|
54100
|
-
yield* call((signal) => client.auth.verifyOtp({
|
|
54332
|
+
yield* submitOtp(otpStdin, (code) => call((signal) => client.auth.verifyOtp({
|
|
54101
54333
|
email,
|
|
54102
54334
|
code
|
|
54103
54335
|
}, {
|
|
54104
54336
|
signal,
|
|
54105
54337
|
journey_id: runtime.invocationId
|
|
54106
|
-
}));
|
|
54338
|
+
})));
|
|
54107
54339
|
session = yield* verifiedSession(client, email);
|
|
54108
54340
|
}
|
|
54109
54341
|
yield* bindPerson(config, store, session);
|
|
54110
|
-
|
|
54111
|
-
const before = yield* profileReport(client, session);
|
|
54112
|
-
if (before.data.setupState === "ready") return before;
|
|
54113
|
-
const profile = yield* readPersonInput(suppliedProfile, current);
|
|
54114
|
-
handle = profile.handle;
|
|
54115
|
-
const invocation = yield* Ref.get(runtime.current);
|
|
54116
|
-
const openfortSignerFactory = (bootstrap) => {
|
|
54117
|
-
const created = createBrowserSignerBridge({
|
|
54118
|
-
bootstrap,
|
|
54119
|
-
environment: config.environment,
|
|
54120
|
-
handle: () => handle,
|
|
54121
|
-
trustedAuthOrigin: new URL(config.bootstrapBaseUrl).origin,
|
|
54122
|
-
sessionToken: () => run(store.tokenIfCurrent())
|
|
54123
|
-
});
|
|
54124
|
-
bridge = created;
|
|
54125
|
-
invocation?.onFinalize?.(Effect.promise(() => created.close()).pipe(Effect.asVoid, Effect.catchCause(() => Effect.void)));
|
|
54126
|
-
return created.signer;
|
|
54127
|
-
};
|
|
54128
|
-
const walletClient = yield* clientFor(config, store, {
|
|
54129
|
-
restore: true,
|
|
54130
|
-
requirement: "deployed",
|
|
54131
|
-
openfortSignerFactory
|
|
54132
|
-
});
|
|
54133
|
-
session = yield* verifiedSession(walletClient, email);
|
|
54134
|
-
yield* writeStderr("Preparing your wallet in the browser. Keep that tab open.\n");
|
|
54135
|
-
const report = yield* Effect.gen(function* () {
|
|
54136
|
-
yield* call((signal) => walletClient.onboarding.completePerson(profile, { signal }));
|
|
54137
|
-
return yield* profileReport(walletClient, session);
|
|
54138
|
-
}).pipe(Effect.ensuring(Effect.suspend(() => flushWalletDiagnostics(bridge, runtime.invocationId))));
|
|
54139
|
-
return {
|
|
54140
|
-
data: {
|
|
54141
|
-
...report.data,
|
|
54142
|
-
signerStatus: walletClient.signer.status()
|
|
54143
|
-
},
|
|
54144
|
-
lines: report.lines
|
|
54145
|
-
};
|
|
54342
|
+
return yield* completeAccountSetup(config, store, client, session, suppliedProfile);
|
|
54146
54343
|
});
|
|
54147
54344
|
const readProfile = (config, suppliedEmail) => Effect.gen(function* () {
|
|
54148
54345
|
const email = yield* readEmail(suppliedEmail);
|
|
54149
54346
|
const store = yield* openSessionRecord(config, email);
|
|
54150
54347
|
if (store.current().token === null) {
|
|
54151
|
-
if (usableGrant(yield* loadGrant(config, email))) return yield* Effect.fail(inputRequired("
|
|
54348
|
+
if (usableGrant(yield* loadGrant(config, email))) return yield* Effect.fail(inputRequired("Sign in again with capxul auth login to use this command."));
|
|
54152
54349
|
return yield* Effect.fail(Errors.notAuthenticated());
|
|
54153
54350
|
}
|
|
54154
54351
|
const client = yield* clientFor(config, store, { restore: true });
|
|
@@ -54207,11 +54404,11 @@ const logout = (config, suppliedEmail) => Effect.gen(function* () {
|
|
|
54207
54404
|
});
|
|
54208
54405
|
//#endregion
|
|
54209
54406
|
//#region src/commands/auth.ts
|
|
54210
|
-
const emailFlag$3 = Flag.string("email").pipe(Flag.withDescription("
|
|
54211
|
-
const otpStdinFlag = Flag.boolean("otp-stdin").pipe(Flag.withDefault(false), Flag.withDescription("Read the six-digit OTP from
|
|
54212
|
-
const displayNameFlag = Flag.string("display-name").pipe(Flag.withDescription("
|
|
54213
|
-
const countryFlag = Flag.string("country").pipe(Flag.withDescription("Two-letter
|
|
54214
|
-
const handleFlag = Flag.string("handle").pipe(Flag.withDescription("
|
|
54407
|
+
const emailFlag$3 = Flag.string("email").pipe(Flag.withDescription("Email address"), Flag.optional);
|
|
54408
|
+
const otpStdinFlag = Flag.boolean("otp-stdin").pipe(Flag.withDefault(false), Flag.withDescription("Read the six-digit OTP from standard input; never pass it as an argument"));
|
|
54409
|
+
const displayNameFlag = Flag.string("display-name").pipe(Flag.withDescription("Your display name"), Flag.optional);
|
|
54410
|
+
const countryFlag = Flag.string("country").pipe(Flag.withDescription("Two-letter country code"), Flag.optional);
|
|
54411
|
+
const handleFlag = Flag.string("handle").pipe(Flag.withDescription("Your unique Capxul handle"), Flag.optional);
|
|
54215
54412
|
const metadata$4 = (name) => ({
|
|
54216
54413
|
name,
|
|
54217
54414
|
capability: "authentication",
|
|
@@ -54223,7 +54420,7 @@ const loginCommand = Command.make("login", {
|
|
|
54223
54420
|
}, ({ email, otpStdin }) => runCommand({
|
|
54224
54421
|
...metadata$4("auth.login"),
|
|
54225
54422
|
network: true
|
|
54226
|
-
}, (config) => login(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.login")), Command.withDescription("
|
|
54423
|
+
}, (config) => login(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.login")), Command.withDescription("Sign in with your email and a one-time code"));
|
|
54227
54424
|
const signupCommand = Command.make("signup", {
|
|
54228
54425
|
email: emailFlag$3,
|
|
54229
54426
|
otpStdin: otpStdinFlag,
|
|
@@ -54237,31 +54434,31 @@ const signupCommand = Command.make("signup", {
|
|
|
54237
54434
|
displayName: Option.getOrUndefined(displayName),
|
|
54238
54435
|
country: Option.getOrUndefined(country),
|
|
54239
54436
|
handle: Option.getOrUndefined(handle)
|
|
54240
|
-
}))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.signup")), Command.withDescription("
|
|
54437
|
+
}))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.signup")), Command.withDescription("Create your Capxul account and wallet"));
|
|
54241
54438
|
const sendCommand = Command.make("send", { email: emailFlag$3 }, ({ email }) => runCommand({
|
|
54242
54439
|
...metadata$4("auth.send"),
|
|
54243
54440
|
network: true
|
|
54244
|
-
}, (config) => sendOtp(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.send")), Command.withDescription("Send
|
|
54441
|
+
}, (config) => sendOtp(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.send")), Command.withDescription("Send a sign-in code to your email"));
|
|
54245
54442
|
const verifyCommand = Command.make("verify", {
|
|
54246
54443
|
email: emailFlag$3,
|
|
54247
54444
|
otpStdin: otpStdinFlag
|
|
54248
54445
|
}, ({ email, otpStdin }) => runCommand({
|
|
54249
54446
|
...metadata$4("auth.verify"),
|
|
54250
54447
|
network: true
|
|
54251
|
-
}, (config) => verifyOtp(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.verify")), Command.withDescription("
|
|
54448
|
+
}, (config) => verifyOtp(config, Option.getOrUndefined(email), otpStdin))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.verify")), Command.withDescription("Finish signing in with the code from your email"));
|
|
54252
54449
|
const profileCommand = Command.make("profile", { email: emailFlag$3 }, ({ email }) => runCommand({
|
|
54253
54450
|
...metadata$4("auth.profile"),
|
|
54254
54451
|
network: true
|
|
54255
|
-
}, (config) => readProfile(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.profile")), Command.withDescription("
|
|
54452
|
+
}, (config) => readProfile(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.profile")), Command.withDescription("Show your profile and account readiness"));
|
|
54256
54453
|
const statusCommand = Command.make("status", { email: emailFlag$3 }, ({ email }) => runCommand({
|
|
54257
54454
|
...metadata$4("auth.status"),
|
|
54258
54455
|
network: true
|
|
54259
|
-
}, (config) => status$2(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.status")), Command.withDescription("
|
|
54456
|
+
}, (config) => status$2(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.status")), Command.withDescription("Show whether you are signed in"));
|
|
54260
54457
|
const logoutCommand = Command.make("logout", { email: emailFlag$3 }, ({ email }) => runCommand({
|
|
54261
54458
|
...metadata$4("auth.logout"),
|
|
54262
54459
|
network: true
|
|
54263
|
-
}, (config) => logout(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.logout")), Command.withDescription("
|
|
54264
|
-
const auth = Command.make("auth").pipe(Command.annotate(CliCommandMetadata, metadata$4("auth")), Command.withDescription("
|
|
54460
|
+
}, (config) => logout(config, Option.getOrUndefined(email)))).pipe(Command.annotate(CliCommandMetadata, metadata$4("auth.logout")), Command.withDescription("Sign out on this device"));
|
|
54461
|
+
const auth = Command.make("auth").pipe(Command.annotate(CliCommandMetadata, metadata$4("auth")), Command.withDescription("Sign in, create your account, and manage your session"), Command.withSubcommands([
|
|
54265
54462
|
loginCommand,
|
|
54266
54463
|
signupCommand,
|
|
54267
54464
|
sendCommand,
|
|
@@ -54334,6 +54531,73 @@ const telemetry = Command.make("telemetry").pipe(Command.annotate(CliCommandMeta
|
|
|
54334
54531
|
disable
|
|
54335
54532
|
]));
|
|
54336
54533
|
//#endregion
|
|
54534
|
+
//#region src/org-recovery.ts
|
|
54535
|
+
/**
|
|
54536
|
+
* The one safe recovery handle for a person's Organization journey.
|
|
54537
|
+
*
|
|
54538
|
+
* `handle` is the exact business handle the create prepared. It is the only
|
|
54539
|
+
* identity the CLI knows while the first durable Organization write is in
|
|
54540
|
+
* flight, and the backend resumes the same Organization for that handle.
|
|
54541
|
+
* `organizationId` is added from the first committed Organization. After that
|
|
54542
|
+
* the ID is the recovery target.
|
|
54543
|
+
*
|
|
54544
|
+
* A record with no handle releases the journey: the backend refused that exact
|
|
54545
|
+
* handle before it wrote any Organization row, so no Organization waits to be
|
|
54546
|
+
* resumed. The record stores no draft, submission, credential, or token.
|
|
54547
|
+
*/
|
|
54548
|
+
const Recovery = Schema.Struct({
|
|
54549
|
+
version: Schema.Literal(1),
|
|
54550
|
+
handle: Schema.optional(Schema.NonEmptyString),
|
|
54551
|
+
organizationId: Schema.optional(Schema.NonEmptyString)
|
|
54552
|
+
});
|
|
54553
|
+
const decode$1 = (value) => Schema.decodeUnknownEffect(Schema.fromJsonString(Recovery), { onExcessProperty: "error" })(value).pipe(Effect.flatMap((record) => {
|
|
54554
|
+
const organizationId = record.organizationId ?? null;
|
|
54555
|
+
if (record.handle === void 0) return organizationId === null ? Effect.succeed(null) : Effect.fail(new StateFileError({ reason: "Corrupt" }));
|
|
54556
|
+
const handle = record.handle;
|
|
54557
|
+
return Effect.try(() => ({
|
|
54558
|
+
handle: String(toHandle(handle)),
|
|
54559
|
+
organizationId: organizationId === null ? null : toOrgId(organizationId)
|
|
54560
|
+
}));
|
|
54561
|
+
}), Effect.mapError(() => new StateFileError({ reason: "Corrupt" })));
|
|
54562
|
+
const encode = (recovery) => JSON.stringify({
|
|
54563
|
+
version: 1,
|
|
54564
|
+
handle: recovery.handle,
|
|
54565
|
+
...recovery.organizationId === null ? {} : { organizationId: String(recovery.organizationId) }
|
|
54566
|
+
});
|
|
54567
|
+
/** The released shape: no handle waits to be resumed. */
|
|
54568
|
+
const RELEASED = JSON.stringify({ version: 1 });
|
|
54569
|
+
/** Read the safe recovery handle without writing. A corrupt record refuses the command. */
|
|
54570
|
+
const readOrganizationRecovery = (config, authUserId) => Effect.gen(function* () {
|
|
54571
|
+
const { directory, scope } = yield* privateRecordScope(config, "organization-recovery", authUserId);
|
|
54572
|
+
const value = yield* readPrivateRecord(directory, scope);
|
|
54573
|
+
return value === null ? null : yield* decode$1(value);
|
|
54574
|
+
});
|
|
54575
|
+
/**
|
|
54576
|
+
* Open the recovery handle for one command.
|
|
54577
|
+
*
|
|
54578
|
+
* `save` and `release` replace only the exact record this command read, so two
|
|
54579
|
+
* commands cannot overwrite each other's Organization journey, and a delayed
|
|
54580
|
+
* write cannot follow a newer one. A record this command did not read refuses
|
|
54581
|
+
* the write.
|
|
54582
|
+
*/
|
|
54583
|
+
const openOrganizationRecovery = (config, authUserId, stillAuthenticated) => Effect.gen(function* () {
|
|
54584
|
+
const { directory, scope } = yield* privateRecordScope(config, "organization-recovery", authUserId);
|
|
54585
|
+
let encoded = yield* readPrivateRecord(directory, scope);
|
|
54586
|
+
let record = encoded === null ? null : yield* decode$1(encoded);
|
|
54587
|
+
const write = (next) => Effect.gen(function* () {
|
|
54588
|
+
const value = next === null ? RELEASED : encode(next);
|
|
54589
|
+
if (value === encoded) return;
|
|
54590
|
+
if (!(yield* compareAndSetPrivateRecord(directory, scope, encoded, value, stillAuthenticated))) return yield* Effect.fail(Errors.notAuthenticated("The local Organization recovery state changed. Retry the command."));
|
|
54591
|
+
encoded = value;
|
|
54592
|
+
record = next;
|
|
54593
|
+
});
|
|
54594
|
+
return {
|
|
54595
|
+
current: () => record,
|
|
54596
|
+
save: (next) => write(next),
|
|
54597
|
+
release: () => write(null)
|
|
54598
|
+
};
|
|
54599
|
+
});
|
|
54600
|
+
//#endregion
|
|
54337
54601
|
//#region src/org-selection.ts
|
|
54338
54602
|
const Selection = Schema.Struct({
|
|
54339
54603
|
version: Schema.Literal(1),
|
|
@@ -54356,6 +54620,37 @@ const saveOrganizationSelection = (config, authUserId, organizationId, stillAuth
|
|
|
54356
54620
|
});
|
|
54357
54621
|
//#endregion
|
|
54358
54622
|
//#region src/org.ts
|
|
54623
|
+
/**
|
|
54624
|
+
* The exact Organization a read uses when the caller named no ID: the saved read
|
|
54625
|
+
* default first, then the recorded recovery handle for this person's
|
|
54626
|
+
* Organization journey. `recoverable` marks the handle source, whose Organization
|
|
54627
|
+
* may still run its setup lane and be absent from the own-access list.
|
|
54628
|
+
*/
|
|
54629
|
+
const readResolvedOrganization = (config, authUserId, organizationId) => Effect.gen(function* () {
|
|
54630
|
+
if (organizationId !== null) return {
|
|
54631
|
+
organizationId,
|
|
54632
|
+
recoverable: false
|
|
54633
|
+
};
|
|
54634
|
+
const selected = yield* readOrganizationSelection(config, authUserId);
|
|
54635
|
+
if (selected !== null) return {
|
|
54636
|
+
organizationId: selected,
|
|
54637
|
+
recoverable: false
|
|
54638
|
+
};
|
|
54639
|
+
const recovery = yield* readOrganizationRecovery(config, authUserId);
|
|
54640
|
+
return {
|
|
54641
|
+
organizationId: recovery?.organizationId ?? null,
|
|
54642
|
+
recoverable: recovery?.organizationId !== null && recovery?.organizationId !== void 0
|
|
54643
|
+
};
|
|
54644
|
+
});
|
|
54645
|
+
/**
|
|
54646
|
+
* A ready Organization this command reports must satisfy current access.
|
|
54647
|
+
*
|
|
54648
|
+
* The own-access list carries a founder's unfinished Organizations through the
|
|
54649
|
+
* backend's founder fallback, and the backend lifecycle read admits the founder
|
|
54650
|
+
* for the durable lane. A confirmed result therefore reads the list again: an
|
|
54651
|
+
* Organization the person cannot reach refuses instead of reporting its state.
|
|
54652
|
+
*/
|
|
54653
|
+
const requireCurrentAccess = (client, organizationId, lifecycle) => lifecycle.status !== "ready" ? Effect.void : Effect.promise((signal) => client.orgs({ signal })).pipe(Effect.flatMap(resultValue), Effect.flatMap((organizations) => organizations.some((organization) => organization.id === organizationId) ? Effect.void : Effect.fail(Errors.invalidInput("org", "The Organization is not accessible."))));
|
|
54359
54654
|
const readOrganization = (config, command, input) => Effect.gen(function* () {
|
|
54360
54655
|
const timeout = yield* Schema.decodeUnknownEffect(Schema.Int.check(Schema.isBetween({
|
|
54361
54656
|
minimum: 1,
|
|
@@ -54382,7 +54677,8 @@ const readOrganization = (config, command, input) => Effect.gen(function* () {
|
|
|
54382
54677
|
address: org.account.address
|
|
54383
54678
|
}))
|
|
54384
54679
|
};
|
|
54385
|
-
|
|
54680
|
+
const resolved = yield* readResolvedOrganization(config, session.authUserId, organizationId);
|
|
54681
|
+
organizationId = resolved.organizationId;
|
|
54386
54682
|
if (organizationId === null) {
|
|
54387
54683
|
const runtime = yield* CliRuntime;
|
|
54388
54684
|
if (!runtime.tty || runtime.ci || runtime.mode === "json" || organizations.length === 0) return yield* Effect.fail(inputRequired("Provide --org or select a read default with org use."));
|
|
@@ -54397,7 +54693,7 @@ const readOrganization = (config, command, input) => Effect.gen(function* () {
|
|
|
54397
54693
|
}))
|
|
54398
54694
|
});
|
|
54399
54695
|
}
|
|
54400
|
-
if (!organizations.some((org) => org.id === organizationId)) return yield* Effect.fail(Errors.invalidInput("org", "The Organization is not accessible."));
|
|
54696
|
+
if (!organizations.some((org) => org.id === organizationId) && !resolved.recoverable) return yield* Effect.fail(Errors.invalidInput("org", "The Organization is not accessible."));
|
|
54401
54697
|
if (command === "use") {
|
|
54402
54698
|
yield* saveOrganizationSelection(config, session.authUserId, organizationId, stillAuthenticated);
|
|
54403
54699
|
return {
|
|
@@ -54450,11 +54746,13 @@ const readOrganization = (config, command, input) => Effect.gen(function* () {
|
|
|
54450
54746
|
const read = Effect.promise((signal) => scoped.getLifecycle({ signal })).pipe(Effect.flatMap(resultValue));
|
|
54451
54747
|
const observed = yield* Effect.gen(function* () {
|
|
54452
54748
|
let lifecycle = yield* read;
|
|
54749
|
+
if (resolved.recoverable) yield* requireCurrentAccess(client, organizationId, lifecycle);
|
|
54453
54750
|
if (command !== "wait") return lifecycle;
|
|
54454
54751
|
while (lifecycle.status !== "ready" && lifecycle.status !== "failed") {
|
|
54455
54752
|
yield* Effect.sleep(1e3);
|
|
54456
54753
|
lifecycle = yield* read;
|
|
54457
54754
|
}
|
|
54755
|
+
if (resolved.recoverable) yield* requireCurrentAccess(client, organizationId, lifecycle);
|
|
54458
54756
|
return lifecycle;
|
|
54459
54757
|
}).pipe(Effect.timeoutOption(timeout * 1e3));
|
|
54460
54758
|
if (Option.isNone(observed)) return yield* Effect.fail(new CliFailure({
|
|
@@ -54538,45 +54836,89 @@ const requireWriteConfirmation = (supplied) => Effect.gen(function* () {
|
|
|
54538
54836
|
*
|
|
54539
54837
|
* The committed Organization ID is retained from the first state that carries
|
|
54540
54838
|
* one and never replaced, so a later failed lane keeps reporting the ID it was
|
|
54541
|
-
* committed to. `flush`
|
|
54542
|
-
*
|
|
54543
|
-
*
|
|
54839
|
+
* committed to. `flush` writes the queued lines first, then makes the ID durable
|
|
54840
|
+
* as this person's safe recovery handle, and only then resolves: the
|
|
54841
|
+
* command-scoped signer awaits it before forwarding a digest, so a failed or
|
|
54842
|
+
* cancelled write saves no handle and forwards no signature.
|
|
54544
54843
|
*/
|
|
54545
|
-
const makeOrganizationProgress = (mode, run) => {
|
|
54844
|
+
const makeOrganizationProgress = (mode, run, persist) => {
|
|
54546
54845
|
const queued = [];
|
|
54846
|
+
const held = [];
|
|
54847
|
+
const heldSet = /* @__PURE__ */ new Set();
|
|
54547
54848
|
const written = /* @__PURE__ */ new Set();
|
|
54548
54849
|
let committed = null;
|
|
54850
|
+
let persisted = null;
|
|
54851
|
+
let readyAllowed = false;
|
|
54549
54852
|
let failure = null;
|
|
54853
|
+
/** One command owns one committed Organization. A second ID is a defect. */
|
|
54854
|
+
const retain = (observed) => {
|
|
54855
|
+
if (committed === null) {
|
|
54856
|
+
committed = observed;
|
|
54857
|
+
return true;
|
|
54858
|
+
}
|
|
54859
|
+
if (committed === observed) return true;
|
|
54860
|
+
failure ??= new CliFailure({
|
|
54861
|
+
code: "CLI_DEFECT",
|
|
54862
|
+
message: "The onboarding stream reported a different Organization.",
|
|
54863
|
+
exitCode: 1,
|
|
54864
|
+
outcome: "failed"
|
|
54865
|
+
});
|
|
54866
|
+
return false;
|
|
54867
|
+
};
|
|
54868
|
+
/**
|
|
54869
|
+
* A ready line waits until the command confirms current access. Every other
|
|
54870
|
+
* line is progress evidence and goes out with the committed ID.
|
|
54871
|
+
*/
|
|
54872
|
+
const enqueue = (text, ready) => {
|
|
54873
|
+
if (written.has(text) || heldSet.has(text)) return;
|
|
54874
|
+
if (ready && !readyAllowed) {
|
|
54875
|
+
heldSet.add(text);
|
|
54876
|
+
held.push(text);
|
|
54877
|
+
return;
|
|
54878
|
+
}
|
|
54879
|
+
written.add(text);
|
|
54880
|
+
queued.push(text);
|
|
54881
|
+
};
|
|
54550
54882
|
const render = (state) => {
|
|
54551
54883
|
const organizationId = state.organizationId;
|
|
54552
54884
|
if (organizationId === null) return null;
|
|
54553
|
-
|
|
54554
|
-
if (committed === null) committed = observed;
|
|
54555
|
-
else if (committed !== observed) {
|
|
54556
|
-
failure ??= new CliFailure({
|
|
54557
|
-
code: "CLI_DEFECT",
|
|
54558
|
-
message: "The onboarding stream reported a different Organization.",
|
|
54559
|
-
exitCode: 1,
|
|
54560
|
-
outcome: "failed"
|
|
54561
|
-
});
|
|
54562
|
-
return null;
|
|
54563
|
-
}
|
|
54885
|
+
if (!retain(String(organizationId))) return null;
|
|
54564
54886
|
const status = state.lifecycle?.status;
|
|
54565
54887
|
if (status === void 0) return null;
|
|
54566
|
-
return
|
|
54567
|
-
|
|
54568
|
-
|
|
54569
|
-
|
|
54570
|
-
|
|
54888
|
+
return {
|
|
54889
|
+
text: `${mode === "json" ? JSON.stringify({
|
|
54890
|
+
type: "organization.lifecycle",
|
|
54891
|
+
organizationId: committed,
|
|
54892
|
+
status
|
|
54893
|
+
}) : `Organization ${JSON.stringify(committed)}: ${status}`}\n`,
|
|
54894
|
+
ready: status === "ready"
|
|
54895
|
+
};
|
|
54571
54896
|
};
|
|
54572
54897
|
return {
|
|
54573
54898
|
/** Synchronous by contract: the SDK publishes state from its own fiber. */
|
|
54574
54899
|
observe: (state) => {
|
|
54575
|
-
const
|
|
54576
|
-
if (
|
|
54577
|
-
|
|
54578
|
-
|
|
54579
|
-
|
|
54900
|
+
const line = render(state);
|
|
54901
|
+
if (line !== null) enqueue(line.text, line.ready);
|
|
54902
|
+
},
|
|
54903
|
+
/** One safe recovery line the command writes before it resumes a lane. */
|
|
54904
|
+
notice: (text) => enqueue(text, false),
|
|
54905
|
+
/** The final result ID joins the same one-Organization rule. */
|
|
54906
|
+
commit: (organizationId) => {
|
|
54907
|
+
retain(organizationId);
|
|
54908
|
+
},
|
|
54909
|
+
/** Release the ready lines once current access is confirmed. */
|
|
54910
|
+
allowReady: () => {
|
|
54911
|
+
readyAllowed = true;
|
|
54912
|
+
while (held.length > 0) {
|
|
54913
|
+
const text = held.shift();
|
|
54914
|
+
heldSet.delete(text);
|
|
54915
|
+
if (written.has(text)) continue;
|
|
54916
|
+
written.add(text);
|
|
54917
|
+
queued.push(text);
|
|
54918
|
+
}
|
|
54919
|
+
},
|
|
54920
|
+
/** The committed ID this command observed, or null when it observed none. */
|
|
54921
|
+
committed: () => committed,
|
|
54580
54922
|
failure: () => failure,
|
|
54581
54923
|
flush: async () => {
|
|
54582
54924
|
while (queued.length > 0) {
|
|
@@ -54589,6 +54931,13 @@ const makeOrganizationProgress = (mode, run) => {
|
|
|
54589
54931
|
}
|
|
54590
54932
|
queued.shift();
|
|
54591
54933
|
}
|
|
54934
|
+
if (committed !== null && persisted !== committed) try {
|
|
54935
|
+
await persist(committed);
|
|
54936
|
+
persisted = committed;
|
|
54937
|
+
} catch (cause) {
|
|
54938
|
+
failure = cause;
|
|
54939
|
+
throw cause;
|
|
54940
|
+
}
|
|
54592
54941
|
if (failure !== null) throw failure;
|
|
54593
54942
|
}
|
|
54594
54943
|
};
|
|
@@ -54603,15 +54952,63 @@ const confirmWrite = (preview) => Effect.gen(function* () {
|
|
|
54603
54952
|
yield* writeStderr(`${preview.lines.join("\n")}\n`);
|
|
54604
54953
|
if (!(yield* Prompt.confirm({ message: preview.question }).pipe(Effect.catch(() => Effect.succeed(false))))) return yield* Effect.fail(inputRequired("The write was not confirmed."));
|
|
54605
54954
|
});
|
|
54606
|
-
/**
|
|
54607
|
-
|
|
54955
|
+
/**
|
|
54956
|
+
* The Organization reads a terminal preview shows before it asks.
|
|
54957
|
+
*
|
|
54958
|
+
* A create preview also names the recovery handle this machine already holds, so
|
|
54959
|
+
* a person who confirms knows whether the command creates one more Organization
|
|
54960
|
+
* or continues the exact one their interrupted journey reached. The preview
|
|
54961
|
+
* reads that recorded Organization's lifecycle only when the requested handle
|
|
54962
|
+
* continues it; a different handle is a new creation, and the recorded
|
|
54963
|
+
* Organization must not block it.
|
|
54964
|
+
*/
|
|
54965
|
+
const organizationPreview = (client, organizationId, recovery, requestedHandle) => organizationId === null ? Effect.gen(function* () {
|
|
54608
54966
|
const organizations = yield* Effect.promise((signal) => client.orgs({ signal })).pipe(Effect.flatMap(resultValue));
|
|
54609
|
-
|
|
54610
|
-
|
|
54967
|
+
const lines = organizations.length === 0 ? ["Existing Organizations: none"] : [`Existing Organizations: ${organizations.length}`, ...organizations.map((organization) => ` ${JSON.stringify(String(organization.id))} ${JSON.stringify(organization.name)}`)];
|
|
54968
|
+
if (recovery === null) return {
|
|
54969
|
+
lines,
|
|
54970
|
+
recovery,
|
|
54971
|
+
recoveryLifecycle: null
|
|
54972
|
+
};
|
|
54973
|
+
const exact = recovery.organizationId;
|
|
54974
|
+
if (exact === null) return {
|
|
54975
|
+
lines: [...lines, `Recovery: a create for handle @${recovery.handle} is prepared on this machine.`],
|
|
54976
|
+
recovery,
|
|
54977
|
+
recoveryLifecycle: null
|
|
54978
|
+
};
|
|
54979
|
+
if (requestedHandle !== recovery.handle) return {
|
|
54980
|
+
lines: [...lines, `Recovery: Organization ${JSON.stringify(String(exact))} (@${recovery.handle}) is recorded on this machine.`],
|
|
54981
|
+
recovery,
|
|
54982
|
+
recoveryLifecycle: null
|
|
54983
|
+
};
|
|
54984
|
+
const lifecycle = yield* Effect.promise((signal) => client.org(exact).getLifecycle({ signal })).pipe(Effect.flatMap(resultValue));
|
|
54985
|
+
yield* requireCurrentAccess(client, exact, lifecycle);
|
|
54986
|
+
return {
|
|
54987
|
+
lines: [
|
|
54988
|
+
...lines,
|
|
54989
|
+
`Recovery: Organization ${JSON.stringify(String(exact))} (@${recovery.handle})`,
|
|
54990
|
+
`Current lifecycle: ${lifecycle.status}`
|
|
54991
|
+
],
|
|
54992
|
+
recovery,
|
|
54993
|
+
recoveryLifecycle: lifecycle
|
|
54994
|
+
};
|
|
54611
54995
|
}) : Effect.gen(function* () {
|
|
54612
|
-
return
|
|
54996
|
+
return {
|
|
54997
|
+
lines: [`Current lifecycle: ${(yield* Effect.promise((signal) => client.org(organizationId).getLifecycle({ signal })).pipe(Effect.flatMap(resultValue))).status}`],
|
|
54998
|
+
recovery,
|
|
54999
|
+
recoveryLifecycle: null
|
|
55000
|
+
};
|
|
54613
55001
|
});
|
|
54614
55002
|
/**
|
|
55003
|
+
* The confirmation question for a create. A matching recovery handle means the
|
|
55004
|
+
* command continues one exact Organization instead of creating another one.
|
|
55005
|
+
*/
|
|
55006
|
+
const createOrganizationQuestion = (handle, preview) => {
|
|
55007
|
+
if (preview.recovery === null || preview.recovery.organizationId === null) return "Create this Organization?";
|
|
55008
|
+
if (preview.recovery.handle !== handle) return "Create this Organization?";
|
|
55009
|
+
return preview.recoveryLifecycle?.status === "ready" ? "Return this ready Organization?" : "Resume this Organization's setup?";
|
|
55010
|
+
};
|
|
55011
|
+
/**
|
|
54615
55012
|
* Settle one exact Organization lifecycle read: poll every second with no
|
|
54616
55013
|
* overlapping read until the lane is ready or failed. The command's own deadline
|
|
54617
55014
|
* bounds this loop together with the write that preceded it.
|
|
@@ -54625,6 +55022,81 @@ const settleOrganizationLifecycle = (read) => Effect.gen(function* () {
|
|
|
54625
55022
|
return lifecycle;
|
|
54626
55023
|
});
|
|
54627
55024
|
/**
|
|
55025
|
+
* Resume the exact durable Organization lane through the SDK recovery owner, then
|
|
55026
|
+
* settle that exact lifecycle. It never creates an Organization and never
|
|
55027
|
+
* resubmits a recorded bootstrap submission.
|
|
55028
|
+
*/
|
|
55029
|
+
const resumeOrganizationSetup = (operation, scoped, deadline) => Effect.gen(function* () {
|
|
55030
|
+
yield* Effect.promise(() => scoped.retrySetup({ signal: deadline.signal })).pipe(Effect.flatMap(resultValue));
|
|
55031
|
+
const lifecycle = yield* settleOrganizationLifecycle(() => scoped.getLifecycle({ signal: deadline.signal }));
|
|
55032
|
+
yield* requireReadyLifecycle(operation, lifecycle);
|
|
55033
|
+
return lifecycle;
|
|
55034
|
+
});
|
|
55035
|
+
/**
|
|
55036
|
+
* A prepared handle owns the next durable Organization for that handle, so a
|
|
55037
|
+
* different handle would create a second Organization and leave the prepared lane
|
|
55038
|
+
* abandoned. The refusal names the exact handle to continue.
|
|
55039
|
+
*/
|
|
55040
|
+
const preparedJourneyRefusal = (handle) => new CliFailure({
|
|
55041
|
+
code: "WRONG_STATE",
|
|
55042
|
+
message: `An Organization creation is already prepared for handle @${handle}. Run capxul org create with the same handle to continue that exact Organization.`,
|
|
55043
|
+
exitCode: 2,
|
|
55044
|
+
outcome: "refused"
|
|
55045
|
+
});
|
|
55046
|
+
/**
|
|
55047
|
+
* Read how this create continues the person's Organization journey.
|
|
55048
|
+
*
|
|
55049
|
+
* The recovery handle is the only source of the exact identity, so a matching
|
|
55050
|
+
* handle resumes the Organization its lane reached and never creates another
|
|
55051
|
+
* one. A different handle creates one Organization only after the recorded
|
|
55052
|
+
* Organization reaches ready.
|
|
55053
|
+
*/
|
|
55054
|
+
const readCreateRoute = (client, recovery, handle, signal) => Effect.gen(function* () {
|
|
55055
|
+
if (recovery === null) return {
|
|
55056
|
+
kind: "create",
|
|
55057
|
+
continuesRecorded: false
|
|
55058
|
+
};
|
|
55059
|
+
if (recovery.organizationId === null) {
|
|
55060
|
+
if (recovery.handle === handle) return {
|
|
55061
|
+
kind: "create",
|
|
55062
|
+
continuesRecorded: true
|
|
55063
|
+
};
|
|
55064
|
+
return yield* Effect.fail(preparedJourneyRefusal(recovery.handle));
|
|
55065
|
+
}
|
|
55066
|
+
const lifecycle = yield* Effect.promise(() => client.org(recovery.organizationId).getLifecycle({ signal })).pipe(Effect.flatMap(resultValue));
|
|
55067
|
+
if (recovery.handle === handle) return {
|
|
55068
|
+
kind: "recover",
|
|
55069
|
+
organizationId: recovery.organizationId,
|
|
55070
|
+
handle: recovery.handle,
|
|
55071
|
+
lifecycle
|
|
55072
|
+
};
|
|
55073
|
+
if (lifecycle.status === "ready") return {
|
|
55074
|
+
kind: "create",
|
|
55075
|
+
continuesRecorded: false
|
|
55076
|
+
};
|
|
55077
|
+
return yield* Effect.fail(Errors.wrongState({
|
|
55078
|
+
method: CAPXUL_OPERATIONS.onboarding.completeOrganization,
|
|
55079
|
+
currentState: lifecycle.status,
|
|
55080
|
+
validStates: ["ready"],
|
|
55081
|
+
organizationId: String(recovery.organizationId)
|
|
55082
|
+
}));
|
|
55083
|
+
});
|
|
55084
|
+
/** One safe recovery line, in the same newline-delimited shape as the lifecycle. */
|
|
55085
|
+
const recoveryNotice = (mode, recovery) => mode === "json" ? `${JSON.stringify({
|
|
55086
|
+
type: "organization.recovery",
|
|
55087
|
+
organizationId: String(recovery.organizationId),
|
|
55088
|
+
handle: recovery.handle
|
|
55089
|
+
})}\n` : `Recovering Organization ${JSON.stringify(String(recovery.organizationId))} (@${recovery.handle}).\n`;
|
|
55090
|
+
/** A taken handle is refused before the backend writes any Organization row. */
|
|
55091
|
+
const refusedOrganizationHandle = (error) => isCapxulError(error) && error.code === "INVALID_INPUT" && error.details?.field === "handle";
|
|
55092
|
+
/** A write forwards no signature before its recovery handle exists; absence is a defect. */
|
|
55093
|
+
const organizationWriteDefect = () => new CliFailure({
|
|
55094
|
+
code: "CLI_DEFECT",
|
|
55095
|
+
message: "The Organization write lost its recovery handle.",
|
|
55096
|
+
exitCode: 1,
|
|
55097
|
+
outcome: "failed"
|
|
55098
|
+
});
|
|
55099
|
+
/**
|
|
54628
55100
|
* The one deadline for a write: it aborts the active SDK signal, so the waiting
|
|
54629
55101
|
* command stops and the signer forwards nothing further, while the durable
|
|
54630
55102
|
* backend lane keeps running and stays resumable by `org retry`.
|
|
@@ -54734,10 +55206,15 @@ const requireReadyLifecycle = (operation, lifecycle) => lifecycle.status === "re
|
|
|
54734
55206
|
* explicit create intent, then settle the exact Organization lifecycle. This
|
|
54735
55207
|
* never saves a read default and never returns an unrelated ready Organization.
|
|
54736
55208
|
*
|
|
55209
|
+
* A create that meets this person's prepared Organization journey continues that
|
|
55210
|
+
* exact Organization: the same handle resumes the lane its ID reached, and a
|
|
55211
|
+
* different handle refuses while that lane is unsettled. Neither path creates a
|
|
55212
|
+
* replacement Organization or treasury.
|
|
55213
|
+
*
|
|
54737
55214
|
* One deadline covers the write and its settlement. At the deadline the active
|
|
54738
55215
|
* SDK signal is aborted: the command stops waiting, forwards no further
|
|
54739
55216
|
* signature, and returns the typed wait failure, while the durable backend lane
|
|
54740
|
-
* keeps running and stays resumable by `org retry`.
|
|
55217
|
+
* keeps running and stays resumable by the same create or by `org retry`.
|
|
54741
55218
|
*/
|
|
54742
55219
|
const createOrganization = (config, input) => Effect.gen(function* () {
|
|
54743
55220
|
const operation = CAPXUL_OPERATIONS.org.create;
|
|
@@ -54747,50 +55224,99 @@ const createOrganization = (config, input) => Effect.gen(function* () {
|
|
|
54747
55224
|
const opened = yield* openAuthenticatedSession(config, input.email);
|
|
54748
55225
|
if (interactivelyConfirm) {
|
|
54749
55226
|
const preview = yield* restoreVerifiedClient(config, opened, { requirement: "none" });
|
|
54750
|
-
const
|
|
55227
|
+
const recovery = yield* readOrganizationRecovery(config, preview.session.authUserId);
|
|
55228
|
+
const resolved = yield* organizationPreview(preview.client, null, recovery, draft.handle);
|
|
54751
55229
|
yield* confirmWrite({
|
|
54752
55230
|
lines: [
|
|
54753
55231
|
`Create Organization ${JSON.stringify(draft.name)} (@${draft.handle}, ${draft.country})`,
|
|
54754
55232
|
...draft.bio === void 0 ? [] : [`Bio: ${JSON.stringify(draft.bio)}`],
|
|
54755
55233
|
...draft.size === void 0 ? [] : [`Size: ${JSON.stringify(draft.size)}`],
|
|
54756
|
-
...resolved
|
|
55234
|
+
...resolved.lines
|
|
54757
55235
|
],
|
|
54758
|
-
question:
|
|
55236
|
+
question: createOrganizationQuestion(draft.handle, resolved)
|
|
54759
55237
|
});
|
|
54760
55238
|
}
|
|
54761
55239
|
const context = yield* Effect.context();
|
|
54762
55240
|
const run = Effect.runPromiseWith(context);
|
|
54763
55241
|
const runtime = yield* CliRuntime;
|
|
54764
|
-
const
|
|
55242
|
+
const progressRef = yield* Ref.make(null);
|
|
54765
55243
|
const deadline = new AbortController();
|
|
54766
55244
|
const openfortSignerFactory = yield* organizationWriteSigner(config, opened, run, async () => {
|
|
54767
55245
|
const cancelled = cancelledWrite(operation, deadline.signal);
|
|
54768
55246
|
if (cancelled !== null) throw cancelled;
|
|
54769
|
-
await
|
|
55247
|
+
const live = await run(Ref.get(progressRef));
|
|
55248
|
+
if (live === null) throw organizationWriteDefect();
|
|
55249
|
+
await live.flush();
|
|
54770
55250
|
const stopped = cancelledWrite(operation, deadline.signal);
|
|
54771
55251
|
if (stopped !== null) throw stopped;
|
|
54772
55252
|
});
|
|
54773
|
-
const { client } = yield* restoreVerifiedClient(config, opened, {
|
|
55253
|
+
const { client, session, stillAuthenticated } = yield* restoreVerifiedClient(config, opened, {
|
|
54774
55254
|
requirement: "deployed",
|
|
54775
55255
|
openfortSignerFactory
|
|
54776
55256
|
});
|
|
54777
|
-
const
|
|
54778
|
-
const
|
|
54779
|
-
const
|
|
54780
|
-
|
|
54781
|
-
|
|
54782
|
-
|
|
55257
|
+
const recovery = yield* openOrganizationRecovery(config, session.authUserId, stillAuthenticated);
|
|
55258
|
+
const persistRecovery = (organizationId) => Effect.gen(function* () {
|
|
55259
|
+
const committed = yield* onboardingOrganizationId(organizationId);
|
|
55260
|
+
yield* recovery.save({
|
|
55261
|
+
handle: draft.handle,
|
|
55262
|
+
organizationId: committed
|
|
55263
|
+
});
|
|
55264
|
+
});
|
|
55265
|
+
const progress = makeOrganizationProgress(runtime.mode, run, (organizationId) => run(persistRecovery(organizationId)));
|
|
55266
|
+
yield* Ref.set(progressRef, progress);
|
|
55267
|
+
const flushProgress = Effect.gen(function* () {
|
|
54783
55268
|
yield* Effect.tryPromise({
|
|
54784
55269
|
try: progress.flush,
|
|
54785
55270
|
catch: (cause) => cause
|
|
54786
55271
|
}).pipe(Effect.catch(() => Effect.void));
|
|
54787
55272
|
const localFailure = progress.failure();
|
|
54788
55273
|
if (localFailure !== null && localFailure !== void 0) return yield* Effect.fail(localFailure);
|
|
54789
|
-
|
|
55274
|
+
});
|
|
55275
|
+
const settled = yield* Effect.gen(function* () {
|
|
55276
|
+
const route = yield* readCreateRoute(client, recovery.current(), draft.handle, deadline.signal);
|
|
55277
|
+
if (route.kind === "recover") {
|
|
55278
|
+
if (route.lifecycle.status === "ready") {
|
|
55279
|
+
yield* requireCurrentAccess(client, route.organizationId, route.lifecycle);
|
|
55280
|
+
progress.allowReady();
|
|
55281
|
+
return {
|
|
55282
|
+
organizationId: route.organizationId,
|
|
55283
|
+
lifecycle: route.lifecycle
|
|
55284
|
+
};
|
|
55285
|
+
}
|
|
55286
|
+
progress.notice(recoveryNotice(runtime.mode, route));
|
|
55287
|
+
yield* flushProgress;
|
|
55288
|
+
const lifecycle = yield* resumeOrganizationSetup(operation, client.org(route.organizationId), deadline);
|
|
55289
|
+
yield* requireCurrentAccess(client, route.organizationId, lifecycle);
|
|
55290
|
+
progress.allowReady();
|
|
55291
|
+
return {
|
|
55292
|
+
organizationId: route.organizationId,
|
|
55293
|
+
lifecycle
|
|
55294
|
+
};
|
|
55295
|
+
}
|
|
55296
|
+
yield* recovery.save({
|
|
55297
|
+
handle: draft.handle,
|
|
55298
|
+
organizationId: null
|
|
55299
|
+
});
|
|
55300
|
+
const unsubscribe = client.onboarding.subscribeOrganization(progress.observe);
|
|
55301
|
+
const completed = yield* Effect.promise(() => client.onboarding.completeOrganization(draft, {
|
|
55302
|
+
intent: "create",
|
|
55303
|
+
signal: deadline.signal
|
|
55304
|
+
})).pipe(Effect.ensuring(Effect.sync(unsubscribe)));
|
|
55305
|
+
if (!completed.ok) {
|
|
55306
|
+
yield* flushProgress;
|
|
55307
|
+
if (refusedOrganizationHandle(completed.error) && progress.committed() === null) yield* recovery.release();
|
|
55308
|
+
return yield* Effect.fail(completed.error);
|
|
55309
|
+
}
|
|
55310
|
+
const onboarding = completed.value;
|
|
54790
55311
|
const organizationId = yield* onboardingOrganizationId(onboarding.organizationId);
|
|
55312
|
+
progress.commit(String(organizationId));
|
|
55313
|
+
yield* flushProgress;
|
|
54791
55314
|
const scoped = client.org(organizationId);
|
|
54792
55315
|
const lifecycle = yield* settleOrganizationLifecycle(() => scoped.getLifecycle({ signal: deadline.signal }));
|
|
54793
55316
|
yield* requireReadyLifecycle(operation, lifecycle);
|
|
55317
|
+
if (route.continuesRecorded) yield* requireCurrentAccess(client, organizationId, lifecycle);
|
|
55318
|
+
progress.allowReady();
|
|
55319
|
+
yield* flushProgress;
|
|
54794
55320
|
return {
|
|
54795
55321
|
organizationId,
|
|
54796
55322
|
lifecycle
|
|
@@ -54835,9 +55361,9 @@ const retryOrganization = (config, input) => Effect.gen(function* () {
|
|
|
54835
55361
|
const opened = yield* openAuthenticatedSession(config, input.email);
|
|
54836
55362
|
if (interactivelyConfirm) {
|
|
54837
55363
|
const preview = yield* restoreVerifiedClient(config, opened, { requirement: "none" });
|
|
54838
|
-
const resolved = yield*
|
|
55364
|
+
const resolved = yield* organizationPreview(preview.client, organizationId, null);
|
|
54839
55365
|
yield* confirmWrite({
|
|
54840
|
-
lines: [`Resume setup for Organization ${JSON.stringify(String(organizationId))}`, ...resolved],
|
|
55366
|
+
lines: [`Resume setup for Organization ${JSON.stringify(String(organizationId))}`, ...resolved.lines],
|
|
54841
55367
|
question: "Resume this Organization's setup?"
|
|
54842
55368
|
});
|
|
54843
55369
|
}
|
|
@@ -54853,12 +55379,7 @@ const retryOrganization = (config, input) => Effect.gen(function* () {
|
|
|
54853
55379
|
openfortSignerFactory
|
|
54854
55380
|
});
|
|
54855
55381
|
const scoped = client.org(organizationId);
|
|
54856
|
-
const settled = yield* Effect.
|
|
54857
|
-
yield* Effect.promise(() => scoped.retrySetup({ signal: deadline.signal })).pipe(Effect.flatMap(resultValue));
|
|
54858
|
-
const lifecycle = yield* settleOrganizationLifecycle(() => scoped.getLifecycle({ signal: deadline.signal }));
|
|
54859
|
-
yield* requireReadyLifecycle(operation, lifecycle);
|
|
54860
|
-
return lifecycle;
|
|
54861
|
-
}).pipe(Effect.onInterrupt(() => Effect.sync(() => deadline.abort()))).pipe(Effect.timeoutOption(timeout * 1e3));
|
|
55382
|
+
const settled = yield* resumeOrganizationSetup(operation, scoped, deadline).pipe(Effect.onInterrupt(() => Effect.sync(() => deadline.abort()))).pipe(Effect.timeoutOption(timeout * 1e3));
|
|
54862
55383
|
if (Option.isNone(settled)) return yield* Effect.fail(organizationWaitTimeout());
|
|
54863
55384
|
return {
|
|
54864
55385
|
data: {
|
|
@@ -55926,7 +56447,7 @@ const create = Command.make("create", {
|
|
|
55926
56447
|
size: Option.getOrUndefined(size),
|
|
55927
56448
|
confirm,
|
|
55928
56449
|
timeoutSeconds
|
|
55929
|
-
}))).pipe(Command.annotate(CliCommandMetadata, metadata(CAPXUL_OPERATIONS.org.create)), Command.withDescription("Create one Organization
|
|
56450
|
+
}))).pipe(Command.annotate(CliCommandMetadata, metadata(CAPXUL_OPERATIONS.org.create)), Command.withDescription("Create one Organization, or resume the exact one this machine recorded"));
|
|
55930
56451
|
const retry = Command.make("retry", {
|
|
55931
56452
|
...orgScope,
|
|
55932
56453
|
email: emailFlag,
|
|
@@ -55941,7 +56462,7 @@ const retry = Command.make("retry", {
|
|
|
55941
56462
|
confirm,
|
|
55942
56463
|
timeoutSeconds
|
|
55943
56464
|
}))).pipe(Command.annotate(CliCommandMetadata, metadata(CAPXUL_OPERATIONS.org.retry)), Command.withDescription("Resume this exact Organization setup lane; never creates one"));
|
|
55944
|
-
const org = Command.make("org").pipe(Command.annotate(CliCommandMetadata, metadata("org")), Command.withDescription("Organization reads, writes, and local read defaults"), Command.withSubcommands([
|
|
56465
|
+
const org = Command.make("org").pipe(Command.annotate(CliCommandMetadata, metadata("org")), Command.withDescription("Organization reads, writes, recovery, and local read defaults"), Command.withSubcommands([
|
|
55945
56466
|
list,
|
|
55946
56467
|
use,
|
|
55947
56468
|
status,
|
|
@@ -55955,29 +56476,6 @@ const org = Command.make("org").pipe(Command.annotate(CliCommandMetadata, metada
|
|
|
55955
56476
|
retry
|
|
55956
56477
|
]));
|
|
55957
56478
|
//#endregion
|
|
55958
|
-
//#region src/runtime/format.ts
|
|
55959
|
-
const jsonFlag = Flag.boolean("json").pipe(Flag.withDefault(false), Flag.withDescription("Print a versioned JSON result"));
|
|
55960
|
-
const JsonOutput = GlobalFlag.setting("json")({ flag: jsonFlag });
|
|
55961
|
-
/** Negotiate only the global format before native CLI error rendering.
|
|
55962
|
-
* Native Flag decoding remains the value authority; the command runner parses everything else.
|
|
55963
|
-
*/
|
|
55964
|
-
const outputMode = (args) => Effect.gen(function* () {
|
|
55965
|
-
const terminator = args.indexOf("--");
|
|
55966
|
-
const flags = terminator < 0 ? args : args.slice(0, terminator);
|
|
55967
|
-
const values = [];
|
|
55968
|
-
for (const [index, arg] of flags.entries()) if (arg === "--no-json") values.push("false");
|
|
55969
|
-
else if (arg.startsWith("--json=")) values.push(arg.slice(7));
|
|
55970
|
-
else if (arg === "--json") {
|
|
55971
|
-
const next = flags[index + 1];
|
|
55972
|
-
const value = next === void 0 ? "true" : yield* Primitive.boolean.parse(next).pipe(Effect.as(next), Effect.catch(() => Effect.succeed("true")));
|
|
55973
|
-
values.push(value);
|
|
55974
|
-
}
|
|
55975
|
-
return yield* jsonFlag.parse({
|
|
55976
|
-
flags: { json: values },
|
|
55977
|
-
arguments: []
|
|
55978
|
-
}).pipe(Effect.map(([, json]) => json ? "json" : "human"), Effect.catch(() => Effect.succeed(values.includes("true") ? "json" : "human")));
|
|
55979
|
-
});
|
|
55980
|
-
//#endregion
|
|
55981
56479
|
//#region src/command.ts
|
|
55982
56480
|
const command = Command.make("capxul").pipe(Command.annotate(CliCommandMetadata, {
|
|
55983
56481
|
name: "capxul",
|
|
@@ -56103,6 +56601,7 @@ const runCli = (platform) => Effect.uninterruptibleMask((restore) => Effect.gen(
|
|
|
56103
56601
|
const ci = yield* Config.string("CI").pipe(Config.withDefault("false"), Effect.map((value) => value !== "" && value !== "false" && value !== "0"), Effect.catch(() => Effect.succeed(false)));
|
|
56104
56602
|
const runtime = {
|
|
56105
56603
|
mode,
|
|
56604
|
+
args,
|
|
56106
56605
|
current,
|
|
56107
56606
|
invocationId,
|
|
56108
56607
|
observation,
|