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 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 `--github` to skip the prompt.
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
- ### Persistence and a public hostname
117
+ ### Lifetime, pausing, and a public hostname
116
118
 
117
- A sandbox pauses when idle by default. Tune that with `--idle-pause <seconds>`,
118
- turn it off with `--persistent`, and publish a guest port at a stable
119
- `<name>-box.run.cloud` hostname with `--expose`:
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
@@ -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.25';
1
+ export const CLI_VERSION = '0.1.27';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcloud",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Create and control run.cloud remote mobile simulators and cloud sandboxes",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [