@pipeline-builder/pipeline-manager 4.1.4 → 4.1.5

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
@@ -1 +1 @@
1
- {"version":3,"file":"token-renew-handler.d.ts","sourceRoot":"","sources":["../../src/lambda/token-renew-handler.ts"],"names":[],"mappings":"AA4DA,eAAO,MAAM,OAAO,QAAa,OAAO,CAAC,IAAI,CAyE5C,CAAC"}
1
+ {"version":3,"file":"token-renew-handler.d.ts","sourceRoot":"","sources":["../../src/lambda/token-renew-handler.ts"],"names":[],"mappings":"AAgHA,eAAO,MAAM,OAAO,QAAa,OAAO,CAAC,IAAI,CA+E5C,CAAC"}
@@ -1,125 +1,162 @@
1
1
  // Copyright 2026 Pipeline Builder Contributors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  /**
4
- * Lambda handler that re-mints the platform JWT in Secrets Manager so it never
5
- * lapses. Deployed + scheduled (once a day) by `pipeline-manager infra store-token
6
- * --schedule`. Rather than re-implementing the token flow, it is a thin
7
- * orchestrator that reuses the tested CLI — each run:
4
+ * Scheduled KEY ROTATOR for the service-account credential in Secrets Manager
5
+ * (#N2). Deployed + scheduled (once a day) by `pipeline-manager infra store-token
6
+ * --schedule`.
8
7
  *
9
- * 1. Point the `@pipeline-builder` npm scope at public npm.
10
- * 2. Download `@pipeline-builder/pipeline-manager` from npm into /tmp.
11
- * 3. Read the current platform JWT from the secret.
12
- * 4. Run `pipeline-manager infra store-token`, which mints a fresh long-lived token
13
- * via /api/user/generate-token and writes it back to the same secret.
8
+ * It used to re-mint a person's machine-session JWT by npm-installing the CLI at
9
+ * runtime and shelling out to `store-token`. There is nothing left to re-mint:
10
+ * the stored credential is an opaque `pb_sa_…` key, and platform rotates it
11
+ * through two small pre-auth endpoints where the KEY ITSELF is the authorization
12
+ * (an unattended machine has no password and cannot step up). So this handler is
13
+ * three HTTP calls and a secret write, with no npm install, no CLI, no `/tmp`.
14
14
  *
15
- * `/tmp` is the only writable path in Lambda, so npm's HOME/cache/prefix all live
16
- * there. store-token does NOT deploy the renewal stack by default (no --schedule),
17
- * so the renewal run never redeploys/recurses into its own stack.
15
+ * THE ORDER IS THE WHOLE DESIGN. At no point may the secret name a credential
16
+ * that does not work:
17
+ *
18
+ * 1. ROTATE — mint a sibling key on the same account. The current key stays
19
+ * LIVE. Fail here and the secret is untouched: the old key still works.
20
+ * 2. STORE — write the new key to the secret. Fail here and the secret still
21
+ * holds the old key, which is still live; the new key is an orphan that
22
+ * expires on its own (and the next run's rotate prunes it at the cap).
23
+ * 3. REVOKE — retire the old key, authenticated with the NEW one. Fail here
24
+ * and BOTH keys work; the credential is healthy and the stale key expires
25
+ * on its own. Logged at ERROR so it is visible, but it does not fail the
26
+ * invocation: a retry would rotate again and churn keys for no gain.
27
+ *
28
+ * Revoke-then-create would invert every one of those: a failure after the revoke
29
+ * leaves the deployment with no working credential at all.
18
30
  *
19
31
  * Env: PLATFORM_SECRET_NAME, PLATFORM_BASE_URL, RENEW_DAYS (default 30),
20
- * PIPELINE_MANAGER_VERSION (default "latest"), PLATFORM_VERIFY_SSL ("false"
21
- * to disable TLS verification).
32
+ * PLATFORM_VERIFY_SSL ("false" to disable TLS verification — refused in
33
+ * production).
22
34
  */
23
- import { execFileSync } from 'node:child_process';
24
- import { mkdirSync, writeFileSync } from 'node:fs';
25
- import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
26
- const PM_PACKAGE = '@pipeline-builder/pipeline-manager';
27
- const TMP = '/tmp';
28
- const PM_PREFIX = `${TMP}/pm`;
29
- const NPM_CACHE = `${TMP}/.npm`;
30
- const NPMRC = `${TMP}/.npmrc`;
31
- const CLI = `${PM_PREFIX}/node_modules/${PM_PACKAGE}/dist/cli.js`;
35
+ import { SecretsManagerClient, GetSecretValueCommand, PutSecretValueCommand } from '@aws-sdk/client-secrets-manager';
36
+ /** Per-request timeout for the platform calls. */
37
+ const HTTP_TIMEOUT_MS = 10_000;
38
+ /** Access-key prefixes platform issues. A stored JWT is not one of them. */
39
+ const ACCESS_KEY_PREFIXES = ['pb_sa_', 'pb_pat_'];
32
40
  function required(name) {
33
41
  const v = process.env[name];
34
42
  if (!v)
35
43
  throw new Error(`${name} environment variable is required`);
36
44
  return v;
37
45
  }
38
- /**
39
- * Read the `scope` claim from a JWT payload without verifying the signature
40
- * (the platform re-verifies on use; this only decides whether to re-mint WITH
41
- * a scope). A scoped credential (e.g. `reporting:ingest`) must renew with the
42
- * same scope or the fresh token would be a full-privilege platform token and
43
- * the scoped consumer's calls would break. Returns undefined for an unscoped
44
- * (platform) token or any decode failure.
45
- */
46
- function decodeJwtScope(jwt) {
46
+ function log(level, msg, data) {
47
+ const line = JSON.stringify({ level, msg, ...data });
48
+ // eslint-disable-next-line no-console
49
+ if (level === 'ERROR')
50
+ console.error(line);
51
+ else
52
+ console.log(line);
53
+ }
54
+ /** POST JSON to the platform, returning the parsed body plus the status. */
55
+ async function post(baseUrl, route, body) {
56
+ const controller = new AbortController();
57
+ const timer = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
47
58
  try {
48
- const payload = jwt.split('.')[1];
49
- if (!payload)
50
- return undefined;
51
- const json = Buffer.from(payload.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
52
- const claims = JSON.parse(json);
53
- return typeof claims.scope === 'string' && claims.scope ? claims.scope : undefined;
59
+ const res = await fetch(`${baseUrl}${route}`, {
60
+ method: 'POST',
61
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
62
+ body: JSON.stringify(body),
63
+ signal: controller.signal,
64
+ });
65
+ const parsed = await res.json().catch(() => ({}));
66
+ return { status: res.status, body: parsed };
67
+ }
68
+ finally {
69
+ clearTimeout(timer);
54
70
  }
55
- catch {
56
- return undefined;
71
+ }
72
+ /**
73
+ * SECURITY: these calls carry a live credential. Refuse to disable TLS
74
+ * verification in production so a MITM can't harvest it. Production is this
75
+ * Lambda's normal mode (the rotation stack sets NODE_ENV=production), so
76
+ * PLATFORM_VERIFY_SSL=false is honored ONLY outside production. This inlines the
77
+ * same `NODE_ENV==='production'` policy as utils/tls.ts#assertSslDisableAllowed
78
+ * (the handler ships as a single self-contained index.mjs and can't import it).
79
+ */
80
+ function applyTlsPolicy() {
81
+ if (process.env.PLATFORM_VERIFY_SSL !== 'false')
82
+ return;
83
+ if (process.env.NODE_ENV === 'production') {
84
+ log('WARN', 'Refusing PLATFORM_VERIFY_SSL=false in production (NODE_ENV=production) — TLS verification stays enabled');
85
+ return;
57
86
  }
87
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
58
88
  }
59
89
  export const handler = async () => {
60
90
  const secretName = required('PLATFORM_SECRET_NAME');
61
- const platformUrl = required('PLATFORM_BASE_URL').replace(/\/$/, '');
62
- const days = process.env.RENEW_DAYS || '30';
63
- const version = process.env.PIPELINE_MANAGER_VERSION || 'latest';
91
+ const envUrl = required('PLATFORM_BASE_URL').replace(/\/+$/, '');
92
+ const days = Number(process.env.RENEW_DAYS || '30');
93
+ if (!Number.isFinite(days) || days < 1 || days > 365) {
94
+ throw new Error(`RENEW_DAYS must be between 1 and 365 (got "${process.env.RENEW_DAYS}")`);
95
+ }
64
96
  const region = process.env.AWS_REGION || 'us-east-1';
65
- // npm in Lambda can only write under /tmp; HOME/cache/userconfig point there.
66
- const npmEnv = { ...process.env, HOME: TMP, npm_config_cache: NPM_CACHE };
67
- // 1. Scope config: resolve @pipeline-builder from public npm regardless of any
68
- // inherited registry. Written as a userconfig file (env mapping of scoped
69
- // keys is unreliable).
70
- writeFileSync(NPMRC, '@pipeline-builder:registry=https://registry.npmjs.org/\n');
71
- // 2. Download pipeline-manager into /tmp.
72
- mkdirSync(PM_PREFIX, { recursive: true });
73
- // eslint-disable-next-line no-console
74
- console.log(JSON.stringify({ level: 'INFO', msg: 'installing pipeline-manager', package: `${PM_PACKAGE}@${version}` }));
75
- execFileSync('npm', ['install', '--prefix', PM_PREFIX, '--no-audit', '--no-fund', '--userconfig', NPMRC, `${PM_PACKAGE}@${version}`], { env: npmEnv, stdio: 'inherit' });
76
- // 3. Read the current platform JWT — it authenticates the renewal request.
97
+ applyTlsPolicy();
77
98
  const sm = new SecretsManagerClient({ region });
78
99
  const current = await sm.send(new GetSecretValueCommand({ SecretId: secretName }));
79
100
  if (!current.SecretString)
80
101
  throw new Error(`Secret "${secretName}" is empty`);
81
- const jwt = JSON.parse(current.SecretString).password;
82
- if (!jwt)
83
- throw new Error(`Secret "${secretName}" missing password (JWT)`);
84
- // 4. Run store-token (without --schedule, the default) so it ONLY re-mints and
85
- // writes the secret — it must not redeploy this stack (would recurse). The
86
- // write uses the Lambda role's creds.
87
- // store-token no longer takes --secret-name; it reads PLATFORM_SECRET_NAME from
88
- // the environment (else derives it from the token's org).
89
- const args = ['infra', 'store-token', '--region', region, '--days', days];
90
- // Preserve the token's capability scope on renewal (else a scoped ingest
91
- // credential re-mints as a scopeless platform token and ingest starts 403ing).
92
- // The re-mint authenticates with THIS token, and a scoped token still carries
93
- // a userId, so /user/generate-token accepts it and re-issues the same scope.
94
- const tokenScope = decodeJwtScope(jwt);
95
- if (tokenScope) {
96
- args.push('--scope', tokenScope);
97
- // eslint-disable-next-line no-console
98
- console.log(JSON.stringify({ level: 'INFO', msg: 'renewing scoped token', scope: tokenScope, secretName }));
102
+ const stored = JSON.parse(current.SecretString);
103
+ const oldKey = stored.password;
104
+ if (!oldKey)
105
+ throw new Error(`Secret "${secretName}" missing password (service-account key)`);
106
+ if (!ACCESS_KEY_PREFIXES.some((p) => oldKey.startsWith(p))) {
107
+ throw new Error(`Secret "${secretName}" does not hold an opaque service-account key. `
108
+ + 'Re-run "pipeline-manager infra store-token" to reissue it as a key (see docs/runbooks/access-key-cutover.md).');
99
109
  }
100
- // SECURITY: the spawned store-token POSTs the JWT to the platform. Refuse to
101
- // disable TLS verification in production so a MITM can't harvest it. Production
102
- // is this Lambda's normal mode (the renew stack sets NODE_ENV=production), so
103
- // PLATFORM_VERIFY_SSL=false is honored ONLY outside production. This inlines the
104
- // same `NODE_ENV==='production'` policy as utils/tls.ts#assertSslDisableAllowed
105
- // (the handler ships as a single self-contained index.mjs and can't import it).
106
- const isProduction = process.env.NODE_ENV === 'production';
107
- if (process.env.PLATFORM_VERIFY_SSL === 'false') {
108
- if (isProduction) {
109
- // eslint-disable-next-line no-console
110
- console.log(JSON.stringify({ level: 'WARN', msg: 'Refusing PLATFORM_VERIFY_SSL=false in production (NODE_ENV=production) — TLS verification stays enabled' }));
111
- }
112
- else {
113
- args.push('--no-verify-ssl');
114
- }
115
- }
116
- execFileSync('node', [CLI, ...args], {
117
- // NODE_ENV is inherited via npmEnv (spread of process.env), so the spawned CLI
118
- // enforces the same production TLS-disable refusal as this handler.
119
- env: { ...npmEnv, PLATFORM_TOKEN: jwt, PLATFORM_BASE_URL: platformUrl, PLATFORM_SECRET_NAME: secretName },
120
- stdio: 'inherit',
110
+ // The platform this credential belongs to is recorded IN the secret; the env
111
+ // var is the fallback for a secret written before that field existed.
112
+ const platformUrl = (stored.platformUrl || envUrl).replace(/\/+$/, '');
113
+ // ── 1. ROTATE — mint the replacement. The old key stays live. ──────────────
114
+ const rotate = await post(platformUrl, '/api/auth/key/rotate', {
115
+ key: oldKey,
116
+ name: `${stored.serviceAccountName || 'rotated'}-${new Date().toISOString().slice(0, 10)}`,
117
+ expiresIn: days * 24 * 60 * 60,
121
118
  });
122
- // eslint-disable-next-line no-console
123
- console.log(JSON.stringify({ level: 'INFO', msg: 'platform token renewed', secretName }));
119
+ if (rotate.status < 200 || rotate.status >= 300) {
120
+ throw new Error(`Key rotation refused (${rotate.status}): ${rotate.body.message || 'unknown reason'}. `
121
+ + 'The stored key is unchanged and still live.');
122
+ }
123
+ const newKey = rotate.body.data?.key;
124
+ const newKeyId = rotate.body.data?.keyId;
125
+ if (!newKey || !newKeyId)
126
+ throw new Error('Key rotation returned no replacement key — the stored key is unchanged');
127
+ const pruned = rotate.body.data?.prunedKeyIds ?? [];
128
+ if (pruned.length > 0) {
129
+ log('WARN', 'Platform retired stale sibling keys to stay under the active-key cap', { secretName, prunedKeyIds: pruned });
130
+ }
131
+ log('INFO', 'Minted the replacement key', { secretName, keyId: newKeyId });
132
+ // ── 2. STORE — the secret now names a live credential. ─────────────────────
133
+ const next = {
134
+ ...stored,
135
+ password: newKey,
136
+ keyId: newKeyId,
137
+ platformUrl,
138
+ expiresIn: days * 24 * 60 * 60,
139
+ ...(rotate.body.data?.expiresAt ? { expiresAt: rotate.body.data.expiresAt } : {}),
140
+ createdAt: new Date().toISOString(),
141
+ };
142
+ await sm.send(new PutSecretValueCommand({ SecretId: secretName, SecretString: JSON.stringify(next) }));
143
+ log('INFO', 'Stored the rotated key', { secretName, keyId: newKeyId });
144
+ // ── 3. REVOKE — retire the predecessor, using the key that replaced it. ────
145
+ // Best-effort by design: the credential is already healthy, and failing the
146
+ // invocation here would only make the next retry rotate again.
147
+ const oldKeyId = stored.keyId;
148
+ if (!oldKeyId) {
149
+ log('WARN', 'Secret recorded no previous keyId — nothing to revoke (it expires on its own)', { secretName });
150
+ return;
151
+ }
152
+ const revoke = await post(platformUrl, '/api/auth/key/revoke', { key: newKey, keyId: oldKeyId })
153
+ .catch((err) => ({ status: 0, body: { message: err instanceof Error ? err.message : String(err) } }));
154
+ if (revoke.status < 200 || revoke.status >= 300) {
155
+ log('ERROR', 'Rotated and stored the new key, but could NOT revoke its predecessor — it stays valid until it expires', {
156
+ secretName, staleKeyId: oldKeyId, status: revoke.status, reason: revoke.body.message,
157
+ });
158
+ return;
159
+ }
160
+ log('INFO', 'Rotation complete', { secretName, keyId: newKeyId, revokedKeyId: oldKeyId });
124
161
  };
125
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tcmVuZXctaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sYW1iZGEvdG9rZW4tcmVuZXctaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBQ0gsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ25ELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTlGLE1BQU0sVUFBVSxHQUFHLG9DQUFvQyxDQUFDO0FBQ3hELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQztBQUNuQixNQUFNLFNBQVMsR0FBRyxHQUFHLEdBQUcsS0FBSyxDQUFDO0FBQzlCLE1BQU0sU0FBUyxHQUFHLEdBQUcsR0FBRyxPQUFPLENBQUM7QUFDaEMsTUFBTSxLQUFLLEdBQUcsR0FBRyxHQUFHLFNBQVMsQ0FBQztBQUM5QixNQUFNLEdBQUcsR0FBRyxHQUFHLFNBQVMsaUJBQWlCLFVBQVUsY0FBYyxDQUFDO0FBRWxFLFNBQVMsUUFBUSxDQUFDLElBQVk7SUFDNUIsTUFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QixJQUFJLENBQUMsQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxJQUFJLG1DQUFtQyxDQUFDLENBQUM7SUFDcEUsT0FBTyxDQUFDLENBQUM7QUFDWCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsY0FBYyxDQUFDLEdBQVc7SUFDakMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNs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LElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBRUQsWUFBWSxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFO1FBQ25DLCtFQUErRTtRQUMvRSxvRUFBb0U7UUFDcEUsR0FBRyxFQUFFLEVBQUUsR0FBRyxNQUFNLEVBQUUsY0FBYyxFQUFFLEdBQUcsRUFBRSxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsb0JBQW9CLEVBQUUsVUFBVSxFQUFFO1FBQ3pHLEtBQUssRUFBRSxTQUFTO0tBQ2pCLENBQUMsQ0FBQztJQUVILHNDQUFzQztJQUN0QyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSx3QkFBd0IsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDNUYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogTGFtYmRhIGhhbmRsZXIgdGhhdCByZS1taW50cyB0aGUgcGxhdGZvcm0gSldUIGluIFNlY3JldHMgTWFuYWdlciBzbyBpdCBuZXZlclxuICogbGFwc2VzLiBEZXBsb3llZCArIHNjaGVkdWxlZCAob25jZSBhIGRheSkgYnkgYHBpcGVsaW5lLW1hbmFnZXIgaW5mcmEgc3RvcmUtdG9rZW5cbiAqIC0tc2NoZWR1bGVgLiBSYXRoZXIgdGhhbiByZS1pbXBsZW1lbnRpbmcgdGhlIHRva2VuIGZsb3csIGl0IGlzIGEgdGhpblxuICogb3JjaGVzdHJhdG9yIHRoYXQgcmV1c2VzIHRoZSB0ZXN0ZWQgQ0xJIOKAlCBlYWNoIHJ1bjpcbiAqXG4gKiAgIDEuIFBvaW50IHRoZSBgQHBpcGVsaW5lLWJ1aWxkZXJgIG5wbSBzY29wZSBhdCBwdWJsaWMgbnBtLlxuICogICAyLiBEb3dubG9hZCBgQHBpcGVsaW5lLWJ1aWxkZXIvcGlwZWxpbmUtbWFuYWdlcmAgZnJvbSBucG0gaW50byAvdG1wLlxuICogICAzLiBSZWFkIHRoZSBjdXJyZW50IHBsYXRmb3JtIEpXVCBmcm9tIHRoZSBzZWNyZXQuXG4gKiAgIDQuIFJ1biBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlbmAsIHdoaWNoIG1pbnRzIGEgZnJlc2ggbG9uZy1saXZlZCB0b2tlblxuICogICAgICB2aWEgL2FwaS91c2VyL2dlbmVyYXRlLXRva2VuIGFuZCB3cml0ZXMgaXQgYmFjayB0byB0aGUgc2FtZSBzZWNyZXQuXG4gKlxuICogYC90bXBgIGlzIHRoZSBvbmx5IHdyaXRhYmxlIHBhdGggaW4gTGFtYmRhLCBzbyBucG0ncyBIT01FL2NhY2hlL3ByZWZpeCBhbGwgbGl2ZVxuICogdGhlcmUuIHN0b3JlLXRva2VuIGRvZXMgTk9UIGRlcGxveSB0aGUgcmVuZXdhbCBzdGFjayBieSBkZWZhdWx0IChubyAtLXNjaGVkdWxlKSxcbiAqIHNvIHRoZSByZW5ld2FsIHJ1biBuZXZlciByZWRlcGxveXMvcmVjdXJzZXMgaW50byBpdHMgb3duIHN0YWNrLlxuICpcbiAqIEVudjogUExBVEZPUk1fU0VDUkVUX05BTUUsIFBMQVRGT1JNX0JBU0VfVVJMLCBSRU5FV19EQVlTIChkZWZhdWx0IDMwKSxcbiAqICAgICAgUElQRUxJTkVfTUFOQUdFUl9WRVJTSU9OIChkZWZhdWx0IFwibGF0ZXN0XCIpLCBQTEFURk9STV9WRVJJRllfU1NMIChcImZhbHNlXCJcbiAqICAgICAgdG8gZGlzYWJsZSBUTFMgdmVyaWZpY2F0aW9uKS5cbiAqL1xuaW1wb3J0IHsgZXhlY0ZpbGVTeW5jIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJztcbmltcG9ydCB7IG1rZGlyU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ25vZGU6ZnMnO1xuaW1wb3J0IHsgU2VjcmV0c01hbmFnZXJDbGllbnQsIEdldFNlY3JldFZhbHVlQ29tbWFuZCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1zZWNyZXRzLW1hbmFnZXInO1xuXG5jb25zdCBQTV9QQUNLQUdFID0gJ0BwaXBlbGluZS1idWlsZGVyL3BpcGVsaW5lLW1hbmFnZXInO1xuY29uc3QgVE1QID0gJy90bXAnO1xuY29uc3QgUE1fUFJFRklYID0gYCR7VE1QfS9wbWA7XG5jb25zdCBOUE1fQ0FDSEUgPSBgJHtUTVB9Ly5ucG1gO1xuY29uc3QgTlBNUkMgPSBgJHtUTVB9Ly5ucG1yY2A7XG5jb25zdCBDTEkgPSBgJHtQTV9QUkVGSVh9L25vZGVfbW9kdWxlcy8ke1BNX1BBQ0tBR0V9L2Rpc3QvY2xpLmpzYDtcblxuZnVuY3Rpb24gcmVxdWlyZWQobmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgdiA9IHByb2Nlc3MuZW52W25hbWVdO1xuICBpZiAoIXYpIHRocm93IG5ldyBFcnJvcihgJHtuYW1lfSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZGApO1xuICByZXR1cm4gdjtcbn1cblxuLyoqXG4gKiBSZWFkIHRoZSBgc2NvcGVgIGNsYWltIGZyb20gYSBKV1QgcGF5bG9hZCB3aXRob3V0IHZlcmlmeWluZyB0aGUgc2lnbmF0dXJlXG4gKiAodGhlIHBsYXRmb3JtIHJlLXZlcmlmaWVzIG9uIHVzZTsgdGhpcyBvbmx5IGRlY2lkZXMgd2hldGhlciB0byByZS1taW50IFdJVEhcbiAqIGEgc2NvcGUpLiBBIHNjb3BlZCBjcmVkZW50aWFsIChlLmcuIGByZXBvcnRpbmc6aW5nZXN0YCkgbXVzdCByZW5ldyB3aXRoIHRoZVxuICogc2FtZSBzY29wZSBvciB0aGUgZnJlc2ggdG9rZW4gd291bGQgYmUgYSBmdWxsLXByaXZpbGVnZSBwbGF0Zm9ybSB0b2tlbiBhbmRcbiAqIHRoZSBzY29wZWQgY29uc3VtZXIncyBjYWxscyB3b3VsZCBicmVhay4gUmV0dXJucyB1bmRlZmluZWQgZm9yIGFuIHVuc2NvcGVkXG4gKiAocGxhdGZvcm0pIHRva2VuIG9yIGFueSBkZWNvZGUgZmFpbHVyZS5cbiAqL1xuZnVuY3Rpb24gZGVjb2RlSnd0U2NvcGUoand0OiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICB0cnkge1xuICAgIGNvbnN0IHBheWxvYWQgPSBqd3Quc3BsaXQoJy4nKVsxXTtcbiAgICBpZiAoIXBheWxvYWQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgY29uc3QganNvbiA9IEJ1ZmZlci5mcm9tKHBheWxvYWQucmVwbGFjZSgvLS9nLCAnKycpLnJlcGxhY2UoL18vZywgJy8nKSwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCd1dGY4Jyk7XG4gICAgY29uc3QgY2xhaW1zID0gSlNPTi5wYXJzZShqc29uKSBhcyB7IHNjb3BlPzogdW5rbm93biB9O1xuICAgIHJldHVybiB0eXBlb2YgY2xhaW1zLnNjb3BlID09PSAnc3RyaW5nJyAmJiBjbGFpbXMuc2NvcGUgPyBjbGFpbXMuc2NvcGUgOiB1bmRlZmluZWQ7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IGhhbmRsZXIgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIGNvbnN0IHNlY3JldE5hbWUgPSByZXF1aXJlZCgnUExBVEZPUk1fU0VDUkVUX05BTUUnKTtcbiAgY29uc3QgcGxhdGZvcm1VcmwgPSByZXF1aXJlZCgnUExBVEZPUk1fQkFTRV9VUkwnKS5yZXBsYWNlKC9cXC8kLywgJycpO1xuICBjb25zdCBkYXlzID0gcHJvY2Vzcy5lbnYuUkVORVdfREFZUyB8fCAnMzAnO1xuICBjb25zdCB2ZXJzaW9uID0gcHJvY2Vzcy5lbnYuUElQRUxJTkVfTUFOQUdFUl9WRVJTSU9OIHx8ICdsYXRlc3QnO1xuICBjb25zdCByZWdpb24gPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OIHx8ICd1cy1lYXN0LTEnO1xuXG4gIC8vIG5wbSBpbiBMYW1iZGEgY2FuIG9ubHkgd3JpdGUgdW5kZXIgL3RtcDsgSE9NRS9jYWNoZS91c2VyY29uZmlnIHBvaW50IHRoZXJlLlxuICBjb25zdCBucG1FbnYgPSB7IC4uLnByb2Nlc3MuZW52LCBIT01FOiBUTVAsIG5wbV9jb25maWdfY2FjaGU6IE5QTV9DQUNIRSB9O1xuXG4gIC8vIDEuIFNjb3BlIGNvbmZpZzogcmVzb2x2ZSBAcGlwZWxpbmUtYnVpbGRlciBmcm9tIHB1YmxpYyBucG0gcmVnYXJkbGVzcyBvZiBhbnlcbiAgLy8gICAgaW5oZXJpdGVkIHJlZ2lzdHJ5LiBXcml0dGVuIGFzIGEgdXNlcmNvbmZpZyBmaWxlIChlbnYgbWFwcGluZyBvZiBzY29wZWRcbiAgLy8gICAga2V5cyBpcyB1bnJlbGlhYmxlKS5cbiAgd3JpdGVGaWxlU3luYyhOUE1SQywgJ0BwaXBlbGluZS1idWlsZGVyOnJlZ2lzdHJ5PWh0dHBzOi8vcmVnaXN0cnkubnBtanMub3JnL1xcbicpO1xuXG4gIC8vIDIuIERvd25sb2FkIHBpcGVsaW5lLW1hbmFnZXIgaW50byAvdG1wLlxuICBta2RpclN5bmMoUE1fUFJFRklYLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ0lORk8nLCBtc2c6ICdpbnN0YWxsaW5nIHBpcGVsaW5lLW1hbmFnZXInLCBwYWNrYWdlOiBgJHtQTV9QQUNLQUdFfUAke3ZlcnNpb259YCB9KSk7XG4gIGV4ZWNGaWxlU3luYyhcbiAgICAnbnBtJyxcbiAgICBbJ2luc3RhbGwnLCAnLS1wcmVmaXgnLCBQTV9QUkVGSVgsICctLW5vLWF1ZGl0JywgJy0tbm8tZnVuZCcsICctLXVzZXJjb25maWcnLCBOUE1SQywgYCR7UE1fUEFDS0FHRX1AJHt2ZXJzaW9ufWBdLFxuICAgIHsgZW52OiBucG1FbnYsIHN0ZGlvOiAnaW5oZXJpdCcgfSxcbiAgKTtcblxuICAvLyAzLiBSZWFkIHRoZSBjdXJyZW50IHBsYXRmb3JtIEpXVCDigJQgaXQgYXV0aGVudGljYXRlcyB0aGUgcmVuZXdhbCByZXF1ZXN0LlxuICBjb25zdCBzbSA9IG5ldyBTZWNyZXRzTWFuYWdlckNsaWVudCh7IHJlZ2lvbiB9KTtcbiAgY29uc3QgY3VycmVudCA9IGF3YWl0IHNtLnNlbmQobmV3IEdldFNlY3JldFZhbHVlQ29tbWFuZCh7IFNlY3JldElkOiBzZWNyZXROYW1lIH0pKTtcbiAgaWYgKCFjdXJyZW50LlNlY3JldFN0cmluZykgdGhyb3cgbmV3IEVycm9yKGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgaXMgZW1wdHlgKTtcbiAgY29uc3Qgand0ID0gKEpTT04ucGFyc2UoY3VycmVudC5TZWNyZXRTdHJpbmcpIGFzIHsgcGFzc3dvcmQ/OiBzdHJpbmcgfSkucGFzc3dvcmQ7XG4gIGlmICghand0KSB0aHJvdyBuZXcgRXJyb3IoYFNlY3JldCBcIiR7c2VjcmV0TmFtZX1cIiBtaXNzaW5nIHBhc3N3b3JkIChKV1QpYCk7XG5cbiAgLy8gNC4gUnVuIHN0b3JlLXRva2VuICh3aXRob3V0IC0tc2NoZWR1bGUsIHRoZSBkZWZhdWx0KSBzbyBpdCBPTkxZIHJlLW1pbnRzIGFuZFxuICAvLyAgICB3cml0ZXMgdGhlIHNlY3JldCDigJQgaXQgbXVzdCBub3QgcmVkZXBsb3kgdGhpcyBzdGFjayAod291bGQgcmVjdXJzZSkuIFRoZVxuICAvLyAgICB3cml0ZSB1c2VzIHRoZSBMYW1iZGEgcm9sZSdzIGNyZWRzLlxuICAvLyBzdG9yZS10b2tlbiBubyBsb25nZXIgdGFrZXMgLS1zZWNyZXQtbmFtZTsgaXQgcmVhZHMgUExBVEZPUk1fU0VDUkVUX05BTUUgZnJvbVxuICAvLyB0aGUgZW52aXJvbm1lbnQgKGVsc2UgZGVyaXZlcyBpdCBmcm9tIHRoZSB0b2tlbidzIG9yZykuXG4gIGNvbnN0IGFyZ3MgPSBbJ2luZnJhJywgJ3N0b3JlLXRva2VuJywgJy0tcmVnaW9uJywgcmVnaW9uLCAnLS1kYXlzJywgZGF5c107XG4gIC8vIFByZXNlcnZlIHRoZSB0b2tlbidzIGNhcGFiaWxpdHkgc2NvcGUgb24gcmVuZXdhbCAoZWxzZSBhIHNjb3BlZCBpbmdlc3RcbiAgLy8gY3JlZGVudGlhbCByZS1taW50cyBhcyBhIHNjb3BlbGVzcyBwbGF0Zm9ybSB0b2tlbiBhbmQgaW5nZXN0IHN0YXJ0cyA0MDNpbmcpLlxuICAvLyBUaGUgcmUtbWludCBhdXRoZW50aWNhdGVzIHdpdGggVEhJUyB0b2tlbiwgYW5kIGEgc2NvcGVkIHRva2VuIHN0aWxsIGNhcnJpZXNcbiAgLy8gYSB1c2VySWQsIHNvIC91c2VyL2dlbmVyYXRlLXRva2VuIGFjY2VwdHMgaXQgYW5kIHJlLWlzc3VlcyB0aGUgc2FtZSBzY29wZS5cbiAgY29uc3QgdG9rZW5TY29wZSA9IGRlY29kZUp3dFNjb3BlKGp3dCk7XG4gIGlmICh0b2tlblNjb3BlKSB7XG4gICAgYXJncy5wdXNoKCctLXNjb3BlJywgdG9rZW5TY29wZSk7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IGxldmVsOiAnSU5GTycsIG1zZzogJ3JlbmV3aW5nIHNjb3BlZCB0b2tlbicsIHNjb3BlOiB0b2tlblNjb3BlLCBzZWNyZXROYW1lIH0pKTtcbiAgfVxuICAvLyBTRUNVUklUWTogdGhlIHNwYXduZWQgc3RvcmUtdG9rZW4gUE9TVHMgdGhlIEpXVCB0byB0aGUgcGxhdGZvcm0uIFJlZnVzZSB0b1xuICAvLyBkaXNhYmxlIFRMUyB2ZXJpZmljYXRpb24gaW4gcHJvZHVjdGlvbiBzbyBhIE1JVE0gY2FuJ3QgaGFydmVzdCBpdC4gUHJvZHVjdGlvblxuICAvLyBpcyB0aGlzIExhbWJkYSdzIG5vcm1hbCBtb2RlICh0aGUgcmVuZXcgc3RhY2sgc2V0cyBOT0RFX0VOVj1wcm9kdWN0aW9uKSwgc29cbiAgLy8gUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpcyBob25vcmVkIE9OTFkgb3V0c2lkZSBwcm9kdWN0aW9uLiBUaGlzIGlubGluZXMgdGhlXG4gIC8vIHNhbWUgYE5PREVfRU5WPT09J3Byb2R1Y3Rpb24nYCBwb2xpY3kgYXMgdXRpbHMvdGxzLnRzI2Fzc2VydFNzbERpc2FibGVBbGxvd2VkXG4gIC8vICh0aGUgaGFuZGxlciBzaGlwcyBhcyBhIHNpbmdsZSBzZWxmLWNvbnRhaW5lZCBpbmRleC5tanMgYW5kIGNhbid0IGltcG9ydCBpdCkuXG4gIGNvbnN0IGlzUHJvZHVjdGlvbiA9IHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSAncHJvZHVjdGlvbic7XG4gIGlmIChwcm9jZXNzLmVudi5QTEFURk9STV9WRVJJRllfU1NMID09PSAnZmFsc2UnKSB7XG4gICAgaWYgKGlzUHJvZHVjdGlvbikge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgbGV2ZWw6ICdXQVJOJywgbXNnOiAnUmVmdXNpbmcgUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpbiBwcm9kdWN0aW9uIChOT0RFX0VOVj1wcm9kdWN0aW9uKSDigJQgVExTIHZlcmlmaWNhdGlvbiBzdGF5cyBlbmFibGVkJyB9KSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGFyZ3MucHVzaCgnLS1uby12ZXJpZnktc3NsJyk7XG4gICAgfVxuICB9XG5cbiAgZXhlY0ZpbGVTeW5jKCdub2RlJywgW0NMSSwgLi4uYXJnc10sIHtcbiAgICAvLyBOT0RFX0VOViBpcyBpbmhlcml0ZWQgdmlhIG5wbUVudiAoc3ByZWFkIG9mIHByb2Nlc3MuZW52KSwgc28gdGhlIHNwYXduZWQgQ0xJXG4gICAgLy8gZW5mb3JjZXMgdGhlIHNhbWUgcHJvZHVjdGlvbiBUTFMtZGlzYWJsZSByZWZ1c2FsIGFzIHRoaXMgaGFuZGxlci5cbiAgICBlbnY6IHsgLi4ubnBtRW52LCBQTEFURk9STV9UT0tFTjogand0LCBQTEFURk9STV9CQVNFX1VSTDogcGxhdGZvcm1VcmwsIFBMQVRGT1JNX1NFQ1JFVF9OQU1FOiBzZWNyZXROYW1lIH0sXG4gICAgc3RkaW86ICdpbmhlcml0JyxcbiAgfSk7XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ0lORk8nLCBtc2c6ICdwbGF0Zm9ybSB0b2tlbiByZW5ld2VkJywgc2VjcmV0TmFtZSB9KSk7XG59O1xuIl19
162
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tcmVuZXctaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sYW1iZGEvdG9rZW4tcmVuZXctaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBK0JHO0FBQ0gsT0FBTyxFQUFFLG9CQUFvQixFQUFFLHFCQUFxQixFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFckgsa0RBQWtEO0FBQ2xELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQztBQUUvQiw0RUFBNEU7QUFDNUUsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLFFBQVEsRUFBRSxTQUFTLENBQUMsQ0FBQztBQUVsRCxTQUFTLFFBQVEsQ0FBQyxJQUFZO0lBQzVCLE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDNUIsSUFBSSxDQUFDLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLEdBQUcsSUFBSSxtQ0FBbUMsQ0FBQyxDQUFDO0lBQ3BFLE9BQU8sQ0FBQyxDQUFDO0FBQ1gsQ0FBQztBQUVELFNBQVMsR0FBRyxDQUFDLEtBQWdDLEVBQUUsR0FBVyxFQUFFLElBQThCO0lBQ3hGLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNyRCxzQ0FBc0M7SUFDdEMsSUFBSSxLQUFLLEtBQUssT0FBTztRQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7O1FBQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNyRSxDQUFDO0FBb0JELDRFQUE0RTtBQUM1RSxLQUFLLFVBQVUsSUFBSSxDQUNqQixPQUFlLEVBQ2YsS0FBYSxFQUNiLElB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RUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO0FBQzVGLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFNjaGVkdWxlZCBLRVkgUk9UQVRPUiBmb3IgdGhlIHNlcnZpY2UtYWNjb3VudCBjcmVkZW50aWFsIGluIFNlY3JldHMgTWFuYWdlclxuICogKCNOMikuIERlcGxveWVkICsgc2NoZWR1bGVkIChvbmNlIGEgZGF5KSBieSBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlblxuICogLS1zY2hlZHVsZWAuXG4gKlxuICogSXQgdXNlZCB0byByZS1taW50IGEgcGVyc29uJ3MgbWFjaGluZS1zZXNzaW9uIEpXVCBieSBucG0taW5zdGFsbGluZyB0aGUgQ0xJIGF0XG4gKiBydW50aW1lIGFuZCBzaGVsbGluZyBvdXQgdG8gYHN0b3JlLXRva2VuYC4gVGhlcmUgaXMgbm90aGluZyBsZWZ0IHRvIHJlLW1pbnQ6XG4gKiB0aGUgc3RvcmVkIGNyZWRlbnRpYWwgaXMgYW4gb3BhcXVlIGBwYl9zYV/igKZgIGtleSwgYW5kIHBsYXRmb3JtIHJvdGF0ZXMgaXRcbiAqIHRocm91Z2ggdHdvIHNtYWxsIHByZS1hdXRoIGVuZHBvaW50cyB3aGVyZSB0aGUgS0VZIElUU0VMRiBpcyB0aGUgYXV0aG9yaXphdGlvblxuICogKGFuIHVuYXR0ZW5kZWQgbWFjaGluZSBoYXMgbm8gcGFzc3dvcmQgYW5kIGNhbm5vdCBzdGVwIHVwKS4gU28gdGhpcyBoYW5kbGVyIGlzXG4gKiB0aHJlZSBIVFRQIGNhbGxzIGFuZCBhIHNlY3JldCB3cml0ZSwgd2l0aCBubyBucG0gaW5zdGFsbCwgbm8gQ0xJLCBubyBgL3RtcGAuXG4gKlxuICogVEhFIE9SREVSIElTIFRIRSBXSE9MRSBERVNJR04uIEF0IG5vIHBvaW50IG1heSB0aGUgc2VjcmV0IG5hbWUgYSBjcmVkZW50aWFsXG4gKiB0aGF0IGRvZXMgbm90IHdvcms6XG4gKlxuICogICAxLiBST1RBVEUg4oCUIG1pbnQgYSBzaWJsaW5nIGtleSBvbiB0aGUgc2FtZSBhY2NvdW50LiBUaGUgY3VycmVudCBrZXkgc3RheXNcbiAqICAgICAgTElWRS4gRmFpbCBoZXJlIGFuZCB0aGUgc2VjcmV0IGlzIHVudG91Y2hlZDogdGhlIG9sZCBrZXkgc3RpbGwgd29ya3MuXG4gKiAgIDIuIFNUT1JFICDigJQgd3JpdGUgdGhlIG5ldyBrZXkgdG8gdGhlIHNlY3JldC4gRmFpbCBoZXJlIGFuZCB0aGUgc2VjcmV0IHN0aWxsXG4gKiAgICAgIGhvbGRzIHRoZSBvbGQga2V5LCB3aGljaCBpcyBzdGlsbCBsaXZlOyB0aGUgbmV3IGtleSBpcyBhbiBvcnBoYW4gdGhhdFxuICogICAgICBleHBpcmVzIG9uIGl0cyBvd24gKGFuZCB0aGUgbmV4dCBydW4ncyByb3RhdGUgcHJ1bmVzIGl0IGF0IHRoZSBjYXApLlxuICogICAzLiBSRVZPS0Ug4oCUIHJldGlyZSB0aGUgb2xkIGtleSwgYXV0aGVudGljYXRlZCB3aXRoIHRoZSBORVcgb25lLiBGYWlsIGhlcmVcbiAqICAgICAgYW5kIEJPVEgga2V5cyB3b3JrOyB0aGUgY3JlZGVudGlhbCBpcyBoZWFsdGh5IGFuZCB0aGUgc3RhbGUga2V5IGV4cGlyZXNcbiAqICAgICAgb24gaXRzIG93bi4gTG9nZ2VkIGF0IEVSUk9SIHNvIGl0IGlzIHZpc2libGUsIGJ1dCBpdCBkb2VzIG5vdCBmYWlsIHRoZVxuICogICAgICBpbnZvY2F0aW9uOiBhIHJldHJ5IHdvdWxkIHJvdGF0ZSBhZ2FpbiBhbmQgY2h1cm4ga2V5cyBmb3Igbm8gZ2Fpbi5cbiAqXG4gKiBSZXZva2UtdGhlbi1jcmVhdGUgd291bGQgaW52ZXJ0IGV2ZXJ5IG9uZSBvZiB0aG9zZTogYSBmYWlsdXJlIGFmdGVyIHRoZSByZXZva2VcbiAqIGxlYXZlcyB0aGUgZGVwbG95bWVudCB3aXRoIG5vIHdvcmtpbmcgY3JlZGVudGlhbCBhdCBhbGwuXG4gKlxuICogRW52OiBQTEFURk9STV9TRUNSRVRfTkFNRSwgUExBVEZPUk1fQkFTRV9VUkwsIFJFTkVXX0RBWVMgKGRlZmF1bHQgMzApLFxuICogICAgICBQTEFURk9STV9WRVJJRllfU1NMIChcImZhbHNlXCIgdG8gZGlzYWJsZSBUTFMgdmVyaWZpY2F0aW9uIOKAlCByZWZ1c2VkIGluXG4gKiAgICAgIHByb2R1Y3Rpb24pLlxuICovXG5pbXBvcnQgeyBTZWNyZXRzTWFuYWdlckNsaWVudCwgR2V0U2VjcmV0VmFsdWVDb21tYW5kLCBQdXRTZWNyZXRWYWx1ZUNvbW1hbmQgfSBmcm9tICdAYXdzLXNkay9jbGllbnQtc2VjcmV0cy1tYW5hZ2VyJztcblxuLyoqIFBlci1yZXF1ZXN0IHRpbWVvdXQgZm9yIHRoZSBwbGF0Zm9ybSBjYWxscy4gKi9cbmNvbnN0IEhUVFBfVElNRU9VVF9NUyA9IDEwXzAwMDtcblxuLyoqIEFjY2Vzcy1rZXkgcHJlZml4ZXMgcGxhdGZvcm0gaXNzdWVzLiBBIHN0b3JlZCBKV1QgaXMgbm90IG9uZSBvZiB0aGVtLiAqL1xuY29uc3QgQUNDRVNTX0tFWV9QUkVGSVhFUyA9IFsncGJfc2FfJywgJ3BiX3BhdF8nXTtcblxuZnVuY3Rpb24gcmVxdWlyZWQobmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgdiA9IHByb2Nlc3MuZW52W25hbWVdO1xuICBpZiAoIXYpIHRocm93IG5ldyBFcnJvcihgJHtuYW1lfSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZGApO1xuICByZXR1cm4gdjtcbn1cblxuZnVuY3Rpb24gbG9nKGxldmVsOiAnSU5GTycgfCAnV0FSTicgfCAnRVJST1InLCBtc2c6IHN0cmluZywgZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogdm9pZCB7XG4gIGNvbnN0IGxpbmUgPSBKU09OLnN0cmluZ2lmeSh7IGxldmVsLCBtc2csIC4uLmRhdGEgfSk7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gIGlmIChsZXZlbCA9PT0gJ0VSUk9SJykgY29uc29sZS5lcnJvcihsaW5lKTsgZWxzZSBjb25zb2xlLmxvZyhsaW5lKTtcbn1cblxuLyoqIFRoZSBzdG9yZWQgc2VjcmV0LCBhcyBgc3RvcmUtdG9rZW5gIHdyaXRlcyBpdC4gKi9cbmludGVyZmFjZSBTdG9yZWRDcmVkZW50aWFsIHtcbiAgdXNlcm5hbWU/OiBzdHJpbmc7XG4gIC8qKiBUaGUgY2Fub25pY2FsIGNyZWRlbnRpYWwgZmllbGQg4oCUIGFuIG9wYXF1ZSBgcGJfc2Ff4oCmYCBrZXkgYWZ0ZXIgdGhlIGN1dG92ZXIuICovXG4gIHBhc3N3b3JkPzogc3RyaW5nO1xuICBwbGF0Zm9ybVVybD86IHN0cmluZztcbiAgb3JnYW5pemF0aW9uSWQ/OiBzdHJpbmc7XG4gIHNlcnZpY2VBY2NvdW50SWQ/OiBzdHJpbmc7XG4gIHNlcnZpY2VBY2NvdW50TmFtZT86IHN0cmluZztcbiAgLyoqIFJlY29yZCBpZCBvZiB0aGUga2V5IGluIGBwYXNzd29yZGA7IHdoYXQgc3RlcCAzIHJldGlyZXMuICovXG4gIGtleUlkPzogc3RyaW5nO1xuICBzY29wZT86IHN0cmluZyB8IG51bGw7XG4gIGV4cGlyZXNJbj86IG51bWJlcjtcbiAgZXhwaXJlc0F0Pzogc3RyaW5nO1xuICBjcmVhdGVkQXQ/OiBzdHJpbmc7XG4gIFtrZXk6IHN0cmluZ106IHVua25vd247XG59XG5cbi8qKiBQT1NUIEpTT04gdG8gdGhlIHBsYXRmb3JtLCByZXR1cm5pbmcgdGhlIHBhcnNlZCBib2R5IHBsdXMgdGhlIHN0YXR1cy4gKi9cbmFzeW5jIGZ1bmN0aW9uIHBvc3QoXG4gIGJhc2VVcmw6IHN0cmluZyxcbiAgcm91dGU6IHN0cmluZyxcbiAgYm9keTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4pOiBQcm9taXNlPHsgc3RhdHVzOiBudW1iZXI7IGJvZHk6IHsgZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+OyBtZXNzYWdlPzogc3RyaW5nIH0gfT4ge1xuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCBIVFRQX1RJTUVPVVRfTVMpO1xuICB0cnkge1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGAke2Jhc2VVcmx9JHtyb3V0ZX1gLCB7XG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LFxuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoYm9keSksXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuICAgIGNvbnN0IHBhcnNlZCA9IGF3YWl0IHJlcy5qc29uKCkuY2F0Y2goKCkgPT4gKHt9KSkgYXMgeyBkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47IG1lc3NhZ2U/OiBzdHJpbmcgfTtcbiAgICByZXR1cm4geyBzdGF0dXM6IHJlcy5zdGF0dXMsIGJvZHk6IHBhcnNlZCB9O1xuICB9IGZpbmFsbHkge1xuICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gIH1cbn1cblxuLyoqXG4gKiBTRUNVUklUWTogdGhlc2UgY2FsbHMgY2FycnkgYSBsaXZlIGNyZWRlbnRpYWwuIFJlZnVzZSB0byBkaXNhYmxlIFRMU1xuICogdmVyaWZpY2F0aW9uIGluIHByb2R1Y3Rpb24gc28gYSBNSVRNIGNhbid0IGhhcnZlc3QgaXQuIFByb2R1Y3Rpb24gaXMgdGhpc1xuICogTGFtYmRhJ3Mgbm9ybWFsIG1vZGUgKHRoZSByb3RhdGlvbiBzdGFjayBzZXRzIE5PREVfRU5WPXByb2R1Y3Rpb24pLCBzb1xuICogUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpcyBob25vcmVkIE9OTFkgb3V0c2lkZSBwcm9kdWN0aW9uLiBUaGlzIGlubGluZXMgdGhlXG4gKiBzYW1lIGBOT0RFX0VOVj09PSdwcm9kdWN0aW9uJ2AgcG9saWN5IGFzIHV0aWxzL3Rscy50cyNhc3NlcnRTc2xEaXNhYmxlQWxsb3dlZFxuICogKHRoZSBoYW5kbGVyIHNoaXBzIGFzIGEgc2luZ2xlIHNlbGYtY29udGFpbmVkIGluZGV4Lm1qcyBhbmQgY2FuJ3QgaW1wb3J0IGl0KS5cbiAqL1xuZnVuY3Rpb24gYXBwbHlUbHNQb2xpY3koKTogdm9pZCB7XG4gIGlmIChwcm9jZXNzLmVudi5QTEFURk9STV9WRVJJRllfU1NMICE9PSAnZmFsc2UnKSByZXR1cm47XG4gIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgbG9nKCdXQVJOJywgJ1JlZnVzaW5nIFBMQVRGT1JNX1ZFUklGWV9TU0w9ZmFsc2UgaW4gcHJvZHVjdGlvbiAoTk9ERV9FTlY9cHJvZHVjdGlvbikg4oCUIFRMUyB2ZXJpZmljYXRpb24gc3RheXMgZW5hYmxlZCcpO1xuICAgIHJldHVybjtcbiAgfVxuICBwcm9jZXNzLmVudi5OT0RFX1RMU19SRUpFQ1RfVU5BVVRIT1JJWkVEID0gJzAnO1xufVxuXG5leHBvcnQgY29uc3QgaGFuZGxlciA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgY29uc3Qgc2VjcmV0TmFtZSA9IHJlcXVpcmVkKCdQTEFURk9STV9TRUNSRVRfTkFNRScpO1xuICBjb25zdCBlbnZVcmwgPSByZXF1aXJlZCgnUExBVEZPUk1fQkFTRV9VUkwnKS5yZXBsYWNlKC9cXC8rJC8sICcnKTtcbiAgY29uc3QgZGF5cyA9IE51bWJlcihwcm9jZXNzLmVudi5SRU5FV19EQVlTIHx8ICczMCcpO1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZShkYXlzKSB8fCBkYXlzIDwgMSB8fCBkYXlzID4gMzY1KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBSRU5FV19EQVlTIG11c3QgYmUgYmV0d2VlbiAxIGFuZCAzNjUgKGdvdCBcIiR7cHJvY2Vzcy5lbnYuUkVORVdfREFZU31cIilgKTtcbiAgfVxuICBjb25zdCByZWdpb24gPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OIHx8ICd1cy1lYXN0LTEnO1xuXG4gIGFwcGx5VGxzUG9saWN5KCk7XG5cbiAgY29uc3Qgc20gPSBuZXcgU2VjcmV0c01hbmFnZXJDbGllbnQoeyByZWdpb24gfSk7XG4gIGNvbnN0IGN1cnJlbnQgPSBhd2FpdCBzbS5zZW5kKG5ldyBHZXRTZWNyZXRWYWx1ZUNvbW1hbmQoeyBTZWNyZXRJZDogc2VjcmV0TmFtZSB9KSk7XG4gIGlmICghY3VycmVudC5TZWNyZXRTdHJpbmcpIHRocm93IG5ldyBFcnJvcihgU2VjcmV0IFwiJHtzZWNyZXROYW1lfVwiIGlzIGVtcHR5YCk7XG4gIGNvbnN0IHN0b3JlZCA9IEpTT04ucGFyc2UoY3VycmVudC5TZWNyZXRTdHJpbmcpIGFzIFN0b3JlZENyZWRlbnRpYWw7XG5cbiAgY29uc3Qgb2xkS2V5ID0gc3RvcmVkLnBhc3N3b3JkO1xuICBpZiAoIW9sZEtleSkgdGhyb3cgbmV3IEVycm9yKGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgbWlzc2luZyBwYXNzd29yZCAoc2VydmljZS1hY2NvdW50IGtleSlgKTtcbiAgaWYgKCFBQ0NFU1NfS0VZX1BSRUZJWEVTLnNvbWUoKHApID0+IG9sZEtleS5zdGFydHNXaXRoKHApKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgZG9lcyBub3QgaG9sZCBhbiBvcGFxdWUgc2VydmljZS1hY2NvdW50IGtleS4gYFxuICAgICAgKyAnUmUtcnVuIFwicGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlblwiIHRvIHJlaXNzdWUgaXQgYXMgYSBrZXkgKHNlZSBkb2NzL3J1bmJvb2tzL2FjY2Vzcy1rZXktY3V0b3Zlci5tZCkuJyxcbiAgICApO1xuICB9XG4gIC8vIFRoZSBwbGF0Zm9ybSB0aGlzIGNyZWRlbnRpYWwgYmVsb25ncyB0byBpcyByZWNvcmRlZCBJTiB0aGUgc2VjcmV0OyB0aGUgZW52XG4gIC8vIHZhciBpcyB0aGUgZmFsbGJhY2sgZm9yIGEgc2VjcmV0IHdyaXR0ZW4gYmVmb3JlIHRoYXQgZmllbGQgZXhpc3RlZC5cbiAgY29uc3QgcGxhdGZvcm1VcmwgPSAoc3RvcmVkLnBsYXRmb3JtVXJsIHx8IGVudlVybCkucmVwbGFjZSgvXFwvKyQvLCAnJyk7XG5cbiAgLy8g4pSA4pSAIDEuIFJPVEFURSDigJQgbWludCB0aGUgcmVwbGFjZW1lbnQuIFRoZSBvbGQga2V5IHN0YXlzIGxpdmUuIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBjb25zdCByb3RhdGUgPSBhd2FpdCBwb3N0KHBsYXRmb3JtVXJsLCAnL2FwaS9hdXRoL2tleS9yb3RhdGUnLCB7XG4gICAga2V5OiBvbGRLZXksXG4gICAgbmFtZTogYCR7c3RvcmVkLnNlcnZpY2VBY2NvdW50TmFtZSB8fCAncm90YXRlZCd9LSR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpLnNsaWNlKDAsIDEwKX1gLFxuICAgIGV4cGlyZXNJbjogZGF5cyAqIDI0ICogNjAgKiA2MCxcbiAgfSk7XG4gIGlmIChyb3RhdGUuc3RhdHVzIDwgMjAwIHx8IHJvdGF0ZS5zdGF0dXMgPj0gMzAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEtleSByb3RhdGlvbiByZWZ1c2VkICgke3JvdGF0ZS5zdGF0dXN9KTogJHtyb3RhdGUuYm9keS5tZXNzYWdlIHx8ICd1bmtub3duIHJlYXNvbid9LiBgXG4gICAgICArICdUaGUgc3RvcmVkIGtleSBpcyB1bmNoYW5nZWQgYW5kIHN0aWxsIGxpdmUuJyxcbiAgICApO1xuICB9XG4gIGNvbnN0IG5ld0tleSA9IHJvdGF0ZS5ib2R5LmRhdGE/LmtleSBhcyBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGNvbnN0IG5ld0tleUlkID0gcm90YXRlLmJvZHkuZGF0YT8ua2V5SWQgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBpZiAoIW5ld0tleSB8fCAhbmV3S2V5SWQpIHRocm93IG5ldyBFcnJvcignS2V5IHJvdGF0aW9uIHJldHVybmVkIG5vIHJlcGxhY2VtZW50IGtleSDigJQgdGhlIHN0b3JlZCBrZXkgaXMgdW5jaGFuZ2VkJyk7XG4gIGNvbnN0IHBydW5lZCA9IChyb3RhdGUuYm9keS5kYXRhPy5wcnVuZWRLZXlJZHMgYXMgc3RyaW5nW10gfCB1bmRlZmluZWQpID8/IFtdO1xuICBpZiAocHJ1bmVkLmxlbmd0aCA+IDApIHtcbiAgICBsb2coJ1dBUk4nLCAnUGxhdGZvcm0gcmV0aXJlZCBzdGFsZSBzaWJsaW5nIGtleXMgdG8gc3RheSB1bmRlciB0aGUgYWN0aXZlLWtleSBjYXAnLCB7IHNlY3JldE5hbWUsIHBydW5lZEtleUlkczogcHJ1bmVkIH0pO1xuICB9XG4gIGxvZygnSU5GTycsICdNaW50ZWQgdGhlIHJlcGxhY2VtZW50IGtleScsIHsgc2VjcmV0TmFtZSwga2V5SWQ6IG5ld0tleUlkIH0pO1xuXG4gIC8vIOKUgOKUgCAyLiBTVE9SRSDigJQgdGhlIHNlY3JldCBub3cgbmFtZXMgYSBsaXZlIGNyZWRlbnRpYWwuIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBjb25zdCBuZXh0OiBTdG9yZWRDcmVkZW50aWFsID0ge1xuICAgIC4uLnN0b3JlZCxcbiAgICBwYXNzd29yZDogbmV3S2V5LFxuICAgIGtleUlkOiBuZXdLZXlJZCxcbiAgICBwbGF0Zm9ybVVybCxcbiAgICBleHBpcmVzSW46IGRheXMgKiAyNCAqIDYwICogNjAsXG4gICAgLi4uKHJvdGF0ZS5ib2R5LmRhdGE/LmV4cGlyZXNBdCA/IHsgZXhwaXJlc0F0OiByb3RhdGUuYm9keS5kYXRhLmV4cGlyZXNBdCBhcyBzdHJpbmcgfSA6IHt9KSxcbiAgICBjcmVhdGVkQXQ6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgfTtcbiAgYXdhaXQgc20uc2VuZChuZXcgUHV0U2VjcmV0VmFsdWVDb21tYW5kKHsgU2VjcmV0SWQ6IHNlY3JldE5hbWUsIFNlY3JldFN0cmluZzogSlNPTi5zdHJpbmdpZnkobmV4dCkgfSkpO1xuICBsb2coJ0lORk8nLCAnU3RvcmVkIHRoZSByb3RhdGVkIGtleScsIHsgc2VjcmV0TmFtZSwga2V5SWQ6IG5ld0tleUlkIH0pO1xuXG4gIC8vIOKUgOKUgCAzLiBSRVZPS0Ug4oCUIHJldGlyZSB0aGUgcHJlZGVjZXNzb3IsIHVzaW5nIHRoZSBrZXkgdGhhdCByZXBsYWNlZCBpdC4g4pSA4pSA4pSA4pSAXG4gIC8vIEJlc3QtZWZmb3J0IGJ5IGRlc2lnbjogdGhlIGNyZWRlbnRpYWwgaXMgYWxyZWFkeSBoZWFsdGh5LCBhbmQgZmFpbGluZyB0aGVcbiAgLy8gaW52b2NhdGlvbiBoZXJlIHdvdWxkIG9ubHkgbWFrZSB0aGUgbmV4dCByZXRyeSByb3RhdGUgYWdhaW4uXG4gIGNvbnN0IG9sZEtleUlkID0gc3RvcmVkLmtleUlkO1xuICBpZiAoIW9sZEtleUlkKSB7XG4gICAgbG9nKCdXQVJOJywgJ1NlY3JldCByZWNvcmRlZCBubyBwcmV2aW91cyBrZXlJZCDigJQgbm90aGluZyB0byByZXZva2UgKGl0IGV4cGlyZXMgb24gaXRzIG93biknLCB7IHNlY3JldE5hbWUgfSk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHJldm9rZSA9IGF3YWl0IHBvc3QocGxhdGZvcm1VcmwsICcvYXBpL2F1dGgva2V5L3Jldm9rZScsIHsga2V5OiBuZXdLZXksIGtleUlkOiBvbGRLZXlJZCB9KVxuICAgIC5jYXRjaCgoZXJyKSA9PiAoeyBzdGF0dXM6IDAsIGJvZHk6IHsgbWVzc2FnZTogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0gfSkpO1xuICBpZiAocmV2b2tlLnN0YXR1cyA8IDIwMCB8fCByZXZva2Uuc3RhdHVzID49IDMwMCkge1xuICAgIGxvZygnRVJST1InLCAnUm90YXRlZCBhbmQgc3RvcmVkIHRoZSBuZXcga2V5LCBidXQgY291bGQgTk9UIHJldm9rZSBpdHMgcHJlZGVjZXNzb3Ig4oCUIGl0IHN0YXlzIHZhbGlkIHVudGlsIGl0IGV4cGlyZXMnLCB7XG4gICAgICBzZWNyZXROYW1lLCBzdGFsZUtleUlkOiBvbGRLZXlJZCwgc3RhdHVzOiByZXZva2Uuc3RhdHVzLCByZWFzb246IHJldm9rZS5ib2R5Lm1lc3NhZ2UsXG4gICAgfSk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGxvZygnSU5GTycsICdSb3RhdGlvbiBjb21wbGV0ZScsIHsgc2VjcmV0TmFtZSwga2V5SWQ6IG5ld0tleUlkLCByZXZva2VkS2V5SWQ6IG9sZEtleUlkIH0pO1xufTtcbiJdfQ==
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "PlatformSecretName": {
10
10
  "Type": "String",
11
- "Description": "Secrets Manager secret name containing the platform JWT token (e.g. pipeline-builder/{orgId}/platform)"
11
+ "Description": "Secrets Manager secret name containing the reporting:ingest service-account key (e.g. pipeline-builder/{orgId}/reporting-ingest)"
12
12
  },
13
13
  "DoraEnabled": {
14
14
  "Type": "String",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "AWSTemplateFormatVersion": "2010-09-09",
3
- "Description": "Pipeline Builder platform-token renewal — a scheduled Lambda that re-mints the platform JWT in Secrets Manager before it expires, so CodeBuild/Lambda credentials never lapse. Lambda code is deployed separately via pipeline-manager infra store-token.",
3
+ "Description": "Pipeline Builder service-account KEY ROTATION — a scheduled Lambda that replaces the pb_sa_ key in Secrets Manager before it expires (rotate, store, then revoke the predecessor), so CodeBuild/Lambda credentials never lapse. Lambda code is deployed separately via pipeline-manager infra store-token.",
4
4
  "Parameters": {
5
5
  "PlatformBaseUrl": {
6
6
  "Type": "String",
@@ -8,29 +8,29 @@
8
8
  },
9
9
  "PlatformSecretName": {
10
10
  "Type": "String",
11
- "Description": "Secrets Manager secret name holding the platform JWT (e.g. pipeline-builder/{orgId}/platform)"
11
+ "Description": "Secrets Manager secret name holding the service-account key (e.g. pipeline-builder/{orgId}/platform)"
12
12
  },
13
13
  "RenewDays": {
14
14
  "Type": "Number",
15
15
  "Default": 30,
16
16
  "MinValue": 1,
17
17
  "MaxValue": 365,
18
- "Description": "Lifetime in days requested for each renewed token"
18
+ "Description": "Lifetime in days requested for each rotated key"
19
19
  },
20
20
  "ScheduleExpression": {
21
21
  "Type": "String",
22
22
  "Default": "cron(0 0 * * ? *)",
23
- "Description": "EventBridge schedule expression for the renewal (default: daily at 00:00 UTC)"
23
+ "Description": "EventBridge schedule expression for the rotation (default: daily at 00:00 UTC)"
24
24
  },
25
25
  "PipelineManagerVersion": {
26
26
  "Type": "String",
27
27
  "Default": "latest",
28
- "Description": "Version of @pipeline-builder/pipeline-manager the handler installs from npm at runtime (pin for reproducible renewals)"
28
+ "Description": "Version of @pipeline-builder/pipeline-manager that deployed this stack. Provenance only — the rotator calls the platform directly and installs nothing at runtime."
29
29
  },
30
30
  "NameSuffix": {
31
31
  "Type": "String",
32
32
  "Default": "",
33
- "Description": "Suffix appended to the renewal stack's resource names so a scoped-token renewal (e.g. '-reporting-ingest') coexists with the platform-token stack instead of clobbering its role/function/rule. Empty for the platform secret."
33
+ "Description": "Suffix appended to the rotation stack's resource names so a scoped-token renewal (e.g. '-reporting-ingest') coexists with the platform-token stack instead of clobbering its role/function/rule. Empty for the platform secret."
34
34
  }
35
35
  },
36
36
  "Resources": {
@@ -77,9 +77,8 @@
77
77
  "ZipFile": "exports.handler=async()=>{console.log('Placeholder handler — run pipeline-manager infra store-token to deploy real code');return{statusCode:200}}"
78
78
  },
79
79
  "Role": { "Fn::GetAtt": ["LambdaRole", "Arn"] },
80
- "Timeout": 300,
81
- "MemorySize": 1024,
82
- "EphemeralStorage": { "Size": 1024 },
80
+ "Timeout": 60,
81
+ "MemorySize": 256,
83
82
  "Environment": {
84
83
  "Variables": {
85
84
  "PLATFORM_BASE_URL": { "Ref": "PlatformBaseUrl" },
@@ -95,7 +94,7 @@
95
94
  "Type": "AWS::Events::Rule",
96
95
  "Properties": {
97
96
  "Name": { "Fn::Sub": "pipeline-builder-token-renew${NameSuffix}" },
98
- "Description": "Scheduled platform-token renewal",
97
+ "Description": "Scheduled service-account key rotation",
99
98
  "State": "ENABLED",
100
99
  "ScheduleExpression": { "Ref": "ScheduleExpression" },
101
100
  "Targets": [{
@@ -1,19 +1,23 @@
1
1
  import https from 'https';
2
2
  /**
3
- * Login / refresh / switch-org response — every platform auth endpoint returns
4
- * the session tokens under this envelope.
3
+ * Session payload as a NON-BROWSER client receives it: the refresh token rides
4
+ * in the body (the browser's goes out as an HttpOnly cookie instead).
5
5
  */
6
+ export interface SessionTokens {
7
+ accessToken: string;
8
+ refreshToken?: string;
9
+ expiresIn?: number;
10
+ }
11
+ /** Envelope every platform auth endpoint wraps its session tokens in. */
6
12
  export interface AuthResponse {
7
13
  success: boolean;
8
- data: {
9
- accessToken: string;
10
- refreshToken: string;
11
- };
14
+ data: SessionTokens;
12
15
  }
13
16
  /**
14
- * Guard the credential-transmitting commands (`login`, `create-pat`) against
15
- * disabling TLS verification in production. These POST plaintext passwords/JWTs,
16
- * so a MITM on a `prod + --no-verify-ssl` run would harvest them.
17
+ * Guard the credential-transmitting commands (`login`, `pat`) against disabling
18
+ * TLS verification in production. The device flow keeps passwords off the wire
19
+ * entirely, but the session tokens it returns are bearer credentials — a MITM on
20
+ * a `prod + --no-verify-ssl` run would harvest them just the same.
17
21
  *
18
22
  * Unlike {@link assertSslDisableAllowed} (which throws — for paths already inside
19
23
  * a try/catch), this prints and hard-exits, matching the pre-flight checks these
@@ -21,18 +25,6 @@ export interface AuthResponse {
21
25
  * `--no-verify-ssl` still works against self-signed dev platforms.
22
26
  */
23
27
  export declare function assertCredentialTlsAllowed(verifySsl: boolean | undefined): void;
24
- /**
25
- * Resolve credentials from flags, falling back to the
26
- * PLATFORM_IDENTIFIER / PLATFORM_PASSWORD env vars (so the password need not
27
- * appear in shell history / `ps`).
28
- */
29
- export declare function resolveCredentials(options: {
30
- identifier?: string;
31
- password?: string;
32
- }): {
33
- identifier?: string;
34
- password?: string;
35
- };
36
28
  /**
37
29
  * Build the https agent used by the credential commands. `--verify-ssl`
38
30
  * defaults to on; only an explicit `--no-verify-ssl` relaxes it (and only
@@ -40,22 +32,13 @@ export declare function resolveCredentials(options: {
40
32
  */
41
33
  export declare function credentialHttpsAgent(verifySsl: boolean | undefined): https.Agent;
42
34
  /**
43
- * POST /api/auth/login and return the access token (or `undefined` if the
44
- * response carried none). Rate-limit accounting (record success/failure) and
45
- * the no-token exit are intentionally left to the caller so each command keeps
46
- * its exact lockout semantics.
47
- */
48
- export declare function postLogin(params: {
49
- url: string;
50
- identifier: string;
51
- password: string;
52
- httpsAgent: https.Agent;
53
- timeout: number;
54
- }): Promise<string | undefined>;
55
- /**
56
- * POST /api/auth/switch-org with the current access token and return the new,
57
- * org-scoped access token. Prints a success line (unless `quiet`) and hard-exits
58
- * on a missing token — both credential commands treat a failed switch the same.
35
+ * POST /api/auth/switch-org with the current access token and return the WHOLE
36
+ * re-issued session.
37
+ *
38
+ * The switch re-issues the same session slot, which ROTATES its refresh token —
39
+ * so a caller that keeps only the access token is left holding a dead refresh
40
+ * token. Prints a success line (unless `quiet`) and hard-exits on a missing
41
+ * token, since both credential commands treat a failed switch the same.
59
42
  */
60
43
  export declare function switchOrganization(params: {
61
44
  url: string;
@@ -64,5 +47,5 @@ export declare function switchOrganization(params: {
64
47
  httpsAgent: https.Agent;
65
48
  timeout: number;
66
49
  quiet: boolean;
67
- }): Promise<string>;
50
+ }): Promise<SessionTokens>;
68
51
  //# sourceMappingURL=auth-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-utils.d.ts","sourceRoot":"","sources":["../../src/utils/auth-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAK/E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAKA;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC,KAAK,CAEhF;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAS9B;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBlB"}
1
+ {"version":3,"file":"auth-utils.d.ts","sourceRoot":"","sources":["../../src/utils/auth-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,aAAa,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAK/E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC,KAAK,CAEhF;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,aAAa,CAAC,CAwBzB"}