runcloud 0.2.196 → 0.2.197
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/support.js +273 -0
- package/dist/commands/supportChat.generated.js +1 -0
- package/dist/index.js +2 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/runcloud-0.2.197.tgz +0 -0
- package/runcloud-0.2.196.tgz +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { ApiClient, friendlyApiError } from '../api.js';
|
|
4
|
+
import { requireCredentials, resolveProject } from '../config.js';
|
|
5
|
+
import { SUPPORT_CHAT_TSX } from './supportChat.generated.js';
|
|
6
|
+
export const SUPPORT_CONFIG = 'src/lib/newly-support.json';
|
|
7
|
+
export const SUPPORT_CHAT = 'src/components/SupportChat.tsx';
|
|
8
|
+
export function writeSupportChat(dir, force = false) {
|
|
9
|
+
const path = join(dir, SUPPORT_CHAT);
|
|
10
|
+
if (existsSync(path) && !force)
|
|
11
|
+
return { path, written: false };
|
|
12
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
13
|
+
writeFileSync(path, Buffer.from(SUPPORT_CHAT_TSX, 'base64'));
|
|
14
|
+
return { path, written: true };
|
|
15
|
+
}
|
|
16
|
+
const ENVS = ['development', 'production'];
|
|
17
|
+
const base = (projectId) => `/projects/${encodeURIComponent(projectId)}/support`;
|
|
18
|
+
export function envFrom(flag) {
|
|
19
|
+
if (flag === undefined || flag === 'dev' || flag === 'development')
|
|
20
|
+
return 'development';
|
|
21
|
+
if (flag === 'prod' || flag === 'production')
|
|
22
|
+
return 'production';
|
|
23
|
+
throw new Error('--env must be dev or prod');
|
|
24
|
+
}
|
|
25
|
+
export function supabaseAuth(url) {
|
|
26
|
+
let host;
|
|
27
|
+
try {
|
|
28
|
+
const parsed = new URL(url);
|
|
29
|
+
if (parsed.protocol !== 'https:')
|
|
30
|
+
throw new Error();
|
|
31
|
+
host = parsed.host;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new Error(`Not a Supabase project URL: ${url}`);
|
|
35
|
+
}
|
|
36
|
+
const issuer = `https://${host}/auth/v1`;
|
|
37
|
+
return { auth_issuer: issuer, auth_jwks_url: `${issuer}/.well-known/jwks.json`, auth_audience: 'authenticated' };
|
|
38
|
+
}
|
|
39
|
+
export function writeSupportConfig(dir, config) {
|
|
40
|
+
const path = join(dir, SUPPORT_CONFIG);
|
|
41
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
42
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`);
|
|
43
|
+
return path;
|
|
44
|
+
}
|
|
45
|
+
export async function runSupportStatus(client, projectId, log) {
|
|
46
|
+
const { settings, keys } = (await client.get(`${base(projectId)}/settings`));
|
|
47
|
+
const summary = await client.get(`${base(projectId)}/summary`);
|
|
48
|
+
log(JSON.stringify({ settings, keys, summary }, null, 2));
|
|
49
|
+
}
|
|
50
|
+
export async function runSupportEnable(client, projectId, apiUrl, dir, log, opts = {}) {
|
|
51
|
+
await client.put(`${base(projectId)}/settings`, { enabled: true });
|
|
52
|
+
const { keys } = (await client.get(`${base(projectId)}/settings`));
|
|
53
|
+
const written = {};
|
|
54
|
+
for (const env of ENVS) {
|
|
55
|
+
written[env] = keys[env].publishable
|
|
56
|
+
?? (await client.post(`${base(projectId)}/keys`, { environment: env, kind: 'publishable' })).key;
|
|
57
|
+
}
|
|
58
|
+
const path = writeSupportConfig(dir, { projectId, apiUrl, keys: written });
|
|
59
|
+
const chat = writeSupportChat(dir, opts.forceChat);
|
|
60
|
+
log(`Customer support is on. Keys written to ${path}.`);
|
|
61
|
+
log(chat.written
|
|
62
|
+
? `Help chat component written to ${chat.path}. Give it a route (for example app/help.tsx rendering <SupportChat appName="..." />) and a way in from Settings.`
|
|
63
|
+
: `Kept the existing ${chat.path} (pass --force-chat to replace it).`);
|
|
64
|
+
}
|
|
65
|
+
export async function runSupportDisable(client, projectId, log) {
|
|
66
|
+
await client.put(`${base(projectId)}/settings`, { enabled: false });
|
|
67
|
+
log('Customer support is off. The help chat in the app now says support is unavailable.');
|
|
68
|
+
}
|
|
69
|
+
export async function runSupportRotate(client, projectId, apiUrl, dir, env, secret, log) {
|
|
70
|
+
const { key } = (await client.post(`${base(projectId)}/keys`, { environment: env, kind: secret ? 'secret' : 'publishable' }));
|
|
71
|
+
if (secret) {
|
|
72
|
+
log(`Secret support key for ${env} (shown once, store it on your server as NEWLY_SUPPORT_SECRET):\n${key}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const path = join(dir, SUPPORT_CONFIG);
|
|
76
|
+
let current = {};
|
|
77
|
+
try {
|
|
78
|
+
current = JSON.parse(readFileSync(path, 'utf8'));
|
|
79
|
+
}
|
|
80
|
+
catch { }
|
|
81
|
+
const keys = { development: current.keys?.development ?? '', production: current.keys?.production ?? '', [env]: key };
|
|
82
|
+
writeSupportConfig(dir, { projectId, apiUrl, keys });
|
|
83
|
+
log(`New ${env} key written to ${path}. The old key stops working now.`);
|
|
84
|
+
}
|
|
85
|
+
export async function runSupportAuth(client, projectId, opts, log) {
|
|
86
|
+
let patch;
|
|
87
|
+
if (opts.clear)
|
|
88
|
+
patch = { auth_issuer: null, auth_jwks_url: null, auth_audience: null };
|
|
89
|
+
else if (opts.supabaseUrl)
|
|
90
|
+
patch = supabaseAuth(opts.supabaseUrl);
|
|
91
|
+
else if (opts.issuer && opts.jwks && opts.audience)
|
|
92
|
+
patch = { auth_issuer: opts.issuer, auth_jwks_url: opts.jwks, auth_audience: opts.audience };
|
|
93
|
+
else
|
|
94
|
+
throw new Error('Pass --supabase-url <url>, or all of --issuer, --jwks and --audience, or --clear.');
|
|
95
|
+
await client.put(`${base(projectId)}/settings`, patch);
|
|
96
|
+
log(opts.clear ? 'Signed-in customers are no longer recognised.' : 'Signed-in customers are now recognised from their sign-in token.');
|
|
97
|
+
}
|
|
98
|
+
export async function runArticlesList(client, projectId, log) {
|
|
99
|
+
const { articles } = (await client.get(`${base(projectId)}/articles`));
|
|
100
|
+
if (!articles.length) {
|
|
101
|
+
log('No articles yet. Add one with newly support articles add --title <title> --file <path>.');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const a of articles)
|
|
105
|
+
log(`${a.id} ${a.title} (updated ${a.updated_at})`);
|
|
106
|
+
}
|
|
107
|
+
const bodyOf = (opts) => {
|
|
108
|
+
if (opts.file)
|
|
109
|
+
return readFileSync(opts.file, 'utf8');
|
|
110
|
+
if (opts.body)
|
|
111
|
+
return opts.body;
|
|
112
|
+
throw new Error('Pass --body <text> or --file <path>.');
|
|
113
|
+
};
|
|
114
|
+
export async function runArticleAdd(client, projectId, opts, log) {
|
|
115
|
+
const { article } = (await client.post(`${base(projectId)}/articles`, { title: opts.title, body: bodyOf(opts) }));
|
|
116
|
+
log(`Added ${article.id} ${article.title}`);
|
|
117
|
+
}
|
|
118
|
+
export async function runArticleEdit(client, projectId, id, opts, log) {
|
|
119
|
+
const { articles } = (await client.get(`${base(projectId)}/articles`));
|
|
120
|
+
const current = articles.find((a) => a.id === id);
|
|
121
|
+
if (!current)
|
|
122
|
+
throw new Error(`No article ${id}. List them with newly support articles list.`);
|
|
123
|
+
const body = opts.body || opts.file ? bodyOf(opts) : current.body;
|
|
124
|
+
await client.put(`${base(projectId)}/articles/${encodeURIComponent(id)}`, { title: opts.title ?? current.title, body });
|
|
125
|
+
log(`Updated ${id}`);
|
|
126
|
+
}
|
|
127
|
+
export async function runArticleRemove(client, projectId, id, log) {
|
|
128
|
+
await client.delete(`${base(projectId)}/articles/${encodeURIComponent(id)}`);
|
|
129
|
+
log(`Removed ${id}`);
|
|
130
|
+
}
|
|
131
|
+
const onOff = (value, flag) => {
|
|
132
|
+
if (value === 'on')
|
|
133
|
+
return true;
|
|
134
|
+
if (value === 'off')
|
|
135
|
+
return false;
|
|
136
|
+
throw new Error(`${flag} must be on or off`);
|
|
137
|
+
};
|
|
138
|
+
const days = (value, flag, max) => {
|
|
139
|
+
if (!/^\d+$/.test(value) || Number(value) > max)
|
|
140
|
+
throw new Error(`${flag} must be a whole number from 0 to ${max}`);
|
|
141
|
+
return Number(value);
|
|
142
|
+
};
|
|
143
|
+
export function settingsPatch(flags) {
|
|
144
|
+
const patch = {};
|
|
145
|
+
if (flags.ai !== undefined)
|
|
146
|
+
patch.ai_enabled = onOff(flags.ai, '--ai');
|
|
147
|
+
if (flags.quality !== undefined) {
|
|
148
|
+
if (flags.quality !== 'standard' && flags.quality !== 'advanced')
|
|
149
|
+
throw new Error('--quality must be standard or advanced');
|
|
150
|
+
patch.ai_quality = flags.quality;
|
|
151
|
+
}
|
|
152
|
+
if (flags.dailyLimit !== undefined)
|
|
153
|
+
patch.ai_daily_cap = days(flags.dailyLimit, '--daily-limit', 5_000);
|
|
154
|
+
if (flags.autoCloseDays !== undefined)
|
|
155
|
+
patch.auto_close_days = days(flags.autoCloseDays, '--auto-close-days', 90);
|
|
156
|
+
if (flags.chatHistory !== undefined)
|
|
157
|
+
patch.chat_history = onOff(flags.chatHistory, '--chat-history');
|
|
158
|
+
return patch;
|
|
159
|
+
}
|
|
160
|
+
export async function runSupportSettings(client, projectId, flags, log) {
|
|
161
|
+
const patch = settingsPatch(flags);
|
|
162
|
+
const result = Object.keys(patch).length
|
|
163
|
+
? (await client.put(`${base(projectId)}/settings`, patch)).settings
|
|
164
|
+
: (await client.get(`${base(projectId)}/settings`)).settings;
|
|
165
|
+
log(JSON.stringify({
|
|
166
|
+
support: result.enabled ? 'on' : 'off', ai_answers: result.ai_enabled ? 'on' : 'off', quality: result.ai_quality ?? 'standard',
|
|
167
|
+
daily_limit: result.ai_daily_cap, auto_close_days: result.auto_close_days ?? 5, chat_history: result.chat_history ? 'on' : 'off',
|
|
168
|
+
}, null, 2));
|
|
169
|
+
}
|
|
170
|
+
export async function runSupportFeedback(client, projectId, log) {
|
|
171
|
+
const f = (await client.get(`${base(projectId)}/feedback`));
|
|
172
|
+
if (!f.count) {
|
|
173
|
+
log('No ratings yet.');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
log(`Average ${f.average} out of 5 from ${f.count} ${f.count === 1 ? 'rating' : 'ratings'} (5: ${f.distribution['5']}, 4: ${f.distribution['4']}, 3: ${f.distribution['3']}, 2: ${f.distribution['2']}, 1: ${f.distribution['1']})`);
|
|
177
|
+
for (const chat of f.chats.slice(0, 10))
|
|
178
|
+
log(`${chat.rating}/5 ${chat.answered_by === 'team' ? 'team' : 'AI'} ${chat.question ?? ''}`);
|
|
179
|
+
}
|
|
180
|
+
const importedLine = (r) => `Imported ${r.articles.length} ${r.articles.length === 1 ? 'article' : 'articles'} (used ${r.credits_charged} ${r.credits_charged === 1 ? 'credit' : 'credits'}):\n`
|
|
181
|
+
+ r.articles.map((a) => `${a.id} ${a.title}`).join('\n');
|
|
182
|
+
export async function runArticleImportPdf(client, projectId, path, log) {
|
|
183
|
+
if (!client.upload)
|
|
184
|
+
throw new Error('This client cannot upload files.');
|
|
185
|
+
const name = path.split('/').pop() || 'document.pdf';
|
|
186
|
+
const result = (await client.upload(`${base(projectId)}/articles/import/pdf?${new URLSearchParams({ name })}`, readFileSync(path), 'application/pdf'));
|
|
187
|
+
log(importedLine(result));
|
|
188
|
+
}
|
|
189
|
+
export async function runArticleImportUrl(client, projectId, url, refresh, log) {
|
|
190
|
+
const result = (await client.post(`${base(projectId)}/articles/import/url`, { url, replace: refresh }));
|
|
191
|
+
log(importedLine(result));
|
|
192
|
+
}
|
|
193
|
+
export function registerSupport(program) {
|
|
194
|
+
const support = program.command('support').description('Customer support inside your app: help chat, AI answers and knowledge base');
|
|
195
|
+
const ctx = (project) => {
|
|
196
|
+
const credentials = requireCredentials();
|
|
197
|
+
return { client: new ApiClient(credentials.apiUrl, credentials.token), projectId: resolveProject(process.cwd(), project), apiUrl: credentials.apiUrl };
|
|
198
|
+
};
|
|
199
|
+
const run = (work) => work().catch((error) => {
|
|
200
|
+
console.error(error instanceof Error && !('status' in error) ? error.message : friendlyApiError(error));
|
|
201
|
+
process.exitCode = 1;
|
|
202
|
+
});
|
|
203
|
+
const log = (m) => console.log(m);
|
|
204
|
+
support.command('status').option('--project <id>').action((o) => run(async () => {
|
|
205
|
+
const c = ctx(o.project);
|
|
206
|
+
await runSupportStatus(c.client, c.projectId, log);
|
|
207
|
+
}));
|
|
208
|
+
support.command('enable').description(`Turn support on, write the app's keys to ${SUPPORT_CONFIG} and the chat to ${SUPPORT_CHAT}`)
|
|
209
|
+
.option('--force-chat', `replace an existing ${SUPPORT_CHAT}`).option('--project <id>')
|
|
210
|
+
.action((o) => run(async () => {
|
|
211
|
+
const c = ctx(o.project);
|
|
212
|
+
await runSupportEnable(c.client, c.projectId, c.apiUrl, process.cwd(), log, { forceChat: Boolean(o.forceChat) });
|
|
213
|
+
}));
|
|
214
|
+
support.command('disable').option('--project <id>').action((o) => run(async () => {
|
|
215
|
+
const c = ctx(o.project);
|
|
216
|
+
await runSupportDisable(c.client, c.projectId, log);
|
|
217
|
+
}));
|
|
218
|
+
support.command('rotate').description('Replace a support key; --secret makes a server key, shown once')
|
|
219
|
+
.option('--env <environment>', 'dev or prod', 'dev').option('--secret', 'a secret key for your server').option('--project <id>')
|
|
220
|
+
.action((o) => run(async () => {
|
|
221
|
+
const c = ctx(o.project);
|
|
222
|
+
await runSupportRotate(c.client, c.projectId, c.apiUrl, process.cwd(), envFrom(o.env), Boolean(o.secret), log);
|
|
223
|
+
}));
|
|
224
|
+
support.command('auth').description("Recognise signed-in customers from your app's sign-in provider")
|
|
225
|
+
.option('--supabase-url <url>', 'your Supabase project URL').option('--issuer <url>').option('--jwks <url>').option('--audience <aud>')
|
|
226
|
+
.option('--clear').option('--project <id>')
|
|
227
|
+
.action((o) => run(async () => {
|
|
228
|
+
const c = ctx(o.project);
|
|
229
|
+
await runSupportAuth(c.client, c.projectId, { supabaseUrl: o.supabaseUrl, issuer: o.issuer, jwks: o.jwks, audience: o.audience, clear: o.clear }, log);
|
|
230
|
+
}));
|
|
231
|
+
support.command('settings').description('AI answers, quality, daily limit, auto-close and chat history; no flags prints them')
|
|
232
|
+
.option('--ai <on|off>').option('--quality <standard|advanced>').option('--daily-limit <n>')
|
|
233
|
+
.option('--auto-close-days <n>', '0 turns auto-close off').option('--chat-history <on|off>').option('--project <id>')
|
|
234
|
+
.action((o) => run(async () => {
|
|
235
|
+
const c = ctx(o.project);
|
|
236
|
+
await runSupportSettings(c.client, c.projectId, o, log);
|
|
237
|
+
}));
|
|
238
|
+
support.command('feedback').description("Customers' ratings of resolved chats").option('--project <id>')
|
|
239
|
+
.action((o) => run(async () => {
|
|
240
|
+
const c = ctx(o.project);
|
|
241
|
+
await runSupportFeedback(c.client, c.projectId, log);
|
|
242
|
+
}));
|
|
243
|
+
const articles = support.command('articles').description('The knowledge base AI answers come from');
|
|
244
|
+
articles.command('list').option('--project <id>').action((o) => run(async () => {
|
|
245
|
+
const c = ctx(o.project);
|
|
246
|
+
await runArticlesList(c.client, c.projectId, log);
|
|
247
|
+
}));
|
|
248
|
+
articles.command('add').requiredOption('--title <title>').option('--body <text>').option('--file <path>').option('--project <id>')
|
|
249
|
+
.action((o) => run(async () => {
|
|
250
|
+
const c = ctx(o.project);
|
|
251
|
+
await runArticleAdd(c.client, c.projectId, o, log);
|
|
252
|
+
}));
|
|
253
|
+
articles.command('edit <id>').option('--title <title>').option('--body <text>').option('--file <path>').option('--project <id>')
|
|
254
|
+
.action((id, o) => run(async () => {
|
|
255
|
+
const c = ctx(o.project);
|
|
256
|
+
await runArticleEdit(c.client, c.projectId, id, o, log);
|
|
257
|
+
}));
|
|
258
|
+
articles.command('import-pdf <path>').description('Turn a PDF into articles (uses 1 credit)').option('--project <id>')
|
|
259
|
+
.action((path, o) => run(async () => {
|
|
260
|
+
const c = ctx(o.project);
|
|
261
|
+
await runArticleImportPdf(c.client, c.projectId, path, log);
|
|
262
|
+
}));
|
|
263
|
+
articles.command('import-url <url>').description("Turn a docs page into articles (uses 1 credit); --refresh replaces that page's articles")
|
|
264
|
+
.option('--refresh').option('--project <id>')
|
|
265
|
+
.action((url, o) => run(async () => {
|
|
266
|
+
const c = ctx(o.project);
|
|
267
|
+
await runArticleImportUrl(c.client, c.projectId, url, Boolean(o.refresh), log);
|
|
268
|
+
}));
|
|
269
|
+
articles.command('remove <id>').option('--project <id>').action((id, o) => run(async () => {
|
|
270
|
+
const c = ctx(o.project);
|
|
271
|
+
await runArticleRemove(c.client, c.projectId, id, log);
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SUPPORT_CHAT_TSX = "aW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0JzsKaW1wb3J0IHsKICBBY3Rpdml0eUluZGljYXRvciwgRmxhdExpc3QsIEtleWJvYXJkQXZvaWRpbmdWaWV3LCBQbGF0Zm9ybSwgUHJlc3NhYmxlLCBTdHlsZVNoZWV0LCBUZXh0LCBUZXh0SW5wdXQsIFZpZXcsIHVzZUNvbG9yU2NoZW1lLAp9IGZyb20gJ3JlYWN0LW5hdGl2ZSc7CgppbXBvcnQgc3VwcG9ydCBmcm9tICdAL2xpYi9uZXdseS1zdXBwb3J0Lmpzb24nOwppbXBvcnQgeyBzdG9yYWdlIH0gZnJvbSAnQC9saWIvc3RvcmFnZSc7CgovLyBgbmV3bHkgc3VwcG9ydCBlbmFibGVgIHdyaXRlcyB0aGlzIGFwcCdzIHByb2plY3QsIEFQSSBvcmlnaW4gYW5kIHB1Ymxpc2hhYmxlIGtleXMgaGVyZS4gVGhlCi8vIGtleXMgYXJlIHB1YmxpYyBieSBkZXNpZ246IHRoZXkgb25seSBvcGVuIHRoaXMgYXBwJ3MgaGVscCBjaGF0Lgpjb25zdCB7IGFwaVVybCwga2V5cyB9ID0gc3VwcG9ydCBhcyB7IHByb2plY3RJZDogc3RyaW5nOyBhcGlVcmw6IHN0cmluZzsga2V5czogeyBkZXZlbG9wbWVudDogc3RyaW5nOyBwcm9kdWN0aW9uOiBzdHJpbmcgfSB9Owpjb25zdCBLRVkgPSBfX0RFVl9fID8ga2V5cy5kZXZlbG9wbWVudCA6IGtleXMucHJvZHVjdGlvbjsKY29uc3QgVE9LRU5fS0VZID0gYG5ld2x5LnN1cHBvcnQudG9rZW4uJHtfX0RFVl9fID8gJ2RldicgOiAncHJvZCd9YDsKY29uc3QgQ0hBVF9LRVkgPSBgbmV3bHkuc3VwcG9ydC5jaGF0LiR7X19ERVZfXyA/ICdkZXYnIDogJ3Byb2QnfWA7Cgp0eXBlIFN0YXR1cyA9ICdvcGVuJyB8ICd3YWl0aW5nJyB8ICdyZXNvbHZlZCc7CnR5cGUgU291cmNlID0geyBraW5kOiAnYXJ0aWNsZSc7IGlkOiBzdHJpbmc7IHRpdGxlOiBzdHJpbmcgfSB8IHsga2luZDogJ2hpc3RvcnknIH07CnR5cGUgTWVzc2FnZSA9IHsgaWQ6IHN0cmluZzsgYXV0aG9yOiAnY3VzdG9tZXInIHwgJ2FpJyB8ICdtYWtlcicgfCAnc3lzdGVtJzsgY29udGVudDogc3RyaW5nOyBzb3VyY2VzOiBTb3VyY2VbXTsgY3JlYXRlZF9hdDogc3RyaW5nIH07CnR5cGUgVGhyZWFkID0geyBzdGF0dXM6IFN0YXR1czsgcmF0aW5nOiBudW1iZXIgfCBudWxsOyBhbnN3ZXJpbmc6IGJvb2xlYW47IG1lc3NhZ2VzOiBNZXNzYWdlW10gfTsKdHlwZSBDaGF0U3VtbWFyeSA9IHsgaWQ6IHN0cmluZzsgc3RhdHVzOiBTdGF0dXM7IHVucmVhZDogbnVtYmVyOyBsYXN0X21lc3NhZ2U6IHN0cmluZyB8IG51bGw7IGxhc3RfbWVzc2FnZV9hdDogc3RyaW5nOyByYXRpbmc6IG51bWJlciB8IG51bGwgfTsKCi8qKgogKiBXaG8gaXMgYXNraW5nLiBQYXNzIGBpZFRva2VuYCAodGhlIHNpZ25lZC1pbiB1c2VyJ3MgdG9rZW4gZnJvbSBTdXBhYmFzZSwgRmlyZWJhc2UsIENsZXJrLi4uKSBzbwogKiB0aGUgYXBwJ3MgdGVhbSBzZWVzIGEgdmVyaWZpZWQgY3VzdG9tZXI7IHdpdGhvdXQgaXQgdGhlIGNoYXQgaXMgYW5vbnltb3VzIHRvIHRoaXMgZGV2aWNlLgogKi8KZXhwb3J0IHR5cGUgU3VwcG9ydEN1c3RvbWVyID0geyBpZFRva2VuPzogc3RyaW5nIHwgbnVsbDsgbmFtZT86IHN0cmluZyB8IG51bGw7IGVtYWlsPzogc3RyaW5nIHwgbnVsbCB9OwoKY29uc3QgU1RBVFVTX0xBQkVMOiBSZWNvcmQ8U3RhdHVzLCBzdHJpbmc+ID0geyBvcGVuOiAnT3BlbicsIHdhaXRpbmc6ICdXYWl0aW5nIGZvciB0aGUgdGVhbScsIHJlc29sdmVkOiAnUmVzb2x2ZWQnIH07Cgphc3luYyBmdW5jdGlvbiBjYWxsPFQ+KHBhdGg6IHN0cmluZywgaW5pdDogUmVxdWVzdEluaXQgPSB7fSk6IFByb21pc2U8VD4gewogIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGAke2FwaVVybH0vYXBwLXN1cHBvcnQvdjEke3BhdGh9YCwgewogICAgLi4uaW5pdCwKICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywgLi4uKGluaXQuaGVhZGVycyA/PyB7fSkgfSwKICB9KTsKICBpZiAocmVzLnN0YXR1cyA9PT0gMjA0KSByZXR1cm4gdW5kZWZpbmVkIGFzIFQ7CiAgY29uc3QgYm9keSA9IGF3YWl0IHJlcy5qc29uKCkuY2F0Y2goKCkgPT4gKHt9KSk7CiAgaWYgKCFyZXMub2spIHRocm93IE9iamVjdC5hc3NpZ24obmV3IEVycm9yKGJvZHkuZXJyb3IgPz8gJ1N1cHBvcnQgaXMgdW5hdmFpbGFibGUgcmlnaHQgbm93LicpLCB7IHN0YXR1czogcmVzLnN0YXR1cyB9KTsKICByZXR1cm4gYm9keSBhcyBUOwp9Cgphc3luYyBmdW5jdGlvbiB2aXNpdG9yVG9rZW4oY3VzdG9tZXI6IFN1cHBvcnRDdXN0b21lciwgZnJlc2ggPSBmYWxzZSk6IFByb21pc2U8c3RyaW5nPiB7CiAgY29uc3Qgc2F2ZWQgPSBzdG9yYWdlLmdldFN0cmluZyhUT0tFTl9LRVkpOwogIGlmIChzYXZlZCAmJiAhZnJlc2gpIHJldHVybiBzYXZlZDsKICBjb25zdCB7IHRva2VuIH0gPSBhd2FpdCBjYWxsPHsgdG9rZW46IHN0cmluZyB9PignL3Zpc2l0b3JzJywgewogICAgbWV0aG9kOiAnUE9TVCcsCiAgICBoZWFkZXJzOiB7ICd4LW5ld2x5LXN1cHBvcnQta2V5JzogS0VZIH0sCiAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGlkVG9rZW46IGN1c3RvbWVyLmlkVG9rZW4gPz8gdW5kZWZpbmVkLCBuYW1lOiBjdXN0b21lci5uYW1lID8/IHVuZGVmaW5lZCwgZW1haWw6IGN1c3RvbWVyLmVtYWlsID8/IHVuZGVmaW5lZCB9KSwKICB9KTsKICBzdG9yYWdlLnNldChUT0tFTl9LRVksIHRva2VuKTsKICByZXR1cm4gdG9rZW47Cn0KCmNvbnN0IHRpbWVMYWJlbCA9IChpc286IHN0cmluZykgPT4gewogIGNvbnN0IG1pbnV0ZXMgPSBNYXRoLnJvdW5kKChEYXRlLm5vdygpIC0gRGF0ZS5wYXJzZShpc28pKSAvIDYwXzAwMCk7CiAgaWYgKG1pbnV0ZXMgPCAxKSByZXR1cm4gJ0p1c3Qgbm93JzsKICBpZiAobWludXRlcyA8IDYwKSByZXR1cm4gYCR7bWludXRlc30gbWluIGFnb2A7CiAgaWYgKG1pbnV0ZXMgPCA2MCAqIDI0KSByZXR1cm4gYCR7TWF0aC5yb3VuZChtaW51dGVzIC8gNjApfSBoIGFnb2A7CiAgcmV0dXJuIG5ldyBEYXRlKGlzbykudG9Mb2NhbGVEYXRlU3RyaW5nKCk7Cn07CgovKioKICogVGhlIGFwcCdzIGhlbHAgY2hhdDogdGhlIEFJIGFuc3dlcnMgZmlyc3QgYW5kIHRoZSB0ZWFtIHJlcGxpZXMgaW4gdGhlIHNhbWUgdGhyZWFkLiAiWW91ciBjaGF0cyIKICogbGlzdHMgZXZlcnkgZWFybGllciBjaGF0IHdpdGggaXRzIHN0YXR1czsgYSByZXNvbHZlZCBjaGF0IGFza3MgZm9yIGEgcmF0aW5nLCBhbmQgd3JpdGluZyBhZ2FpbgogKiBzdGFydHMgYSBuZXcgY2hhdC4KICovCmV4cG9ydCBmdW5jdGlvbiBTdXBwb3J0Q2hhdCh7IGN1c3RvbWVyID0ge30sIGFwcE5hbWUgPSAnSGVscCcgfTogeyBjdXN0b21lcj86IFN1cHBvcnRDdXN0b21lcjsgYXBwTmFtZT86IHN0cmluZyB9KSB7CiAgY29uc3QgYyA9IHVzZUNvbG9yU2NoZW1lKCkgPT09ICdkYXJrJyA/IHBhbGV0dGUuZGFyayA6IHBhbGV0dGUubGlnaHQ7CiAgY29uc3QgW3Rva2VuLCBzZXRUb2tlbl0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKTsKICBjb25zdCBbdmlldywgc2V0Vmlld10gPSB1c2VTdGF0ZTwnY2hhdCcgfCAnbGlzdCc+KCdjaGF0Jyk7CiAgY29uc3QgW2NoYXRJZCwgc2V0Q2hhdElkXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KCgpID0+IHN0b3JhZ2UuZ2V0U3RyaW5nKENIQVRfS0VZKSA/PyBudWxsKTsKICBjb25zdCBbdGhyZWFkLCBzZXRUaHJlYWRdID0gdXNlU3RhdGU8VGhyZWFkIHwgbnVsbD4obnVsbCk7CiAgY29uc3QgW2NoYXRzLCBzZXRDaGF0c10gPSB1c2VTdGF0ZTxDaGF0U3VtbWFyeVtdIHwgbnVsbD4obnVsbCk7CiAgY29uc3QgW2RyYWZ0LCBzZXREcmFmdF0gPSB1c2VTdGF0ZSgnJyk7CiAgY29uc3QgW2Vycm9yLCBzZXRFcnJvcl0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKTsKICBjb25zdCBbc2VuZGluZywgc2V0U2VuZGluZ10gPSB1c2VTdGF0ZShmYWxzZSk7CiAgY29uc3Qgc2lnbmVkSW4gPSBCb29sZWFuKGN1c3RvbWVyLmlkVG9rZW4pOwogIGNvbnN0IGxhc3RTaWduZWRJbiA9IHVzZVJlZihzaWduZWRJbik7CgogIC8vIEEgc2lnbi1pbiBvciBzaWduLW91dCBnZXRzIGEgbmV3IHZpc2l0b3IsIHNvIGEgdmVyaWZpZWQgY3VzdG9tZXIgbmV2ZXIgc2hhcmVzIGFuIGFub255bW91cyBoaXN0b3J5LgogIHVzZUVmZmVjdCgoKSA9PiB7CiAgICBjb25zdCBjaGFuZ2VkID0gbGFzdFNpZ25lZEluLmN1cnJlbnQgIT09IHNpZ25lZEluOwogICAgbGFzdFNpZ25lZEluLmN1cnJlbnQgPSBzaWduZWRJbjsKICAgIGlmIChjaGFuZ2VkKSB7CiAgICAgIHN0b3JhZ2UucmVtb3ZlKENIQVRfS0VZKTsKICAgICAgc2V0Q2hhdElkKG51bGwpOwogICAgICBzZXRUaHJlYWQobnVsbCk7CiAgICAgIHNldENoYXRzKG51bGwpOwogICAgfQogICAgdmlzaXRvclRva2VuKGN1c3RvbWVyLCBjaGFuZ2VkKS50aGVuKHNldFRva2VuLCAoZTogRXJyb3IpID0+IHNldEVycm9yKGUubWVzc2FnZSkpOwogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwcwogIH0sIFtzaWduZWRJbl0pOwoKICBjb25zdCBhdXRoZWQgPSB1c2VDYWxsYmFjayg8VCw+KHBhdGg6IHN0cmluZywgaW5pdDogUmVxdWVzdEluaXQgPSB7fSkgPT4KICAgIGNhbGw8VD4ocGF0aCwgeyAuLi5pbml0LCBoZWFkZXJzOiB7IEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHt0b2tlbn1gLCAuLi4oaW5pdC5oZWFkZXJzID8/IHt9KSB9IH0pLmNhdGNoKGFzeW5jIChlOiBFcnJvciAmIHsgc3RhdHVzPzogbnVtYmVyIH0pID0+IHsKICAgICAgaWYgKGUuc3RhdHVzID09PSA0MDEpIHNldFRva2VuKGF3YWl0IHZpc2l0b3JUb2tlbihjdXN0b21lciwgdHJ1ZSkpOwogICAgICB0aHJvdyBlOwogICAgfSksIFt0b2tlbiwgY3VzdG9tZXJdKTsKCiAgY29uc3Qgb3BlbiA9IChpZDogc3RyaW5nIHwgbnVsbCkgPT4gewogICAgaWYgKGlkKSBzdG9yYWdlLnNldChDSEFUX0tFWSwgaWQpOwogICAgZWxzZSBzdG9yYWdlLnJlbW92ZShDSEFUX0tFWSk7CiAgICBzZXRDaGF0SWQoaWQpOwogICAgc2V0VGhyZWFkKG51bGwpOwogICAgc2V0VmlldygnY2hhdCcpOwogIH07CgogIGNvbnN0IGxvYWRDaGF0cyA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHsKICAgIGlmICghdG9rZW4pIHJldHVybjsKICAgIHRyeSB7CiAgICAgIHNldENoYXRzKChhd2FpdCBhdXRoZWQ8eyBjb252ZXJzYXRpb25zOiBDaGF0U3VtbWFyeVtdIH0+KCcvY29udmVyc2F0aW9ucycpKS5jb252ZXJzYXRpb25zKTsKICAgIH0gY2F0Y2ggewogICAgICAvLyB0aGUgbGlzdCBzdGF5cyBhcyBpdCB3YXM7IHRoZSBjaGF0IGl0c2VsZiBrZWVwcyB3b3JraW5nCiAgICB9CiAgfSwgW3Rva2VuLCBhdXRoZWRdKTsKCiAgY29uc3QgbG9hZFRocmVhZCA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHsKICAgIGlmICghdG9rZW4gfHwgIWNoYXRJZCkgcmV0dXJuOwogICAgdHJ5IHsKICAgICAgc2V0VGhyZWFkKGF3YWl0IGF1dGhlZDxUaHJlYWQ+KGAvY29udmVyc2F0aW9ucy8ke2NoYXRJZH0vbWVzc2FnZXNgKSk7CiAgICAgIGF3YWl0IGF1dGhlZChgL2NvbnZlcnNhdGlvbnMvJHtjaGF0SWR9L3JlYWRgLCB7IG1ldGhvZDogJ1BPU1QnIH0pOwogICAgfSBjYXRjaCAoZSkgewogICAgICBpZiAoKGUgYXMgeyBzdGF0dXM/OiBudW1iZXIgfSkuc3RhdHVzID09PSA0MDQpIG9wZW4obnVsbCk7CiAgICB9CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVhY3QtaG9va3MvZXhoYXVzdGl2ZS1kZXBzCiAgfSwgW3Rva2VuLCBjaGF0SWQsIGF1dGhlZF0pOwoKICB1c2VFZmZlY3QoKCkgPT4geyB2b2lkIGxvYWRDaGF0cygpOyB9LCBbbG9hZENoYXRzXSk7CgogIC8vIEZhc3RlciB3aGlsZSBhbiBhbnN3ZXIgaXMgYmVpbmcgd3JpdHRlbiwgc2xvd2VyIG90aGVyd2lzZTsgdGhlIGxpc3QgcmVmcmVzaGVzIHdoaWxlIGl0J3Mgc2hvd24uCiAgdXNlRWZmZWN0KCgpID0+IHsKICAgIGNvbnN0IHRpY2sgPSAoKSA9PiB2b2lkICh2aWV3ID09PSAnY2hhdCcgPyBsb2FkVGhyZWFkKCkgOiBsb2FkQ2hhdHMoKSk7CiAgICB0aWNrKCk7CiAgICBjb25zdCB0aW1lciA9IHNldEludGVydmFsKHRpY2ssIHZpZXcgPT09ICdjaGF0JyAmJiB0aHJlYWQ/LmFuc3dlcmluZyA/IDE1MDAgOiA0MDAwKTsKICAgIHJldHVybiAoKSA9PiBjbGVhckludGVydmFsKHRpbWVyKTsKICB9LCBbdmlldywgbG9hZFRocmVhZCwgbG9hZENoYXRzLCB0aHJlYWQ/LmFuc3dlcmluZ10pOwoKICBjb25zdCByZXNvbHZlZCA9IHRocmVhZD8uc3RhdHVzID09PSAncmVzb2x2ZWQnOwogIGNvbnN0IHNlbmQgPSBhc3luYyAoKSA9PiB7CiAgICBjb25zdCBtZXNzYWdlID0gZHJhZnQudHJpbSgpOwogICAgaWYgKCFtZXNzYWdlIHx8ICF0b2tlbiB8fCBzZW5kaW5nKSByZXR1cm47CiAgICBzZXRTZW5kaW5nKHRydWUpOwogICAgc2V0RXJyb3IobnVsbCk7CiAgICB0cnkgewogICAgICBpZiAoY2hhdElkICYmICFyZXNvbHZlZCkgewogICAgICAgIGF3YWl0IGF1dGhlZChgL2NvbnZlcnNhdGlvbnMvJHtjaGF0SWR9L21lc3NhZ2VzYCwgeyBtZXRob2Q6ICdQT1NUJywgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBtZXNzYWdlIH0pIH0pOwogICAgICAgIGF3YWl0IGxvYWRUaHJlYWQoKTsKICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBObyBjaGF0IHlldCwgb3IgdGhlIGxhc3Qgb25lIGlzIHJlc29sdmVkOiB0aGlzIG1lc3NhZ2Ugc3RhcnRzIGEgbmV3IGNoYXQuCiAgICAgICAgY29uc3QgeyBjb252ZXJzYXRpb24gfSA9IGF3YWl0IGF1dGhlZDx7IGNvbnZlcnNhdGlvbjogeyBpZDogc3RyaW5nIH0gfT4oJy9jb252ZXJzYXRpb25zJywgeyBtZXRob2Q6ICdQT1NUJywgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBtZXNzYWdlIH0pIH0pOwogICAgICAgIG9wZW4oY29udmVyc2F0aW9uLmlkKTsKICAgICAgfQogICAgICBzZXREcmFmdCgnJyk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHNldEVycm9yKChlIGFzIEVycm9yKS5tZXNzYWdlKTsKICAgIH0gZmluYWxseSB7CiAgICAgIHNldFNlbmRpbmcoZmFsc2UpOwogICAgfQogIH07CgogIGNvbnN0IHJhdGUgPSBhc3luYyAocmF0aW5nOiBudW1iZXIpID0+IHsKICAgIGlmICghY2hhdElkKSByZXR1cm47CiAgICBhd2FpdCBhdXRoZWQoYC9jb252ZXJzYXRpb25zLyR7Y2hhdElkfS9yYXRpbmdgLCB7IG1ldGhvZDogJ1BPU1QnLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IHJhdGluZyB9KSB9KS5jYXRjaCgoKSA9PiB7fSk7CiAgICBhd2FpdCBsb2FkVGhyZWFkKCk7CiAgfTsKCiAgY29uc3QgdW5yZWFkRWxzZXdoZXJlID0gKGNoYXRzID8/IFtdKS5zb21lKChjaGF0KSA9PiBjaGF0LnVucmVhZCA+IDAgJiYgY2hhdC5pZCAhPT0gY2hhdElkKTsKICByZXR1cm4gKAogICAgPEtleWJvYXJkQXZvaWRpbmdWaWV3IHN0eWxlPXtbc3R5bGVzLnNjcmVlbiwgeyBiYWNrZ3JvdW5kQ29sb3I6IGMuYmcgfV19IGJlaGF2aW9yPXtQbGF0Zm9ybS5PUyA9PT0gJ2lvcycgPyAncGFkZGluZycgOiB1bmRlZmluZWR9PgogICAgICA8VmlldyBzdHlsZT17W3N0eWxlcy5oZWFkZXIsIHsgYm9yZGVyQ29sb3I6IGMuYm9yZGVyLCBiYWNrZ3JvdW5kQ29sb3I6IGMuY2FyZCB9XX0+CiAgICAgICAge3ZpZXcgPT09ICdsaXN0JyA/ICgKICAgICAgICAgIDxQcmVzc2FibGUgb25QcmVzcz17KCkgPT4gc2V0VmlldygnY2hhdCcpfSBhY2Nlc3NpYmlsaXR5Um9sZT0iYnV0dG9uIiBhY2Nlc3NpYmlsaXR5TGFiZWw9IkJhY2sgdG8gdGhlIGNoYXQiIHN0eWxlPXtzdHlsZXMuaGVhZGVyQnV0dG9ufT4KICAgICAgICAgICAgPFRleHQgc3R5bGU9e1tzdHlsZXMuaGVhZGVyQnV0dG9uVGV4dCwgeyBjb2xvcjogYy5hY2NlbnQgfV19PkJhY2s8L1RleHQ+CiAgICAgICAgICA8L1ByZXNzYWJsZT4KICAgICAgICApIDogbnVsbH0KICAgICAgICA8VmlldyBzdHlsZT17c3R5bGVzLmhlYWRlclRleHR9PgogICAgICAgICAgPFRleHQgc3R5bGU9e1tzdHlsZXMudGl0bGUsIHsgY29sb3I6IGMudGV4dCB9XX0+e3ZpZXcgPT09ICdsaXN0JyA/ICdZb3VyIGNoYXRzJyA6IGFwcE5hbWV9PC9UZXh0PgogICAgICAgICAgPFRleHQgc3R5bGU9e1tzdHlsZXMuc3VidGl0bGUsIHsgY29sb3I6IGMubXV0ZWQgfV19PgogICAgICAgICAgICB7dmlldyA9PT0gJ2xpc3QnID8gJ0V2ZXJ5IHF1ZXN0aW9uIHlvdSBhc2tlZCwgbmV3ZXN0IGZpcnN0JyA6IHJlc29sdmVkID8gJ1Jlc29sdmVkJyA6ICdBSSBhc3Npc3RhbnQsIGFuZCB0aGUgdGVhbSB3aGVuIHlvdSBuZWVkIHRoZW0nfQogICAgICAgICAgPC9UZXh0PgogICAgICAgIDwvVmlldz4KICAgICAgICB7dmlldyA9PT0gJ2NoYXQnID8gKAogICAgICAgICAgPFByZXNzYWJsZSBvblByZXNzPXsoKSA9PiB7IHNldFZpZXcoJ2xpc3QnKTsgdm9pZCBsb2FkQ2hhdHMoKTsgfX0gYWNjZXNzaWJpbGl0eVJvbGU9ImJ1dHRvbiIgYWNjZXNzaWJpbGl0eUxhYmVsPSJZb3VyIGNoYXRzIiBzdHlsZT17c3R5bGVzLmhlYWRlckJ1dHRvbn0+CiAgICAgICAgICAgIDxUZXh0IHN0eWxlPXtbc3R5bGVzLmhlYWRlckJ1dHRvblRleHQsIHsgY29sb3I6IGMuYWNjZW50IH1dfT5Zb3VyIGNoYXRze3VucmVhZEVsc2V3aGVyZSA/ICcg4oCiJyA6ICcnfTwvVGV4dD4KICAgICAgICAgIDwvUHJlc3NhYmxlPgogICAgICAgICkgOiAoCiAgICAgICAgICA8UHJlc3NhYmxlIG9uUHJlc3M9eygpID0+IG9wZW4obnVsbCl9IGFjY2Vzc2liaWxpdHlSb2xlPSJidXR0b24iIGFjY2Vzc2liaWxpdHlMYWJlbD0iTmV3IGNoYXQiIHN0eWxlPXtzdHlsZXMuaGVhZGVyQnV0dG9ufT4KICAgICAgICAgICAgPFRleHQgc3R5bGU9e1tzdHlsZXMuaGVhZGVyQnV0dG9uVGV4dCwgeyBjb2xvcjogYy5hY2NlbnQgfV19Pk5ldyBjaGF0PC9UZXh0PgogICAgICAgICAgPC9QcmVzc2FibGU+CiAgICAgICAgKX0KICAgICAgPC9WaWV3PgoKICAgICAge3ZpZXcgPT09ICdsaXN0JyA/ICgKICAgICAgICA8RmxhdExpc3QKICAgICAgICAgIGRhdGE9e2NoYXRzID8/IFtdfQogICAgICAgICAga2V5RXh0cmFjdG9yPXsoY2hhdCkgPT4gY2hhdC5pZH0KICAgICAgICAgIGNvbnRlbnRDb250YWluZXJTdHlsZT17c3R5bGVzLmxpc3R9CiAgICAgICAgICBMaXN0RW1wdHlDb21wb25lbnQ9e2NoYXRzID09PSBudWxsID8gPEFjdGl2aXR5SW5kaWNhdG9yIGNvbG9yPXtjLm11dGVkfSAvPiA6IDxUZXh0IHN0eWxlPXtbc3R5bGVzLmVtcHR5LCB7IGNvbG9yOiBjLm11dGVkIH1dfT5ObyBjaGF0cyB5ZXQuIEFzayBhbnl0aGluZyBhYm91dCB7YXBwTmFtZX0uPC9UZXh0Pn0KICAgICAgICAgIHJlbmRlckl0ZW09eyh7IGl0ZW0gfSkgPT4gKAogICAgICAgICAgICA8UHJlc3NhYmxlIG9uUHJlc3M9eygpID0+IG9wZW4oaXRlbS5pZCl9IGFjY2Vzc2liaWxpdHlSb2xlPSJidXR0b24iIGFjY2Vzc2liaWxpdHlMYWJlbD17YCR7U1RBVFVTX0xBQkVMW2l0ZW0uc3RhdHVzXX0gY2hhdGB9CiAgICAgICAgICAgICAgc3R5bGU9e1tzdHlsZXMuY2hhdFJvdywgeyBib3JkZXJDb2xvcjogYy5ib3JkZXIsIGJhY2tncm91bmRDb2xvcjogYy5jYXJkIH1dfT4KICAgICAgICAgICAgICA8VmlldyBzdHlsZT17c3R5bGVzLmNoYXRSb3dUZXh0fT4KICAgICAgICAgICAgICAgIDxUZXh0IHN0eWxlPXtbc3R5bGVzLmNoYXRQcmV2aWV3LCB7IGNvbG9yOiBjLnRleHQgfV19IG51bWJlck9mTGluZXM9ezJ9PntpdGVtLmxhc3RfbWVzc2FnZSA/PyAnTmV3IGNoYXQnfTwvVGV4dD4KICAgICAgICAgICAgICAgIDxUZXh0IHN0eWxlPXtbc3R5bGVzLnN1YnRpdGxlLCB7IGNvbG9yOiBjLm11dGVkIH1dfT57dGltZUxhYmVsKGl0ZW0ubGFzdF9tZXNzYWdlX2F0KX08L1RleHQ+CiAgICAgICAgICAgICAgPC9WaWV3PgogICAgICAgICAgICAgIDxWaWV3IHN0eWxlPXtzdHlsZXMuY2hhdFJvd1JpZ2h0fT4KICAgICAgICAgICAgICAgIDxUZXh0IHN0eWxlPXtbc3R5bGVzLnBpbGwsIGl0ZW0uc3RhdHVzID09PSAncmVzb2x2ZWQnID8geyBjb2xvcjogYy5tdXRlZCwgYm9yZGVyQ29sb3I6IGMuYm9yZGVyIH0gOiB7IGNvbG9yOiBjLmFjY2VudCwgYm9yZGVyQ29sb3I6IGMuYWNjZW50IH1dfT4KICAgICAgICAgICAgICAgICAge1NUQVRVU19MQUJFTFtpdGVtLnN0YXR1c119CiAgICAgICAgICAgICAgICA8L1RleHQ+CiAgICAgICAgICAgICAgICB7aXRlbS51bnJlYWQgPiAwID8gPFZpZXcgc3R5bGU9e1tzdHlsZXMuZG90LCB7IGJhY2tncm91bmRDb2xvcjogYy5hY2NlbnQgfV19IGFjY2Vzc2liaWxpdHlMYWJlbD0iVW5yZWFkIiAvPiA6IG51bGx9CiAgICAgICAgICAgICAgPC9WaWV3PgogICAgICAgICAgICA8L1ByZXNzYWJsZT4KICAgICAgICAgICl9CiAgICAgICAgLz4KICAgICAgKSA6ICgKICAgICAgICA8RmxhdExpc3QKICAgICAgICAgIGRhdGE9e3RocmVhZD8ubWVzc2FnZXMgPz8gW119CiAgICAgICAgICBrZXlFeHRyYWN0b3I9eyhtKSA9PiBtLmlkfQogICAgICAgICAgY29udGVudENvbnRhaW5lclN0eWxlPXtzdHlsZXMubGlzdH0KICAgICAgICAgIExpc3RFbXB0eUNvbXBvbmVudD17Y2hhdElkICYmICF0aHJlYWQgPyA8QWN0aXZpdHlJbmRpY2F0b3IgY29sb3I9e2MubXV0ZWR9IC8+IDogPFRleHQgc3R5bGU9e1tzdHlsZXMuZW1wdHksIHsgY29sb3I6IGMubXV0ZWQgfV19PkFzayBhbnl0aGluZyBhYm91dCB7YXBwTmFtZX0uIFlvdSdsbCBnZXQgYW4gYW5zd2VyIGhlcmUuPC9UZXh0Pn0KICAgICAgICAgIExpc3RGb290ZXJDb21wb25lbnQ9e3RocmVhZD8uYW5zd2VyaW5nID8gPEFjdGl2aXR5SW5kaWNhdG9yIHN0eWxlPXtzdHlsZXMudHlwaW5nfSBjb2xvcj17Yy5tdXRlZH0gLz4gOiBudWxsfQogICAgICAgICAgcmVuZGVySXRlbT17KHsgaXRlbSB9KSA9PiA8QnViYmxlIG1lc3NhZ2U9e2l0ZW19IGNvbG9ycz17Y30gLz59CiAgICAgICAgLz4KICAgICAgKX0KCiAgICAgIHt2aWV3ID09PSAnY2hhdCcgJiYgcmVzb2x2ZWQgPyAoCiAgICAgICAgPFZpZXcgc3R5bGU9e1tzdHlsZXMucmVzb2x2ZWQsIHsgYm9yZGVyQ29sb3I6IGMuYm9yZGVyLCBiYWNrZ3JvdW5kQ29sb3I6IGMuY2FyZCB9XX0gYWNjZXNzaWJpbGl0eUxpdmVSZWdpb249InBvbGl0ZSI+CiAgICAgICAgICA8VGV4dCBzdHlsZT17W3N0eWxlcy5yZXNvbHZlZFRpdGxlLCB7IGNvbG9yOiBjLnRleHQgfV19PlRoaXMgY2hhdCBpcyByZXNvbHZlZDwvVGV4dD4KICAgICAgICAgIHt0aHJlYWQ/LnJhdGluZyA9PT0gbnVsbCA/ICgKICAgICAgICAgICAgPD4KICAgICAgICAgICAgICA8VGV4dCBzdHlsZT17W3N0eWxlcy5zdWJ0aXRsZSwgeyBjb2xvcjogYy5tdXRlZCB9XX0+SG93IGRpZCB3ZSBkbz88L1RleHQ+CiAgICAgICAgICAgICAgPFZpZXcgc3R5bGU9e3N0eWxlcy5mYWNlc30+CiAgICAgICAgICAgICAgICB7Wyfwn5ieJywgJ/CfmYEnLCAn8J+YkCcsICfwn5mCJywgJ/CfmIQnXS5tYXAoKGZhY2UsIGkpID0+ICgKICAgICAgICAgICAgICAgICAgPFByZXNzYWJsZSBrZXk9e2ZhY2V9IG9uUHJlc3M9eygpID0+IHZvaWQgcmF0ZShpICsgMSl9IGFjY2Vzc2liaWxpdHlMYWJlbD17YCR7aSArIDF9IG9mIDVgfSBzdHlsZT17c3R5bGVzLmZhY2V9PgogICAgICAgICAgICAgICAgICAgIDxUZXh0IHN0eWxlPXtzdHlsZXMuZmFjZVRleHR9PntmYWNlfTwvVGV4dD4KICAgICAgICAgICAgICAgICAgPC9QcmVzc2FibGU+CiAgICAgICAgICAgICAgICApKX0KICAgICAgICAgICAgICA8L1ZpZXc+CiAgICAgICAgICAgIDwvPgogICAgICAgICAgKSA6IDxUZXh0IHN0eWxlPXtbc3R5bGVzLnN1YnRpdGxlLCB7IGNvbG9yOiBjLm11dGVkIH1dfT5UaGFua3MgZm9yIHlvdXIgcmF0aW5nLiBXcml0ZSBiZWxvdyB0byBzdGFydCBhIG5ldyBjaGF0LjwvVGV4dD59CiAgICAgICAgPC9WaWV3PgogICAgICApIDogbnVsbH0KICAgICAge2Vycm9yID8gPFRleHQgc3R5bGU9e1tzdHlsZXMuZXJyb3IsIHsgY29sb3I6IGMuZGFuZ2VyIH1dfT57ZXJyb3J9PC9UZXh0PiA6IG51bGx9CiAgICAgIHt2aWV3ID09PSAnY2hhdCcgPyAoCiAgICAgICAgPFZpZXcgc3R5bGU9e1tzdHlsZXMuY29tcG9zZXIsIHsgYm9yZGVyQ29sb3I6IGMuYm9yZGVyLCBiYWNrZ3JvdW5kQ29sb3I6IGMuY2FyZCB9XX0+CiAgICAgICAgICA8VGV4dElucHV0CiAgICAgICAgICAgIHZhbHVlPXtkcmFmdH0KICAgICAgICAgICAgb25DaGFuZ2VUZXh0PXtzZXREcmFmdH0KICAgICAgICAgICAgcGxhY2Vob2xkZXI9e3Jlc29sdmVkID8gJ0FzayBzb21ldGhpbmcgbmV3JyA6IGBNZXNzYWdlICR7YXBwTmFtZX1gfQogICAgICAgICAgICBwbGFjZWhvbGRlclRleHRDb2xvcj17Yy5tdXRlZH0KICAgICAgICAgICAgc3R5bGU9e1tzdHlsZXMuaW5wdXQsIHsgY29sb3I6IGMudGV4dCB9XX0KICAgICAgICAgICAgbXVsdGlsaW5lCiAgICAgICAgICAgIG1heExlbmd0aD17NDAwMH0KICAgICAgICAgIC8+CiAgICAgICAgICA8UHJlc3NhYmxlIG9uUHJlc3M9eygpID0+IHZvaWQgc2VuZCgpfSBkaXNhYmxlZD17IWRyYWZ0LnRyaW0oKSB8fCBzZW5kaW5nfSBzdHlsZT17W3N0eWxlcy5zZW5kLCB7IGJhY2tncm91bmRDb2xvcjogYy5hY2NlbnQsIG9wYWNpdHk6IGRyYWZ0LnRyaW0oKSA/IDEgOiAwLjQgfV19IGFjY2Vzc2liaWxpdHlMYWJlbD0iU2VuZCI+CiAgICAgICAgICAgIDxUZXh0IHN0eWxlPXtzdHlsZXMuc2VuZFRleHR9PuKGkTwvVGV4dD4KICAgICAgICAgIDwvUHJlc3NhYmxlPgogICAgICAgIDwvVmlldz4KICAgICAgKSA6IG51bGx9CiAgICA8L0tleWJvYXJkQXZvaWRpbmdWaWV3PgogICk7Cn0KCmZ1bmN0aW9uIEJ1YmJsZSh7IG1lc3NhZ2UsIGNvbG9yczogYyB9OiB7IG1lc3NhZ2U6IE1lc3NhZ2U7IGNvbG9yczogdHlwZW9mIHBhbGV0dGUubGlnaHQgfSkgewogIGlmIChtZXNzYWdlLmF1dGhvciA9PT0gJ3N5c3RlbScpIHJldHVybiA8VGV4dCBzdHlsZT17W3N0eWxlcy5zeXN0ZW0sIHsgY29sb3I6IGMubXV0ZWQgfV19PnttZXNzYWdlLmNvbnRlbnR9PC9UZXh0PjsKICBjb25zdCBtaW5lID0gbWVzc2FnZS5hdXRob3IgPT09ICdjdXN0b21lcic7CiAgcmV0dXJuICgKICAgIDxWaWV3IHN0eWxlPXtbc3R5bGVzLnJvdywgbWluZSA/IHN0eWxlcy5yaWdodCA6IHN0eWxlcy5sZWZ0XX0+CiAgICAgIHttZXNzYWdlLmF1dGhvciA9PT0gJ21ha2VyJyA/IDxUZXh0IHN0eWxlPXtbc3R5bGVzLndobywgeyBjb2xvcjogYy5tdXRlZCB9XX0+VGVhbTwvVGV4dD4gOiBudWxsfQogICAgICA8VmlldyBzdHlsZT17W3N0eWxlcy5idWJibGUsIG1pbmUgPyB7IGJhY2tncm91bmRDb2xvcjogYy5hY2NlbnQgfSA6IHsgYmFja2dyb3VuZENvbG9yOiBjLmNhcmQsIGJvcmRlckNvbG9yOiBjLmJvcmRlciwgYm9yZGVyV2lkdGg6IDEgfV19PgogICAgICAgIDxUZXh0IHN0eWxlPXt7IGNvbG9yOiBtaW5lID8gJyNmZmYnIDogYy50ZXh0LCBmb250U2l6ZTogMTYsIGxpbmVIZWlnaHQ6IDIyIH19PnttZXNzYWdlLmNvbnRlbnR9PC9UZXh0PgogICAgICA8L1ZpZXc+CiAgICAgIHttZXNzYWdlLnNvdXJjZXMubGVuZ3RoID8gKAogICAgICAgIDxWaWV3IHN0eWxlPXtzdHlsZXMuY2hpcHN9PgogICAgICAgICAge21lc3NhZ2Uuc291cmNlcy5tYXAoKHMpID0+ICgKICAgICAgICAgICAgPFRleHQga2V5PXtzLmtpbmQgPT09ICdhcnRpY2xlJyA/IHMuaWQgOiAnaGlzdG9yeSd9IHN0eWxlPXtbc3R5bGVzLmNoaXAsIHsgY29sb3I6IGMuYWNjZW50LCBib3JkZXJDb2xvcjogYy5ib3JkZXIgfV19PgogICAgICAgICAgICAgIHtzLmtpbmQgPT09ICdhcnRpY2xlJyA/IHMudGl0bGUgOiAnWW91ciBjaGF0IGhpc3RvcnknfQogICAgICAgICAgICA8L1RleHQ+CiAgICAgICAgICApKX0KICAgICAgICA8L1ZpZXc+CiAgICAgICkgOiBudWxsfQogICAgPC9WaWV3PgogICk7Cn0KCmNvbnN0IHBhbGV0dGUgPSB7CiAgbGlnaHQ6IHsgYmc6ICcjRjdGOEZBJywgY2FyZDogJyNGRkZGRkYnLCB0ZXh0OiAnIzBCMEIwRicsIG11dGVkOiAnIzU5NjQ3NCcsIGJvcmRlcjogJ3JnYmEoMTUsMjMsNDIsMC4xMCknLCBhY2NlbnQ6ICcjM0M2M0ZGJywgZGFuZ2VyOiAnI0MyNDEwQycgfSwKICBkYXJrOiB7IGJnOiAnIzBCMEIwRicsIGNhcmQ6ICcjMTYxODFEJywgdGV4dDogJyNGMkY0RjcnLCBtdXRlZDogJyM5OEEyQjMnLCBib3JkZXI6ICdyZ2JhKDI1NSwyNTUsMjU1LDAuMTApJywgYWNjZW50OiAnIzZCOEFGRicsIGRhbmdlcjogJyNGQjkyM0MnIH0sCn07Cgpjb25zdCBzdHlsZXMgPSBTdHlsZVNoZWV0LmNyZWF0ZSh7CiAgc2NyZWVuOiB7IGZsZXg6IDEgfSwKICBoZWFkZXI6IHsgZmxleERpcmVjdGlvbjogJ3JvdycsIGFsaWduSXRlbXM6ICdjZW50ZXInLCBnYXA6IDgsIHBhZGRpbmdIb3Jpem9udGFsOiAxNiwgcGFkZGluZ1ZlcnRpY2FsOiAxMiwgYm9yZGVyQm90dG9tV2lkdGg6IDEgfSwKICBoZWFkZXJUZXh0OiB7IGZsZXg6IDEgfSwKICBoZWFkZXJCdXR0b246IHsgbWluSGVpZ2h0OiA0NCwganVzdGlmeUNvbnRlbnQ6ICdjZW50ZXInLCBwYWRkaW5nSG9yaXpvbnRhbDogNCB9LAogIGhlYWRlckJ1dHRvblRleHQ6IHsgZm9udFNpemU6IDE1LCBmb250V2VpZ2h0OiAnNjAwJyB9LAogIHRpdGxlOiB7IGZvbnRTaXplOiAxNywgZm9udFdlaWdodDogJzcwMCcgfSwKICBzdWJ0aXRsZTogeyBmb250U2l6ZTogMTMsIG1hcmdpblRvcDogMiB9LAogIGxpc3Q6IHsgcGFkZGluZzogMTYsIGdhcDogMTAsIGZsZXhHcm93OiAxIH0sCiAgZW1wdHk6IHsgdGV4dEFsaWduOiAnY2VudGVyJywgbWFyZ2luVG9wOiA0MCwgZm9udFNpemU6IDE1IH0sCiAgdHlwaW5nOiB7IGFsaWduU2VsZjogJ2ZsZXgtc3RhcnQnLCBtYXJnaW5Ub3A6IDQgfSwKICBjaGF0Um93OiB7IGZsZXhEaXJlY3Rpb246ICdyb3cnLCBhbGlnbkl0ZW1zOiAnY2VudGVyJywgZ2FwOiAxMiwgcGFkZGluZzogMTQsIGJvcmRlclJhZGl1czogMTYsIGJvcmRlcldpZHRoOiAxIH0sCiAgY2hhdFJvd1RleHQ6IHsgZmxleDogMSwgZ2FwOiAyIH0sCiAgY2hhdFByZXZpZXc6IHsgZm9udFNpemU6IDE1LCBmb250V2VpZ2h0OiAnNjAwJyB9LAogIGNoYXRSb3dSaWdodDogeyBhbGlnbkl0ZW1zOiAnZmxleC1lbmQnLCBnYXA6IDYgfSwKICBwaWxsOiB7IGZvbnRTaXplOiAxMiwgZm9udFdlaWdodDogJzYwMCcsIGJvcmRlcldpZHRoOiAxLCBib3JkZXJSYWRpdXM6IDEyLCBwYWRkaW5nSG9yaXpvbnRhbDogOCwgcGFkZGluZ1ZlcnRpY2FsOiAyLCBvdmVyZmxvdzogJ2hpZGRlbicgfSwKICBkb3Q6IHsgd2lkdGg6IDgsIGhlaWdodDogOCwgYm9yZGVyUmFkaXVzOiA0IH0sCiAgcm93OiB7IG1heFdpZHRoOiAnODUlJywgZ2FwOiA2IH0sCiAgbGVmdDogeyBhbGlnblNlbGY6ICdmbGV4LXN0YXJ0JyB9LAogIHJpZ2h0OiB7IGFsaWduU2VsZjogJ2ZsZXgtZW5kJyB9LAogIHdobzogeyBmb250U2l6ZTogMTIgfSwKICBidWJibGU6IHsgYm9yZGVyUmFkaXVzOiAyMCwgcGFkZGluZ0hvcml6b250YWw6IDE0LCBwYWRkaW5nVmVydGljYWw6IDEwIH0sCiAgY2hpcHM6IHsgZmxleERpcmVjdGlvbjogJ3JvdycsIGZsZXhXcmFwOiAnd3JhcCcsIGdhcDogNiB9LAogIGNoaXA6IHsgZm9udFNpemU6IDEzLCBmb250V2VpZ2h0OiAnNjAwJywgYm9yZGVyV2lkdGg6IDEsIGJvcmRlclJhZGl1czogMTQsIHBhZGRpbmdIb3Jpem9udGFsOiAxMCwgcGFkZGluZ1ZlcnRpY2FsOiA0LCBvdmVyZmxvdzogJ2hpZGRlbicgfSwKICBzeXN0ZW06IHsgYWxpZ25TZWxmOiAnY2VudGVyJywgZm9udFNpemU6IDEzLCB0ZXh0QWxpZ246ICdjZW50ZXInLCBwYWRkaW5nSG9yaXpvbnRhbDogMjQgfSwKICByZXNvbHZlZDogeyBtYXJnaW5Ib3Jpem9udGFsOiAxMiwgbWFyZ2luVG9wOiA0LCBwYWRkaW5nOiAxNCwgYm9yZGVyUmFkaXVzOiAxNiwgYm9yZGVyV2lkdGg6IDEsIGdhcDogNiB9LAogIHJlc29sdmVkVGl0bGU6IHsgZm9udFNpemU6IDE1LCBmb250V2VpZ2h0OiAnNjAwJyB9LAogIGZhY2VzOiB7IGZsZXhEaXJlY3Rpb246ICdyb3cnLCBqdXN0aWZ5Q29udGVudDogJ3NwYWNlLWJldHdlZW4nIH0sCiAgZmFjZTogeyBtaW5XaWR0aDogNDQsIG1pbkhlaWdodDogNDQsIGFsaWduSXRlbXM6ICdjZW50ZXInLCBqdXN0aWZ5Q29udGVudDogJ2NlbnRlcicgfSwKICBmYWNlVGV4dDogeyBmb250U2l6ZTogMjYgfSwKICBlcnJvcjogeyBwYWRkaW5nSG9yaXpvbnRhbDogMTYsIHBhZGRpbmdCb3R0b206IDYsIGZvbnRTaXplOiAxMyB9LAogIGNvbXBvc2VyOiB7IGZsZXhEaXJlY3Rpb246ICdyb3cnLCBhbGlnbkl0ZW1zOiAnZmxleC1lbmQnLCBnYXA6IDgsIG1hcmdpbjogMTIsIHBhZGRpbmdMZWZ0OiAxNiwgcGFkZGluZ1JpZ2h0OiA2LCBwYWRkaW5nVmVydGljYWw6IDYsIGJvcmRlclJhZGl1czogMjQsIGJvcmRlcldpZHRoOiAxIH0sCiAgaW5wdXQ6IHsgZmxleDogMSwgZm9udFNpemU6IDE2LCBtYXhIZWlnaHQ6IDEyMCwgcGFkZGluZ1ZlcnRpY2FsOiA4IH0sCiAgc2VuZDogeyB3aWR0aDogMzgsIGhlaWdodDogMzgsIGJvcmRlclJhZGl1czogMTksIGFsaWduSXRlbXM6ICdjZW50ZXInLCBqdXN0aWZ5Q29udGVudDogJ2NlbnRlcicgfSwKICBzZW5kVGV4dDogeyBjb2xvcjogJyNmZmYnLCBmb250U2l6ZTogMTgsIGZvbnRXZWlnaHQ6ICc3MDAnIH0sCn0pOwo=";
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { registerCreds } from './commands/creds.js';
|
|
|
15
15
|
import { registerBuild } from './commands/build.js';
|
|
16
16
|
import { registerServices } from './commands/services.js';
|
|
17
17
|
import { registerEmail } from './commands/email.js';
|
|
18
|
+
import { registerSupport } from './commands/support.js';
|
|
18
19
|
import { registerWebsite } from './commands/website.js';
|
|
19
20
|
import { registerDomains } from './commands/domains.js';
|
|
20
21
|
import { registerDns } from './commands/dns.js';
|
|
@@ -44,6 +45,7 @@ registerCreds(program);
|
|
|
44
45
|
registerBuild(program);
|
|
45
46
|
registerServices(program);
|
|
46
47
|
registerEmail(program);
|
|
48
|
+
registerSupport(program);
|
|
47
49
|
registerWebsite(program);
|
|
48
50
|
registerDomains(program);
|
|
49
51
|
registerDns(program);
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = '0.2.
|
|
1
|
+
export const CLI_VERSION = '0.2.197';
|
package/package.json
CHANGED
|
Binary file
|
package/runcloud-0.2.196.tgz
DELETED
|
Binary file
|