@runalabs/rill-cli 0.1.16 → 0.1.17

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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/cli.js +29 -14
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -370,6 +370,8 @@ Progress runs through `uploading` → `queued` → `encoding` → `processing`
370
370
 
371
371
  Run `rill connect --email you@example.com` to authorize account access or keep your anonymous recordings. Open the displayed link, sign in, and enter the code yourself. Approval allows creation and reading of your existing and new recordings for 90 days, with automatic renewal of one-hour access tokens. Revoke the connection in Settings → Agent access to stop access and renewal. Deletion and changes to existing sharing settings are not included.
372
372
 
373
+ Browser approval does not finish the handshake: the CLI must collect and save credentials. With `--no-wait`, keep polling the returned `connect status <authorizationId>` command until `authenticated`. Starting with 0.1.17, the CLI checks the server even after its local approval deadline. Approval codes expire after ten minutes; successful approval starts a separate 30-minute collection window, with a 60-second lost-response retry window after first collection. If collection expires, run `connect --email` again with the same account to request fresh human approval while preserving the registration and recordings. Do not delete saved credentials to recover. Auth requests include the CLI version for consent-aware diagnostics.
374
+
373
375
  Use `rill record list` to list accessible recordings and `rill inspect <recording-id>` for private recording details. `rill inspect <share-url>` continues to work without account access. The existing `rill login` and `RILL_TOKEN` flows remain supported. Local credential storage requires macOS Keychain, Linux Secret Service, or Windows current-user DPAPI.
374
376
 
375
377
  ### Check the connected account
package/dist/cli.js CHANGED
@@ -127,6 +127,11 @@ function browserAttachment(cdpUrl, tabId) {
127
127
  };
128
128
  }
129
129
 
130
+ // src/version.ts
131
+ import { createRequire } from "module";
132
+ var packageJson = createRequire(import.meta.url)("../package.json");
133
+ var CLI_VERSION = packageJson.version;
134
+
130
135
  // src/agent-session.ts
131
136
  var JWT_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
132
137
  var CLAIM_GRANT = "urn:workos:agent-auth:grant-type:claim";
@@ -150,7 +155,7 @@ async function request(apiUrl, path, body, form = false) {
150
155
  method: "POST",
151
156
  redirect: "error",
152
157
  signal: AbortSignal.timeout(15e3),
153
- headers: { "Content-Type": form ? "application/x-www-form-urlencoded" : "application/json" },
158
+ headers: { "Content-Type": form ? "application/x-www-form-urlencoded" : "application/json", "X-Rill-CLI-Version": CLI_VERSION },
154
159
  body: form ? new URLSearchParams(Object.entries(body).map(([key2, value2]) => [key2, String(value2)])) : JSON.stringify(body)
155
160
  });
156
161
  const value = await response.json();
@@ -246,6 +251,12 @@ async function connectAgent(apiUrl, email3, options = {}) {
246
251
  async function beginConnection(apiUrl, email3) {
247
252
  if (process.env.RILL_TOKEN) throw new Error("Unset RILL_TOKEN before connecting a local identity.");
248
253
  let saved = parseAuthorization(await loadStoredCredential(apiUrl));
254
+ if (saved?.connection?.status === "pending" && saved.connection.expiresAt <= Date.now()) {
255
+ if (saved.connection.email !== email3) throw new Error("Use the original account to finish or recover this connection.");
256
+ const result = await pollSavedConnection(apiUrl, saved.connection.id);
257
+ if (result.status !== "expired") return result;
258
+ saved = parseAuthorization(await loadStoredCredential(apiUrl));
259
+ }
249
260
  if (saved?.connection?.status === "pending" && saved.connection.expiresAt > Date.now()) {
250
261
  if (saved.connection.email !== email3) throw new Error("A connection for another email is pending. Let it expire before starting a different connection.");
251
262
  process.stderr.write(`Open ${saved.connection.verificationUri}
@@ -259,6 +270,22 @@ Sign in as ${email3} and enter ${saved.connection.userCode}. Approve only the re
259
270
  const result = await request(apiUrl, "/agent/identity/claim", { claim_token: saved.claimToken, email: email3 });
260
271
  claim = result.claim_attempt;
261
272
  } catch (error51) {
273
+ if (error51 instanceof AgentAuthError && error51.code === "claim_already_approved") {
274
+ saved.connection ??= {
275
+ id: randomBytes(16).toString("hex"),
276
+ email: email3,
277
+ verificationUri: "",
278
+ userCode: "",
279
+ expiresAt: Date.now(),
280
+ interval: 5,
281
+ nextPollAt: 0,
282
+ status: "pending"
283
+ };
284
+ saved.connection.status = "pending";
285
+ saved.connection.nextPollAt = 0;
286
+ await save(apiUrl, saved);
287
+ return pollSavedConnection(apiUrl, saved.connection.id);
288
+ }
262
289
  if (!(error51 instanceof AgentAuthError) || error51.code !== "claim_expired") throw error51;
263
290
  }
264
291
  }
@@ -309,11 +336,6 @@ async function pollSavedConnection(apiUrl, authorizationId) {
309
336
  if (!saved?.connection || saved.connection.id !== authorizationId) throw new AgentAuthError("authorization_not_found", "No matching saved connection for this API URL. Use the latest connect response.");
310
337
  const connection = saved.connection;
311
338
  if (connection.status !== "pending") return connectionView(apiUrl, saved);
312
- if (Date.now() >= connection.expiresAt) {
313
- connection.status = "expired";
314
- await save(apiUrl, saved);
315
- return connectionView(apiUrl, saved);
316
- }
317
339
  if (Date.now() < connection.nextPollAt) return connectionView(apiUrl, saved);
318
340
  connection.nextPollAt = Date.now() + connection.interval * 1e3;
319
341
  await save(apiUrl, saved);
@@ -322,7 +344,7 @@ async function pollSavedConnection(apiUrl, authorizationId) {
322
344
  saved = withTokens({ version: 1, anonymous: false, connection: { ...connection, status: "authenticated", userCode: "" } }, tokens);
323
345
  } catch (error51) {
324
346
  if (!(error51 instanceof AgentAuthError)) throw error51;
325
- if (error51.code === "expired_token" || error51.code === "claim_expired") connection.status = "expired";
347
+ if (error51.code === "expired_token" || error51.code === "claim_expired" || error51.code === "claim_delivery_expired" || error51.code === "claimed_or_in_flight" && Date.now() >= connection.expiresAt) connection.status = "expired";
326
348
  else if (error51.code === "access_denied" || error51.code === "claim_revoked") connection.status = "denied";
327
349
  else if (error51.code === "slow_down") {
328
350
  connection.interval += 5;
@@ -16771,13 +16793,6 @@ async function withRunFeedback(session, start) {
16771
16793
 
16772
16794
  // src/doctor.ts
16773
16795
  import { spawn as spawn7 } from "child_process";
16774
-
16775
- // src/version.ts
16776
- import { createRequire } from "module";
16777
- var packageJson = createRequire(import.meta.url)("../package.json");
16778
- var CLI_VERSION = packageJson.version;
16779
-
16780
- // src/doctor.ts
16781
16796
  var defaultDependencies = {
16782
16797
  platform: process.platform,
16783
16798
  encoding: "remote",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runalabs/rill-cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Record, inspect, and share agent-controlled browser runs with Rill.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {