@pipeline-builder/pipeline-manager 4.1.4 → 4.1.6

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.
Files changed (52) hide show
  1. package/README.md +2 -1
  2. package/dist/agent/env-file.d.ts +1 -1
  3. package/dist/agent/env-file.js +2 -2
  4. package/dist/agent/post-steps.d.ts.map +1 -1
  5. package/dist/agent/post-steps.js +38 -18
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +19 -9
  8. package/dist/commands/audit-tokens.d.ts.map +1 -1
  9. package/dist/commands/audit-tokens.js +32 -21
  10. package/dist/commands/create-pat.d.ts +20 -13
  11. package/dist/commands/create-pat.d.ts.map +1 -1
  12. package/dist/commands/create-pat.js +93 -98
  13. package/dist/commands/login.d.ts +14 -8
  14. package/dist/commands/login.d.ts.map +1 -1
  15. package/dist/commands/login.js +89 -124
  16. package/dist/commands/setup-events.d.ts.map +1 -1
  17. package/dist/commands/setup-events.js +6 -5
  18. package/dist/commands/status.d.ts.map +1 -1
  19. package/dist/commands/status.js +9 -3
  20. package/dist/commands/store-token.d.ts +16 -11
  21. package/dist/commands/store-token.d.ts.map +1 -1
  22. package/dist/commands/store-token.js +213 -131
  23. package/dist/lambda/token-renew-handler.d.ts.map +1 -1
  24. package/dist/lambda/token-renew-handler.js +134 -97
  25. package/dist/templates/events-stack.json +1 -1
  26. package/dist/templates/token-renew-stack.json +9 -10
  27. package/dist/utils/auth-utils.d.ts +21 -38
  28. package/dist/utils/auth-utils.d.ts.map +1 -1
  29. package/dist/utils/auth-utils.js +20 -33
  30. package/dist/utils/command-utils.d.ts +8 -6
  31. package/dist/utils/command-utils.d.ts.map +1 -1
  32. package/dist/utils/command-utils.js +71 -15
  33. package/dist/utils/config-loader.d.ts +2 -1
  34. package/dist/utils/config-loader.d.ts.map +1 -1
  35. package/dist/utils/config-loader.js +18 -8
  36. package/dist/utils/credential-store.d.ts +23 -0
  37. package/dist/utils/credential-store.d.ts.map +1 -0
  38. package/dist/utils/credential-store.js +92 -0
  39. package/dist/utils/device-auth.d.ts +59 -0
  40. package/dist/utils/device-auth.d.ts.map +1 -0
  41. package/dist/utils/device-auth.js +139 -0
  42. package/dist/utils/pipeline-config.js +2 -2
  43. package/dist/utils/platform-secret.d.ts +19 -1
  44. package/dist/utils/platform-secret.d.ts.map +1 -1
  45. package/dist/utils/platform-secret.js +26 -4
  46. package/dist/utils/service-account.d.ts +73 -0
  47. package/dist/utils/service-account.d.ts.map +1 -0
  48. package/dist/utils/service-account.js +158 -0
  49. package/package.json +4 -4
  50. package/dist/utils/rate-limiter.d.ts +0 -15
  51. package/dist/utils/rate-limiter.d.ts.map +0 -1
  52. package/dist/utils/rate-limiter.js +0 -164
