@nebulr-group/bridge-cli 0.4.4 → 0.5.1-beta.0
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 +67 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/auth/logout.command.d.ts +10 -3
- package/dist/commands/auth/logout.command.d.ts.map +1 -1
- package/dist/commands/auth/logout.command.js +58 -18
- package/dist/commands/auth/logout.command.js.map +1 -1
- package/dist/commands/auth/status.command.d.ts +4 -0
- package/dist/commands/auth/status.command.d.ts.map +1 -1
- package/dist/commands/auth/status.command.js +50 -19
- package/dist/commands/auth/status.command.js.map +1 -1
- package/dist/commands/auth/use.command.d.ts +17 -0
- package/dist/commands/auth/use.command.d.ts.map +1 -0
- package/dist/commands/auth/use.command.js +47 -0
- package/dist/commands/auth/use.command.js.map +1 -0
- package/dist/commands/auth.command.d.ts +1 -0
- package/dist/commands/auth.command.d.ts.map +1 -1
- package/dist/commands/auth.command.js +3 -0
- package/dist/commands/auth.command.js.map +1 -1
- package/dist/commands/setup.command.d.ts +60 -0
- package/dist/commands/setup.command.d.ts.map +1 -1
- package/dist/commands/setup.command.js +129 -28
- package/dist/commands/setup.command.js.map +1 -1
- package/dist/commands/stripe.command.d.ts +36 -0
- package/dist/commands/stripe.command.d.ts.map +1 -1
- package/dist/commands/stripe.command.js +70 -14
- package/dist/commands/stripe.command.js.map +1 -1
- package/dist/config.d.ts +20 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +120 -32
- package/dist/config.js.map +1 -1
- package/dist/credentials.d.ts +68 -5
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +202 -11
- package/dist/credentials.js.map +1 -1
- package/dist/prompts/billing/master.md +3 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.command.d.ts","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.command.d.ts","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAuB5C,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWhB,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,aAAa,CAAC;AAS9D,sEAAsE;AACtE,qBAAa,UAAW,SAAQ,KAAK;IACN,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAtC,OAAO,EAAE,MAAM,EAAW,IAAI,EAAE,MAAM;CAInD;AASD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe;;;;;GA4CnD;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB;;;;;GA+BvE;AAKD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyC5D"}
|
|
@@ -1,25 +1,133 @@
|
|
|
1
|
+
import { Option } from 'commander';
|
|
1
2
|
import { getManagementClient } from '../config.js';
|
|
2
3
|
import { outputSuccess, outputError } from '../output.js';
|
|
4
|
+
/*
|
|
5
|
+
* TBP-663 — `setup sso` and `setup communication` talk to the app service
|
|
6
|
+
* (`app.updateCredentials` / `app.update` / `app.get`) directly instead of
|
|
7
|
+
* `workflows.setupSSO` / `workflows.setupCommunication`.
|
|
8
|
+
*
|
|
9
|
+
* Why not the workflows: in auth-core <= 0.7.0-beta.0 they send fields the
|
|
10
|
+
* API rejects under forbidNonWhitelisted — azure as `azureClientId`, saml/oidc
|
|
11
|
+
* as `samlMetadataUrl` / `oidcDiscoveryUrl`, email as `sendgridApiKey` — so
|
|
12
|
+
* both commands 400'd. auth-core main (#34) fixed them but changed their
|
|
13
|
+
* signatures and throws on the options this CLI used to pass. The app service
|
|
14
|
+
* has the same shape in both, so calling it with the server's own field names
|
|
15
|
+
* behaves identically whichever auth-core is installed.
|
|
16
|
+
*
|
|
17
|
+
* Field names are the server's: bridge-api
|
|
18
|
+
* microservices/account/nebulr-api/app/dto/update-credentials-request.dto.ts
|
|
19
|
+
* and update-app-request.dto.ts. Same table as auth-core's `setupSSO` and the
|
|
20
|
+
* MCP `setup_sso` tool. `setup-command.test.ts` pins them to those DTOs.
|
|
21
|
+
*/
|
|
22
|
+
export const SSO_PROVIDERS = {
|
|
23
|
+
google: { clientId: 'googleClientId', clientSecret: 'googleClientSecret', enable: 'googleSsoEnabled' },
|
|
24
|
+
github: { clientId: 'githubClientId', clientSecret: 'githubClientSecret', enable: 'githubSsoEnabled' },
|
|
25
|
+
linkedin: { clientId: 'linkedinClientId', clientSecret: 'linkedinClientSecret', enable: 'linkedinSsoEnabled' },
|
|
26
|
+
facebook: { clientId: 'facebookClientId', clientSecret: 'facebookClientSecret', enable: 'facebookSsoEnabled' },
|
|
27
|
+
azure: {
|
|
28
|
+
clientId: 'microsoftAzureADClientId',
|
|
29
|
+
clientSecret: 'microsoftAzureADClientSecret',
|
|
30
|
+
tenantId: 'microsoftAzureADTenantId',
|
|
31
|
+
enable: 'azureAdSsoEnabled',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
const SUPPORTED = Object.keys(SSO_PROVIDERS).join(', ');
|
|
35
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
36
|
+
/** Carries a machine-readable `code`, which `outputError` reports. */
|
|
37
|
+
export class SetupError extends Error {
|
|
38
|
+
code;
|
|
39
|
+
constructor(message, code) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.name = 'SetupError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const NOT_SAVED = ' Nothing was saved.';
|
|
46
|
+
const invalid = (message) => new SetupError(message + NOT_SAVED, 'INVALID_ARGUMENT');
|
|
47
|
+
function isSupported(provider) {
|
|
48
|
+
return Object.prototype.hasOwnProperty.call(SSO_PROVIDERS, provider);
|
|
49
|
+
}
|
|
50
|
+
export async function setupSso(opts) {
|
|
51
|
+
const provider = String(opts.provider).toLowerCase();
|
|
52
|
+
if (!isSupported(provider)) {
|
|
53
|
+
throw new SetupError(provider === 'saml' || provider === 'oidc'
|
|
54
|
+
? `SSO provider "${provider}" is not supported by the Bridge API: it has no fields for SAML or OIDC ` +
|
|
55
|
+
`connections. Set them up in the Bridge admin UI. Supported here: ${SUPPORTED}.${NOT_SAVED}`
|
|
56
|
+
: `Unknown SSO provider "${provider}". Supported: ${SUPPORTED}.${NOT_SAVED}`, 'SSO_PROVIDER_NOT_SUPPORTED');
|
|
57
|
+
}
|
|
58
|
+
for (const [flag, value] of [['--metadata-url', opts.metadataUrl], ['--discovery-url', opts.discoveryUrl]]) {
|
|
59
|
+
if (value !== undefined) {
|
|
60
|
+
throw new SetupError(`${flag} was removed: the Bridge API has no field for it (SAML and OIDC are set up in the Bridge admin UI).${NOT_SAVED}`, 'SSO_PROVIDER_NOT_SUPPORTED');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!opts.clientId || !opts.clientSecret) {
|
|
64
|
+
throw invalid('--client-id and --client-secret are required (from the provider console).');
|
|
65
|
+
}
|
|
66
|
+
const fields = SSO_PROVIDERS[provider];
|
|
67
|
+
const credentials = {
|
|
68
|
+
[fields.clientId]: opts.clientId,
|
|
69
|
+
[fields.clientSecret]: opts.clientSecret,
|
|
70
|
+
};
|
|
71
|
+
if ('tenantId' in fields) {
|
|
72
|
+
if (!opts.tenantId) {
|
|
73
|
+
throw invalid('azure needs --tenant-id: the Directory (tenant) ID from the Entra ID app registration.');
|
|
74
|
+
}
|
|
75
|
+
credentials[fields.tenantId] = opts.tenantId;
|
|
76
|
+
}
|
|
77
|
+
else if (opts.tenantId !== undefined) {
|
|
78
|
+
throw invalid(`--tenant-id applies to azure only, not "${provider}".`);
|
|
79
|
+
}
|
|
80
|
+
const mgmt = getManagementClient();
|
|
81
|
+
await mgmt.app.updateCredentials(credentials);
|
|
82
|
+
const enable = { [fields.enable]: true };
|
|
83
|
+
await mgmt.app.update(enable);
|
|
84
|
+
const app = await mgmt.app.get();
|
|
85
|
+
return { provider, enabled: true, callbackUrl: app.defaultCallbackUri, app };
|
|
86
|
+
}
|
|
87
|
+
export async function setupCommunication(opts) {
|
|
88
|
+
if (opts.provider !== undefined || opts.apiKey !== undefined) {
|
|
89
|
+
throw new SetupError('--provider and --api-key were removed: Bridge sends all email through its own provider, so there is ' +
|
|
90
|
+
'no provider or API key to configure (the Bridge API has never accepted one). ' +
|
|
91
|
+
`Pass only --from-address and/or --from-name.${NOT_SAVED}`, 'EMAIL_PROVIDER_NOT_SUPPORTED');
|
|
92
|
+
}
|
|
93
|
+
const { fromAddress, fromName } = opts;
|
|
94
|
+
if (fromAddress === undefined && fromName === undefined) {
|
|
95
|
+
throw invalid('Nothing to set: pass --from-address, --from-name, or both.');
|
|
96
|
+
}
|
|
97
|
+
if (fromAddress !== undefined && !EMAIL.test(fromAddress)) {
|
|
98
|
+
throw invalid('--from-address must be an email address, e.g. "no-reply@example.com".');
|
|
99
|
+
}
|
|
100
|
+
if (fromName !== undefined && fromName.trim() === '') {
|
|
101
|
+
throw invalid('--from-name must not be empty.');
|
|
102
|
+
}
|
|
103
|
+
const update = {};
|
|
104
|
+
if (fromAddress !== undefined)
|
|
105
|
+
update.emailSenderEmail = fromAddress;
|
|
106
|
+
if (fromName !== undefined)
|
|
107
|
+
update.emailSenderName = fromName;
|
|
108
|
+
const app = await getManagementClient().app.update(update);
|
|
109
|
+
return {
|
|
110
|
+
configured: true,
|
|
111
|
+
emailSenderEmail: app.emailSenderEmail ?? null,
|
|
112
|
+
emailSenderName: app.emailSenderName ?? null,
|
|
113
|
+
app,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Accepted only to refuse it with an explanation instead of commander's bare "unknown option". */
|
|
117
|
+
const removed = (flags, description) => new Option(flags, description).hideHelp();
|
|
3
118
|
export function registerSetupCommands(program) {
|
|
4
119
|
const setup = program.command('setup').description('Run setup workflows (multi-step operations)');
|
|
5
120
|
setup.command('sso')
|
|
6
|
-
.description('Enable an SSO provider and get callback URL')
|
|
7
|
-
.requiredOption('--provider <provider>',
|
|
8
|
-
.
|
|
9
|
-
.
|
|
10
|
-
.option('--
|
|
11
|
-
.
|
|
121
|
+
.description('Enable an SSO login provider and get the callback URL to register with it')
|
|
122
|
+
.requiredOption('--provider <provider>', `SSO provider: ${SUPPORTED} (SAML/OIDC are set up in the Bridge admin UI)`)
|
|
123
|
+
.requiredOption('--client-id <id>', 'OAuth client ID from the provider console')
|
|
124
|
+
.requiredOption('--client-secret <secret>', 'OAuth client secret from the provider console')
|
|
125
|
+
.option('--tenant-id <id>', 'azure only, required: Entra ID Directory (tenant) ID (not a Bridge tenant)')
|
|
126
|
+
.addOption(removed('--metadata-url <url>', 'removed: SAML is not supported by the Bridge API'))
|
|
127
|
+
.addOption(removed('--discovery-url <url>', 'removed: OIDC is not supported by the Bridge API'))
|
|
12
128
|
.action(async (opts) => {
|
|
13
129
|
try {
|
|
14
|
-
outputSuccess(await
|
|
15
|
-
provider: opts.provider,
|
|
16
|
-
config: {
|
|
17
|
-
clientId: opts.clientId,
|
|
18
|
-
clientSecret: opts.clientSecret,
|
|
19
|
-
metadataUrl: opts.metadataUrl,
|
|
20
|
-
discoveryUrl: opts.discoveryUrl,
|
|
21
|
-
},
|
|
22
|
-
}));
|
|
130
|
+
outputSuccess(await setupSso(opts));
|
|
23
131
|
}
|
|
24
132
|
catch (err) {
|
|
25
133
|
outputError(err);
|
|
@@ -41,21 +149,14 @@ export function registerSetupCommands(program) {
|
|
|
41
149
|
}
|
|
42
150
|
});
|
|
43
151
|
setup.command('communication')
|
|
44
|
-
.description('
|
|
45
|
-
.
|
|
46
|
-
.
|
|
47
|
-
.
|
|
48
|
-
.
|
|
152
|
+
.description("Set the sender name and address of your app's emails (sent through Bridge's own email provider)")
|
|
153
|
+
.option('--from-address <email>', 'Sender email address. A new address is verified by email before Bridge uses it')
|
|
154
|
+
.option('--from-name <name>', 'Sender display name')
|
|
155
|
+
.addOption(removed('--provider <provider>', 'removed: email goes through Bridge\'s own provider'))
|
|
156
|
+
.addOption(removed('--api-key <key>', 'removed: email goes through Bridge\'s own provider'))
|
|
49
157
|
.action(async (opts) => {
|
|
50
158
|
try {
|
|
51
|
-
outputSuccess(await
|
|
52
|
-
provider: opts.provider,
|
|
53
|
-
config: {
|
|
54
|
-
apiKey: opts.apiKey,
|
|
55
|
-
fromAddress: opts.fromAddress,
|
|
56
|
-
fromName: opts.fromName,
|
|
57
|
-
},
|
|
58
|
-
}));
|
|
159
|
+
outputSuccess(await setupCommunication(opts));
|
|
59
160
|
}
|
|
60
161
|
catch (err) {
|
|
61
162
|
outputError(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.command.js","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.command.js","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,39 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
export interface ServerCredentialsState {
|
|
3
|
+
stripeCredentialsAdded: boolean;
|
|
4
|
+
/** TBP-658. Optional: older account APIs do not send it. */
|
|
5
|
+
stripeWebhookConfigured?: boolean;
|
|
6
|
+
azureMarketplaceCredentialsAdded: boolean;
|
|
7
|
+
azureAdSsoCredentialsAdded: boolean;
|
|
8
|
+
linkedinSsoCredentialsAdded: boolean;
|
|
9
|
+
googleSsoCredentialsAdded: boolean;
|
|
10
|
+
appleSsoCredentialsAdded: boolean;
|
|
11
|
+
githubSsoCredentialsAdded: boolean;
|
|
12
|
+
facebookSsoCredentialsAdded: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Webhook health fields on the app response (TBP-658). All optional: older APIs omit them. */
|
|
15
|
+
export interface ServerAppStripeState {
|
|
16
|
+
stripeSetupStatus?: 'pending' | 'completed' | 'failed' | null;
|
|
17
|
+
stripeSetupError?: string | null;
|
|
18
|
+
stripeLastWebhookAt?: string | null;
|
|
19
|
+
stripeLastWebhookRejectedAt?: string | null;
|
|
20
|
+
stripeLastWebhookRejectionReason?: string | null;
|
|
21
|
+
}
|
|
22
|
+
/** `true` only when the server confirms stored Stripe keys; never undefined. */
|
|
23
|
+
export declare function stripeConnectedFrom(state: unknown): boolean;
|
|
24
|
+
/** `null` when the API does not report it (older account API), so absence is not read as broken. */
|
|
25
|
+
export declare function stripeWebhookConfiguredFrom(state: unknown): boolean | null;
|
|
26
|
+
export interface StripeStatus {
|
|
27
|
+
connected: boolean;
|
|
28
|
+
webhookConfigured: boolean | null;
|
|
29
|
+
webhookHealthy: boolean | null;
|
|
30
|
+
lastWebhookAt: string | null;
|
|
31
|
+
lastWebhookRejectedAt: string | null;
|
|
32
|
+
lastWebhookRejectionReason: string | null;
|
|
33
|
+
setupStatus: string | null;
|
|
34
|
+
setupError: string | null;
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function stripeStatusFrom(state: unknown, app: unknown): StripeStatus;
|
|
2
38
|
export declare function registerStripeCommands(program: Command): void;
|
|
3
39
|
//# sourceMappingURL=stripe.command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stripe.command.d.ts","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"stripe.command.d.ts","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,MAAM,WAAW,sBAAsB;IACrC,sBAAsB,EAAE,OAAO,CAAC;IAChC,4DAA4D;IAC5D,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gCAAgC,EAAE,OAAO,CAAC;IAC1C,0BAA0B,EAAE,OAAO,CAAC;IACpC,2BAA2B,EAAE,OAAO,CAAC;IACrC,yBAAyB,EAAE,OAAO,CAAC;IACnC,wBAAwB,EAAE,OAAO,CAAC;IAClC,yBAAyB,EAAE,OAAO,CAAC;IACnC,2BAA2B,EAAE,OAAO,CAAC;CACtC;AAED,+FAA+F;AAC/F,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC9D,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,gCAAgC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClD;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED,oGAAoG;AACpG,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAG1E;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAyBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,YAAY,CAuB3E;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqC7D"}
|
|
@@ -1,18 +1,71 @@
|
|
|
1
1
|
import { getManagementClient } from '../config.js';
|
|
2
2
|
import { outputSuccess, outputError } from '../output.js';
|
|
3
|
+
/** `true` only when the server confirms stored Stripe keys; never undefined. */
|
|
4
|
+
export function stripeConnectedFrom(state) {
|
|
5
|
+
return state?.stripeCredentialsAdded === true;
|
|
6
|
+
}
|
|
7
|
+
/** `null` when the API does not report it (older account API), so absence is not read as broken. */
|
|
8
|
+
export function stripeWebhookConfiguredFrom(state) {
|
|
9
|
+
const value = state?.stripeWebhookConfigured;
|
|
10
|
+
return typeof value === 'boolean' ? value : null;
|
|
11
|
+
}
|
|
12
|
+
const CONNECT_HINT = 'run: bridge stripe connect --secret-key sk_... --publishable-key pk_...';
|
|
13
|
+
const REPAIR_HINT = 'Checkout still works, but subscription changes, cancellations and payment failures are not ' +
|
|
14
|
+
'reaching Bridge. Repair: re-run `bridge setup payments` with the same keys, or use "Repair ' +
|
|
15
|
+
'webhook" in the Bridge admin payment settings.';
|
|
16
|
+
/*
|
|
17
|
+
* Mirrors bridge-api's `AppService._stripeWebhookUnhealthy` plus the two
|
|
18
|
+
* explicit failure signals. `null` = the API gave us nothing to judge by.
|
|
19
|
+
*/
|
|
20
|
+
function webhookHealthy(configured, app) {
|
|
21
|
+
if (configured === false || app.stripeSetupStatus === 'failed')
|
|
22
|
+
return false;
|
|
23
|
+
const rejectedAt = app.stripeLastWebhookRejectedAt;
|
|
24
|
+
if (rejectedAt) {
|
|
25
|
+
const lastAt = app.stripeLastWebhookAt;
|
|
26
|
+
if (!lastAt || new Date(rejectedAt).getTime() > new Date(lastAt).getTime())
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return configured;
|
|
30
|
+
}
|
|
31
|
+
export function stripeStatusFrom(state, app) {
|
|
32
|
+
const a = (app ?? {});
|
|
33
|
+
const connected = stripeConnectedFrom(state);
|
|
34
|
+
const webhookConfigured = stripeWebhookConfiguredFrom(state);
|
|
35
|
+
const healthy = connected ? webhookHealthy(webhookConfigured, a) : null;
|
|
36
|
+
let message;
|
|
37
|
+
if (!connected)
|
|
38
|
+
message = `Stripe is not connected — ${CONNECT_HINT}`;
|
|
39
|
+
else if (healthy === false)
|
|
40
|
+
message = `Stripe is connected, but its webhook is not working. ${REPAIR_HINT}`;
|
|
41
|
+
else if (healthy === null)
|
|
42
|
+
message = 'Stripe is connected (webhook health not reported by this API)';
|
|
43
|
+
else
|
|
44
|
+
message = 'Stripe is connected and its webhook is configured';
|
|
45
|
+
return {
|
|
46
|
+
connected,
|
|
47
|
+
webhookConfigured,
|
|
48
|
+
webhookHealthy: healthy,
|
|
49
|
+
lastWebhookAt: a.stripeLastWebhookAt ?? null,
|
|
50
|
+
lastWebhookRejectedAt: a.stripeLastWebhookRejectedAt ?? null,
|
|
51
|
+
lastWebhookRejectionReason: a.stripeLastWebhookRejectionReason ?? null,
|
|
52
|
+
setupStatus: a.stripeSetupStatus ?? null,
|
|
53
|
+
setupError: a.stripeSetupError ?? null,
|
|
54
|
+
message,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
3
57
|
export function registerStripeCommands(program) {
|
|
4
58
|
const stripe = program.command('stripe').description('Manage Stripe integration');
|
|
5
59
|
stripe.command('status')
|
|
6
|
-
.description('Show whether Stripe credentials are configured')
|
|
60
|
+
.description('Show whether Stripe credentials are configured and whether its webhook works')
|
|
7
61
|
.action(async () => {
|
|
8
62
|
try {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
63
|
+
const client = getManagementClient();
|
|
64
|
+
const [state, app] = await Promise.all([
|
|
65
|
+
client.app.getCredentialsState(),
|
|
66
|
+
client.app.get(),
|
|
67
|
+
]);
|
|
68
|
+
outputSuccess(stripeStatusFrom(state, app));
|
|
16
69
|
}
|
|
17
70
|
catch (err) {
|
|
18
71
|
outputError(err);
|
|
@@ -28,12 +81,15 @@ export function registerStripeCommands(program) {
|
|
|
28
81
|
stripeSecretKey: opts.secretKey,
|
|
29
82
|
stripePublicKey: opts.publishableKey,
|
|
30
83
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
84
|
+
const connected = stripeConnectedFrom(state);
|
|
85
|
+
const webhookConfigured = stripeWebhookConfiguredFrom(state);
|
|
86
|
+
let message = connected
|
|
87
|
+
? 'Stripe connected successfully'
|
|
88
|
+
: 'Credentials saved but Stripe did not confirm — check your keys and try again';
|
|
89
|
+
if (connected && webhookConfigured === false) {
|
|
90
|
+
message += '. Warning: the Stripe webhook is not configured — run `bridge stripe status` for details';
|
|
91
|
+
}
|
|
92
|
+
outputSuccess({ connected, webhookConfigured, message });
|
|
37
93
|
}
|
|
38
94
|
catch (err) {
|
|
39
95
|
outputError(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stripe.command.js","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"stripe.command.js","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import { BridgeManagement } from '@nebulr-group/bridge-auth-core';
|
|
2
2
|
export declare const DEFAULT_BASE_URL = "https://api.thebridge.dev";
|
|
3
|
+
/** Called once from the CLI entrypoint, before any action runs. */
|
|
4
|
+
export declare function setProfileOverride(profile: string | null | undefined): void;
|
|
3
5
|
/**
|
|
4
6
|
* Resolution order:
|
|
5
|
-
* 1.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* 2.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* 1. `--profile <label|id|name>` / `BRIDGE_PROFILE` — a stored credential,
|
|
8
|
+
* named for this one invocation. Never falls back: naming a profile and
|
|
9
|
+
* silently getting a different app is the exact failure TBP-628 exists to
|
|
10
|
+
* stop, so an unknown or expired profile is an error.
|
|
11
|
+
* 2. The ACTIVE credential in `~/.config/bridge/credentials.json` (or
|
|
12
|
+
* `$XDG_CONFIG_HOME/...`), written by `bridge auth login` and moved by
|
|
13
|
+
* `bridge auth use`.
|
|
14
|
+
* 3. `BRIDGE_API_KEY` env var (CI / service-account fallback). Used when no
|
|
15
|
+
* usable credentials file exists — the typical CI runner shape.
|
|
16
|
+
* 4. Throw `ConfigError("Not logged in. Run `bridge auth login`.")`.
|
|
12
17
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
18
|
+
* The credentials file still wins over `BRIDGE_API_KEY`, unchanged, so
|
|
19
|
+
* `bridge auth login` takes effect immediately and CI is unaffected. What HAS
|
|
20
|
+
* changed is that being ignored is now said out loud: `BRIDGE_API_KEY` and
|
|
21
|
+
* `BRIDGE_APP_ID` look like a way to retarget the CLI and are not, and reading
|
|
22
|
+
* that in the output beats discovering it from a write that went to the wrong
|
|
23
|
+
* app (TBP-628).
|
|
16
24
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* friendly error pointing the user back to `bridge auth login`.
|
|
25
|
+
* Every resolution also prints one line to stderr naming the app that is about
|
|
26
|
+
* to answer. See `writeContextBanner`.
|
|
20
27
|
*/
|
|
21
28
|
export declare function getManagementClient(): BridgeManagement;
|
|
22
29
|
/**
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AASlE,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAa5D,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAE3E;AAQD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CA0EtD;AAoDD;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAQnE;AAED,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/config.js
CHANGED
|
@@ -1,24 +1,47 @@
|
|
|
1
1
|
import { BridgeManagement } from '@nebulr-group/bridge-auth-core';
|
|
2
|
-
import { isExpired, readCredentials } from './credentials.js';
|
|
2
|
+
import { CredentialSelectorError, findCredential, isExpired, readCredentials, } from './credentials.js';
|
|
3
3
|
export const DEFAULT_BASE_URL = 'https://api.thebridge.dev';
|
|
4
4
|
let _client = null;
|
|
5
|
+
/**
|
|
6
|
+
* The `--profile` value for this invocation, set by the CLI's preAction hook.
|
|
7
|
+
*
|
|
8
|
+
* A per-invocation override rather than a persisted switch, because that is
|
|
9
|
+
* what scripts and long-running agent sessions need: target one app for one
|
|
10
|
+
* command without mutating state another process is reading (TBP-628).
|
|
11
|
+
*/
|
|
12
|
+
let _profileOverride = null;
|
|
13
|
+
/** Called once from the CLI entrypoint, before any action runs. */
|
|
14
|
+
export function setProfileOverride(profile) {
|
|
15
|
+
_profileOverride = profile?.trim() || null;
|
|
16
|
+
}
|
|
17
|
+
/** The profile in force: `--profile` beats `BRIDGE_PROFILE`. */
|
|
18
|
+
function activeProfileSelector() {
|
|
19
|
+
if (_profileOverride)
|
|
20
|
+
return _profileOverride;
|
|
21
|
+
return process.env.BRIDGE_PROFILE?.trim() || null;
|
|
22
|
+
}
|
|
5
23
|
/**
|
|
6
24
|
* Resolution order:
|
|
7
|
-
* 1.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* 2.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
25
|
+
* 1. `--profile <label|id|name>` / `BRIDGE_PROFILE` — a stored credential,
|
|
26
|
+
* named for this one invocation. Never falls back: naming a profile and
|
|
27
|
+
* silently getting a different app is the exact failure TBP-628 exists to
|
|
28
|
+
* stop, so an unknown or expired profile is an error.
|
|
29
|
+
* 2. The ACTIVE credential in `~/.config/bridge/credentials.json` (or
|
|
30
|
+
* `$XDG_CONFIG_HOME/...`), written by `bridge auth login` and moved by
|
|
31
|
+
* `bridge auth use`.
|
|
32
|
+
* 3. `BRIDGE_API_KEY` env var (CI / service-account fallback). Used when no
|
|
33
|
+
* usable credentials file exists — the typical CI runner shape.
|
|
34
|
+
* 4. Throw `ConfigError("Not logged in. Run `bridge auth login`.")`.
|
|
14
35
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
36
|
+
* The credentials file still wins over `BRIDGE_API_KEY`, unchanged, so
|
|
37
|
+
* `bridge auth login` takes effect immediately and CI is unaffected. What HAS
|
|
38
|
+
* changed is that being ignored is now said out loud: `BRIDGE_API_KEY` and
|
|
39
|
+
* `BRIDGE_APP_ID` look like a way to retarget the CLI and are not, and reading
|
|
40
|
+
* that in the output beats discovering it from a write that went to the wrong
|
|
41
|
+
* app (TBP-628).
|
|
18
42
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* friendly error pointing the user back to `bridge auth login`.
|
|
43
|
+
* Every resolution also prints one line to stderr naming the app that is about
|
|
44
|
+
* to answer. See `writeContextBanner`.
|
|
22
45
|
*/
|
|
23
46
|
export function getManagementClient() {
|
|
24
47
|
if (_client)
|
|
@@ -29,29 +52,56 @@ export function getManagementClient() {
|
|
|
29
52
|
let apiKey;
|
|
30
53
|
let baseUrl;
|
|
31
54
|
let source;
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
55
|
+
let creds = null;
|
|
56
|
+
const selector = activeProfileSelector();
|
|
57
|
+
if (selector) {
|
|
58
|
+
// 1. Explicit profile. Resolve it or fail — no fallback of any kind.
|
|
59
|
+
let entry;
|
|
60
|
+
try {
|
|
61
|
+
entry = findCredential(selector);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (err instanceof CredentialSelectorError)
|
|
65
|
+
throw new ConfigError(err.message);
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
if (isExpired(entry.creds)) {
|
|
69
|
+
throw new ConfigError(`Credential for "${selector}" (${entry.creds.app.name}) expired ` +
|
|
70
|
+
`${entry.creds.expiresAt}. Run \`bridge auth login\` to re-authenticate it. ` +
|
|
71
|
+
'Refusing to fall back to another app.');
|
|
72
|
+
}
|
|
73
|
+
creds = entry.creds;
|
|
35
74
|
apiKey = creds.apiKey;
|
|
36
|
-
// Env override still wins for baseUrl (useful for hitting a local bridge-api
|
|
37
|
-
// with a token issued by prod, or vice versa during dev).
|
|
38
75
|
baseUrl = envBaseUrl && envBaseUrl.length > 0 ? envBaseUrl : creds.baseUrl;
|
|
39
|
-
source = '
|
|
40
|
-
}
|
|
41
|
-
else if (envApiKey && envApiKey.length > 0) {
|
|
42
|
-
// 2. BRIDGE_API_KEY env var (CI / service-account fallback).
|
|
43
|
-
apiKey = envApiKey;
|
|
44
|
-
baseUrl = envBaseUrl && envBaseUrl.length > 0 ? envBaseUrl : DEFAULT_BASE_URL;
|
|
45
|
-
source = 'env';
|
|
46
|
-
}
|
|
47
|
-
else if (creds && isExpired(creds)) {
|
|
48
|
-
// 3a. Found a credentials file but token expired; no env fallback either.
|
|
49
|
-
throw new ConfigError('Token expired. Run `bridge auth login` to re-authenticate.');
|
|
76
|
+
source = 'profile';
|
|
50
77
|
}
|
|
51
78
|
else {
|
|
52
|
-
//
|
|
53
|
-
|
|
79
|
+
// 2. Active credential from `bridge auth login` / `bridge auth use`.
|
|
80
|
+
const active = safeReadCredentials();
|
|
81
|
+
if (active && !isExpired(active)) {
|
|
82
|
+
creds = active;
|
|
83
|
+
apiKey = active.apiKey;
|
|
84
|
+
// Env override still wins for baseUrl (useful for hitting a local
|
|
85
|
+
// bridge-api with a token issued by prod, or vice versa during dev).
|
|
86
|
+
baseUrl = envBaseUrl && envBaseUrl.length > 0 ? envBaseUrl : active.baseUrl;
|
|
87
|
+
source = 'credentials-file';
|
|
88
|
+
}
|
|
89
|
+
else if (envApiKey && envApiKey.length > 0) {
|
|
90
|
+
// 3. BRIDGE_API_KEY env var (CI / service-account fallback).
|
|
91
|
+
apiKey = envApiKey;
|
|
92
|
+
baseUrl = envBaseUrl && envBaseUrl.length > 0 ? envBaseUrl : DEFAULT_BASE_URL;
|
|
93
|
+
source = 'env';
|
|
94
|
+
}
|
|
95
|
+
else if (active && isExpired(active)) {
|
|
96
|
+
// 4a. Found a credentials file but token expired; no env fallback either.
|
|
97
|
+
throw new ConfigError('Token expired. Run `bridge auth login` to re-authenticate.');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// 4b. No credentials at all.
|
|
101
|
+
throw new ConfigError('Not logged in. Run `bridge auth login`.');
|
|
102
|
+
}
|
|
54
103
|
}
|
|
104
|
+
writeContextBanner({ creds, source, baseUrl, selector });
|
|
55
105
|
if (debug) {
|
|
56
106
|
const baseSource = envBaseUrl && envBaseUrl.length > 0
|
|
57
107
|
? 'env'
|
|
@@ -63,6 +113,44 @@ export function getManagementClient() {
|
|
|
63
113
|
_client = new BridgeManagement({ apiKey, baseUrl, debug });
|
|
64
114
|
return _client;
|
|
65
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* One line on stderr naming the app that is about to answer.
|
|
118
|
+
*
|
|
119
|
+
* On EVERY command, not only writes. The incident behind TBP-628 started with a
|
|
120
|
+
* read: `bridge role list` was believed to describe the local app and described
|
|
121
|
+
* production, and nothing in the output said otherwise. A banner that only
|
|
122
|
+
* appears on writes would not have prevented it, because the wrong belief was
|
|
123
|
+
* already formed by then.
|
|
124
|
+
*
|
|
125
|
+
* stderr, so stdout stays pure JSON for the agents and scripts that parse it.
|
|
126
|
+
* Suppress with `BRIDGE_NO_BANNER=true` — for the one caller who is already
|
|
127
|
+
* certain and is merging the two streams.
|
|
128
|
+
*/
|
|
129
|
+
function writeContextBanner(ctx) {
|
|
130
|
+
if (process.env.BRIDGE_NO_BANNER === 'true')
|
|
131
|
+
return;
|
|
132
|
+
const { creds, source, baseUrl, selector } = ctx;
|
|
133
|
+
const who = creds
|
|
134
|
+
? `${creds.label ?? creds.app.name} (${creds.app.id})`
|
|
135
|
+
: 'BRIDGE_API_KEY (app unknown — the key carries it)';
|
|
136
|
+
const via = source === 'profile'
|
|
137
|
+
? `--profile ${selector}`
|
|
138
|
+
: source === 'env'
|
|
139
|
+
? 'BRIDGE_API_KEY'
|
|
140
|
+
: 'saved default';
|
|
141
|
+
process.stderr.write(`bridge: ${who} · ${baseUrl} · via ${via}\n`);
|
|
142
|
+
// The two env vars that look like a way to retarget the CLI and are not.
|
|
143
|
+
// Saying so here, at the moment they are being ignored, is the whole point:
|
|
144
|
+
// the reported incident was a session that set them and believed them.
|
|
145
|
+
if (source !== 'env' && process.env.BRIDGE_API_KEY?.trim()) {
|
|
146
|
+
process.stderr.write('bridge: BRIDGE_API_KEY is set but IGNORED — the credentials file wins. ' +
|
|
147
|
+
'Use `--profile <label>` to pick a stored app, or `bridge auth logout` to use the key.\n');
|
|
148
|
+
}
|
|
149
|
+
if (process.env.BRIDGE_APP_ID?.trim()) {
|
|
150
|
+
process.stderr.write('bridge: BRIDGE_APP_ID has no effect — the app is carried by the credential itself. ' +
|
|
151
|
+
'Use `--profile <label|app id>` or `BRIDGE_PROFILE` to target another app.\n');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
66
154
|
/**
|
|
67
155
|
* For tests and for `bridge auth logout` / `bridge auth status` — they need
|
|
68
156
|
* to reset the cached client between operations.
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,SAAS,EACT,eAAe,GAEhB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAE5D,IAAI,OAAO,GAA4B,IAAI,CAAC;AAE5C;;;;;;GAMG;AACH,IAAI,gBAAgB,GAAkB,IAAI,CAAC;AAE3C,mEAAmE;AACnE,MAAM,UAAU,kBAAkB,CAAC,OAAkC;IACnE,gBAAgB,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;AAC7C,CAAC;AAED,gEAAgE;AAChE,SAAS,qBAAqB;IAC5B,IAAI,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IAC9C,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,mBAAmB;IACjC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAE5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,MAAM,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IACrD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAEvD,IAAI,MAAc,CAAC;IACnB,IAAI,OAAe,CAAC;IACpB,IAAI,MAAc,CAAC;IACnB,IAAI,KAAK,GAA6B,IAAI,CAAC;IAE3C,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;IACzC,IAAI,QAAQ,EAAE,CAAC;QACb,qEAAqE;QACrE,IAAI,KAAK,CAAC;QACV,IAAI,CAAC;YACH,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,uBAAuB;gBAAE,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC/E,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,WAAW,CACnB,mBAAmB,QAAQ,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,YAAY;gBAC/D,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,qDAAqD;gBAC7E,uCAAuC,CAC1C,CAAC;QACJ,CAAC;QACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACpB,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QACtB,OAAO,GAAG,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QAC3E,MAAM,GAAG,SAAS,CAAC;IACrB,CAAC;SAAM,CAAC;QACN,qEAAqE;QACrE,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACrC,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,KAAK,GAAG,MAAM,CAAC;YACf,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YACvB,kEAAkE;YAClE,qEAAqE;YACrE,OAAO,GAAG,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YAC5E,MAAM,GAAG,kBAAkB,CAAC;QAC9B,CAAC;aAAM,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,6DAA6D;YAC7D,MAAM,GAAG,SAAS,CAAC;YACnB,OAAO,GAAG,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC9E,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;aAAM,IAAI,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,0EAA0E;YAC1E,MAAM,IAAI,WAAW,CACnB,4DAA4D,CAC7D,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,6BAA6B;YAC7B,MAAM,IAAI,WAAW,CAAC,yCAAyC,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,kBAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEzD,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,UAAU,GACd,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YACjC,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,MAAM,KAAK,KAAK;gBAChB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,kBAAkB,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,sCAAsC,MAAM,aAAa,OAAO,KAAK,UAAU,GAAG,CAAC,CAAC;IACpG,CAAC;IAED,OAAO,GAAG,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAE3D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,kBAAkB,CAAC,GAK3B;IACC,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,MAAM;QAAE,OAAO;IAEpD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACjD,MAAM,GAAG,GAAG,KAAK;QACf,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG;QACtD,CAAC,CAAC,mDAAmD,CAAC;IACxD,MAAM,GAAG,GACP,MAAM,KAAK,SAAS;QAClB,CAAC,CAAC,aAAa,QAAQ,EAAE;QACzB,CAAC,CAAC,MAAM,KAAK,KAAK;YAChB,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,eAAe,CAAC;IACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,MAAM,OAAO,UAAU,GAAG,IAAI,CAAC,CAAC;IAEnE,yEAAyE;IACzE,4EAA4E;IAC5E,uEAAuE;IACvE,IAAI,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,CAAC;QAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yEAAyE;YACvE,yFAAyF,CAC5F,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC;QACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qFAAqF;YACnF,6EAA6E,CAChF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAA2B;IACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC/D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,WAAW,CACnB,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED;;;;GAIG;AACH,SAAS,mBAAmB;IAC1B,IAAI,CAAC;QACH,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,oCAAoC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI;YACtF,kEAAkE,CACrE,CAAC;IACJ,CAAC;AACH,CAAC"}
|