runcloud 0.1.131 → 0.1.133
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/api.js +2 -1
- package/dist/commands/email.js +10 -83
- package/dist/commands/environment.js +3 -3
- package/dist/commands/login.js +6 -11
- package/dist/commands/token.js +4 -9
- package/dist/config.js +2 -5
- package/dist/runCloudAuth.js +0 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/runcloud-0.1.133.tgz +0 -0
- package/runcloud-0.1.131.tgz +0 -0
package/dist/api.js
CHANGED
|
@@ -257,8 +257,9 @@ function headline(err) {
|
|
|
257
257
|
return `Session expired or invalid — run \`${command}\`.`;
|
|
258
258
|
}
|
|
259
259
|
if (err.status === 403) {
|
|
260
|
-
if (err.detail)
|
|
260
|
+
if (err.detail && !/^forbidden\.?$/i.test(err.detail.trim())) {
|
|
261
261
|
return `Forbidden: ${err.detail}`;
|
|
262
|
+
}
|
|
262
263
|
const where = err.path ? ` on ${err.path}` : '';
|
|
263
264
|
return (`Forbidden, and the API did not say why. Either the credential was not accepted ` +
|
|
264
265
|
`(run \`${loginCommandName()}\`), or it is valid but not allowed${where}.`);
|
package/dist/commands/email.js
CHANGED
|
@@ -1,86 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { assertDevOnly } from './misc.js';
|
|
4
|
-
function makeClient(project) {
|
|
5
|
-
const creds = requireCredentials();
|
|
6
|
-
return {
|
|
7
|
-
client: new ApiClient(creds.apiUrl, creds.token),
|
|
8
|
-
projectId: resolveProject(process.cwd(), project),
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
export async function getEmailStatus(client, projectId, env) {
|
|
12
|
-
return await client.get(`/newly_backend/email/status?project_id=${encodeURIComponent(projectId)}&env=${env}`);
|
|
13
|
-
}
|
|
14
|
-
export function formatEmailStatus(status) {
|
|
15
|
-
const lines = [
|
|
16
|
-
`email: ${status.configured ? 'configured' : 'not configured'}`,
|
|
17
|
-
`from: ${status.from ?? '(not set)'}`,
|
|
18
|
-
];
|
|
19
|
-
if (status.requires_deploy)
|
|
20
|
-
lines.push('requires deploy: yes');
|
|
21
|
-
if (status.notes)
|
|
22
|
-
lines.push(`notes: ${status.notes}`);
|
|
23
|
-
return lines.join('\n');
|
|
24
|
-
}
|
|
25
|
-
export async function sendTestEmail(client, opts) {
|
|
26
|
-
return await client.post('/newly_backend/email/test', {
|
|
27
|
-
project_id: opts.projectId,
|
|
28
|
-
env: opts.env,
|
|
29
|
-
to: opts.to,
|
|
30
|
-
from: opts.from,
|
|
31
|
-
subject: opts.subject,
|
|
32
|
-
text: opts.text,
|
|
33
|
-
html: opts.html,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
1
|
+
export const EMAIL_RETIRED_MESSAGE = '`newly email` was retired: it never had a server to talk to. ' +
|
|
2
|
+
'There is no CLI replacement; send mail from your API service with your own provider.';
|
|
36
3
|
export function registerEmail(program) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.
|
|
40
|
-
.
|
|
41
|
-
.
|
|
42
|
-
.
|
|
43
|
-
.
|
|
44
|
-
|
|
45
|
-
assertDevOnly(opts.env, 'email status');
|
|
46
|
-
const { client, projectId } = makeClient(opts.project);
|
|
47
|
-
console.log(formatEmailStatus(await getEmailStatus(client, projectId, opts.env)));
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
console.error(friendlyApiError(e));
|
|
51
|
-
process.exitCode = 1;
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
email
|
|
55
|
-
.command('test')
|
|
56
|
-
.description('Send a test email through the deployed API service')
|
|
57
|
-
.requiredOption('--to <email>', 'recipient email address')
|
|
58
|
-
.option('--from <email>', 'sender email address; defaults to the backend sender setting')
|
|
59
|
-
.option('--subject <subject>', 'email subject', 'Newly email test')
|
|
60
|
-
.option('--text <text>', 'plain-text body', 'This is a test email from your Newly backend.')
|
|
61
|
-
.option('--html <html>', 'optional HTML body')
|
|
62
|
-
.option('--env <env>', 'dev or prod', 'dev')
|
|
63
|
-
.option('--project <id>', 'project id')
|
|
64
|
-
.action(async (opts) => {
|
|
65
|
-
try {
|
|
66
|
-
assertDevOnly(opts.env, 'email test');
|
|
67
|
-
const { client, projectId } = makeClient(opts.project);
|
|
68
|
-
const result = await sendTestEmail(client, {
|
|
69
|
-
projectId,
|
|
70
|
-
env: opts.env,
|
|
71
|
-
to: opts.to,
|
|
72
|
-
from: opts.from,
|
|
73
|
-
subject: opts.subject,
|
|
74
|
-
text: opts.text,
|
|
75
|
-
html: opts.html,
|
|
76
|
-
});
|
|
77
|
-
console.log(result.message_id
|
|
78
|
-
? `Email sent. message_id=${result.message_id}`
|
|
79
|
-
: 'Email sent.');
|
|
80
|
-
}
|
|
81
|
-
catch (e) {
|
|
82
|
-
console.error(friendlyApiError(e));
|
|
83
|
-
process.exitCode = 1;
|
|
84
|
-
}
|
|
4
|
+
program
|
|
5
|
+
.command('email')
|
|
6
|
+
.description('Retired: send mail from your API service instead')
|
|
7
|
+
.allowUnknownOption()
|
|
8
|
+
.allowExcessArguments()
|
|
9
|
+
.action(() => {
|
|
10
|
+
console.error(EMAIL_RETIRED_MESSAGE);
|
|
11
|
+
process.exitCode = 1;
|
|
85
12
|
});
|
|
86
13
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createInterface } from 'node:readline/promises';
|
|
2
2
|
import { ApiClient, friendlyApiError } from '../api.js';
|
|
3
3
|
import { runEnvironmentDatabaseShell } from '../shellSession.js';
|
|
4
|
-
import { clearEnvironmentSelection, readEnvironmentSelection,
|
|
4
|
+
import { clearEnvironmentSelection, readEnvironmentSelection, requireCredentials, requireEnvironmentSelection, resolveProject, writeEnvironmentSelection, } from '../config.js';
|
|
5
5
|
const POLL_INTERVAL_MS = 2_000;
|
|
6
6
|
const POLL_TIMEOUT_MS = 10 * 60 * 1_000;
|
|
7
7
|
function client() {
|
|
8
8
|
const credentials = requireCredentials();
|
|
9
|
-
return new ApiClient(
|
|
9
|
+
return new ApiClient(credentials.apiUrl, credentials.token);
|
|
10
10
|
}
|
|
11
11
|
async function projectEnvironments(api, projectId) {
|
|
12
12
|
const response = await api.get(`/projects/${encodeURIComponent(projectId)}/environments`);
|
|
@@ -248,7 +248,7 @@ export function registerEnvironment(program) {
|
|
|
248
248
|
const projectId = resolveProject(process.cwd(), opts.project);
|
|
249
249
|
const selected = requireEnvironmentSelection(process.cwd(), projectId);
|
|
250
250
|
const credentials = requireCredentials();
|
|
251
|
-
const environment = await new ApiClient(
|
|
251
|
+
const environment = await new ApiClient(credentials.apiUrl, credentials.token).get(`/environments/${encodeURIComponent(selected.id)}`);
|
|
252
252
|
const resource = resolveNamedResource(environment.databases, value, 'database');
|
|
253
253
|
if (resource.status !== 'ready')
|
|
254
254
|
throw new Error(`Database is not ready (${resource.status}).`);
|
package/dist/commands/login.js
CHANGED
|
@@ -49,9 +49,8 @@ export function openBrowser(url) {
|
|
|
49
49
|
});
|
|
50
50
|
child.unref();
|
|
51
51
|
}
|
|
52
|
-
export function
|
|
53
|
-
|
|
54
|
-
return { apiUrl, ...(same ? {} : { authApiUrl }) };
|
|
52
|
+
export function commandApiUrl(isRunCloud, authApiUrl, offered) {
|
|
53
|
+
return isRunCloud && offered ? offered : authApiUrl;
|
|
55
54
|
}
|
|
56
55
|
export async function emailLogin(apiUrl, presetEmail, io) {
|
|
57
56
|
const email = presetEmail?.trim()
|
|
@@ -135,7 +134,6 @@ export function whoamiReport(creds) {
|
|
|
135
134
|
const rows = [
|
|
136
135
|
['Account', creds.account ?? 'not recorded (browser sign-in never sees an address)'],
|
|
137
136
|
['API', creds.apiUrl],
|
|
138
|
-
...(creds.authApiUrl ? [['Issued by', creds.authApiUrl]] : []),
|
|
139
137
|
['Expires', expiresAtLabel(creds) ?? 'never'],
|
|
140
138
|
['Credential', credentialsDisplayPath()],
|
|
141
139
|
];
|
|
@@ -223,10 +221,8 @@ export function registerLogin(program) {
|
|
|
223
221
|
let method = methodFromFlags(opts);
|
|
224
222
|
const terminal = method === 'email' || !method ? acquirePromptIO() : null;
|
|
225
223
|
try {
|
|
226
|
-
let providers = null;
|
|
227
224
|
if (!method && terminal) {
|
|
228
|
-
|
|
229
|
-
const choices = loginChoices(providers);
|
|
225
|
+
const choices = loginChoices(await fetchProviders(authApiUrl));
|
|
230
226
|
if (choices.length === 0) {
|
|
231
227
|
throw new Error(`${authApiUrl} has no sign-in methods enabled.`);
|
|
232
228
|
}
|
|
@@ -234,15 +230,14 @@ export function registerLogin(program) {
|
|
|
234
230
|
}
|
|
235
231
|
if (method === 'email') {
|
|
236
232
|
const preset = typeof opts.email === 'string' ? opts.email : undefined;
|
|
237
|
-
providers ??= await fetchProviders(authApiUrl);
|
|
238
233
|
const { email, token } = await emailLogin(authApiUrl, preset, terminal?.io);
|
|
239
234
|
saveCredentials({
|
|
240
235
|
token,
|
|
236
|
+
apiUrl: authApiUrl,
|
|
241
237
|
appUrl: opts.appUrl,
|
|
242
238
|
expiresAt: Number.MAX_SAFE_INTEGER,
|
|
243
239
|
revocable: true,
|
|
244
240
|
account: email,
|
|
245
|
-
...commandApiUrls(providers.cliApiUrl ?? authApiUrl, authApiUrl),
|
|
246
241
|
});
|
|
247
242
|
console.log(`\n Signed in as ${email}`);
|
|
248
243
|
console.log(` Credential saved to ${credentialsDisplayPath()}\n`);
|
|
@@ -274,11 +269,11 @@ export function registerLogin(program) {
|
|
|
274
269
|
await verifyRunCloudCredential(authApiUrl, payload.token);
|
|
275
270
|
saveCredentials({
|
|
276
271
|
token: payload.token,
|
|
272
|
+
apiUrl: commandApiUrl(isRunCloud, authApiUrl, payload.apiUrl),
|
|
277
273
|
appUrl: opts.appUrl,
|
|
278
274
|
expiresAt: Math.floor(Date.now() / 1000) + payload.expiresIn,
|
|
279
275
|
revocable: true,
|
|
280
276
|
...(payload.account ? { account: payload.account } : {}),
|
|
281
|
-
...commandApiUrls(payload.apiUrl, authApiUrl),
|
|
282
277
|
});
|
|
283
278
|
console.log(`Logged in${payload.account ? ` as ${payload.account}` : ''}. ` +
|
|
284
279
|
`Credentials saved to ${credentialsDisplayPath()}`);
|
|
@@ -313,7 +308,7 @@ export function registerLogin(program) {
|
|
|
313
308
|
.action(async () => {
|
|
314
309
|
const creds = loadCredentials();
|
|
315
310
|
if (creds?.revocable) {
|
|
316
|
-
console.log(await revokeRunCloudCredential(creds.
|
|
311
|
+
console.log(await revokeRunCloudCredential(creds.apiUrl, creds.token));
|
|
317
312
|
}
|
|
318
313
|
clearCredentials();
|
|
319
314
|
console.log('Logged out.');
|
package/dist/commands/token.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { acquirePromptIO, select, PromptAbortedError } from '../prompt.js';
|
|
2
2
|
import { fetchProviders } from '../runCloudAuth.js';
|
|
3
|
-
import { authApiUrlFor,
|
|
3
|
+
import { authApiUrlFor, commandApiUrl, emailLogin, loginChoices, methodFromFlags, openBrowser, verifyRunCloudCredential, } from './login.js';
|
|
4
4
|
export function tokenReport(isRunCloud, minted) {
|
|
5
5
|
const bin = isRunCloud ? 'runcloud' : 'newly';
|
|
6
6
|
const tokenVar = isRunCloud ? 'RUN_CLOUD_API_TOKEN' : 'NEWLY_API_TOKEN';
|
|
@@ -49,10 +49,8 @@ export function registerToken(program) {
|
|
|
49
49
|
let method = methodFromFlags(opts);
|
|
50
50
|
const terminal = method === 'email' || !method ? acquirePromptIO() : null;
|
|
51
51
|
try {
|
|
52
|
-
let providers = null;
|
|
53
52
|
if (!method && terminal) {
|
|
54
|
-
|
|
55
|
-
const choices = loginChoices(providers);
|
|
53
|
+
const choices = loginChoices(await fetchProviders(authApiUrl));
|
|
56
54
|
if (choices.length === 0) {
|
|
57
55
|
throw new Error(`${authApiUrl} has no sign-in methods enabled.`);
|
|
58
56
|
}
|
|
@@ -60,11 +58,9 @@ export function registerToken(program) {
|
|
|
60
58
|
}
|
|
61
59
|
if (method === 'email') {
|
|
62
60
|
const preset = typeof opts.email === 'string' ? opts.email : undefined;
|
|
63
|
-
providers ??= await fetchProviders(authApiUrl);
|
|
64
61
|
const { email, token: minted } = await emailLogin(authApiUrl, preset, terminal?.io);
|
|
65
62
|
await verifyRunCloudCredential(authApiUrl, minted);
|
|
66
|
-
|
|
67
|
-
console.log(tokenReport(isRunCloud, { token: minted, apiUrl: urls.apiUrl, account: email }));
|
|
63
|
+
console.log(tokenReport(isRunCloud, { token: minted, apiUrl: authApiUrl, account: email }));
|
|
68
64
|
return;
|
|
69
65
|
}
|
|
70
66
|
}
|
|
@@ -82,10 +78,9 @@ export function registerToken(program) {
|
|
|
82
78
|
openBrowser,
|
|
83
79
|
});
|
|
84
80
|
await verifyRunCloudCredential(authApiUrl, payload.token);
|
|
85
|
-
const urls = commandApiUrls(payload.apiUrl, authApiUrl);
|
|
86
81
|
console.log(tokenReport(isRunCloud, {
|
|
87
82
|
token: payload.token,
|
|
88
|
-
apiUrl:
|
|
83
|
+
apiUrl: commandApiUrl(isRunCloud, authApiUrl, payload.apiUrl),
|
|
89
84
|
...(payload.account ? { account: payload.account } : {}),
|
|
90
85
|
}));
|
|
91
86
|
}
|
package/dist/config.js
CHANGED
|
@@ -36,9 +36,6 @@ export function loginCommandName() {
|
|
|
36
36
|
export function normalizeApiUrl(url) {
|
|
37
37
|
return url.replace(/\/+$/, '');
|
|
38
38
|
}
|
|
39
|
-
export function controlPlaneUrl(creds) {
|
|
40
|
-
return normalizeApiUrl(creds.authApiUrl ?? creds.apiUrl);
|
|
41
|
-
}
|
|
42
39
|
export function saveCredentials(creds) {
|
|
43
40
|
mkdirSync(credentialsHome(), { recursive: true, mode: 0o700 });
|
|
44
41
|
const normalized = { ...creds, apiUrl: normalizeApiUrl(creds.apiUrl) };
|
|
@@ -67,10 +64,10 @@ export function loadCredentials() {
|
|
|
67
64
|
if (!existsSync(credentialsPath()))
|
|
68
65
|
return null;
|
|
69
66
|
try {
|
|
70
|
-
const creds = JSON.parse(readFileSync(credentialsPath(), 'utf8'));
|
|
67
|
+
const { authApiUrl, ...creds } = JSON.parse(readFileSync(credentialsPath(), 'utf8'));
|
|
71
68
|
if (!creds.token || creds.expiresAt < Date.now() / 1000)
|
|
72
69
|
return null;
|
|
73
|
-
return { ...creds, apiUrl: normalizeApiUrl(creds.apiUrl) };
|
|
70
|
+
return { ...creds, apiUrl: normalizeApiUrl(authApiUrl ?? creds.apiUrl) };
|
|
74
71
|
}
|
|
75
72
|
catch {
|
|
76
73
|
return null;
|
package/dist/runCloudAuth.js
CHANGED
|
@@ -41,13 +41,11 @@ export async function fetchProviders(apiUrl) {
|
|
|
41
41
|
if (!response.ok)
|
|
42
42
|
throw new AuthError(await readError(response), response.status);
|
|
43
43
|
const payload = (await response.json());
|
|
44
|
-
const cliApiUrl = typeof payload.cliApiUrl === 'string' ? payload.cliApiUrl.trim() : '';
|
|
45
44
|
return {
|
|
46
45
|
emailOtp: payload.emailOtp === true,
|
|
47
46
|
google: payload.google === true,
|
|
48
47
|
github: payload.github === true,
|
|
49
48
|
apple: payload.apple === true,
|
|
50
|
-
...(cliApiUrl ? { cliApiUrl } : {}),
|
|
51
49
|
};
|
|
52
50
|
}
|
|
53
51
|
export async function sendEmailCode(apiUrl, email) {
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = '0.1.
|
|
1
|
+
export const CLI_VERSION = '0.1.133';
|
package/package.json
CHANGED
|
Binary file
|
package/runcloud-0.1.131.tgz
DELETED
|
Binary file
|