@scalebrowser/sdk 0.3.0 → 0.4.1

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 CHANGED
@@ -15,7 +15,17 @@ var ErrorCode = {
15
15
  * Distinct from Capacity (4003 = this machine is full, wait): the caller's move
16
16
  * is to stop a running browser anywhere or upgrade the plan. */
17
17
  ConcurrencyLimit: 4008,
18
- Unauthorized: 4010
18
+ Unauthorized: 4010,
19
+ /** No account is connected to this machine, so nothing may run (DEC-014).
20
+ * Distinct from Unauthorized (4010 = the daemon's own bearer token is wrong, so
21
+ * re-read the token): 4011 means the machine needs to be signed in, and no token
22
+ * a client holds can substitute for that. Connect it in the desktop app. */
23
+ NotSignedIn: 4011,
24
+ /** This ONE profile is open on another machine of the same account (DEC-015).
25
+ * Distinct from ConcurrencyLimit (4008 = the plan is full, buy more or free any
26
+ * browser): the caller's move here costs nothing. Close it on that machine, or
27
+ * take it over from the app running there. */
28
+ ProfileInUse: 4012
19
29
  };
20
30
  var CODE_LABELS = {
21
31
  4001: "Not found",
@@ -25,7 +35,9 @@ var CODE_LABELS = {
25
35
  4005: "Launch preflight failed",
26
36
  4006: "Profile already running",
27
37
  4008: "Concurrent-browser limit reached (subscription-wide)",
28
- 4010: "Unauthorized"
38
+ 4010: "Unauthorized",
39
+ 4011: "No account connected to this machine",
40
+ 4012: "This profile is open on another machine"
29
41
  };
30
42
  var ScalebrowserError = class extends Error {
31
43
  constructor(message) {
@@ -127,7 +139,7 @@ var ClientCore = class {
127
139
  return text ? JSON.parse(text) : void 0;
128
140
  }
129
141
  /**
130
- * A route that answers with bytes, not JSON — artifacts and run stills.
142
+ * A route that answers with bytes, not JSON: artifacts and run stills.
131
143
  *
132
144
  * Separate from {@link ClientCore.request} rather than a flag on it, because the two
133
145
  * differ in the error path as well: a failed byte fetch still answers JSON,
@@ -245,7 +257,7 @@ var ControlApi = class extends ClientCore {
245
257
  });
246
258
  }
247
259
  /**
248
- * Fetch one artifact's bytes — a screenshot, a download, a saved PDF.
260
+ * Fetch one artifact's bytes: a screenshot, a download, a saved PDF.
249
261
  *
250
262
  * A miss is a plain 404 whether the id never existed, expired, or belonged to
251
263
  * a released lease: the caller's next move is the same in all three cases.
@@ -258,7 +270,7 @@ var ControlApi = class extends ClientCore {
258
270
  * Licence state for display. The launch gate stays the authority on starts.
259
271
  *
260
272
  * A daemon predating the route answers 404, and that is not an error worth
261
- * throwing — but it is read pessimistically: nothing can be shown to be
273
+ * throwing, but it is read pessimistically: nothing can be shown to be
262
274
  * connected or permitted. Same shape of tolerance as {@link getMetrics}.
263
275
  */
264
276
  async getAccount(signal) {
@@ -273,7 +285,7 @@ var ControlApi = class extends ClientCore {
273
285
  health(signal) {
274
286
  return this.request("GET", "/health", void 0, { signal });
275
287
  }
276
- /** "Can it serve?" Local facts only — the control plane is deliberately not consulted. */
288
+ /** "Can it serve?" Local facts only; the control plane is deliberately not consulted. */
277
289
  ready(signal) {
278
290
  return this.request("GET", "/health/ready", void 0, { signal });
279
291
  }
@@ -285,7 +297,7 @@ var ExtensionsApi = class extends ClientCore {
285
297
  //
286
298
  // Upload a `.crx`; the daemon lifts its public key out of the CRX header and
287
299
  // writes it into the unpacked `manifest.json`, so every profile loads the
288
- // package under its CANONICAL Web-Store id. A key-less package is refused — it
300
+ // package under its CANONICAL Web-Store id. A key-less package is refused, since it
289
301
  // would take a path-derived id, identical across the fleet.
290
302
  /** The library, newest first. */
291
303
  listLibraryExtensions(signal) {
@@ -298,7 +310,7 @@ var ExtensionsApi = class extends ClientCore {
298
310
  uploadExtension(crx) {
299
311
  return this.request("POST", "/v1/extensions", crx);
300
312
  }
301
- /** One library entry — the most recently added version. */
313
+ /** One library entry: the most recently added version. */
302
314
  getLibraryExtension(id, signal) {
303
315
  return this.request("GET", `/v1/extensions/${enc(id)}`, void 0, { signal });
304
316
  }
@@ -309,7 +321,7 @@ var ExtensionsApi = class extends ClientCore {
309
321
  /**
310
322
  * Remove ONE stocked version.
311
323
  *
312
- * Assignments survive — they name the extension, not the version — unless this
324
+ * Assignments survive, because they name the extension and not the version, unless this
313
325
  * was the last version, in which case they go with it.
314
326
  */
315
327
  deleteLibraryExtensionVersion(id, version) {
@@ -348,14 +360,14 @@ var IdentityApi = class extends ClientCore {
348
360
  // ── Platform credentials ────────────────────────────────────────────────────
349
361
  //
350
362
  // Stored logins, so an agent can sign a profile into a platform WITHOUT ever
351
- // holding the password — the daemon decrypts and types it. Only `revealCredential`
363
+ // holding the password: the daemon decrypts and types it. Only `revealCredential`
352
364
  // and the bundle calls return plaintext, and every one of them needs the vault
353
365
  // password: the bearer token alone is not enough, because the MCP agent layer
354
366
  // authenticates with exactly the same one.
355
367
  //
356
368
  // `platform` is whatever you have: the service's domain ("discord.com"), its
357
369
  // name ("Discord") or a host under it ("www.discord.com"). The daemon resolves
358
- // all of them to ONE key — the registrable domain — and every response echoes
370
+ // all of them to ONE key (the registrable domain), and every response echoes
359
371
  // that key, so `listCredentials` and a later `deleteCredential` agree. Pass a
360
372
  // whole sign-in URL only to the MCP tools: here the platform is a path segment,
361
373
  // and a `/` in it is refused rather than read as a path.
@@ -371,7 +383,7 @@ var IdentityApi = class extends ClientCore {
371
383
  /**
372
384
  * Store or update one login.
373
385
  *
374
- * An omitted secret keeps its stored value rather than clearing it — an edit form
386
+ * An omitted secret keeps its stored value rather than clearing it, because an edit form
375
387
  * never saw the password in the clear, so it cannot resend one; delete the row to
376
388
  * remove a credential. `totp_secret` may be pasted exactly as a site presents it
377
389
  * (grouped, lower case): it is normalised and test-decoded by the daemon, so a
@@ -404,7 +416,7 @@ var IdentityApi = class extends ClientCore {
404
416
  *
405
417
  * `password` is deliberately separate from the vault password: a backup gets
406
418
  * handed to another machine, and that must not also hand over daemon access. This
407
- * is the answer to a lost master key — a daemon-generated password exists nowhere
419
+ * is the answer to a lost master key: a daemon-generated password exists nowhere
408
420
  * else.
409
421
  */
410
422
  exportCredentials(profileId, vaultPassword, password) {
@@ -476,6 +488,72 @@ var IdentityApi = class extends ClientCore {
476
488
  deletePasskey(profileId, credentialId) {
477
489
  return this.request("DELETE", `/v1/profiles/${enc(profileId)}/passkeys/${enc(credentialId)}`);
478
490
  }
491
+ // ── Secret Layer ───────────────────────────────────────────────────────────
492
+ //
493
+ // Values a profile holds that no agent ever reads. Nothing here returns a
494
+ // value, and that is not an oversight: the wrapper below starts the command
495
+ // INSIDE the daemon precisely so that no route has to hand plaintext out.
496
+ /** The entries this profile keeps, with the placeholder that stands for each. */
497
+ listSecrets(profileId, signal) {
498
+ return this.request(
499
+ "GET",
500
+ `/v1/profiles/${enc(profileId)}/secrets`,
501
+ void 0,
502
+ { signal }
503
+ );
504
+ }
505
+ /** Change a title, a binding, or the state. Never the label. */
506
+ updateSecret(profileId, secretId, body) {
507
+ return this.request(
508
+ "PATCH",
509
+ `/v1/profiles/${enc(profileId)}/secrets/${enc(secretId)}`,
510
+ body
511
+ );
512
+ }
513
+ /** Delete an entry. The reply names the runs it appeared in. */
514
+ deleteSecret(profileId, secretId) {
515
+ return this.request(
516
+ "DELETE",
517
+ `/v1/profiles/${enc(profileId)}/secrets/${enc(secretId)}`
518
+ );
519
+ }
520
+ /**
521
+ * Filter a text you hold against this profile's values.
522
+ *
523
+ * The SDK path exists because censoring only makes sense where a MODEL is
524
+ * reading: your own code is not, and a placeholder in the middle of it would
525
+ * break parsing. So the daemon offers the filter as a call rather than
526
+ * applying it to a channel it does not own.
527
+ */
528
+ scrubText(profileId, text) {
529
+ return this.request(
530
+ "POST",
531
+ `/v1/profiles/${enc(profileId)}/secrets/scrub`,
532
+ { text }
533
+ );
534
+ }
535
+ /**
536
+ * Run a command with named values in its environment.
537
+ *
538
+ * The daemon starts the child, so no value crosses a socket. At least one
539
+ * `env` entry is required: without the rule an `npm publish` would carry every
540
+ * value of the profile.
541
+ */
542
+ runWithSecrets(profileId, body) {
543
+ return this.request(
544
+ "POST",
545
+ `/v1/profiles/${enc(profileId)}/secrets/run`,
546
+ body
547
+ );
548
+ }
549
+ /** Is the secret layer on? It is by default. */
550
+ getSecretLayer(signal) {
551
+ return this.request("GET", "/v1/secret-layer", void 0, { signal });
552
+ }
553
+ /** Turn the secret layer on or off for the whole daemon. */
554
+ setSecretLayer(enabled) {
555
+ return this.request("POST", "/v1/secret-layer", { enabled });
556
+ }
479
557
  // ── Cookies ────────────────────────────────────────────────────────────────
480
558
  /**
481
559
  * The one door a cookie VALUE leaves through, and it needs the vault password.
@@ -502,7 +580,7 @@ var RunsApi = class extends ClientCore {
502
580
  getRun(runId, signal) {
503
581
  return this.request("GET", `/v1/runs/${enc(runId)}`, void 0, { signal });
504
582
  }
505
- /** A run's steps, oldest first — the order they happened in. */
583
+ /** A run's steps, oldest first: the order they happened in. */
506
584
  listRunSteps(runId, params = {}, signal) {
507
585
  return this.request("GET", `/v1/runs/${enc(runId)}/steps`, void 0, {
508
586
  query: { limit: params.limit, offset: params.offset },
@@ -707,7 +785,7 @@ var CdpConnection = class {
707
785
  return () => this.#subscribers.delete(cb);
708
786
  }
709
787
  /** Register a callback fired once when the connection closes (immediately if
710
- * it already has) — event streams use this to end instead of hanging. */
788
+ * it already has); event streams use this to end instead of hanging. */
711
789
  onClose(cb) {
712
790
  if (this.#closeReason) {
713
791
  cb();
@@ -991,7 +1069,7 @@ var ScalebrowserClient = class extends ClientCore {
991
1069
  });
992
1070
  }
993
1071
  /**
994
- * Every id matching `params`, unpaged — what "act on all matches" needs.
1072
+ * Every id matching `params`, unpaged: what "act on all matches" needs.
995
1073
  *
996
1074
  * `listProfiles` is paged, so acting on "everything" from it only ever covers the
997
1075
  * page you fetched. Asking for ids costs one request and no persona payloads.
@@ -1027,6 +1105,14 @@ var ScalebrowserClient = class extends ClientCore {
1027
1105
  stopProfile(id) {
1028
1106
  return this.request("POST", `/v1/profiles/${enc(id)}/stop`, {});
1029
1107
  }
1108
+ /** Ask for a profile that is open on another machine of this account.
1109
+ *
1110
+ * Closes nothing here and nothing there: the control plane revokes the holder's
1111
+ * right, the holder closes its own browser at its next heartbeat. Start only
1112
+ * after this answered `free`. Errors: 4012 when it is held elsewhere. */
1113
+ takeoverProfile(id) {
1114
+ return this.request("POST", `/v1/profiles/${enc(id)}/takeover`, {});
1115
+ }
1030
1116
  // ── Bulk (PRD §D2) ───────────────────────────────────────────────────────
1031
1117
  bulkCreateProfiles(body) {
1032
1118
  return this.request("POST", "/v1/bulk/profiles", body);
@@ -1124,7 +1210,7 @@ var ScalebrowserClient = class extends ClientCore {
1124
1210
  return this.request("POST", `/v1/proxies/${enc(id)}/check`, {});
1125
1211
  }
1126
1212
  /**
1127
- * Probe a proxy config WITHOUT persisting it — validate before you commit.
1213
+ * Probe a proxy config WITHOUT persisting it: validate before you commit.
1128
1214
  * Pass `id` (and omit the credentials) to probe an existing proxy's stored,
1129
1215
  * write-only credentials in an edit form.
1130
1216
  */
@@ -1235,6 +1321,6 @@ var PROXY_KINDS = ["http", "socks5", "gateway"];
1235
1321
  var ROTATIONS = ["sticky", "rotating"];
1236
1322
 
1237
1323
  // src/index.ts
1238
- var VERSION = "0.3.0";
1324
+ var VERSION = "0.4.1";
1239
1325
 
1240
1326
  export { ApiError, CdpError, CdpSession, DEFAULT_CAPACITY, ErrorCode, GEO_MODES, HOST_MODES, NetworkError, PROXY_KINDS, ROTATIONS, RUNTIME_STATES, ScalebrowserClient, ScalebrowserError, VERSION, deriveMetricsFromProfiles, errorMessage, eventStream, openCdpSession, parseSseFrame, subscribeEvents };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalebrowser/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Official Node/TypeScript SDK for the Scalebrowser daemon — typed REST client + direct-CDP driver plane (nodriver-style, not Playwright).",
5
5
  "type": "module",
6
6
  "license": "MIT",