@proappstore/cli 2.6.3 → 2.6.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.
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/integrate.d.ts +3 -0
- package/dist/integrate.d.ts.map +1 -0
- package/dist/integrate.js +125 -0
- package/dist/integrate.js.map +1 -0
- package/dist/integrations.json +131 -0
- package/dist/proxy.d.ts +7 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +107 -0
- package/dist/proxy.js.map +1 -0
- package/dist/secret.d.ts +8 -0
- package/dist/secret.d.ts.map +1 -0
- package/dist/secret.js +112 -0
- package/dist/secret.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,6 +10,9 @@ import { loginCommand } from './login.js';
|
|
|
10
10
|
import { logoutCommand } from './logout.js';
|
|
11
11
|
import { whoamiCommand } from './whoami.js';
|
|
12
12
|
import { publishApp } from './publish.js';
|
|
13
|
+
import { secretCommand } from './secret.js';
|
|
14
|
+
import { proxyCommand } from './proxy.js';
|
|
15
|
+
import { integrateCommand } from './integrate.js';
|
|
13
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
17
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
15
18
|
const program = new Command();
|
|
@@ -46,6 +49,9 @@ program
|
|
|
46
49
|
});
|
|
47
50
|
program.addCommand(checkCommand);
|
|
48
51
|
program.addCommand(domainCommand);
|
|
52
|
+
program.addCommand(secretCommand);
|
|
53
|
+
program.addCommand(proxyCommand);
|
|
54
|
+
program.addCommand(integrateCommand);
|
|
49
55
|
program.parseAsync().catch((err) => {
|
|
50
56
|
const msg = err instanceof Error ? err.message : String(err);
|
|
51
57
|
process.stderr.write(`pas: ${msg}\n`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrate.d.ts","sourceRoot":"","sources":["../src/integrate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,eAAO,MAAM,gBAAgB,SA6FzB,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { bearer, dieFromHttp, requireSession, resolveAppIdOrExit } from './secret.js';
|
|
3
|
+
import catalog from './integrations.json' with { type: 'json' };
|
|
4
|
+
const integrations = catalog;
|
|
5
|
+
export const integrateCommand = new Command('integrate')
|
|
6
|
+
.description('Connect a third-party API using pre-configured proxy rules.')
|
|
7
|
+
.argument('<name>', `integration name (${Object.keys(integrations).join(', ')})`)
|
|
8
|
+
.option('--app <id>', 'app id (defaults to package.json name in cwd)')
|
|
9
|
+
.option('--list', 'list all available integrations')
|
|
10
|
+
.action(async (name, opts) => {
|
|
11
|
+
if (name === 'list' || opts.list) {
|
|
12
|
+
process.stdout.write('\nAvailable integrations:\n\n');
|
|
13
|
+
for (const [id, int] of Object.entries(integrations)) {
|
|
14
|
+
process.stdout.write(` ${id.padEnd(20)} ${int.name}\n`);
|
|
15
|
+
}
|
|
16
|
+
process.stdout.write(`\nUsage: pas integrate <name> --app <id>\n`);
|
|
17
|
+
process.stdout.write(`Then follow the prompts to enter your API credentials.\n\n`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const integration = integrations[name];
|
|
21
|
+
if (!integration) {
|
|
22
|
+
process.stderr.write(`pas: unknown integration "${name}".\n`);
|
|
23
|
+
process.stderr.write(`Available: ${Object.keys(integrations).join(', ')}\n`);
|
|
24
|
+
process.stderr.write(`Run: pas integrate list\n`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const cfg = await requireSession();
|
|
28
|
+
const appId = await resolveAppIdOrExit(opts.app);
|
|
29
|
+
const prefix = name.toUpperCase().replace(/-/g, '_');
|
|
30
|
+
process.stdout.write(`\n Integrating ${integration.name} for ${appId}\n\n`);
|
|
31
|
+
if (integration.note) {
|
|
32
|
+
process.stdout.write(` Note: ${integration.note}\n\n`);
|
|
33
|
+
}
|
|
34
|
+
// Collect secrets interactively
|
|
35
|
+
const secretValues = {};
|
|
36
|
+
for (const secretSuffix of integration.secrets) {
|
|
37
|
+
const secretName = `${prefix}_${secretSuffix}`;
|
|
38
|
+
process.stdout.write(` ${secretName}: `);
|
|
39
|
+
const value = await readLine();
|
|
40
|
+
if (!value.trim()) {
|
|
41
|
+
process.stderr.write(`\n Aborted — ${secretName} is required.\n`);
|
|
42
|
+
process.stderr.write(` Get yours at: ${integration.docs}\n\n`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
secretValues[secretName] = value.trim();
|
|
46
|
+
}
|
|
47
|
+
// Store secrets
|
|
48
|
+
for (const [secretName, value] of Object.entries(secretValues)) {
|
|
49
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/secrets/${secretName}`, {
|
|
50
|
+
method: 'PUT',
|
|
51
|
+
headers: bearer(cfg),
|
|
52
|
+
body: JSON.stringify({ value }),
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok)
|
|
55
|
+
await dieFromHttp(res, `store ${secretName}`);
|
|
56
|
+
process.stdout.write(` [+] Stored ${secretName}\n`);
|
|
57
|
+
}
|
|
58
|
+
// Create proxy allowlist rules
|
|
59
|
+
if (integration.patterns.length === 0) {
|
|
60
|
+
process.stdout.write(`\n Secrets stored. Add proxy rules for your specific API hosts:\n`);
|
|
61
|
+
process.stdout.write(` pas proxy allow 'https://<host>/' --inject ${mapAuth(integration)} --secret ${prefix}_${integration.secrets[0]} --app ${appId}\n\n`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const secretNames = Object.keys(secretValues);
|
|
65
|
+
for (const pattern of integration.patterns) {
|
|
66
|
+
const body = {
|
|
67
|
+
pattern,
|
|
68
|
+
injectKind: mapInjectKind(integration),
|
|
69
|
+
injectName: mapInjectName(integration),
|
|
70
|
+
secretName: secretNames[0],
|
|
71
|
+
methods: integration.methods,
|
|
72
|
+
};
|
|
73
|
+
if (integration.auth === 'oauth2_cc' && secretNames[1]) {
|
|
74
|
+
body.secretName2 = secretNames[1];
|
|
75
|
+
body.tokenUrl = integration.tokenUrl;
|
|
76
|
+
}
|
|
77
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/allowlist`, {
|
|
78
|
+
method: 'PUT',
|
|
79
|
+
headers: bearer(cfg),
|
|
80
|
+
body: JSON.stringify(body),
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok)
|
|
83
|
+
await dieFromHttp(res, `add rule for ${pattern}`);
|
|
84
|
+
process.stdout.write(` [+] Proxy rule: ${pattern}\n`);
|
|
85
|
+
}
|
|
86
|
+
process.stdout.write(`\n Done! Use in your app:\n`);
|
|
87
|
+
const exampleHost = new URL(integration.patterns[0]).host;
|
|
88
|
+
const examplePath = new URL(integration.patterns[0]).pathname;
|
|
89
|
+
process.stdout.write(` const res = await app.proxy.fetch('${exampleHost}${examplePath}...')\n\n`);
|
|
90
|
+
});
|
|
91
|
+
function mapInjectKind(int) {
|
|
92
|
+
switch (int.auth) {
|
|
93
|
+
case 'bearer': return 'bearer';
|
|
94
|
+
case 'header': return 'header';
|
|
95
|
+
case 'query': return 'query';
|
|
96
|
+
case 'oauth2_cc': return 'oauth2_cc';
|
|
97
|
+
default: return 'bearer';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function mapInjectName(int) {
|
|
101
|
+
if (int.auth === 'header')
|
|
102
|
+
return int.headerName ?? 'X-API-Key';
|
|
103
|
+
if (int.auth === 'query')
|
|
104
|
+
return int.queryParam ?? 'key';
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
function mapAuth(int) {
|
|
108
|
+
if (int.auth === 'header')
|
|
109
|
+
return `header:${int.headerName ?? 'X-API-Key'}`;
|
|
110
|
+
if (int.auth === 'query')
|
|
111
|
+
return `query:${int.queryParam ?? 'key'}`;
|
|
112
|
+
return int.auth;
|
|
113
|
+
}
|
|
114
|
+
function readLine() {
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
let data = '';
|
|
117
|
+
process.stdin.setEncoding('utf8');
|
|
118
|
+
process.stdin.once('data', (chunk) => {
|
|
119
|
+
data = String(chunk).replace(/\n$/, '');
|
|
120
|
+
resolve(data);
|
|
121
|
+
});
|
|
122
|
+
process.stdin.resume();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=integrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrate.js","sourceRoot":"","sources":["../src/integrate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,OAAO,MAAM,qBAAqB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAiBhE,MAAM,YAAY,GAAG,OAAsC,CAAC;AAE5D,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,OAAO,CAAC,WAAW,CAAC;KACrD,WAAW,CAAC,6DAA6D,CAAC;KAC1E,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;KAChF,MAAM,CAAC,YAAY,EAAE,+CAA+C,CAAC;KACrE,MAAM,CAAC,QAAQ,EAAE,iCAAiC,CAAC;KACnD,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAAsC,EAAE,EAAE;IACrE,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACtD,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QACnF,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,IAAI,MAAM,CAAC,CAAC;QAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,cAAc,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAErD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,WAAW,CAAC,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;IAE7E,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,WAAW,CAAC,IAAI,MAAM,CAAC,CAAC;IAC1D,CAAC;IAED,gCAAgC;IAChC,MAAM,YAAY,GAA2B,EAAE,CAAC;IAChD,KAAK,MAAM,YAAY,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;QAC/C,MAAM,UAAU,GAAG,GAAG,MAAM,IAAI,YAAY,EAAE,CAAC;QAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,UAAU,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,UAAU,iBAAiB,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,WAAW,CAAC,IAAI,MAAM,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,YAAY,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1C,CAAC;IAED,gBAAgB;IAChB,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC/D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,YAAY,KAAK,YAAY,UAAU,EAAE,EAAE;YAC/E,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC;YACpB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,WAAW,CAAC,GAAG,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,UAAU,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,+BAA+B;IAC/B,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;QAC3F,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kDAAkD,OAAO,CAAC,WAAW,CAAC,aAAa,MAAM,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC;QAC/J,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC9C,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;QAC3C,MAAM,IAAI,GAA4B;YACpC,OAAO;YACP,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC;YACtC,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC;YACtC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;YAC1B,OAAO,EAAE,WAAW,CAAC,OAAO;SAC7B,CAAC;QACF,IAAI,WAAW,CAAC,IAAI,KAAK,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;QACvC,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,YAAY,KAAK,YAAY,EAAE;YACnE,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC;YACpB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,WAAW,CAAC,GAAG,EAAE,gBAAgB,OAAO,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,OAAO,IAAI,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC,IAAI,CAAC;IAC3D,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC,QAAQ,CAAC;IAC/D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,WAAW,GAAG,WAAW,WAAW,CAAC,CAAC;AACvG,CAAC,CAAC,CAAC;AAEL,SAAS,aAAa,CAAC,GAAgB;IACrC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,QAAQ,CAAC,CAAC,OAAO,QAAQ,CAAC;QAC/B,KAAK,QAAQ,CAAC,CAAC,OAAO,QAAQ,CAAC;QAC/B,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,KAAK,WAAW,CAAC,CAAC,OAAO,WAAW,CAAC;QACrC,OAAO,CAAC,CAAC,OAAO,QAAQ,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,GAAgB;IACrC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC;IAChE,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,GAAG,CAAC,UAAU,IAAI,KAAK,CAAC;IACzD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,OAAO,CAAC,GAAgB;IAC/B,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,GAAG,CAAC,UAAU,IAAI,WAAW,EAAE,CAAC;IAC5E,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,SAAS,GAAG,CAAC,UAAU,IAAI,KAAK,EAAE,CAAC;IACpE,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YACnC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openai": {
|
|
3
|
+
"name": "OpenAI (GPT, DALL-E, Whisper)",
|
|
4
|
+
"auth": "bearer",
|
|
5
|
+
"patterns": ["https://api.openai.com/"],
|
|
6
|
+
"methods": ["GET", "POST"],
|
|
7
|
+
"secrets": ["API_KEY"],
|
|
8
|
+
"docs": "https://platform.openai.com/api-keys"
|
|
9
|
+
},
|
|
10
|
+
"anthropic": {
|
|
11
|
+
"name": "Anthropic (Claude)",
|
|
12
|
+
"auth": "header",
|
|
13
|
+
"headerName": "x-api-key",
|
|
14
|
+
"patterns": ["https://api.anthropic.com/"],
|
|
15
|
+
"methods": ["GET", "POST"],
|
|
16
|
+
"secrets": ["API_KEY"],
|
|
17
|
+
"docs": "https://console.anthropic.com/settings/keys"
|
|
18
|
+
},
|
|
19
|
+
"google-ai": {
|
|
20
|
+
"name": "Google AI (Gemini)",
|
|
21
|
+
"auth": "query",
|
|
22
|
+
"queryParam": "key",
|
|
23
|
+
"patterns": ["https://generativelanguage.googleapis.com/"],
|
|
24
|
+
"methods": ["GET", "POST"],
|
|
25
|
+
"secrets": ["API_KEY"],
|
|
26
|
+
"docs": "https://aistudio.google.com/apikey"
|
|
27
|
+
},
|
|
28
|
+
"openrouter": {
|
|
29
|
+
"name": "OpenRouter (multi-model gateway)",
|
|
30
|
+
"auth": "bearer",
|
|
31
|
+
"patterns": ["https://openrouter.ai/api/"],
|
|
32
|
+
"methods": ["GET", "POST"],
|
|
33
|
+
"secrets": ["API_KEY"],
|
|
34
|
+
"docs": "https://openrouter.ai/keys"
|
|
35
|
+
},
|
|
36
|
+
"replicate": {
|
|
37
|
+
"name": "Replicate (open source models)",
|
|
38
|
+
"auth": "bearer",
|
|
39
|
+
"patterns": ["https://api.replicate.com/"],
|
|
40
|
+
"methods": ["GET", "POST"],
|
|
41
|
+
"secrets": ["API_KEY"],
|
|
42
|
+
"docs": "https://replicate.com/account/api-tokens"
|
|
43
|
+
},
|
|
44
|
+
"stability": {
|
|
45
|
+
"name": "Stability AI (image generation)",
|
|
46
|
+
"auth": "bearer",
|
|
47
|
+
"patterns": ["https://api.stability.ai/"],
|
|
48
|
+
"methods": ["GET", "POST"],
|
|
49
|
+
"secrets": ["API_KEY"],
|
|
50
|
+
"docs": "https://platform.stability.ai/account/keys"
|
|
51
|
+
},
|
|
52
|
+
"elevenlabs": {
|
|
53
|
+
"name": "ElevenLabs (text-to-speech)",
|
|
54
|
+
"auth": "header",
|
|
55
|
+
"headerName": "xi-api-key",
|
|
56
|
+
"patterns": ["https://api.elevenlabs.io/"],
|
|
57
|
+
"methods": ["GET", "POST"],
|
|
58
|
+
"secrets": ["API_KEY"],
|
|
59
|
+
"docs": "https://elevenlabs.io/app/settings/api-keys"
|
|
60
|
+
},
|
|
61
|
+
"amadeus": {
|
|
62
|
+
"name": "Amadeus (flights & hotels)",
|
|
63
|
+
"auth": "oauth2_cc",
|
|
64
|
+
"tokenUrl": "https://test.api.amadeus.com/v1/security/oauth2/token",
|
|
65
|
+
"patterns": ["https://test.api.amadeus.com/v2/"],
|
|
66
|
+
"methods": ["GET", "POST"],
|
|
67
|
+
"secrets": ["CLIENT_ID", "CLIENT_SECRET"],
|
|
68
|
+
"docs": "https://developers.amadeus.com/register"
|
|
69
|
+
},
|
|
70
|
+
"amadeus-prod": {
|
|
71
|
+
"name": "Amadeus Production (flights & hotels)",
|
|
72
|
+
"auth": "oauth2_cc",
|
|
73
|
+
"tokenUrl": "https://api.amadeus.com/v1/security/oauth2/token",
|
|
74
|
+
"patterns": ["https://api.amadeus.com/v2/"],
|
|
75
|
+
"methods": ["GET", "POST"],
|
|
76
|
+
"secrets": ["CLIENT_ID", "CLIENT_SECRET"],
|
|
77
|
+
"docs": "https://developers.amadeus.com/register"
|
|
78
|
+
},
|
|
79
|
+
"spotify": {
|
|
80
|
+
"name": "Spotify (music, playlists)",
|
|
81
|
+
"auth": "oauth2_cc",
|
|
82
|
+
"tokenUrl": "https://accounts.spotify.com/api/token",
|
|
83
|
+
"patterns": ["https://api.spotify.com/v1/"],
|
|
84
|
+
"methods": ["GET", "POST", "PUT", "DELETE"],
|
|
85
|
+
"secrets": ["CLIENT_ID", "CLIENT_SECRET"],
|
|
86
|
+
"docs": "https://developer.spotify.com/dashboard"
|
|
87
|
+
},
|
|
88
|
+
"github": {
|
|
89
|
+
"name": "GitHub API",
|
|
90
|
+
"auth": "bearer",
|
|
91
|
+
"patterns": ["https://api.github.com/"],
|
|
92
|
+
"methods": ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
93
|
+
"secrets": ["TOKEN"],
|
|
94
|
+
"docs": "https://github.com/settings/tokens"
|
|
95
|
+
},
|
|
96
|
+
"openweathermap": {
|
|
97
|
+
"name": "OpenWeatherMap",
|
|
98
|
+
"auth": "query",
|
|
99
|
+
"queryParam": "appid",
|
|
100
|
+
"patterns": ["https://api.openweathermap.org/data/2.5/", "https://api.openweathermap.org/data/3.0/"],
|
|
101
|
+
"methods": ["GET"],
|
|
102
|
+
"secrets": ["API_KEY"],
|
|
103
|
+
"docs": "https://home.openweathermap.org/api_keys"
|
|
104
|
+
},
|
|
105
|
+
"rapidapi": {
|
|
106
|
+
"name": "RapidAPI (any API on the hub)",
|
|
107
|
+
"auth": "header",
|
|
108
|
+
"headerName": "X-RapidAPI-Key",
|
|
109
|
+
"patterns": [],
|
|
110
|
+
"methods": ["GET", "POST"],
|
|
111
|
+
"secrets": ["API_KEY"],
|
|
112
|
+
"docs": "https://rapidapi.com/developer/dashboard",
|
|
113
|
+
"note": "After integrating, add proxy rules for the specific RapidAPI host(s) you use."
|
|
114
|
+
},
|
|
115
|
+
"resend": {
|
|
116
|
+
"name": "Resend (email API)",
|
|
117
|
+
"auth": "bearer",
|
|
118
|
+
"patterns": ["https://api.resend.com/"],
|
|
119
|
+
"methods": ["GET", "POST"],
|
|
120
|
+
"secrets": ["API_KEY"],
|
|
121
|
+
"docs": "https://resend.com/api-keys"
|
|
122
|
+
},
|
|
123
|
+
"stripe": {
|
|
124
|
+
"name": "Stripe API",
|
|
125
|
+
"auth": "bearer",
|
|
126
|
+
"patterns": ["https://api.stripe.com/"],
|
|
127
|
+
"methods": ["GET", "POST", "DELETE"],
|
|
128
|
+
"secrets": ["SECRET_KEY"],
|
|
129
|
+
"docs": "https://dashboard.stripe.com/apikeys"
|
|
130
|
+
}
|
|
131
|
+
}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG;IACtC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;CACd,CAQA;AAED,eAAO,MAAM,YAAY,SAoGtB,CAAC"}
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { bearer, dieFromHttp, requireSession, resolveAppIdOrExit } from './secret.js';
|
|
3
|
+
export function parseInject(s) {
|
|
4
|
+
if (s === 'bearer')
|
|
5
|
+
return { kind: 'bearer', name: '' };
|
|
6
|
+
if (s === 'oauth2_cc')
|
|
7
|
+
return { kind: 'oauth2_cc', name: '' };
|
|
8
|
+
const m = /^(query|header):(.+)$/.exec(s);
|
|
9
|
+
if (!m) {
|
|
10
|
+
throw new Error(`--inject must be 'bearer', 'oauth2_cc', 'query:<name>', or 'header:<name>' (got ${s})`);
|
|
11
|
+
}
|
|
12
|
+
return { kind: m[1], name: m[2] };
|
|
13
|
+
}
|
|
14
|
+
export const proxyCommand = new Command('proxy')
|
|
15
|
+
.description('Manage the URL allowlist for the per-app secret-injecting proxy.')
|
|
16
|
+
.addCommand(new Command('allow')
|
|
17
|
+
.description('Allow the proxy to inject <secret> when calling URLs starting with <pattern>.')
|
|
18
|
+
.argument('<pattern>', 'URL prefix (must start with https://)')
|
|
19
|
+
.requiredOption('--secret <name>', 'name of a previously stored secret')
|
|
20
|
+
.requiredOption('--inject <spec>', "how to inject: 'query:<name>', 'header:<name>', 'bearer', or 'oauth2_cc'")
|
|
21
|
+
.option('--secret2 <name>', 'second secret (client_secret for oauth2_cc)')
|
|
22
|
+
.option('--token-url <url>', 'OAuth2 token endpoint (required for oauth2_cc)')
|
|
23
|
+
.option('--methods <list>', 'comma-separated HTTP methods', 'GET')
|
|
24
|
+
.option('--app <id>', 'app id (defaults to package.json name in cwd)')
|
|
25
|
+
.action(async (pattern, opts) => {
|
|
26
|
+
const cfg = await requireSession();
|
|
27
|
+
const appId = await resolveAppIdOrExit(opts.app);
|
|
28
|
+
let inject;
|
|
29
|
+
try {
|
|
30
|
+
inject = parseInject(opts.inject);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
process.stderr.write(`pas: ${err.message}\n`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/allowlist`, {
|
|
37
|
+
method: 'PUT',
|
|
38
|
+
headers: bearer(cfg),
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
pattern,
|
|
41
|
+
injectKind: inject.kind,
|
|
42
|
+
injectName: inject.name,
|
|
43
|
+
secretName: opts.secret,
|
|
44
|
+
...(opts.secret2 ? { secretName2: opts.secret2 } : {}),
|
|
45
|
+
...(opts.tokenUrl ? { tokenUrl: opts.tokenUrl } : {}),
|
|
46
|
+
methods: opts.methods
|
|
47
|
+
.split(',')
|
|
48
|
+
.map((m) => m.trim())
|
|
49
|
+
.filter(Boolean),
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok)
|
|
53
|
+
await dieFromHttp(res, 'add allowlist rule');
|
|
54
|
+
process.stdout.write(`✓ allowed ${pattern} for ${appId}\n`);
|
|
55
|
+
}))
|
|
56
|
+
.addCommand(new Command('list')
|
|
57
|
+
.alias('ls')
|
|
58
|
+
.description('Show the proxy allowlist for an app.')
|
|
59
|
+
.option('--app <id>', 'app id (defaults to package.json name in cwd)')
|
|
60
|
+
.option('--json', 'Output JSON.')
|
|
61
|
+
.action(async (opts) => {
|
|
62
|
+
const cfg = await requireSession();
|
|
63
|
+
const appId = await resolveAppIdOrExit(opts.app);
|
|
64
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/allowlist`, {
|
|
65
|
+
headers: bearer(cfg),
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok)
|
|
68
|
+
await dieFromHttp(res, 'list allowlist');
|
|
69
|
+
const { rules } = (await res.json());
|
|
70
|
+
if (opts.json) {
|
|
71
|
+
process.stdout.write(`${JSON.stringify(rules, null, 2)}\n`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (rules.length === 0) {
|
|
75
|
+
process.stdout.write(`No allowlist rules for ${appId}.\n`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
for (const r of rules) {
|
|
79
|
+
const inject = r.injectKind === 'bearer' || r.injectKind === 'oauth2_cc'
|
|
80
|
+
? r.injectKind
|
|
81
|
+
: `${r.injectKind}:${r.injectName}`;
|
|
82
|
+
let line = `${r.pattern}\n secret=${r.secretName} inject=${inject} methods=${r.methods.join(',')}`;
|
|
83
|
+
if (r.secretName2)
|
|
84
|
+
line += ` secret2=${r.secretName2}`;
|
|
85
|
+
if (r.tokenUrl)
|
|
86
|
+
line += `\n token-url=${r.tokenUrl}`;
|
|
87
|
+
process.stdout.write(`${line}\n`);
|
|
88
|
+
}
|
|
89
|
+
}))
|
|
90
|
+
.addCommand(new Command('deny')
|
|
91
|
+
.alias('rm')
|
|
92
|
+
.description('Remove an allowlist rule by pattern.')
|
|
93
|
+
.argument('<pattern>', 'exact pattern to remove')
|
|
94
|
+
.option('--app <id>', 'app id (defaults to package.json name in cwd)')
|
|
95
|
+
.action(async (pattern, opts) => {
|
|
96
|
+
const cfg = await requireSession();
|
|
97
|
+
const appId = await resolveAppIdOrExit(opts.app);
|
|
98
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/allowlist`, {
|
|
99
|
+
method: 'DELETE',
|
|
100
|
+
headers: bearer(cfg),
|
|
101
|
+
body: JSON.stringify({ pattern }),
|
|
102
|
+
});
|
|
103
|
+
if (!res.ok)
|
|
104
|
+
await dieFromHttp(res, 'remove allowlist rule');
|
|
105
|
+
process.stdout.write(`✓ removed ${pattern} from ${appId}\n`);
|
|
106
|
+
}));
|
|
107
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/secret.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { type CliConfig } from './lib/config.js';
|
|
3
|
+
export declare const secretCommand: Command;
|
|
4
|
+
export declare function requireSession(): Promise<CliConfig>;
|
|
5
|
+
export declare function bearer(cfg: CliConfig): Record<string, string>;
|
|
6
|
+
export declare function resolveAppIdOrExit(explicit: string | undefined): Promise<string>;
|
|
7
|
+
export declare function dieFromHttp(res: Response, action: string): Promise<never>;
|
|
8
|
+
//# sourceMappingURL=secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../src/secret.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,iBAAiB,CAAC;AAQ7D,eAAO,MAAM,aAAa,SAgEvB,CAAC;AAIJ,wBAAsB,cAAc,IAAI,OAAO,CAAC,SAAS,CAAC,CAOzD;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK7D;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBtF;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAS/E"}
|
package/dist/secret.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { readConfig } from './lib/config.js';
|
|
5
|
+
export const secretCommand = new Command('secret')
|
|
6
|
+
.description('Manage server-side API keys for an app (set/list/rm).')
|
|
7
|
+
.addCommand(new Command('set')
|
|
8
|
+
.description('Store or replace an encrypted API key for an app.')
|
|
9
|
+
.argument('<name>', 'secret name (uppercase + underscores, e.g. AMADEUS_CLIENT_ID)')
|
|
10
|
+
.argument('<value>', 'plaintext value to encrypt and store')
|
|
11
|
+
.option('--app <id>', 'app id (defaults to package.json name in cwd)')
|
|
12
|
+
.action(async (name, value, opts) => {
|
|
13
|
+
const cfg = await requireSession();
|
|
14
|
+
const appId = await resolveAppIdOrExit(opts.app);
|
|
15
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/secrets/${name}`, {
|
|
16
|
+
method: 'PUT',
|
|
17
|
+
headers: bearer(cfg),
|
|
18
|
+
body: JSON.stringify({ value }),
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok)
|
|
21
|
+
await dieFromHttp(res, `set ${name}`);
|
|
22
|
+
process.stdout.write(`✓ stored ${name} for ${appId}\n`);
|
|
23
|
+
}))
|
|
24
|
+
.addCommand(new Command('list')
|
|
25
|
+
.alias('ls')
|
|
26
|
+
.description('List secret names registered for an app (values are never returned).')
|
|
27
|
+
.option('--app <id>', 'app id (defaults to package.json name in cwd)')
|
|
28
|
+
.option('--json', 'Output JSON.')
|
|
29
|
+
.action(async (opts) => {
|
|
30
|
+
const cfg = await requireSession();
|
|
31
|
+
const appId = await resolveAppIdOrExit(opts.app);
|
|
32
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/secrets`, {
|
|
33
|
+
headers: bearer(cfg),
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok)
|
|
36
|
+
await dieFromHttp(res, 'list secrets');
|
|
37
|
+
const { secrets } = (await res.json());
|
|
38
|
+
if (opts.json) {
|
|
39
|
+
process.stdout.write(`${JSON.stringify(secrets, null, 2)}\n`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (secrets.length === 0) {
|
|
43
|
+
process.stdout.write(`No secrets for ${appId}.\n`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const s of secrets) {
|
|
47
|
+
const last = s.lastUsedAt ? new Date(s.lastUsedAt).toISOString() : 'never';
|
|
48
|
+
process.stdout.write(`${s.name.padEnd(32)} last used: ${last}\n`);
|
|
49
|
+
}
|
|
50
|
+
}))
|
|
51
|
+
.addCommand(new Command('rm')
|
|
52
|
+
.alias('remove')
|
|
53
|
+
.description('Delete a stored secret.')
|
|
54
|
+
.argument('<name>', 'secret name')
|
|
55
|
+
.option('--app <id>', 'app id (defaults to package.json name in cwd)')
|
|
56
|
+
.action(async (name, opts) => {
|
|
57
|
+
const cfg = await requireSession();
|
|
58
|
+
const appId = await resolveAppIdOrExit(opts.app);
|
|
59
|
+
const res = await fetch(`${cfg.apiBase}/v1/apps/${appId}/secrets/${name}`, {
|
|
60
|
+
method: 'DELETE',
|
|
61
|
+
headers: bearer(cfg),
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok)
|
|
64
|
+
await dieFromHttp(res, `rm ${name}`);
|
|
65
|
+
process.stdout.write(`✓ removed ${name} from ${appId}\n`);
|
|
66
|
+
}));
|
|
67
|
+
// Shared helpers (also used by proxy.ts)
|
|
68
|
+
export async function requireSession() {
|
|
69
|
+
const cfg = await readConfig();
|
|
70
|
+
if (!cfg.session?.token) {
|
|
71
|
+
process.stdout.write('\n⚠ Not signed in. Run: pas login\n');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
return cfg;
|
|
75
|
+
}
|
|
76
|
+
export function bearer(cfg) {
|
|
77
|
+
return {
|
|
78
|
+
Authorization: `Bearer ${cfg.session.token}`,
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export async function resolveAppIdOrExit(explicit) {
|
|
83
|
+
if (explicit) {
|
|
84
|
+
if (!/^[a-z][a-z0-9-]*$/.test(explicit) || explicit.length > 58) {
|
|
85
|
+
process.stderr.write(`pas: invalid app id "${explicit}".\n`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
return explicit;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const raw = await readFile(join(process.cwd(), 'package.json'), 'utf8');
|
|
92
|
+
const name = JSON.parse(raw).name;
|
|
93
|
+
if (name && /^[a-z][a-z0-9-]*$/.test(name))
|
|
94
|
+
return name;
|
|
95
|
+
}
|
|
96
|
+
catch { }
|
|
97
|
+
process.stderr.write('pas: no app id. Pass --app <id> or run from a directory whose package.json `name` is the app id.\n');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
export async function dieFromHttp(res, action) {
|
|
101
|
+
const text = await res.text();
|
|
102
|
+
let msg = text;
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(text);
|
|
105
|
+
if (parsed.error)
|
|
106
|
+
msg = parsed.error;
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
process.stderr.write(`pas: ${action} failed (${res.status}): ${msg}\n`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=secret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.js","sourceRoot":"","sources":["../src/secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAkB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAQ7D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,uDAAuD,CAAC;KACpE,UAAU,CACT,IAAI,OAAO,CAAC,KAAK,CAAC;KACf,WAAW,CAAC,mDAAmD,CAAC;KAChE,QAAQ,CAAC,QAAQ,EAAE,+DAA+D,CAAC;KACnF,QAAQ,CAAC,SAAS,EAAE,sCAAsC,CAAC;KAC3D,MAAM,CAAC,YAAY,EAAE,+CAA+C,CAAC;KACrE,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,KAAa,EAAE,IAAsB,EAAE,EAAE;IACpE,MAAM,GAAG,GAAG,MAAM,cAAc,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,YAAY,KAAK,YAAY,IAAI,EAAE,EAAE;QACzE,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC;QACpB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;KAChC,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,WAAW,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,QAAQ,KAAK,IAAI,CAAC,CAAC;AAC1D,CAAC,CAAC,CACL;KACA,UAAU,CACT,IAAI,OAAO,CAAC,MAAM,CAAC;KAChB,KAAK,CAAC,IAAI,CAAC;KACX,WAAW,CAAC,sEAAsE,CAAC;KACnF,MAAM,CAAC,YAAY,EAAE,+CAA+C,CAAC;KACrE,MAAM,CAAC,QAAQ,EAAE,cAAc,CAAC;KAChC,MAAM,CAAC,KAAK,EAAE,IAAsC,EAAE,EAAE;IACvD,MAAM,GAAG,GAAG,MAAM,cAAc,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,YAAY,KAAK,UAAU,EAAE;QACjE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC;KACrB,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,WAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACpD,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAiC,CAAC;IACvE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,KAAK,KAAK,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,IAAI,IAAI,CAAC,CAAC;IACpE,CAAC;AACH,CAAC,CAAC,CACL;KACA,UAAU,CACT,IAAI,OAAO,CAAC,IAAI,CAAC;KACd,KAAK,CAAC,QAAQ,CAAC;KACf,WAAW,CAAC,yBAAyB,CAAC;KACtC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;KACjC,MAAM,CAAC,YAAY,EAAE,+CAA+C,CAAC;KACrE,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAAsB,EAAE,EAAE;IACrD,MAAM,GAAG,GAAG,MAAM,cAAc,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,YAAY,KAAK,YAAY,IAAI,EAAE,EAAE;QACzE,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC;KACrB,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,WAAW,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,IAAI,SAAS,KAAK,IAAI,CAAC,CAAC;AAC5D,CAAC,CAAC,CACL,CAAC;AAEJ,yCAAyC;AAEzC,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,GAAc;IACnC,OAAO;QACL,aAAa,EAAE,UAAU,GAAG,CAAC,OAAQ,CAAC,KAAK,EAAE;QAC7C,cAAc,EAAE,kBAAkB;KACnC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAA4B;IACnE,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,QAAQ,MAAM,CAAC,CAAC;YAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;QACxE,MAAM,IAAI,GAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC,IAAI,CAAC;QACzD,IAAI,IAAI,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oGAAoG,CACrG,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAa,EAAE,MAAc;IAC7D,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAuB,CAAC;QACtD,IAAI,MAAM,CAAC,KAAK;YAAE,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,MAAM,YAAY,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC;IACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|