opensteer 0.4.2 → 0.4.4

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.
@@ -35,30 +35,30 @@ function flattenExtractionDataToFieldPlan(data) {
35
35
  flattenExtractionDataToFieldPlanRecursive(data, "", fields);
36
36
  return fields;
37
37
  }
38
- function flattenExtractionDataToFieldPlanRecursive(value, prefix, out) {
38
+ function flattenExtractionDataToFieldPlanRecursive(value, prefix2, out) {
39
39
  if (value == null) return;
40
40
  if (typeof value === "number" && Number.isFinite(value)) {
41
- const key = String(prefix || "").trim();
41
+ const key = String(prefix2 || "").trim();
42
42
  if (!key) return;
43
43
  out[key] = { element: Math.trunc(value) };
44
44
  return;
45
45
  }
46
46
  if (typeof value === "string" && value.trim().toUpperCase() === CURRENT_URL_SENTINEL) {
47
- const key = String(prefix || "").trim();
47
+ const key = String(prefix2 || "").trim();
48
48
  if (!key) return;
49
49
  out[key] = { source: "current_url" };
50
50
  return;
51
51
  }
52
52
  const counterLeaf = parseCounterLeafDescriptor(value);
53
53
  if (counterLeaf) {
54
- const key = String(prefix || "").trim();
54
+ const key = String(prefix2 || "").trim();
55
55
  if (!key) return;
56
56
  out[key] = counterLeaf;
57
57
  return;
58
58
  }
59
59
  if (Array.isArray(value)) {
60
60
  for (let i = 0; i < value.length; i++) {
61
- const nextPrefix = prefix ? `${prefix}[${i}]` : `[${i}]`;
61
+ const nextPrefix = prefix2 ? `${prefix2}[${i}]` : `[${i}]`;
62
62
  flattenExtractionDataToFieldPlanRecursive(value[i], nextPrefix, out);
63
63
  }
64
64
  return;
@@ -67,7 +67,7 @@ function flattenExtractionDataToFieldPlanRecursive(value, prefix, out) {
67
67
  for (const [key, child] of Object.entries(
68
68
  value
69
69
  )) {
70
- const nextPrefix = prefix ? `${prefix}.${key}` : key;
70
+ const nextPrefix = prefix2 ? `${prefix2}.${key}` : key;
71
71
  flattenExtractionDataToFieldPlanRecursive(child, nextPrefix, out);
72
72
  }
73
73
  }
@@ -103,8 +103,8 @@ var init_extract_field_plan = __esm({
103
103
 
104
104
  // src/ai/model.ts
105
105
  function resolveProviderInfo(modelStr) {
106
- for (const [prefix, info] of Object.entries(PROVIDER_MAP)) {
107
- if (modelStr.startsWith(prefix)) {
106
+ for (const [prefix2, info] of Object.entries(PROVIDER_MAP)) {
107
+ if (modelStr.startsWith(prefix2)) {
108
108
  return info;
109
109
  }
110
110
  }
@@ -335,7 +335,7 @@ var init_extractor = __esm({
335
335
 
336
336
  // src/cli/server.ts
337
337
  var import_net = require("net");
338
- var import_fs5 = require("fs");
338
+ var import_fs4 = require("fs");
339
339
 
340
340
  // src/opensteer.ts
341
341
  var import_crypto2 = require("crypto");
@@ -817,15 +817,15 @@ function normalizeNamespace(input) {
817
817
  if (!segments.length) return DEFAULT_NAMESPACE;
818
818
  return segments.join("/");
819
819
  }
820
- function resolveNamespaceDir(rootDir, namespace) {
820
+ function resolveNamespaceDir(rootDir, namespace2) {
821
821
  const selectorsRoot = import_path2.default.resolve(rootDir, ".opensteer", "selectors");
822
- const normalizedNamespace = normalizeNamespace(namespace);
822
+ const normalizedNamespace = normalizeNamespace(namespace2);
823
823
  const namespaceDir = import_path2.default.resolve(selectorsRoot, normalizedNamespace);
824
824
  const relative = import_path2.default.relative(selectorsRoot, namespaceDir);
825
825
  if (relative === "" || relative === ".") return namespaceDir;
826
826
  if (relative.startsWith("..") || import_path2.default.isAbsolute(relative)) {
827
827
  throw new Error(
828
- `Namespace "${namespace}" resolves outside selectors root.`
828
+ `Namespace "${namespace2}" resolves outside selectors root.`
829
829
  );
830
830
  }
831
831
  return namespaceDir;
@@ -1275,9 +1275,9 @@ function createEmptyRegistry(name) {
1275
1275
  var LocalSelectorStorage = class {
1276
1276
  rootDir;
1277
1277
  namespace;
1278
- constructor(rootDir, namespace) {
1278
+ constructor(rootDir, namespace2) {
1279
1279
  this.rootDir = rootDir;
1280
- this.namespace = normalizeNamespace(namespace);
1280
+ this.namespace = normalizeNamespace(namespace2);
1281
1281
  }
1282
1282
  getRootDir() {
1283
1283
  return this.rootDir;
@@ -1461,7 +1461,7 @@ function shouldKeepAttributeForPath(name, value, options = {}) {
1461
1461
  if (key === "c") return false;
1462
1462
  if (/^on[a-z]/i.test(key)) return false;
1463
1463
  if (ATTRIBUTE_DENY_KEYS.has(key)) return false;
1464
- if (INTERNAL_ATTR_PREFIXES.some((prefix) => key.startsWith(prefix)))
1464
+ if (INTERNAL_ATTR_PREFIXES.some((prefix2) => key.startsWith(prefix2)))
1465
1465
  return false;
1466
1466
  if (options.allowClass === false && key === "class") return false;
1467
1467
  if (isMediaTag(options.tag) && VOLATILE_LAZY_LOADING_ATTRS.has(key)) {
@@ -1711,7 +1711,7 @@ async function buildElementPathFromHandle(handle) {
1711
1711
  if (/^on[a-z]/i.test(normalized)) return false;
1712
1712
  if (ATTRIBUTE_DENY_KEYS2.has(normalized)) return false;
1713
1713
  if (INTERNAL_ATTR_PREFIXES2.some(
1714
- (prefix) => normalized.startsWith(prefix)
1714
+ (prefix2) => normalized.startsWith(prefix2)
1715
1715
  )) {
1716
1716
  return false;
1717
1717
  }
@@ -5884,12 +5884,12 @@ function isPersistedArrayNode(node) {
5884
5884
  function isPersistedObjectNode(node) {
5885
5885
  return !!node && typeof node === "object" && !Array.isArray(node) && !isPersistedValueNode(node) && !isPersistedSourceNode(node) && !isPersistedArrayNode(node);
5886
5886
  }
5887
- function collectArrayItemFieldDescriptors(node, prefix = "") {
5887
+ function collectArrayItemFieldDescriptors(node, prefix2 = "") {
5888
5888
  if (isPersistedValueNode(node)) {
5889
5889
  return [
5890
5890
  {
5891
5891
  kind: "path",
5892
- path: prefix,
5892
+ path: prefix2,
5893
5893
  selector: {
5894
5894
  elementPath: cloneElementPath(node.$path),
5895
5895
  attribute: node.attribute
@@ -5901,7 +5901,7 @@ function collectArrayItemFieldDescriptors(node, prefix = "") {
5901
5901
  return [
5902
5902
  {
5903
5903
  kind: "source",
5904
- path: prefix,
5904
+ path: prefix2,
5905
5905
  source: "current_url"
5906
5906
  }
5907
5907
  ];
@@ -5913,7 +5913,7 @@ function collectArrayItemFieldDescriptors(node, prefix = "") {
5913
5913
  }
5914
5914
  const out = [];
5915
5915
  for (const [key, child] of Object.entries(node)) {
5916
- const nextPath = joinDataPath(prefix, key);
5916
+ const nextPath = joinDataPath(prefix2, key);
5917
5917
  out.push(...collectArrayItemFieldDescriptors(child, nextPath));
5918
5918
  }
5919
5919
  return out;
@@ -6973,7 +6973,7 @@ function withTokenQuery(wsUrl, token) {
6973
6973
  var import_fs3 = __toESM(require("fs"), 1);
6974
6974
  var import_path5 = __toESM(require("path"), 1);
6975
6975
  function collectLocalSelectorCacheEntries(storage) {
6976
- const namespace = storage.getNamespace();
6976
+ const namespace2 = storage.getNamespace();
6977
6977
  const namespaceDir = storage.getNamespaceDir();
6978
6978
  if (!import_fs3.default.existsSync(namespaceDir)) return [];
6979
6979
  const entries = [];
@@ -6994,7 +6994,7 @@ function collectLocalSelectorCacheEntries(storage) {
6994
6994
  continue;
6995
6995
  }
6996
6996
  entries.push({
6997
- namespace,
6997
+ namespace: namespace2,
6998
6998
  siteOrigin,
6999
6999
  method,
7000
7000
  descriptionHash,
@@ -7403,6 +7403,9 @@ var Opensteer = class _Opensteer {
7403
7403
  }
7404
7404
  return this.contextRef;
7405
7405
  }
7406
+ getRemoteSessionId() {
7407
+ return this.remote?.sessionId ?? null;
7408
+ }
7406
7409
  async launch(options = {}) {
7407
7410
  if (this.pageRef && !this.ownsBrowser) {
7408
7411
  throw new Error(
@@ -9033,9 +9036,9 @@ var Opensteer = class _Opensteer {
9033
9036
  );
9034
9037
  return fields;
9035
9038
  }
9036
- async collectFieldTargetsFromSchemaObject(obj, prefix, fields) {
9039
+ async collectFieldTargetsFromSchemaObject(obj, prefix2, fields) {
9037
9040
  for (const [key, value] of Object.entries(obj)) {
9038
- const fieldKey = prefix ? `${prefix}.${key}` : key;
9041
+ const fieldKey = prefix2 ? `${prefix2}.${key}` : key;
9039
9042
  await this.collectFieldTargetsFromValue(fieldKey, value, fields);
9040
9043
  }
9041
9044
  }
@@ -9496,19 +9499,16 @@ function getScrollDelta2(options) {
9496
9499
  }
9497
9500
 
9498
9501
  // src/cli/paths.ts
9499
- var import_path6 = require("path");
9500
9502
  var import_os2 = require("os");
9501
- var import_fs4 = require("fs");
9502
- function getRuntimeDir() {
9503
- const dir = (0, import_path6.join)((0, import_os2.homedir)(), ".opensteer");
9504
- (0, import_fs4.mkdirSync)(dir, { recursive: true });
9505
- return dir;
9503
+ var import_path6 = require("path");
9504
+ function prefix(namespace2) {
9505
+ return `opensteer-${namespace2}`;
9506
9506
  }
9507
- function getSocketPath() {
9508
- return (0, import_path6.join)(getRuntimeDir(), "opensteer.sock");
9507
+ function getSocketPath(namespace2) {
9508
+ return (0, import_path6.join)((0, import_os2.tmpdir)(), `${prefix(namespace2)}.sock`);
9509
9509
  }
9510
- function getPidPath() {
9511
- return (0, import_path6.join)(getRuntimeDir(), "opensteer.pid");
9510
+ function getPidPath(namespace2) {
9511
+ return (0, import_path6.join)((0, import_os2.tmpdir)(), `${prefix(namespace2)}.pid`);
9512
9512
  }
9513
9513
 
9514
9514
  // src/cli/commands.ts
@@ -9746,15 +9746,33 @@ function getCommandHandler(name) {
9746
9746
  // src/cli/server.ts
9747
9747
  var instance = null;
9748
9748
  var launchPromise = null;
9749
- var socketPath = getSocketPath();
9750
- var pidPath = getPidPath();
9749
+ function invalidateInstance() {
9750
+ if (!instance) return;
9751
+ instance.close().catch(() => {
9752
+ });
9753
+ instance = null;
9754
+ }
9755
+ function attachLifecycleListeners(inst) {
9756
+ try {
9757
+ inst.page.on("close", invalidateInstance);
9758
+ inst.context.on("close", invalidateInstance);
9759
+ } catch {
9760
+ }
9761
+ }
9762
+ var namespace = process.env.OPENSTEER_NAME?.trim();
9763
+ if (!namespace) {
9764
+ process.stderr.write("Missing OPENSTEER_NAME environment variable.\n");
9765
+ process.exit(1);
9766
+ }
9767
+ var socketPath = getSocketPath(namespace);
9768
+ var pidPath = getPidPath(namespace);
9751
9769
  function cleanup() {
9752
9770
  try {
9753
- (0, import_fs5.unlinkSync)(socketPath);
9771
+ (0, import_fs4.unlinkSync)(socketPath);
9754
9772
  } catch {
9755
9773
  }
9756
9774
  try {
9757
- (0, import_fs5.unlinkSync)(pidPath);
9775
+ (0, import_fs4.unlinkSync)(pidPath);
9758
9776
  } catch {
9759
9777
  }
9760
9778
  }
@@ -9770,13 +9788,21 @@ async function handleRequest(request, socket) {
9770
9788
  try {
9771
9789
  const url = args.url;
9772
9790
  const headless = args.headless;
9773
- const name = args.name;
9774
9791
  const connectUrl = args["connect-url"];
9775
9792
  const channel = args.channel;
9776
9793
  const profileDir = args["profile-dir"];
9794
+ if (instance && !launchPromise) {
9795
+ try {
9796
+ if (instance.page.isClosed()) {
9797
+ invalidateInstance();
9798
+ }
9799
+ } catch {
9800
+ invalidateInstance();
9801
+ }
9802
+ }
9777
9803
  if (!instance) {
9778
9804
  instance = new Opensteer({
9779
- name: name ?? "cli",
9805
+ name: namespace,
9780
9806
  browser: {
9781
9807
  headless: headless ?? false,
9782
9808
  connectUrl,
@@ -9790,6 +9816,7 @@ async function handleRequest(request, socket) {
9790
9816
  });
9791
9817
  try {
9792
9818
  await launchPromise;
9819
+ attachLifecycleListeners(instance);
9793
9820
  } catch (err) {
9794
9821
  instance = null;
9795
9822
  throw err;
@@ -9805,7 +9832,11 @@ async function handleRequest(request, socket) {
9805
9832
  sendResponse(socket, {
9806
9833
  id,
9807
9834
  ok: true,
9808
- result: { url: instance.page.url() }
9835
+ result: {
9836
+ url: instance.page.url(),
9837
+ sessionId: instance.getRemoteSessionId() ?? void 0,
9838
+ name: namespace
9839
+ }
9809
9840
  });
9810
9841
  } catch (err) {
9811
9842
  sendResponse(socket, {
@@ -9822,7 +9853,11 @@ async function handleRequest(request, socket) {
9822
9853
  await instance.close();
9823
9854
  instance = null;
9824
9855
  }
9825
- sendResponse(socket, { id, ok: true, result: {} });
9856
+ sendResponse(socket, {
9857
+ id,
9858
+ ok: true,
9859
+ result: { sessionClosed: true }
9860
+ });
9826
9861
  } catch (err) {
9827
9862
  sendResponse(socket, {
9828
9863
  id,
@@ -9844,7 +9879,7 @@ async function handleRequest(request, socket) {
9844
9879
  sendResponse(socket, {
9845
9880
  id,
9846
9881
  ok: false,
9847
- error: "No browser session. Call 'opensteer open' first."
9882
+ error: `No browser session in namespace '${namespace}'. Call 'opensteer open --name ${namespace}' first, or use 'opensteer sessions' to list active sessions.`
9848
9883
  });
9849
9884
  return;
9850
9885
  }
@@ -9868,8 +9903,8 @@ async function handleRequest(request, socket) {
9868
9903
  });
9869
9904
  }
9870
9905
  }
9871
- if ((0, import_fs5.existsSync)(socketPath)) {
9872
- (0, import_fs5.unlinkSync)(socketPath);
9906
+ if ((0, import_fs4.existsSync)(socketPath)) {
9907
+ (0, import_fs4.unlinkSync)(socketPath);
9873
9908
  }
9874
9909
  var server = (0, import_net.createServer)((socket) => {
9875
9910
  let buffer = "";
@@ -9895,7 +9930,7 @@ var server = (0, import_net.createServer)((socket) => {
9895
9930
  });
9896
9931
  });
9897
9932
  server.listen(socketPath, () => {
9898
- (0, import_fs5.writeFileSync)(pidPath, String(process.pid));
9933
+ (0, import_fs4.writeFileSync)(pidPath, String(process.pid));
9899
9934
  if (process.send) {
9900
9935
  process.send("ready");
9901
9936
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Opensteer
3
- } from "../chunk-6L24FEKD.js";
3
+ } from "../chunk-2NKR4JZ6.js";
4
4
  import "../chunk-3H5RRIMZ.js";
5
5
 
6
6
  // src/cli/server.ts
@@ -8,19 +8,16 @@ import { createServer } from "net";
8
8
  import { writeFileSync, unlinkSync, existsSync } from "fs";
9
9
 
10
10
  // src/cli/paths.ts
11
+ import { tmpdir } from "os";
11
12
  import { join } from "path";
12
- import { homedir } from "os";
13
- import { mkdirSync } from "fs";
14
- function getRuntimeDir() {
15
- const dir = join(homedir(), ".opensteer");
16
- mkdirSync(dir, { recursive: true });
17
- return dir;
13
+ function prefix(namespace2) {
14
+ return `opensteer-${namespace2}`;
18
15
  }
19
- function getSocketPath() {
20
- return join(getRuntimeDir(), "opensteer.sock");
16
+ function getSocketPath(namespace2) {
17
+ return join(tmpdir(), `${prefix(namespace2)}.sock`);
21
18
  }
22
- function getPidPath() {
23
- return join(getRuntimeDir(), "opensteer.pid");
19
+ function getPidPath(namespace2) {
20
+ return join(tmpdir(), `${prefix(namespace2)}.pid`);
24
21
  }
25
22
 
26
23
  // src/cli/commands.ts
@@ -258,8 +255,26 @@ function getCommandHandler(name) {
258
255
  // src/cli/server.ts
259
256
  var instance = null;
260
257
  var launchPromise = null;
261
- var socketPath = getSocketPath();
262
- var pidPath = getPidPath();
258
+ function invalidateInstance() {
259
+ if (!instance) return;
260
+ instance.close().catch(() => {
261
+ });
262
+ instance = null;
263
+ }
264
+ function attachLifecycleListeners(inst) {
265
+ try {
266
+ inst.page.on("close", invalidateInstance);
267
+ inst.context.on("close", invalidateInstance);
268
+ } catch {
269
+ }
270
+ }
271
+ var namespace = process.env.OPENSTEER_NAME?.trim();
272
+ if (!namespace) {
273
+ process.stderr.write("Missing OPENSTEER_NAME environment variable.\n");
274
+ process.exit(1);
275
+ }
276
+ var socketPath = getSocketPath(namespace);
277
+ var pidPath = getPidPath(namespace);
263
278
  function cleanup() {
264
279
  try {
265
280
  unlinkSync(socketPath);
@@ -282,13 +297,21 @@ async function handleRequest(request, socket) {
282
297
  try {
283
298
  const url = args.url;
284
299
  const headless = args.headless;
285
- const name = args.name;
286
300
  const connectUrl = args["connect-url"];
287
301
  const channel = args.channel;
288
302
  const profileDir = args["profile-dir"];
303
+ if (instance && !launchPromise) {
304
+ try {
305
+ if (instance.page.isClosed()) {
306
+ invalidateInstance();
307
+ }
308
+ } catch {
309
+ invalidateInstance();
310
+ }
311
+ }
289
312
  if (!instance) {
290
313
  instance = new Opensteer({
291
- name: name ?? "cli",
314
+ name: namespace,
292
315
  browser: {
293
316
  headless: headless ?? false,
294
317
  connectUrl,
@@ -302,6 +325,7 @@ async function handleRequest(request, socket) {
302
325
  });
303
326
  try {
304
327
  await launchPromise;
328
+ attachLifecycleListeners(instance);
305
329
  } catch (err) {
306
330
  instance = null;
307
331
  throw err;
@@ -317,7 +341,11 @@ async function handleRequest(request, socket) {
317
341
  sendResponse(socket, {
318
342
  id,
319
343
  ok: true,
320
- result: { url: instance.page.url() }
344
+ result: {
345
+ url: instance.page.url(),
346
+ sessionId: instance.getRemoteSessionId() ?? void 0,
347
+ name: namespace
348
+ }
321
349
  });
322
350
  } catch (err) {
323
351
  sendResponse(socket, {
@@ -334,7 +362,11 @@ async function handleRequest(request, socket) {
334
362
  await instance.close();
335
363
  instance = null;
336
364
  }
337
- sendResponse(socket, { id, ok: true, result: {} });
365
+ sendResponse(socket, {
366
+ id,
367
+ ok: true,
368
+ result: { sessionClosed: true }
369
+ });
338
370
  } catch (err) {
339
371
  sendResponse(socket, {
340
372
  id,
@@ -356,7 +388,7 @@ async function handleRequest(request, socket) {
356
388
  sendResponse(socket, {
357
389
  id,
358
390
  ok: false,
359
- error: "No browser session. Call 'opensteer open' first."
391
+ error: `No browser session in namespace '${namespace}'. Call 'opensteer open --name ${namespace}' first, or use 'opensteer sessions' to list active sessions.`
360
392
  });
361
393
  return;
362
394
  }
package/dist/index.cjs CHANGED
@@ -7487,6 +7487,9 @@ var Opensteer = class _Opensteer {
7487
7487
  }
7488
7488
  return this.contextRef;
7489
7489
  }
7490
+ getRemoteSessionId() {
7491
+ return this.remote?.sessionId ?? null;
7492
+ }
7490
7493
  async launch(options = {}) {
7491
7494
  if (this.pageRef && !this.ownsBrowser) {
7492
7495
  throw new Error(
package/dist/index.d.cts CHANGED
@@ -317,6 +317,7 @@ declare class Opensteer {
317
317
  private buildActionError;
318
318
  get page(): Page;
319
319
  get context(): BrowserContext;
320
+ getRemoteSessionId(): string | null;
320
321
  launch(options?: LaunchOptions): Promise<void>;
321
322
  static from(page: Page, config?: OpensteerConfig): Opensteer;
322
323
  close(): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -317,6 +317,7 @@ declare class Opensteer {
317
317
  private buildActionError;
318
318
  get page(): Page;
319
319
  get context(): BrowserContext;
320
+ getRemoteSessionId(): string | null;
320
321
  launch(options?: LaunchOptions): Promise<void>;
321
322
  static from(page: Page, config?: OpensteerConfig): Opensteer;
322
323
  close(): Promise<void>;
package/dist/index.js CHANGED
@@ -67,7 +67,7 @@ import {
67
67
  switchTab,
68
68
  typeText,
69
69
  waitForVisualStability
70
- } from "./chunk-6L24FEKD.js";
70
+ } from "./chunk-2NKR4JZ6.js";
71
71
  import {
72
72
  createResolveCallback
73
73
  } from "./chunk-SRJLH34D.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opensteer",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "packageManager": "pnpm@10.29.3",
5
5
  "description": "Open-source browser automation SDK with robust selectors and deterministic replay.",
6
6
  "license": "MIT",
@@ -26,7 +26,8 @@
26
26
  "CHANGELOG.md"
27
27
  ],
28
28
  "scripts": {
29
- "build": "tsup src/index.ts src/cli/server.ts --dts --format esm,cjs --external ai --external zod --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/google --external @ai-sdk/xai --external @ai-sdk/groq",
29
+ "build": "tsup src/index.ts src/cli/server.ts --dts --format esm,cjs --clean --external ai --external zod --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/google --external @ai-sdk/xai --external @ai-sdk/groq",
30
+ "prepublishOnly": "pnpm run build",
30
31
  "test": "vitest run",
31
32
  "test:live-web": "vitest run --config vitest.live-web.config.ts",
32
33
  "test:unit": "vitest run tests/html tests/element-path tests/config.test.ts tests/storage",