@@ -0,0 +1,158 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { printInfo, printSuccess, printWarning } from './output-utils.js';
4
+ /** Step-up header the platform reads (see api-core `requireStepUp`). */
5
+ const STEP_UP_HEADER = 'X-Step-Up-Token';
6
+ /** HTTP status of a failed CLI request, or undefined for a transport error.
7
+ * `ApiError` carries it directly; a raw axios error only on the response. */
8
+ function statusOf(err) {
9
+ const e = err;
10
+ const status = e?.status ?? e?.response?.status;
11
+ return typeof status === 'number' ? status : undefined;
12
+ }
13
+ /** The platform's error code on a failed request, when it sent one. */
14
+ function errorCodeOf(err) {
15
+ const data = err?.response?.data;
16
+ return data?.error?.code ?? data?.code;
17
+ }
18
+ /** Unwrap api-core's `{ success, data }` envelope (some paths return data bare). */
19
+ function unwrap(response) {
20
+ const body = response;
21
+ return (body?.data ?? response);
22
+ }
23
+ /**
24
+ * Mint a single-use step-up token for the signed-in operator. Short-lived by
25
+ * design, so callers mint one immediately before each gated write rather than
26
+ * holding one across the whole command.
27
+ */
28
+ async function stepUp(client, password) {
29
+ try {
30
+ const res = await client.post('/api/auth/step-up', { password });
31
+ const token = unwrap(res)?.stepUpToken;
32
+ if (!token)
33
+ throw new Error('no stepUpToken in response');
34
+ return token;
35
+ }
36
+ catch (err) {
37
+ throw new Error('Could not re-verify your password (step-up). Creating a service account and issuing its key '
38
+ + 'are both step-up gated. Check the password passed via --password / PLATFORM_PASSWORD. '
39
+ + `(${err instanceof Error ? err.message : String(err)})`);
40
+ }
41
+ }
42
+ /** The org the operator's token is active in. */
43
+ export async function resolveOrganizationId(client) {
44
+ const res = await client.get('/api/organization');
45
+ const orgId = unwrap(res)?.organization?.id;
46
+ if (!orgId) {
47
+ throw new Error('Could not resolve your active organization — the platform returned no organization for this token.');
48
+ }
49
+ return orgId;
50
+ }
51
+ /**
52
+ * The org Role to give a full-privilege machine account: its Super Admin role if
53
+ * the org has one (the system org does), else its Admin role. Both are subject to
54
+ * the ordinary assignment ceiling on the platform side, so this can never grant
55
+ * the account more than the operator holds.
56
+ */
57
+ async function resolveAdminRoleId(client, orgId) {
58
+ const res = await client.get(`/api/organization/${orgId}/roles`);
59
+ const roles = unwrap(res)?.roles ?? [];
60
+ const role = roles.find((r) => r.grantsRole === 'superadmin') ?? roles.find((r) => r.grantsRole === 'admin');
61
+ if (!role) {
62
+ throw new Error(`Organization ${orgId} has no admin role to grant the machine service account.`);
63
+ }
64
+ return role.id;
65
+ }
66
+ /** Find an existing account by name (the name is the stable machine identifier). */
67
+ async function findAccount(client, orgId, name) {
68
+ const res = await client.get(`/api/organization/${orgId}/service-accounts`);
69
+ const accounts = unwrap(res)?.serviceAccounts ?? [];
70
+ return accounts.find((a) => a.name === name);
71
+ }
72
+ /**
73
+ * Create the account if it isn't there yet, and return its id. IDEMPOTENT: a
74
+ * re-run reuses the existing account (including the 409 race where two runs
75
+ * create it at once), and never re-grants Roles — an operator who narrowed the
76
+ * account's Roles by hand must not have that undone by the next `store-token`.
77
+ */
78
+ async function ensureAccount(opts, orgId) {
79
+ const existing = await findAccount(opts.client, orgId, opts.accountName);
80
+ if (existing) {
81
+ printInfo('Reusing the existing service account', { account: opts.accountName, id: existing.id });
82
+ return existing.id;
83
+ }
84
+ const roleIds = opts.roles === 'admin' ? [await resolveAdminRoleId(opts.client, orgId)] : [];
85
+ try {
86
+ const res = await opts.client.post(`/api/organization/${orgId}/service-accounts`, { name: opts.accountName, description: opts.description, roleIds }, { [STEP_UP_HEADER]: await stepUp(opts.client, opts.password) });
87
+ const account = unwrap(res)?.serviceAccount;
88
+ if (!account?.id)
89
+ throw new Error('service-account create returned no id');
90
+ printSuccess(`Created the '${opts.accountName}' service account`);
91
+ return account.id;
92
+ }
93
+ catch (err) {
94
+ if (statusOf(err) === 409) {
95
+ const raced = await findAccount(opts.client, orgId, opts.accountName);
96
+ if (raced)
97
+ return raced.id;
98
+ }
99
+ throw err;
100
+ }
101
+ }
102
+ /** Revoke one of an account's keys. Best-effort by contract — see the call sites. */
103
+ export async function revokeServiceAccountKey(client, password, orgId, accountId, keyId) {
104
+ await client.delete(`/api/organization/${orgId}/service-accounts/${accountId}/keys/${keyId}`, { [STEP_UP_HEADER]: await stepUp(client, password) });
105
+ }
106
+ /**
107
+ * Issue a key, self-healing past the per-account active-key cap: at the cap the
108
+ * OLDEST active key is retired and the mint retried once. The cap exists to bound
109
+ * a compromise, not to make provisioning fail, and the credential a re-run is
110
+ * replacing is the very thing that should go.
111
+ */
112
+ async function issueKey(opts, orgId, accountId) {
113
+ const body = {
114
+ name: opts.keyName,
115
+ expiresIn: opts.expiresInSeconds,
116
+ ...(opts.scope ? { scope: opts.scope } : {}),
117
+ };
118
+ const mint = async () => unwrap(await opts.client.post(`/api/organization/${orgId}/service-accounts/${accountId}/keys`, body, { [STEP_UP_HEADER]: await stepUp(opts.client, opts.password) }));
119
+ try {
120
+ return await mint();
121
+ }
122
+ catch (err) {
123
+ if (statusOf(err) !== 409 && errorCodeOf(err) !== 'SA_KEY_LIMIT')
124
+ throw err;
125
+ const account = await findAccount(opts.client, orgId, opts.accountName);
126
+ const oldest = (account?.keys ?? [])
127
+ .filter((k) => k.status === 'active')
128
+ .sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
129
+ if (!oldest)
130
+ throw err;
131
+ printWarning(`Service account '${opts.accountName}' is at its active-key limit — retiring its oldest key (${oldest.id}).`);
132
+ await revokeServiceAccountKey(opts.client, opts.password, orgId, accountId, oldest.id);
133
+ return mint();
134
+ }
135
+ }
136
+ /**
137
+ * Ensure the named service account exists and issue it a fresh key.
138
+ *
139
+ * Deliberately does NOT revoke anything the caller was previously holding: the
140
+ * caller stores the new key first and retires the old one afterwards (see
141
+ * `store-token`), so a failure at any point leaves a working credential in the
142
+ * secret rather than an empty one.
143
+ */
144
+ export async function provisionServiceAccountKey(opts) {
145
+ const organizationId = await resolveOrganizationId(opts.client);
146
+ const serviceAccountId = await ensureAccount(opts, organizationId);
147
+ const { key, accessKey } = await issueKey(opts, organizationId, serviceAccountId);
148
+ return {
149
+ key,
150
+ keyId: accessKey.id,
151
+ serviceAccountId,
152
+ serviceAccountName: opts.accountName,
153
+ organizationId,
154
+ expiresAt: accessKey.expiresAt,
155
+ scope: accessKey.scope ?? null,
156
+ };
157
+ }
158
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS1hY2NvdW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3NlcnZpY2UtYWNjb3VudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBNEJ0QyxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUUxRSx3RUFBd0U7QUFDeEUsTUFBTSxjQUFjLEdBQUcsaUJBQWlCLENBQUM7QUE2Q3pDOzhFQUM4RTtBQUM5RSxTQUFTLFFBQVEsQ0FBQyxHQUFZO0lBQzVCLE1BQU0sQ0FBQyxHQUFHLEdBQTBELENBQUM7SUFDckUsTUFBTSxNQUFNLEdBQUcsQ0FBQyxFQUFFLE1BQU0sSUFBSSxDQUFDLEVBQUUsUUFBUSxFQUFFLE1BQU0sQ0FBQztJQUNoRCxPQUFPLE9BQU8sTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDekQsQ0FBQztBQUVELHVFQUF1RTtBQUN2RSxTQUFTLFdBQVcsQ0FBQyxHQUFZO0lBQy9CLE1BQU0sSUFBSSxHQUFJLEdBQThFLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQztJQUM3RyxPQUFPLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxJQUFJLElBQUksRUFBRSxJQUFJLENBQUM7QUFDekMsQ0FBQztBQUVELG9GQUFvRjtBQUNwRixTQUFTLE1BQU0sQ0FBSSxRQUFpQjtJQUNsQyxNQUFNLElBQUksR0FBRyxRQUF3QixDQUFDO0lBQ3RDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxJQUFJLFFBQVEsQ0FBTSxDQUFDO0FBQ3ZDLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsS0FBSyxVQUFVLE1BQU0sQ0FBQyxNQUFpQixFQUFFLFFBQWdCO0lBQ3ZELElBQUksQ0FBQztRQUNILE1BQU0sR0FBRyxHQUFHLE1BQU0sTUFBTSxDQUFDLElBQUks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QUUsU0FBUyxFQUFFLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ2xGLE9BQU87UUFDTCxHQUFHO1FBQ0gsS0FBSyxFQUFFLFNBQVMsQ0FBQyxFQUFFO1FBQ25CLGdCQUFnQjtRQUNoQixrQkFBa0IsRUFBRSxJQUFJLENBQUMsV0FBVztRQUNwQyxjQUFjO1FBQ2QsU0FBUyxFQUFFLFNBQVMsQ0FBQyxTQUFTO1FBQzlCLEtBQUssRUFBRSxTQUFTLENBQUMsS0FBSyxJQUFJLElBQUk7S0FDL0IsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBQcm92aXNpb25pbmcgdGhlIG9yZydzIE1BQ0hJTkUgaWRlbnRpdHkgZm9yIHN0b3JlZCBjcmVkZW50aWFscyAoIzEyIC8gI04yKS5cbiAqXG4gKiBFdmVyeSBjcmVkZW50aWFsIHRoZSBDTEkgcGFya3MgaW4gQVdTIFNlY3JldHMgTWFuYWdlciB1c2VkIHRvIGJlIGEgbWFjaGluZVxuICogU0VTU0lPTiBiZWxvbmdpbmcgdG8gd2hvZXZlciByYW4gdGhlIGNvbW1hbmQ6IGEgcGVyc29uJ3MgSldULCByZW5ld2VkIGRhaWx5IGJ5XG4gKiByZS1taW50aW5nIGl0IHVuZGVyIHRoYXQgcGVyc29uJ3Mgc2Vzc2lvbi4gSXQgb3V0bGl2ZWQgdGhlaXIgZW1wbG95bWVudCwgaXRcbiAqIGNhcnJpZWQgdGhlaXIgYXV0aG9yaXR5LCBhbmQgdGhlIGF1ZGl0IHRyYWlsIHJlYWQgYXMgaWYgdGhleSBoYWQgcGVyc29uYWxseVxuICogcHVzaGVkIGV2ZXJ5IGltYWdlLlxuICpcbiAqIFdoYXQgaXQgd3JpdGVzIGluc3RlYWQgaXMgYSBgcGJfc2Ff4oCmYCBrZXkgb24gYW4gb3JnIFNFUlZJQ0UgQUNDT1VOVDpcbiAqICAgLSB0aGUgYWNjb3VudCBpcyB0aGUgcHJpbmNpcGFsLCBzbyBhdWRpdCByb3dzIG5hbWUgaXQsIG5vdCB0aGUgb3BlcmF0b3I7XG4gKiAgIC0gaXRzIFJvbGVzIGFyZSB0aGUgbmFycm93ZXN0IHRoYXQgd29yayDigJQgZm9yIGEgU0NPUEVEIGNyZWRlbnRpYWwgdGhhdCBpcyBub1xuICogICAgIFJvbGVzIGF0IGFsbCwganVzdCB0aGUgb25lIGNhcGFiaWxpdHkgKGByZXBvcnRpbmc6aW5nZXN0YCwgYHJlZ2lzdHJ5OnB1c2hgKTtcbiAqICAgLSB0aGUga2V5IGlzIG9wYXF1ZSBhbmQgc2hvcnQtbGl2ZWQgYXQgdGhlIHBvaW50IG9mIHVzZSAoNS1taW51dGUgZXhjaGFuZ2VkXG4gKiAgICAgdG9rZW5zKSwgc28gcmV2b2tpbmcgaXQgYWN0dWFsbHkgc3RvcHMgdGhlIGF1dG9tYXRpb247XG4gKiAgIC0gaXQgdGFrZXMgbm8gc2VhdCBhbmQgc3Vydml2ZXMgdGhlIG9wZXJhdG9yIGxlYXZpbmcuXG4gKlxuICogQ3JlYXRpbmcgYW4gYWNjb3VudCBhbmQgaXNzdWluZyBhIGtleSBhcmUgQk9USCBzdGVwLXVwIGdhdGVkIG9uIHRoZSBwbGF0Zm9ybSDigJRcbiAqIHRoZXkgbWludCBkdXJhYmxlIGJlYXJlciBjcmVkZW50aWFscywgdGhlIHNhbWUgY2xhc3Mgb2YgYWN0aW9uIGEgcGVyc29uYWwga2V5XG4gKiBpcyBnYXRlZCBmb3IuIFNvIHRoaXMgbW9kdWxlIG5lZWRzIHRoZSBvcGVyYXRvcidzIHBhc3N3b3JkIChgLS1wYXNzd29yZGAgb3JcbiAqIGBQTEFURk9STV9QQVNTV09SRGApLCB3aGljaCBpcyBhbHNvIHRoZSBvbmx5IHJlYXNvbiBgc3RvcmUtdG9rZW5gIHN0aWxsIGhhcyBhXG4gKiBwYXNzd29yZCBwYXRoIGF0IGFsbC4gQSBzdGVwLXVwIHRva2VuIGlzIHNpbmdsZS11c2UgYW5kIGxpdmVzIGFib3V0IGEgbWludXRlLFxuICogc28gb25lIGlzIG1pbnRlZCBpbW1lZGlhdGVseSBiZWZvcmUgZWFjaCBnYXRlZCBjYWxsLlxuICovXG5cbmltcG9ydCB0eXBlIHsgQXBpQ2xpZW50IH0gZnJvbSAnLi9hcGktY2xpZW50LmpzJztcbmltcG9ydCB7IHByaW50SW5mbywgcHJpbnRTdWNjZXNzLCBwcmludFdhcm5pbmcgfSBmcm9tICcuL291dHB1dC11dGlscy5qcyc7XG5cbi8qKiBTdGVwLXVwIGhlYWRlciB0aGUgcGxhdGZvcm0gcmVhZHMgKHNlZSBhcGktY29yZSBgcmVxdWlyZVN0ZXBVcGApLiAqL1xuY29uc3QgU1RFUF9VUF9IRUFERVIgPSAnWC1TdGVwLVVwLVRva2VuJztcblxuLyoqIFdoYXQgdGhlIGNhbGxlciB3YW50cyBwcm92aXNpb25lZC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHJvdmlzaW9uS2V5T3B0aW9ucyB7XG4gIC8qKiBBbiBhdXRoZW50aWNhdGVkIGNsaWVudCBmb3IgdGhlIG9wZXJhdG9yJ3Mgb3duIHNlc3Npb24uICovXG4gIGNsaWVudDogQXBpQ2xpZW50O1xuICAvKiogVGhlIG9wZXJhdG9yJ3MgcGFzc3dvcmQg4oCUIHJlcXVpcmVkLCBiZWNhdXNlIGV2ZXJ5IGtleSB3cml0ZSBpcyBzdGVwLXVwIGdhdGVkLiAqL1xuICBwYXNzd29yZDogc3RyaW5nO1xuICAvKiogTWFjaGluZSBuYW1lIG9mIHRoZSBzZXJ2aWNlIGFjY291bnQgKGBbYS16MC05XVthLXowLTlfLV17MSw2M31gKS4gKi9cbiAgYWNjb3VudE5hbWU6IHN0cmluZztcbiAgLyoqIEh1bWFuIGRlc2NyaXB0aW9uIHJlY29yZGVkIG9uIGEgbmV3bHkgY3JlYXRlZCBhY2NvdW50LiAqL1xuICBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICAvKipcbiAgICogUm9sZXMgdG8gZ3JhbnQgYSBORVdMWSBjcmVhdGVkIGFjY291bnQ6IGAnYWRtaW4nYCBmb3IgdGhlIGZ1bGwtcHJpdmlsZWdlXG4gICAqIHBsYXRmb3JtIGNyZWRlbnRpYWwgKHN5bnRoL2RlcGxveSBjYWxsYmFja3MsIHBsdWdpbiBsb29rdXAsIGltYWdlIHB1c2gpLFxuICAgKiBgJ25vbmUnYCBmb3IgYSBzY29wZWQgY3JlZGVudGlhbCwgd2hpY2ggbmVlZHMgbm8gUm9sZXMgYXQgYWxsLlxuICAgKi9cbiAgcm9sZXM6ICdhZG1pbicgfCAnbm9uZSc7XG4gIC8qKiBOYXJyb3cgY2FwYWJpbGl0eSBzY29wZSBzdGFtcGVkIG9uIHRoZSBLRVkgKG5vdCB0aGUgYWNjb3VudCkuICovXG4gIHNjb3BlPzogc3RyaW5nO1xuICAvKiogS2V5IGxpZmV0aW1lIGluIHNlY29uZHMgKHBsYXRmb3JtIGNhcHMgdGhpcyBhdCAzNjUgZGF5cykuICovXG4gIGV4cGlyZXNJblNlY29uZHM6IG51bWJlcjtcbiAgLyoqIEtleSBsYWJlbCDigJQgc2hvd24gb24gdGhlIG9yZydzIHNlcnZpY2UtYWNjb3VudHMgcGFnZS4gKi9cbiAga2V5TmFtZTogc3RyaW5nO1xufVxuXG4vKiogQSBmcmVzaGx5IG1pbnRlZCBrZXksIHBsdXMgZXZlcnl0aGluZyB0aGUgc2VjcmV0IG5lZWRzIHRvIHJlY29yZCBhYm91dCBpdC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHJvdmlzaW9uZWRLZXkge1xuICAvKiogVGhlIHJhdyBgcGJfc2Ff4oCmYCBrZXkg4oCUIHJldHVybmVkIE9OQ0UgYnkgdGhlIHBsYXRmb3JtLCBuZXZlciByZWFkYWJsZSBhZ2Fpbi4gKi9cbiAga2V5OiBzdHJpbmc7XG4gIGtleUlkOiBzdHJpbmc7XG4gIHNlcnZpY2VBY2NvdW50SWQ6IHN0cmluZztcbiAgc2VydmljZUFjY291bnROYW1lOiBzdHJpbmc7XG4gIG9yZ2FuaXphdGlvbklkOiBzdHJpbmc7XG4gIGV4cGlyZXNBdDogc3RyaW5nO1xuICBzY29wZTogc3RyaW5nIHwgbnVsbDtcbn1cblxuLyoqIE5hcnJvdyBzaGFwZSBvZiBhIHNlcnZpY2UtYWNjb3VudCBsaXN0aW5nIGVudHJ5LiAqL1xuaW50ZXJmYWNlIEFjY291bnRTdW1tYXJ5IHtcbiAgaWQ6IHN0cmluZztcbiAgbmFtZTogc3RyaW5nO1xuICBrZXlzPzogQXJyYXk8eyBpZDogc3RyaW5nOyBzdGF0dXM6IHN0cmluZzsgY3JlYXRlZEF0OiBzdHJpbmcgfT47XG59XG5cbi8qKiBIVFRQIHN0YXR1cyBvZiBhIGZhaWxlZCBDTEkgcmVxdWVzdCwgb3IgdW5kZWZpbmVkIGZvciBhIHRyYW5zcG9ydCBlcnJvci5cbiAqICBgQXBpRXJyb3JgIGNhcnJpZXMgaXQgZGlyZWN0bHk7IGEgcmF3IGF4aW9zIGVycm9yIG9ubHkgb24gdGhlIHJlc3BvbnNlLiAqL1xuZnVuY3Rpb24gc3RhdHVzT2YoZXJyOiB1bmtub3duKTogbnVtYmVyIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgZSA9IGVyciBhcyB7IHN0YXR1cz86IG51bWJlcjsgcmVzcG9uc2U/OiB7IHN0YXR1cz86IG51bWJlciB9IH07XG4gIGNvbnN0IHN0YXR1cyA9IGU/LnN0YXR1cyA/PyBlPy5yZXNwb25zZT8uc3RhdHVzO1xuICByZXR1cm4gdHlwZW9mIHN0YXR1cyA9PT0gJ251bWJlcicgPyBzdGF0dXMgOiB1bmRlZmluZWQ7XG59XG5cbi8qKiBUaGUgcGxhdGZvcm0ncyBlcnJvciBjb2RlIG9uIGEgZmFpbGVkIHJlcXVlc3QsIHdoZW4gaXQgc2VudCBvbmUuICovXG5mdW5jdGlvbiBlcnJvckNvZGVPZihlcnI6IHVua25vd24pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBjb25zdCBkYXRhID0gKGVyciBhcyB7IHJlc3BvbnNlPzogeyBkYXRhPzogeyBlcnJvcj86IHsgY29kZT86IHN0cmluZyB9OyBjb2RlPzogc3RyaW5nIH0gfSB9KT8ucmVzcG9uc2U/LmRhdGE7XG4gIHJldHVybiBkYXRhPy5lcnJvcj8uY29kZSA/PyBkYXRhPy5jb2RlO1xufVxuXG4vKiogVW53cmFwIGFwaS1jb3JlJ3MgYHsgc3VjY2VzcywgZGF0YSB9YCBlbnZlbG9wZSAoc29tZSBwYXRocyByZXR1cm4gZGF0YSBiYXJlKS4gKi9cbmZ1bmN0aW9uIHVud3JhcDxUPihyZXNwb25zZTogdW5rbm93bik6IFQge1xuICBjb25zdCBib2R5ID0gcmVzcG9uc2UgYXMgeyBkYXRhPzogVCB9O1xuICByZXR1cm4gKGJvZHk/LmRhdGEgPz8gcmVzcG9uc2UpIGFzIFQ7XG59XG5cbi8qKlxuICogTWludCBhIHNpbmdsZS11c2Ugc3RlcC11cCB0b2tlbiBmb3IgdGhlIHNpZ25lZC1pbiBvcGVyYXRvci4gU2hvcnQtbGl2ZWQgYnlcbiAqIGRlc2lnbiwgc28gY2FsbGVycyBtaW50IG9uZSBpbW1lZGlhdGVseSBiZWZvcmUgZWFjaCBnYXRlZCB3cml0ZSByYXRoZXIgdGhhblxuICogaG9sZGluZyBvbmUgYWNyb3NzIHRoZSB3aG9sZSBjb21tYW5kLlxuICovXG5hc3luYyBmdW5jdGlvbiBzdGVwVXAoY2xpZW50OiBBcGlDbGllbnQsIHBhc3N3b3JkOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICB0cnkge1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGNsaWVudC5wb3N0PHVua25vd24+KCcvYXBpL2F1dGgvc3RlcC11cCcsIHsgcGFzc3dvcmQgfSk7XG4gICAgY29uc3QgdG9rZW4gPSB1bndyYXA8eyBzdGVwVXBUb2tlbj86IHN0cmluZyB9PihyZXMpPy5zdGVwVXBUb2tlbjtcbiAgICBpZiAoIXRva2VuKSB0aHJvdyBuZXcgRXJyb3IoJ25vIHN0ZXBVcFRva2VuIGluIHJlc3BvbnNlJyk7XG4gICAgcmV0dXJuIHRva2VuO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAnQ291bGQgbm90IHJlLXZlcmlmeSB5b3VyIHBhc3N3b3JkIChzdGVwLXVwKS4gQ3JlYXRpbmcgYSBzZXJ2aWNlIGFjY291bnQgYW5kIGlzc3VpbmcgaXRzIGtleSAnXG4gICAgICArICdhcmUgYm90aCBzdGVwLXVwIGdhdGVkLiBDaGVjayB0aGUgcGFzc3dvcmQgcGFzc2VkIHZpYSAtLXBhc3N3b3JkIC8gUExBVEZPUk1fUEFTU1dPUkQuICdcbiAgICAgICsgYCgke2VyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKX0pYCxcbiAgICApO1xuICB9XG59XG5cbi8qKiBUaGUgb3JnIHRoZSBvcGVyYXRvcidzIHRva2VuIGlzIGFjdGl2ZSBpbi4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXNvbHZlT3JnYW5pemF0aW9uSWQoY2xpZW50OiBBcGlDbGllbnQpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCByZXMgPSBhd2FpdCBjbGllbnQuZ2V0PHVua25vd24+KCcvYXBpL29yZ2FuaXphdGlvbicpO1xuICBjb25zdCBvcmdJZCA9IHVud3JhcDx7IG9yZ2FuaXphdGlvbj86IHsgaWQ/OiBzdHJpbmcgfSB9PihyZXMpPy5vcmdhbml6YXRpb24/LmlkO1xuICBpZiAoIW9yZ0lkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgcmVzb2x2ZSB5b3VyIGFjdGl2ZSBvcmdhbml6YXRpb24g4oCUIHRoZSBwbGF0Zm9ybSByZXR1cm5lZCBubyBvcmdhbml6YXRpb24gZm9yIHRoaXMgdG9rZW4uJyk7XG4gIH1cbiAgcmV0dXJuIG9yZ0lkO1xufVxuXG4vKipcbiAqIFRoZSBvcmcgUm9sZSB0byBnaXZlIGEgZnVsbC1wcml2aWxlZ2UgbWFjaGluZSBhY2NvdW50OiBpdHMgU3VwZXIgQWRtaW4gcm9sZSBpZlxuICogdGhlIG9yZyBoYXMgb25lICh0aGUgc3lzdGVtIG9yZyBkb2VzKSwgZWxzZSBpdHMgQWRtaW4gcm9sZS4gQm90aCBhcmUgc3ViamVjdCB0b1xuICogdGhlIG9yZGluYXJ5IGFzc2lnbm1lbnQgY2VpbGluZyBvbiB0aGUgcGxhdGZvcm0gc2lkZSwgc28gdGhpcyBjYW4gbmV2ZXIgZ3JhbnRcbiAqIHRoZSBhY2NvdW50IG1vcmUgdGhhbiB0aGUgb3BlcmF0b3IgaG9sZHMuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVBZG1pblJvbGVJZChjbGllbnQ6IEFwaUNsaWVudCwgb3JnSWQ6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHJlcyA9IGF3YWl0IGNsaWVudC5nZXQ8dW5rbm93bj4oYC9hcGkvb3JnYW5pemF0aW9uLyR7b3JnSWR9L3JvbGVzYCk7XG4gIGNvbnN0IHJvbGVzID0gdW53cmFwPHsgcm9sZXM/OiBBcnJheTx7IGlkOiBzdHJpbmc7IGdyYW50c1JvbGU/OiBzdHJpbmcgfT4gfT4ocmVzKT8ucm9sZXMgPz8gW107XG4gIGNvbnN0IHJvbGUgPSByb2xlcy5maW5kKChyKSA9PiByLmdyYW50c1JvbGUgPT09ICdzdXBlcmFkbWluJykgPz8gcm9sZXMuZmluZCgocikgPT4gci5ncmFudHNSb2xlID09PSAnYWRtaW4nKTtcbiAgaWYgKCFyb2xlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBPcmdhbml6YXRpb24gJHtvcmdJZH0gaGFzIG5vIGFkbWluIHJvbGUgdG8gZ3JhbnQgdGhlIG1hY2hpbmUgc2VydmljZSBhY2NvdW50LmApO1xuICB9XG4gIHJldHVybiByb2xlLmlkO1xufVxuXG4vKiogRmluZCBhbiBleGlzdGluZyBhY2NvdW50IGJ5IG5hbWUgKHRoZSBuYW1lIGlzIHRoZSBzdGFibGUgbWFjaGluZSBpZGVudGlmaWVyKS4gKi9cbmFzeW5jIGZ1bmN0aW9uIGZpbmRBY2NvdW50KGNsaWVudDogQXBpQ2xpZW50LCBvcmdJZDogc3RyaW5nLCBuYW1lOiBzdHJpbmcpOiBQcm9taXNlPEFjY291bnRTdW1tYXJ5IHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IHJlcyA9IGF3YWl0IGNsaWVudC5nZXQ8dW5rbm93bj4oYC9hcGkvb3JnYW5pemF0aW9uLyR7b3JnSWR9L3NlcnZpY2UtYWNjb3VudHNgKTtcbiAgY29uc3QgYWNjb3VudHMgPSB1bndyYXA8eyBzZXJ2aWNlQWNjb3VudHM/OiBBY2NvdW50U3VtbWFyeVtdIH0+KHJlcyk/LnNlcnZpY2VBY2NvdW50cyA/PyBbXTtcbiAgcmV0dXJuIGFjY291bnRzLmZpbmQoKGEpID0+IGEubmFtZSA9PT0gbmFtZSk7XG59XG5cbi8qKlxuICogQ3JlYXRlIHRoZSBhY2NvdW50IGlmIGl0IGlzbid0IHRoZXJlIHlldCwgYW5kIHJldHVybiBpdHMgaWQuIElERU1QT1RFTlQ6IGFcbiAqIHJlLXJ1biByZXVzZXMgdGhlIGV4aXN0aW5nIGFjY291bnQgKGluY2x1ZGluZyB0aGUgNDA5IHJhY2Ugd2hlcmUgdHdvIHJ1bnNcbiAqIGNyZWF0ZSBpdCBhdCBvbmNlKSwgYW5kIG5ldmVyIHJlLWdyYW50cyBSb2xlcyDigJQgYW4gb3BlcmF0b3Igd2hvIG5hcnJvd2VkIHRoZVxuICogYWNjb3VudCdzIFJvbGVzIGJ5IGhhbmQgbXVzdCBub3QgaGF2ZSB0aGF0IHVuZG9uZSBieSB0aGUgbmV4dCBgc3RvcmUtdG9rZW5gLlxuICovXG5hc3luYyBmdW5jdGlvbiBlbnN1cmVBY2NvdW50KG9wdHM6IFByb3Zpc2lvbktleU9wdGlvbnMsIG9yZ0lkOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBleGlzdGluZyA9IGF3YWl0IGZpbmRBY2NvdW50KG9wdHMuY2xpZW50LCBvcmdJZCwgb3B0cy5hY2NvdW50TmFtZSk7XG4gIGlmIChleGlzdGluZykge1xuICAgIHByaW50SW5mbygnUmV1c2luZyB0aGUgZXhpc3Rpbmcgc2VydmljZSBhY2NvdW50JywgeyBhY2NvdW50OiBvcHRzLmFjY291bnROYW1lLCBpZDogZXhpc3RpbmcuaWQgfSk7XG4gICAgcmV0dXJuIGV4aXN0aW5nLmlkO1xuICB9XG5cbiAgY29uc3Qgcm9sZUlkcyA9IG9wdHMucm9sZXMgPT09ICdhZG1pbicgPyBbYXdhaXQgcmVzb2x2ZUFkbWluUm9sZUlkKG9wdHMuY2xpZW50LCBvcmdJZCldIDogW107XG4gIHRyeSB7XG4gICAgY29uc3QgcmVzID0gYXdhaXQgb3B0cy5jbGllbnQucG9zdDx1bmtub3duPihcbiAgICAgIGAvYXBpL29yZ2FuaXphdGlvbi8ke29yZ0lkfS9zZXJ2aWNlLWFjY291bnRzYCxcbiAgICAgIHsgbmFtZTogb3B0cy5hY2NvdW50TmFtZSwgZGVzY3JpcHRpb246IG9wdHMuZGVzY3JpcHRpb24sIHJvbGVJZHMgfSxcbiAgICAgIHsgW1NURVBfVVBfSEVBREVSXTogYXdhaXQgc3RlcFVwKG9wdHMuY2xpZW50LCBvcHRzLnBhc3N3b3JkKSB9LFxuICAgICk7XG4gICAgY29uc3QgYWNjb3VudCA9IHVud3JhcDx7IHNlcnZpY2VBY2NvdW50PzogeyBpZD86IHN0cmluZyB9IH0+KHJlcyk/LnNlcnZpY2VBY2NvdW50O1xuICAgIGlmICghYWNjb3VudD8uaWQpIHRocm93IG5ldyBFcnJvcignc2VydmljZS1hY2NvdW50IGNyZWF0ZSByZXR1cm5lZCBubyBpZCcpO1xuICAgIHByaW50U3VjY2VzcyhgQ3JlYXRlZCB0aGUgJyR7b3B0cy5hY2NvdW50TmFtZX0nIHNlcnZpY2UgYWNjb3VudGApO1xuICAgIHJldHVybiBhY2NvdW50LmlkO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBpZiAoc3RhdHVzT2YoZXJyKSA9PT0gNDA5KSB7XG4gICAgICBjb25zdCByYWNlZCA9IGF3YWl0IGZpbmRBY2NvdW50KG9wdHMuY2xpZW50LCBvcmdJZCwgb3B0cy5hY2NvdW50TmFtZSk7XG4gICAgICBpZiAocmFjZWQpIHJldHVybiByYWNlZC5pZDtcbiAgICB9XG4gICAgdGhyb3cgZXJyO1xuICB9XG59XG5cbi8qKiBSZXZva2Ugb25lIG9mIGFuIGFjY291bnQncyBrZXlzLiBCZXN0LWVmZm9ydCBieSBjb250cmFjdCDigJQgc2VlIHRoZSBjYWxsIHNpdGVzLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJldm9rZVNlcnZpY2VBY2NvdW50S2V5KFxuICBjbGllbnQ6IEFwaUNsaWVudCxcbiAgcGFzc3dvcmQ6IHN0cmluZyxcbiAgb3JnSWQ6IHN0cmluZyxcbiAgYWNjb3VudElkOiBzdHJpbmcsXG4gIGtleUlkOiBzdHJpbmcsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgY2xpZW50LmRlbGV0ZShcbiAgICBgL2FwaS9vcmdhbml6YXRpb24vJHtvcmdJZH0vc2VydmljZS1hY2NvdW50cy8ke2FjY291bnRJZH0va2V5cy8ke2tleUlkfWAsXG4gICAgeyBbU1RFUF9VUF9IRUFERVJdOiBhd2FpdCBzdGVwVXAoY2xpZW50LCBwYXNzd29yZCkgfSxcbiAgKTtcbn1cblxuLyoqXG4gKiBJc3N1ZSBhIGtleSwgc2VsZi1oZWFsaW5nIHBhc3QgdGhlIHBlci1hY2NvdW50IGFjdGl2ZS1rZXkgY2FwOiBhdCB0aGUgY2FwIHRoZVxuICogT0xERVNUIGFjdGl2ZSBrZXkgaXMgcmV0aXJlZCBhbmQgdGhlIG1pbnQgcmV0cmllZCBvbmNlLiBUaGUgY2FwIGV4aXN0cyB0byBib3VuZFxuICogYSBjb21wcm9taXNlLCBub3QgdG8gbWFrZSBwcm92aXNpb25pbmcgZmFpbCwgYW5kIHRoZSBjcmVkZW50aWFsIGEgcmUtcnVuIGlzXG4gKiByZXBsYWNpbmcgaXMgdGhlIHZlcnkgdGhpbmcgdGhhdCBzaG91bGQgZ28uXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGlzc3VlS2V5KFxuICBvcHRzOiBQcm92aXNpb25LZXlPcHRpb25zLFxuICBvcmdJZDogc3RyaW5nLFxuICBhY2NvdW50SWQ6IHN0cmluZyxcbik6IFByb21pc2U8eyBrZXk6IHN0cmluZzsgYWNjZXNzS2V5OiB7IGlkOiBzdHJpbmc7IGV4cGlyZXNBdDogc3RyaW5nOyBzY29wZTogc3RyaW5nIHwgbnVsbCB9IH0+IHtcbiAgY29uc3QgYm9keSA9IHtcbiAgICBuYW1lOiBvcHRzLmtleU5hbWUsXG4gICAgZXhwaXJlc0luOiBvcHRzLmV4cGlyZXNJblNlY29uZHMsXG4gICAgLi4uKG9wdHMuc2NvcGUgPyB7IHNjb3BlOiBvcHRzLnNjb3BlIH0gOiB7fSksXG4gIH07XG4gIGNvbnN0IG1pbnQgPSBhc3luYyAoKSA9PiB1bndyYXA8eyBrZXk6IHN0cmluZzsgYWNjZXNzS2V5OiB7IGlkOiBzdHJpbmc7IGV4cGlyZXNBdDogc3RyaW5nOyBzY29wZTogc3RyaW5nIHwgbnVsbCB9IH0+KFxuICAgIGF3YWl0IG9wdHMuY2xpZW50LnBvc3Q8dW5rbm93bj4oXG4gICAgICBgL2FwaS9vcmdhbml6YXRpb24vJHtvcmdJZH0vc2VydmljZS1hY2NvdW50cy8ke2FjY291bnRJZH0va2V5c2AsXG4gICAgICBib2R5LFxuICAgICAgeyBbU1RFUF9VUF9IRUFERVJdOiBhd2FpdCBzdGVwVXAob3B0cy5jbGllbnQsIG9wdHMucGFzc3dvcmQpIH0sXG4gICAgKSxcbiAgKTtcblxuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCBtaW50KCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGlmIChzdGF0dXNPZihlcnIpICE9PSA0MDkgJiYgZXJyb3JDb2RlT2YoZXJyKSAhPT0gJ1NBX0tFWV9MSU1JVCcpIHRocm93IGVycjtcbiAgICBjb25zdCBhY2NvdW50ID0gYXdhaXQgZmluZEFjY291bnQob3B0cy5jbGllbnQsIG9yZ0lkLCBvcHRzLmFjY291bnROYW1lKTtcbiAgICBjb25zdCBvbGRlc3QgPSAoYWNjb3VudD8ua2V5cyA/PyBbXSlcbiAgICAgIC5maWx0ZXIoKGspID0+IGsuc3RhdHVzID09PSAnYWN0aXZlJylcbiAgICAgIC5zb3J0KChhLCBiKSA9PiBhLmNyZWF0ZWRBdC5sb2NhbGVDb21wYXJlKGIuY3JlYXRlZEF0KSlbMF07XG4gICAgaWYgKCFvbGRlc3QpIHRocm93IGVycjtcbiAgICBwcmludFdhcm5pbmcoYFNlcnZpY2UgYWNjb3VudCAnJHtvcHRzLmFjY291bnROYW1lfScgaXMgYXQgaXRzIGFjdGl2ZS1rZXkgbGltaXQg4oCUIHJldGlyaW5nIGl0cyBvbGRlc3Qga2V5ICgke29sZGVzdC5pZH0pLmApO1xuICAgIGF3YWl0IHJldm9rZVNlcnZpY2VBY2NvdW50S2V5KG9wdHMuY2xpZW50LCBvcHRzLnBhc3N3b3JkLCBvcmdJZCwgYWNjb3VudElkLCBvbGRlc3QuaWQpO1xuICAgIHJldHVybiBtaW50KCk7XG4gIH1cbn1cblxuLyoqXG4gKiBFbnN1cmUgdGhlIG5hbWVkIHNlcnZpY2UgYWNjb3VudCBleGlzdHMgYW5kIGlzc3VlIGl0IGEgZnJlc2gga2V5LlxuICpcbiAqIERlbGliZXJhdGVseSBkb2VzIE5PVCByZXZva2UgYW55dGhpbmcgdGhlIGNhbGxlciB3YXMgcHJldmlvdXNseSBob2xkaW5nOiB0aGVcbiAqIGNhbGxlciBzdG9yZXMgdGhlIG5ldyBrZXkgZmlyc3QgYW5kIHJldGlyZXMgdGhlIG9sZCBvbmUgYWZ0ZXJ3YXJkcyAoc2VlXG4gKiBgc3RvcmUtdG9rZW5gKSwgc28gYSBmYWlsdXJlIGF0IGFueSBwb2ludCBsZWF2ZXMgYSB3b3JraW5nIGNyZWRlbnRpYWwgaW4gdGhlXG4gKiBzZWNyZXQgcmF0aGVyIHRoYW4gYW4gZW1wdHkgb25lLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHJvdmlzaW9uU2VydmljZUFjY291bnRLZXkob3B0czogUHJvdmlzaW9uS2V5T3B0aW9ucyk6IFByb21pc2U8UHJvdmlzaW9uZWRLZXk+IHtcbiAgY29uc3Qgb3JnYW5pemF0aW9uSWQgPSBhd2FpdCByZXNvbHZlT3JnYW5pemF0aW9uSWQob3B0cy5jbGllbnQpO1xuICBjb25zdCBzZXJ2aWNlQWNjb3VudElkID0gYXdhaXQgZW5zdXJlQWNjb3VudChvcHRzLCBvcmdhbml6YXRpb25JZCk7XG4gIGNvbnN0IHsga2V5LCBhY2Nlc3NLZXkgfSA9IGF3YWl0IGlzc3VlS2V5KG9wdHMsIG9yZ2FuaXphdGlvbklkLCBzZXJ2aWNlQWNjb3VudElkKTtcbiAgcmV0dXJuIHtcbiAgICBrZXksXG4gICAga2V5SWQ6IGFjY2Vzc0tleS5pZCxcbiAgICBzZXJ2aWNlQWNjb3VudElkLFxuICAgIHNlcnZpY2VBY2NvdW50TmFtZTogb3B0cy5hY2NvdW50TmFtZSxcbiAgICBvcmdhbml6YXRpb25JZCxcbiAgICBleHBpcmVzQXQ6IGFjY2Vzc0tleS5leHBpcmVzQXQsXG4gICAgc2NvcGU6IGFjY2Vzc0tleS5zY29wZSA/PyBudWxsLFxuICB9O1xufVxuIl19
package/package.json CHANGED
@@ -44,9 +44,9 @@
44
44
  "typescript": "npm:@typescript/typescript6@^6.0.2",
