runcloud 0.1.25 → 0.1.27
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/README.md +61 -5
- package/dist/commands/login.js +30 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ runcloud account # show your entitlement and metered balance
|
|
|
34
34
|
|
|
35
35
|
Email signs you in without leaving the terminal. Google and GitHub open a browser and
|
|
36
36
|
show a short code to confirm, so login works over SSH and on headless machines too —
|
|
37
|
-
pick with `--email`, `--google`, or `--
|
|
37
|
+
pick with `--email`, `--google`, `--github`, or `--browser` to skip the prompt.
|
|
38
38
|
|
|
39
39
|
The token expires — re-run `runcloud login` when a command reports you're logged out.
|
|
40
40
|
|
|
@@ -84,6 +84,8 @@ runcloud sandbox create --disk 80 # request an 80 GiB root filesy
|
|
|
84
84
|
runcloud sandbox exec <id> npm run build # run a command inside it
|
|
85
85
|
runcloud sandbox shell <id> # interactive shell, streamed over the API
|
|
86
86
|
runcloud sandbox logs <id> --lines 200 # tail its logs
|
|
87
|
+
runcloud sandbox metrics <id> --range 24h # CPU, memory, network, and disk usage
|
|
88
|
+
runcloud sandbox pause <id> # park it; `resume` brings it back warm
|
|
87
89
|
runcloud sandbox list
|
|
88
90
|
runcloud sandbox rm <id> # destroy it
|
|
89
91
|
```
|
|
@@ -112,13 +114,21 @@ runcloud sandbox key "$SANDBOX_ID" "ctrl+l"
|
|
|
112
114
|
Treat a signed desktop URL as a secret. It grants temporary access to that
|
|
113
115
|
desktop without asking the browser to send your CLI credential.
|
|
114
116
|
|
|
115
|
-
###
|
|
117
|
+
### Lifetime, pausing, and a public hostname
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
Two clocks stop a sandbox, and the first one bites after five minutes:
|
|
120
|
+
|
|
121
|
+
- `--timeout <seconds>` — maximum lifetime, wall-clock from boot or the last
|
|
122
|
+
resume, busy or not. **Defaults to 300**; `0` removes the limit.
|
|
123
|
+
- `--idle-pause <seconds>` — pause after that much inactivity.
|
|
124
|
+
|
|
125
|
+
Both *pause* rather than destroy. `runcloud sandbox resume <id>` brings a
|
|
126
|
+
sandbox back warm with its filesystem intact, and a paused sandbox is destroyed
|
|
127
|
+
after 48 hours. `--persistent` stops both clocks; `--expose` implies it and
|
|
128
|
+
publishes a guest port at a stable `<name>-box.run.cloud` hostname.
|
|
120
129
|
|
|
121
130
|
```bash
|
|
131
|
+
runcloud sandbox create --timeout 3600 # an hour of wall clock, then pause
|
|
122
132
|
runcloud sandbox create --idle-pause 600 # pause after 10 idle minutes
|
|
123
133
|
runcloud sandbox create --name my-project --persistent # never pauses
|
|
124
134
|
runcloud sandbox create --name my-project --expose 3000 # persistent + https://my-project-box.run.cloud
|
|
@@ -126,6 +136,10 @@ runcloud sandbox expose <id> --port 8080 # expose later, or m
|
|
|
126
136
|
runcloud sandbox get <id> # inspect, including the hostname
|
|
127
137
|
```
|
|
128
138
|
|
|
139
|
+
The CLI only sets the timer at create time. To extend it on a running sandbox —
|
|
140
|
+
what a long job or a live agent session needs — call
|
|
141
|
+
`sandbox.setTimeout(id, seconds)` from [`@run-cloud/sdk`](https://www.npmjs.com/package/@run-cloud/sdk).
|
|
142
|
+
|
|
129
143
|
`--expose` needs `--name` — that name is the hostname prefix, and it is fixed
|
|
130
144
|
once minted.
|
|
131
145
|
|
|
@@ -152,6 +166,48 @@ runcloud sandbox restore <snapshot-id> --name fork # fork a NEW sandbox from t
|
|
|
152
166
|
runcloud sandbox snapshot rm <snapshot-id>
|
|
153
167
|
```
|
|
154
168
|
|
|
169
|
+
### Secrets
|
|
170
|
+
|
|
171
|
+
Store secrets in a named group, then attach the group when you create a sandbox:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
runcloud secret-group create aws --key AWS_ACCESS_KEY_ID --key AWS_SECRET_ACCESS_KEY
|
|
175
|
+
runcloud secret-group create prod --from-dotenv ./.env.production
|
|
176
|
+
runcloud secret-group list
|
|
177
|
+
runcloud secret-group show aws # names only, never values
|
|
178
|
+
runcloud secrets set DATABASE_URL --group prod # hidden prompt, or --stdin
|
|
179
|
+
runcloud secrets set-file sa.json --group prod --path .config/gcloud/sa.json --from-file ./sa.json
|
|
180
|
+
|
|
181
|
+
runcloud sandbox create --secret-group prod --env LOG_LEVEL=debug
|
|
182
|
+
runcloud sandbox create --secret prod/DATABASE_URL # one secret out of a group
|
|
183
|
+
runcloud sandbox create --no-secrets # state explicitly that it holds none
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`--secret-group`, `--secret`, and `--env` are repeatable and **order matters** —
|
|
187
|
+
a later one wins a name collision, and `--env` is applied last.
|
|
188
|
+
|
|
189
|
+
`runcloud sandbox secrets <id>` re-attaches on a running sandbox. It *replaces*
|
|
190
|
+
the whole set rather than merging, which is how a credential gets revoked:
|
|
191
|
+
passing no flags at all takes them all back.
|
|
192
|
+
|
|
193
|
+
A value can never be passed as a command argument — it would land in your shell
|
|
194
|
+
history, in `ps` output, and in CI logs. Use a hidden prompt, a file, or stdin.
|
|
195
|
+
|
|
196
|
+
## Custom images
|
|
197
|
+
|
|
198
|
+
A sandbox boots an OCI image. Register your own once, then create sandboxes from
|
|
199
|
+
it:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
runcloud image create --ref python:3.12-slim # registers, and waits for the build
|
|
203
|
+
runcloud image list
|
|
204
|
+
runcloud image refresh python:3.12-slim # re-pin the tag to its current upstream digest
|
|
205
|
+
runcloud sandbox create --image python:3.12-slim
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Built in: `runcloud/agent-base` (the default), `runcloud/desktop`, and
|
|
209
|
+
`runcloud/box` (the default with `--expose`).
|
|
210
|
+
|
|
155
211
|
## Demos
|
|
156
212
|
|
|
157
213
|
```bash
|
package/dist/commands/login.js
CHANGED
|
@@ -215,6 +215,31 @@ export async function revokeRunCloudCredential(apiUrl, token) {
|
|
|
215
215
|
'revoke it from the run.cloud dashboard.');
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
|
+
export function existingLoginSummary(creds) {
|
|
219
|
+
const who = creds.account ? ` as ${creds.account}` : '';
|
|
220
|
+
const expiry = creds.expiresAt < Number.MAX_SAFE_INTEGER
|
|
221
|
+
? `, valid until ${new Date(creds.expiresAt * 1000).toLocaleString()}`
|
|
222
|
+
: '';
|
|
223
|
+
return `You are already logged in${who} (${creds.apiUrl}${expiry}).`;
|
|
224
|
+
}
|
|
225
|
+
export async function confirmReplaceExistingLogin(creds) {
|
|
226
|
+
if (!creds)
|
|
227
|
+
return true;
|
|
228
|
+
const terminal = acquirePromptIO();
|
|
229
|
+
if (!terminal)
|
|
230
|
+
return true;
|
|
231
|
+
try {
|
|
232
|
+
console.log(`\n ${existingLoginSummary(creds)}`);
|
|
233
|
+
const choice = await select(' Do you want to continue?', [
|
|
234
|
+
{ value: 'keep', label: 'Keep this session', hint: 'nothing to do' },
|
|
235
|
+
{ value: 'again', label: 'Log in again', hint: 'replaces the saved credential' },
|
|
236
|
+
], terminal.io);
|
|
237
|
+
return choice === 'again';
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
terminal.close();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
218
243
|
export function registerLogin(program) {
|
|
219
244
|
const isRunCloud = program.name() === 'runcloud';
|
|
220
245
|
const defaultAppUrl = isRunCloud
|
|
@@ -265,6 +290,10 @@ export function registerLogin(program) {
|
|
|
265
290
|
console.log(`Logged in headlessly. Credentials written for API ${apiUrl}.`);
|
|
266
291
|
return;
|
|
267
292
|
}
|
|
293
|
+
if (!(await confirmReplaceExistingLogin(loadCredentials()))) {
|
|
294
|
+
console.log(` Keeping the credential at ${credentialsDisplayPath()}\n`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
268
297
|
if (opts.manual && !isRunCloud) {
|
|
269
298
|
const { createInterface } = await import('node:readline/promises');
|
|
270
299
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -303,6 +332,7 @@ export function registerLogin(program) {
|
|
|
303
332
|
appUrl: opts.appUrl,
|
|
304
333
|
expiresAt: Number.MAX_SAFE_INTEGER,
|
|
305
334
|
revocable: true,
|
|
335
|
+
account: email,
|
|
306
336
|
});
|
|
307
337
|
console.log(`\n Signed in as ${email}`);
|
|
308
338
|
console.log(` Credential saved to ${credentialsDisplayPath()}\n`);
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = '0.1.
|
|
1
|
+
export const CLI_VERSION = '0.1.27';
|