45
45
  "yaml": "2.9.0",
46
46
  "zod": "4.4.3",
47
- "@pipeline-builder/api-core": "4.1.3",
48
- "@pipeline-builder/pipeline-core": "4.1.4",
49
- "@pipeline-builder/ai-core": "4.1.3"
47
+ "@pipeline-builder/ai-core": "4.1.5",
48
+ "@pipeline-builder/pipeline-core": "4.1.6",
49
+ "@pipeline-builder/api-core": "4.1.5"
50
50
  },
51
51
  "keywords": [
52
52
  "ci-cd",
@@ -101,7 +101,7 @@
101
101
  "access": "public",
102
102
  "registry": "https://registry.npmjs.org/"
103
103
  },
104
- "version": "4.1.4",
104
+ "version": "4.1.6",
105
105
  "bugs": {
106
106
  "url": "https://github.com/mwashburn160/pipeline-builder/issues"
107
107
  },
@@ -1,15 +0,0 @@
1
- /**
2
- * Check if auth attempts for this identifier are currently rate-limited.
3
- * @returns null if allowed, or a message string if blocked.
4
- */
5
- export declare function checkAuthRateLimit(identifier?: string, baseUrl?: string): string | null;
6
- /**
7
- * Record a successful auth for this identifier — clears its failure counter.
8
- */
9
- export declare function recordAuthSuccess(identifier?: string, baseUrl?: string): void;
10
- /**
11
- * Record a failed auth attempt for this identifier. After MAX_FAILURES, locks out
12
- * that identifier for COOLDOWN_MS.
13
- */
14
- export declare function recordAuthFailure(identifier?: string, baseUrl?: string): void;
15
- //# sourceMappingURL=rate-limiter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":"AAgHA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBvF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAM7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAgB7E"}
@@ -1,164 +0,0 @@
1
- // Copyright 2026 Pipeline Builder Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
- import crypto from 'crypto';
4
- import fs from 'fs';
5
- import os from 'os';
6
- import path from 'path';
7
- /**
8
- * Simple file-based rate limiter for auth operations.
9
- * Tracks failed attempts and enforces a cooldown period after too many failures.
10
- * State is persisted in a temp file so it survives across CLI invocations.
11
- *
12
- * State is keyed per (baseUrl, identifier) so a failed login for one account does
13
- * NOT lock out another account on a shared workstation / CI host. Calls without an
14
- * identifier fall back to a shared `_default` bucket (coarse anti-bruteforce).
15
- */
16
- const STATE_FILE = path.join(os.tmpdir(), '.pipeline-manager-auth-state.json');
17
- // Directory-as-mutex guarding the read-modify-write. `mkdir` is atomic on every
18
- // platform, so concurrent CLI invocations serialize on it instead of racing (a
19
- // lost increment would under-count toward lockout — see withLock).
20
- const LOCK_DIR = `${STATE_FILE}.lock`;
21
- const MAX_FAILURES = 5;
22
- const COOLDOWN_MS = 60_000; // 1 minute
23
- const PRUNE_MS = 60 * 60_000; // drop buckets idle for >1h to bound file growth
24
- const LOCK_STALE_MS = 5_000; // reclaim a lock left by a crashed process
25
- const LOCK_WAIT_MS = 1_000; // give up waiting after this (best-effort: proceed unlocked)
26
- const LOCK_RETRY_MS = 15; // spin interval while the lock is held
27
- const EMPTY = { failures: 0, lastFailure: 0, lockedUntil: 0 };
28
- /** Stable, non-reversible bucket key for a (baseUrl, identifier) pair. */
29
- function keyFor(identifier, baseUrl) {
30
- if (!identifier)
31
- return '_default';
32
- return crypto.createHash('sha256').update(`${baseUrl ?? ''}\n${identifier}`).digest('hex').slice(0, 16);
33
- }
34
- function readMap() {
35
- try {
36
- if (fs.existsSync(STATE_FILE)) {
37
- const parsed = JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
38
- if (parsed && typeof parsed === 'object')
39
- return parsed;
40
- }
41
- }
42
- catch { /* ignore corrupt state */ }
43
- return {};
44
- }
45
- function writeMap(map) {
46
- const now = Date.now();
47
- // Prune idle buckets so the file doesn't grow unbounded across many identifiers.
48
- for (const [k, s] of Object.entries(map)) {
49
- if (s.lockedUntil <= now && now - s.lastFailure > PRUNE_MS)
50
- delete map[k];
51
- }
52
- try {
53
- // Write to a per-process temp file then rename: rename is atomic on the same
54
- // filesystem, so a concurrent reader never observes a torn/half-written file.
55
- const tmp = `${STATE_FILE}.${process.pid}.tmp`;
56
- fs.writeFileSync(tmp, JSON.stringify(map), { mode: 0o600 });
57
- fs.renameSync(tmp, STATE_FILE);
58
- }
59
- catch { /* best-effort */ }
60
- }
61
- /** Block the current thread for `ms` without a busy-wait (CLI-only, short waits). */
62
- function sleep(ms) {
63
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
64
- }
65
- /**
66
- * Run `fn` while holding a cross-process advisory lock so the read-modify-write in
67
- * recordAuthFailure/Success can't lose an update to a concurrent CLI invocation
68
- * (the TOCTOU that let parallel runs under-count toward lockout). Best-effort: if
69
- * the lock can't be acquired within LOCK_WAIT_MS we proceed anyway rather than
70
- * block the user — server-side rate limiting remains the real enforcement.
71
- */
72
- function withLock(fn) {
73
- const deadline = Date.now() + LOCK_WAIT_MS;
74
- let held = false;
75
- while (Date.now() < deadline) {
76
- try {
77
- fs.mkdirSync(LOCK_DIR);
78
- held = true;
79
- break;
80
- }
81
- catch {
82
- // Held elsewhere — reclaim it if it's stale (owner crashed without cleanup),
83
- // otherwise wait a beat and retry.
84
- try {
85
- if (Date.now() - fs.statSync(LOCK_DIR).mtimeMs > LOCK_STALE_MS) {
86
- fs.rmdirSync(LOCK_DIR);
87
- continue;
88
- }
89
- }
90
- catch { /* lock vanished between calls — just retry */ }
91
- sleep(LOCK_RETRY_MS);
92
- }
93
- }
94
- try {
95
- return fn();
96
- }
97
- finally {
98
- if (held) {
99
- try {
100
- fs.rmdirSync(LOCK_DIR);
101
- }
102
- catch { /* already gone */ }
103
- }
104
- }
105
- }
106
- /**
107
- * Check if auth attempts for this identifier are currently rate-limited.
108
- * @returns null if allowed, or a message string if blocked.
109
- */
110
- export function checkAuthRateLimit(identifier, baseUrl) {
111
- const key = keyFor(identifier, baseUrl);
112
- const map = readMap();
113
- const state = map[key] ?? EMPTY;
114
- const now = Date.now();
115
- if (state.lockedUntil > now) {
116
- const waitSec = Math.ceil((state.lockedUntil - now) / 1000);
117
- return `Too many failed login attempts. Try again in ${waitSec}s.`;
118
- }
119
- // Reset this bucket once the cooldown window has passed. Re-read under the lock
120
- // so we don't clobber a concurrent recordAuthFailure for another bucket.
121
- if (state.lastFailure !== 0 && now - state.lastFailure > COOLDOWN_MS) {
122
- withLock(() => {
123
- const m = readMap();
124
- const s = m[key];
125
- if (s && s.lastFailure !== 0 && Date.now() - s.lastFailure > COOLDOWN_MS) {
126
- delete m[key];
127
- writeMap(m);
128
- }
129
- });
130
- }
131
- return null;
132
- }
133
- /**
134
- * Record a successful auth for this identifier — clears its failure counter.
135
- */
136
- export function recordAuthSuccess(identifier, baseUrl) {
137
- withLock(() => {
138
- const map = readMap();
139
- delete map[keyFor(identifier, baseUrl)];
140
- writeMap(map);
141
- });
142
- }
143
- /**
144
- * Record a failed auth attempt for this identifier. After MAX_FAILURES, locks out
145
- * that identifier for COOLDOWN_MS.
146
- */
147
- export function recordAuthFailure(identifier, baseUrl) {
148
- const key = keyFor(identifier, baseUrl);
149
- // Read-modify-write under the lock: a concurrent CLI invocation must not read the
150
- // same pre-increment count and overwrite this one (which would under-count).
151
- withLock(() => {
152
- const map = readMap();
153
- const now = Date.now();
154
- const state = { ...(map[key] ?? EMPTY) };
155
- state.failures += 1;
156
- state.lastFailure = now;
157
- if (state.failures >= MAX_FAILURES) {
158
- state.lockedUntil = now + COOLDOWN_MS;
159
- }
160
- map[key] = state;
161
- writeMap(map);
162
- });
163
- }
164
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmF0ZS1saW1pdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3JhdGUtbGltaXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUM1QixPQUFPLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDcEIsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUV4Qjs7Ozs7Ozs7R0FRRztBQUVILE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLG1DQUFtQyxDQUFDLENBQUM7QUFDL0UsZ0ZBQWdGO0FBQ2hGLCtFQUErRTtBQUMvRSxtRUFBbUU7QUFDbkUsTUFBTSxRQUFRLEdBQUcsR0FBRyxVQUFVLE9BQU8sQ0FBQztBQUN0QyxNQUFNLFlBQVksR0FBRyxDQUFDLENBQUM7QUFDdkIsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLENBQUMsV0FBVztBQUN2QyxNQUFNLFFBQVEsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUMsaURBQWlEO0FBQy9FLE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxDQUFDLDJDQUEyQztBQUN4RSxNQUFNLFlBQVksR0FBRyxLQUFLLENBQUMsQ0FBQyw2REFBNkQ7QUFDekYsTUFBTSxhQUFhLEdBQUcsRUFBRSxDQUFDLENBQUMsdUNBQXVDO0FBVWpFLE1BQU0sS0FBSyxHQUFjLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxXQUFXLEVBQUUsQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLEVBQUUsQ0FBQztBQUV6RSwwRUFBMEU7QUFDMUUsU0FBUyxNQUFNLENBQUMsVUFBbUIsRUFBRSxPQUFnQjtJQUNuRCxJQUFJLENBQUMsVUFBVTtRQUFFLE9BQU8sVUFBVSxDQUFDO0lBQ25DLE9BQU8s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R0FBRyxDQUFDO1FBQ3hCLElBQUksS0FBSyxDQUFDLFFBQVEsSUFBSSxZQUFZLEVBQUUsQ0FBQztZQUNuQyxLQUFLLENBQUMsV0FBVyxHQUFHLEdBQUcsR0FBRyxXQUFXLENBQUM7UUFDeEMsQ0FBQztRQUNELEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUM7UUFDakIsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgZnMgZnJvbSAnZnMnO1xuaW1wb3J0IG9zIGZyb20gJ29zJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuXG4vKipcbiAqIFNpbXBsZSBmaWxlLWJhc2VkIHJhdGUgbGltaXRlciBmb3IgYXV0aCBvcGVyYXRpb25zLlxuICogVHJhY2tzIGZhaWxlZCBhdHRlbXB0cyBhbmQgZW5mb3JjZXMgYSBjb29sZG93biBwZXJpb2QgYWZ0ZXIgdG9vIG1hbnkgZmFpbHVyZXMuXG4gKiBTdGF0ZSBpcyBwZXJzaXN0ZWQgaW4gYSB0ZW1wIGZpbGUgc28gaXQgc3Vydml2ZXMgYWNyb3NzIENMSSBpbnZvY2F0aW9ucy5cbiAqXG4gKiBTdGF0ZSBpcyBrZXllZCBwZXIgKGJhc2VVcmwsIGlkZW50aWZpZXIpIHNvIGEgZmFpbGVkIGxvZ2luIGZvciBvbmUgYWNjb3VudCBkb2VzXG4gKiBOT1QgbG9jayBvdXQgYW5vdGhlciBhY2NvdW50IG9uIGEgc2hhcmVkIHdvcmtzdGF0aW9uIC8gQ0kgaG9zdC4gQ2FsbHMgd2l0aG91dCBhblxuICogaWRlbnRpZmllciBmYWxsIGJhY2sgdG8gYSBzaGFyZWQgYF9kZWZhdWx0YCBidWNrZXQgKGNvYXJzZSBhbnRpLWJydXRlZm9yY2UpLlxuICovXG5cbmNvbnN0IFNUQVRFX0ZJTEUgPSBwYXRoLmpvaW4ob3MudG1wZGlyKCksICcucGlwZWxpbmUtbWFuYWdlci1hdXRoLXN0YXRlLmpzb24nKTtcbi8vIERpcmVjdG9yeS1hcy1tdXRleCBndWFyZGluZyB0aGUgcmVhZC1tb2RpZnktd3JpdGUuIGBta2RpcmAgaXMgYXRvbWljIG9uIGV2ZXJ5XG4vLyBwbGF0Zm9ybSwgc28gY29uY3VycmVudCBDTEkgaW52b2NhdGlvbnMgc2VyaWFsaXplIG9uIGl0IGluc3RlYWQgb2YgcmFjaW5nIChhXG4vLyBsb3N0IGluY3JlbWVudCB3b3VsZCB1bmRlci1jb3VudCB0b3dhcmQgbG9ja291dCDigJQgc2VlIHdpdGhMb2NrKS5cbmNvbnN0IExPQ0tfRElSID0gYCR7U1RBVEVfRklMRX0ubG9ja2A7XG5jb25zdCBNQVhfRkFJTFVSRVMgPSA1O1xuY29uc3QgQ09PTERPV05fTVMgPSA2MF8wMDA7IC8vIDEgbWludXRlXG5jb25zdCBQUlVORV9NUyA9IDYwICogNjBfMDAwOyAvLyBkcm9wIGJ1Y2tldHMgaWRsZSBmb3IgPjFoIHRvIGJvdW5kIGZpbGUgZ3Jvd3RoXG5jb25zdCBMT0NLX1NUQUxFX01TID0gNV8wMDA7IC8vIHJlY2xhaW0gYSBsb2NrIGxlZnQgYnkgYSBjcmFzaGVkIHByb2Nlc3NcbmNvbnN0IExPQ0tfV0FJVF9NUyA9IDFfMDAwOyAvLyBnaXZlIHVwIHdhaXRpbmcgYWZ0ZXIgdGhpcyAoYmVzdC1lZmZvcnQ6IHByb2NlZWQgdW5sb2NrZWQpXG5jb25zdCBMT0NLX1JFVFJZX01TID0gMTU7IC8vIHNwaW4gaW50ZXJ2YWwgd2hpbGUgdGhlIGxvY2sgaXMgaGVsZFxuXG5pbnRlcmZhY2UgQXV0aFN0YXRlIHtcbiAgZmFpbHVyZXM6IG51bWJlcjtcbiAgbGFzdEZhaWx1cmU6IG51bWJlcjtcbiAgbG9ja2VkVW50aWw6IG51bWJlcjtcbn1cblxudHlwZSBTdGF0ZU1hcCA9IFJlY29yZDxzdHJpbmcsIEF1dGhTdGF0ZT47XG5cbmNvbnN0IEVNUFRZOiBBdXRoU3RhdGUgPSB7IGZhaWx1cmVzOiAwLCBsYXN0RmFpbHVyZTogMCwgbG9ja2VkVW50aWw6IDAgfTtcblxuLyoqIFN0YWJsZSwgbm9uLXJldmVyc2libGUgYnVja2V0IGtleSBmb3IgYSAoYmFzZVVybCwgaWRlbnRpZmllcikgcGFpci4gKi9cbmZ1bmN0aW9uIGtleUZvcihpZGVudGlmaWVyPzogc3RyaW5nLCBiYXNlVXJsPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgaWYgKCFpZGVudGlmaWVyKSByZXR1cm4gJ19kZWZhdWx0JztcbiAgcmV0dXJuIGNyeXB0by5jcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoYCR7YmFzZVVybCA/PyAnJ31cXG4ke2lkZW50aWZpZXJ9YCkuZGlnZXN0KCdoZXgnKS5zbGljZSgwLCAxNik7XG59XG5cbmZ1bmN0aW9uIHJlYWRNYXAoKTogU3RhdGVNYXAge1xuICB0cnkge1xuICAgIGlmIChmcy5leGlzdHNTeW5jKFNUQVRFX0ZJTEUpKSB7XG4gICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKGZzLnJlYWRGaWxlU3luYyhTVEFURV9GSUxFLCAndXRmLTgnKSkgYXMgdW5rbm93bjtcbiAgICAgIGlmIChwYXJzZWQgJiYgdHlwZW9mIHBhcnNlZCA9PT0gJ29iamVjdCcpIHJldHVybiBwYXJzZWQgYXMgU3RhdGVNYXA7XG4gICAgfVxuICB9IGNhdGNoIHsgLyogaWdub3JlIGNvcnJ1cHQgc3RhdGUgKi8gfVxuICByZXR1cm4ge307XG59XG5cbmZ1bmN0aW9uIHdyaXRlTWFwKG1hcDogU3RhdGVNYXApOiB2b2lkIHtcbiAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgLy8gUHJ1bmUgaWRsZSBidWNrZXRzIHNvIHRoZSBmaWxlIGRvZXNuJ3QgZ3JvdyB1bmJvdW5kZWQgYWNyb3NzIG1hbnkgaWRlbnRpZmllcnMuXG4gIGZvciAoY29uc3QgW2ssIHNdIG9mIE9iamVjdC5lbnRyaWVzKG1hcCkpIHtcbiAgICBpZiAocy5sb2NrZWRVbnRpbCA8PSBub3cgJiYgbm93IC0gcy5sYXN0RmFpbHVyZSA+IFBSVU5FX01TKSBkZWxldGUgbWFwW2tdO1xuICB9XG4gIHRyeSB7XG4gICAgLy8gV3JpdGUgdG8gYSBwZXItcHJvY2VzcyB0ZW1wIGZpbGUgdGhlbiByZW5hbWU6IHJlbmFtZSBpcyBhdG9taWMgb24gdGhlIHNhbWVcbiAgICAvLyBmaWxlc3lzdGVtLCBzbyBhIGNvbmN1cnJlbnQgcmVhZGVyIG5ldmVyIG9ic2VydmVzIGEgdG9ybi9oYWxmLXdyaXR0ZW4gZmlsZS5cbiAgICBjb25zdCB0bXAgPSBgJHtTVEFURV9GSUxFfS4ke3Byb2Nlc3MucGlkfS50bXBgO1xuICAgIGZzLndyaXRlRmlsZVN5bmModG1wLCBKU09OLnN0cmluZ2lmeShtYXApLCB7IG1vZGU6IDBvNjAwIH0pO1xuICAgIGZzLnJlbmFtZVN5bmModG1wLCBTVEFURV9GSUxFKTtcbiAgfSBjYXRjaCB7IC8qIGJlc3QtZWZmb3J0ICovIH1cbn1cblxuLyoqIEJsb2NrIHRoZSBjdXJyZW50IHRocmVhZCBmb3IgYG1zYCB3aXRob3V0IGEgYnVzeS13YWl0IChDTEktb25seSwgc2hvcnQgd2FpdHMpLiAqL1xuZnVuY3Rpb24gc2xlZXAobXM6IG51bWJlcik6IHZvaWQge1xuICBBdG9taWNzLndhaXQobmV3IEludDMyQXJyYXkobmV3IFNoYXJlZEFycmF5QnVmZmVyKDQpKSwgMCwgMCwgbXMpO1xufVxuXG4vKipcbiAqIFJ1biBgZm5gIHdoaWxlIGhvbGRpbmcgYSBjcm9zcy1wcm9jZXNzIGFkdmlzb3J5IGxvY2sgc28gdGhlIHJlYWQtbW9kaWZ5LXdyaXRlIGluXG4gKiByZWNvcmRBdXRoRmFpbHVyZS9TdWNjZXNzIGNhbid0IGxvc2UgYW4gdXBkYXRlIHRvIGEgY29uY3VycmVudCBDTEkgaW52b2NhdGlvblxuICogKHRoZSBUT0NUT1UgdGhhdCBsZXQgcGFyYWxsZWwgcnVucyB1bmRlci1jb3VudCB0b3dhcmQgbG9ja291dCkuIEJlc3QtZWZmb3J0OiBpZlxuICogdGhlIGxvY2sgY2FuJ3QgYmUgYWNxdWlyZWQgd2l0aGluIExPQ0tfV0FJVF9NUyB3ZSBwcm9jZWVkIGFueXdheSByYXRoZXIgdGhhblxuICogYmxvY2sgdGhlIHVzZXIg4oCUIHNlcnZlci1zaWRlIHJhdGUgbGltaXRpbmcgcmVtYWlucyB0aGUgcmVhbCBlbmZvcmNlbWVudC5cbiAqL1xuZnVuY3Rpb24gd2l0aExvY2s8VD4oZm46ICgpID0+IFQpOiBUIHtcbiAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgTE9DS19XQUlUX01TO1xuICBsZXQgaGVsZCA9IGZhbHNlO1xuICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRsaW5lKSB7XG4gICAgdHJ5IHtcbiAgICAgIGZzLm1rZGlyU3luYyhMT0NLX0RJUik7XG4gICAgICBoZWxkID0gdHJ1ZTtcbiAgICAgIGJyZWFrO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gSGVsZCBlbHNld2hlcmUg4oCUIHJlY2xhaW0gaXQgaWYgaXQncyBzdGFsZSAob3duZXIgY3Jhc2hlZCB3aXRob3V0IGNsZWFudXApLFxuICAgICAgLy8gb3RoZXJ3aXNlIHdhaXQgYSBiZWF0IGFuZCByZXRyeS5cbiAgICAgIHRyeSB7XG4gICAgICAgIGlmIChEYXRlLm5vdygpIC0gZnMuc3RhdFN5bmMoTE9DS19ESVIpLm10aW1lTXMgPiBMT0NLX1NUQUxFX01TKSB7XG4gICAgICAgICAgZnMucm1kaXJTeW5jKExPQ0tfRElSKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCB7IC8qIGxvY2sgdmFuaXNoZWQgYmV0d2VlbiBjYWxscyDigJQganVzdCByZXRyeSAqLyB9XG4gICAgICBzbGVlcChMT0NLX1JFVFJZX01TKTtcbiAgICB9XG4gIH1cbiAgdHJ5IHtcbiAgICByZXR1cm4gZm4oKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBpZiAoaGVsZCkge1xuICAgICAgdHJ5IHsgZnMucm1kaXJTeW5jKExPQ0tfRElSKTsgfSBjYXRjaCB7IC8qIGFscmVhZHkgZ29uZSAqLyB9XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ2hlY2sgaWYgYXV0aCBhdHRlbXB0cyBmb3IgdGhpcyBpZGVudGlmaWVyIGFyZSBjdXJyZW50bHkgcmF0ZS1saW1pdGVkLlxuICogQHJldHVybnMgbnVsbCBpZiBhbGxvd2VkLCBvciBhIG1lc3NhZ2Ugc3RyaW5nIGlmIGJsb2NrZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjaGVja0F1dGhSYXRlTGltaXQoaWRlbnRpZmllcj86IHN0cmluZywgYmFzZVVybD86IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xuICBjb25zdCBrZXkgPSBrZXlGb3IoaWRlbnRpZmllciwgYmFzZVVybCk7XG4gIGNvbnN0IG1hcCA9IHJlYWRNYXAoKTtcbiAgY29uc3Qgc3RhdGUgPSBtYXBba2V5XSA/PyBFTVBUWTtcbiAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcblxuICBpZiAoc3RhdGUubG9ja2VkVW50aWwgPiBub3cpIHtcbiAgICBjb25zdCB3YWl0U2VjID0gTWF0aC5jZWlsKChzdGF0ZS5sb2NrZWRVbnRpbCAtIG5vdykgLyAxMDAwKTtcbiAgICByZXR1cm4gYFRvbyBtYW55IGZhaWxlZCBsb2dpbiBhdHRlbXB0cy4gVHJ5IGFnYWluIGluICR7d2FpdFNlY31zLmA7XG4gIH1cblxuICAvLyBSZXNldCB0aGlzIGJ1Y2tldCBvbmNlIHRoZSBjb29sZG93biB3aW5kb3cgaGFzIHBhc3NlZC4gUmUtcmVhZCB1bmRlciB0aGUgbG9ja1xuICAvLyBzbyB3ZSBkb24ndCBjbG9iYmVyIGEgY29uY3VycmVudCByZWNvcmRBdXRoRmFpbHVyZSBmb3IgYW5vdGhlciBidWNrZXQuXG4gIGlmIChzdGF0ZS5sYXN0RmFpbHVyZSAhPT0gMCAmJiBub3cgLSBzdGF0ZS5sYXN0RmFpbHVyZSA+IENPT0xET1dOX01TKSB7XG4gICAgd2l0aExvY2soKCkgPT4ge1xuICAgICAgY29uc3QgbSA9IHJlYWRNYXAoKTtcbiAgICAgIGNvbnN0IHMgPSBtW2tleV07XG4gICAgICBpZiAocyAmJiBzLmxhc3RGYWlsdXJlICE9PSAwICYmIERhdGUubm93KCkgLSBzLmxhc3RGYWlsdXJlID4gQ09PTERPV05fTVMpIHtcbiAgICAgICAgZGVsZXRlIG1ba2V5XTtcbiAgICAgICAgd3JpdGVNYXAobSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBSZWNvcmQgYSBzdWNjZXNzZnVsIGF1dGggZm9yIHRoaXMgaWRlbnRpZmllciDigJQgY2xlYXJzIGl0cyBmYWlsdXJlIGNvdW50ZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWNvcmRBdXRoU3VjY2VzcyhpZGVudGlmaWVyPzogc3RyaW5nLCBiYXNlVXJsPzogc3RyaW5nKTogdm9pZCB7XG4gIHdpdGhMb2NrKCgpID0+IHtcbiAgICBjb25zdCBtYXAgPSByZWFkTWFwKCk7XG4gICAgZGVsZXRlIG1hcFtrZXlGb3IoaWRlbnRpZmllciwgYmFzZVVybCldO1xuICAgIHdyaXRlTWFwKG1hcCk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFJlY29yZCBhIGZhaWxlZCBhdXRoIGF0dGVtcHQgZm9yIHRoaXMgaWRlbnRpZmllci4gQWZ0ZXIgTUFYX0ZBSUxVUkVTLCBsb2NrcyBvdXRcbiAqIHRoYXQgaWRlbnRpZmllciBmb3IgQ09PTERPV05fTVMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWNvcmRBdXRoRmFpbHVyZShpZGVudGlmaWVyPzogc3RyaW5nLCBiYXNlVXJsPzogc3RyaW5nKTogdm9pZCB7XG4gIGNvbnN0IGtleSA9IGtleUZvcihpZGVudGlmaWVyLCBiYXNlVXJsKTtcbiAgLy8gUmVhZC1tb2RpZnktd3JpdGUgdW5kZXIgdGhlIGxvY2s6IGEgY29uY3VycmVudCBDTEkgaW52b2NhdGlvbiBtdXN0IG5vdCByZWFkIHRoZVxuICAvLyBzYW1lIHByZS1pbmNyZW1lbnQgY291bnQgYW5kIG92ZXJ3cml0ZSB0aGlzIG9uZSAod2hpY2ggd291bGQgdW5kZXItY291bnQpLlxuICB3aXRoTG9jaygoKSA9PiB7XG4gICAgY29uc3QgbWFwID0gcmVhZE1hcCgpO1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3Qgc3RhdGUgPSB7IC4uLihtYXBba2V5XSA/PyBFTVBUWSkgfTtcbiAgICBzdGF0ZS5mYWlsdXJlcyArPSAxO1xuICAgIHN0YXRlLmxhc3RGYWlsdXJlID0gbm93O1xuICAgIGlmIChzdGF0ZS5mYWlsdXJlcyA+PSBNQVhfRkFJTFVSRVMpIHtcbiAgICAgIHN0YXRlLmxvY2tlZFVudGlsID0gbm93ICsgQ09PTERPV05fTVM7XG4gICAgfVxuICAgIG1hcFtrZXldID0gc3RhdGU7XG4gICAgd3JpdGVNYXAobWFwKTtcbiAgfSk7XG59XG4iXX0=