opensteer 0.3.0 → 0.4.0

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/bin/opensteer.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { createHash } from 'crypto'
3
4
  import { spawn } from 'child_process'
4
- import { existsSync, readFileSync, readdirSync, unlinkSync } from 'fs'
5
+ import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from 'fs'
5
6
  import { connect } from 'net'
6
7
  import { tmpdir } from 'os'
7
8
  import { basename, dirname, join } from 'path'
@@ -18,6 +19,15 @@ const SOCKET_SUFFIX = '.sock'
18
19
  const PID_SUFFIX = '.pid'
19
20
  const CLOSE_ALL_REQUEST = { id: 1, command: 'close', args: {} }
20
21
 
22
+ function getVersion() {
23
+ try {
24
+ const pkgPath = join(__dirname, '..', 'package.json')
25
+ return JSON.parse(readFileSync(pkgPath, 'utf-8')).version
26
+ } catch {
27
+ return 'unknown'
28
+ }
29
+ }
30
+
21
31
  function parseArgs(argv) {
22
32
  const args = argv.slice(2)
23
33
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
@@ -25,6 +35,11 @@ function parseArgs(argv) {
25
35
  process.exit(0)
26
36
  }
27
37
 
38
+ if (args[0] === '--version' || args[0] === '-v') {
39
+ console.log(getVersion())
40
+ process.exit(0)
41
+ }
42
+
28
43
  const command = args[0]
29
44
  const flags = {}
30
45
  const positional = []
@@ -69,24 +84,51 @@ function sanitizeNamespace(value) {
69
84
  return bounded || 'default'
70
85
  }
71
86
 
87
+ function getActiveNamespacePath() {
88
+ const hash = createHash('md5').update(process.cwd()).digest('hex').slice(0, 16)
89
+ return join(tmpdir(), `${RUNTIME_PREFIX}active-${hash}`)
90
+ }
91
+
92
+ function readActiveNamespace() {
93
+ try {
94
+ const filePath = getActiveNamespacePath()
95
+ if (!existsSync(filePath)) return null
96
+ const ns = readFileSync(filePath, 'utf-8').trim()
97
+ return ns || null
98
+ } catch {
99
+ return null
100
+ }
101
+ }
102
+
103
+ function writeActiveNamespace(namespace) {
104
+ try {
105
+ writeFileSync(getActiveNamespacePath(), namespace)
106
+ } catch { /* best-effort */ }
107
+ }
108
+
72
109
  function resolveNamespace(flags) {
73
110
  if (flags.name !== undefined && String(flags.name).trim().length > 0) {
74
- return sanitizeNamespace(String(flags.name))
111
+ return { namespace: sanitizeNamespace(String(flags.name)), source: 'flag' }
75
112
  }
76
113
 
77
114
  if (
78
115
  typeof process.env.OPENSTEER_NAME === 'string' &&
79
116
  process.env.OPENSTEER_NAME.trim().length > 0
80
117
  ) {
81
- return sanitizeNamespace(process.env.OPENSTEER_NAME)
118
+ return { namespace: sanitizeNamespace(process.env.OPENSTEER_NAME), source: 'env' }
119
+ }
120
+
121
+ const active = readActiveNamespace()
122
+ if (active && isServerRunning(active)) {
123
+ return { namespace: active, source: 'active' }
82
124
  }
83
125
 
84
126
  const cwdBase = basename(process.cwd())
85
127
  if (cwdBase && cwdBase !== '.' && cwdBase !== '/') {
86
- return sanitizeNamespace(cwdBase)
128
+ return { namespace: sanitizeNamespace(cwdBase), source: 'cwd' }
87
129
  }
88
130
 
89
- return 'default'
131
+ return { namespace: 'default', source: 'default' }
90
132
  }
91
133
 
92
134
  function getSocketPath(namespace) {
@@ -427,6 +469,7 @@ Navigation:
427
469
 
428
470
  Sessions:
429
471
  sessions List active namespace-scoped sessions
472
+ status Show resolved namespace and session state
430
473
 
431
474
  Observation:
432
475
  snapshot [--mode action] Get page snapshot
@@ -481,6 +524,7 @@ Global Flags:
481
524
  --selector <css> Target element by CSS selector
482
525
  --description <text> Description for selector persistence
483
526
  --help Show this help
527
+ --version, -v Show version
484
528
 
485
529
  Environment:
486
530
  OPENSTEER_NAME Default session namespace when --name is omitted
@@ -492,7 +536,7 @@ Environment:
492
536
 
493
537
  async function main() {
494
538
  const { command, flags, positional } = parseArgs(process.argv)
495
- const namespace = resolveNamespace(flags)
539
+ const { namespace, source: namespaceSource } = resolveNamespace(flags)
496
540
  const socketPath = getSocketPath(namespace)
497
541
 
498
542
  if (command === 'sessions') {
@@ -500,6 +544,18 @@ async function main() {
500
544
  return
501
545
  }
502
546
 
547
+ if (command === 'status') {
548
+ output({
549
+ ok: true,
550
+ namespace,
551
+ namespaceSource,
552
+ serverRunning: isServerRunning(namespace),
553
+ socketPath,
554
+ sessions: listSessions(),
555
+ })
556
+ return
557
+ }
558
+
503
559
  if (command === 'close' && flags.all === true) {
504
560
  try {
505
561
  const closed = await closeAllSessions()
@@ -515,6 +571,11 @@ async function main() {
515
571
  const request = buildRequest(command, flags, positional)
516
572
 
517
573
  if (!isServerRunning(namespace)) {
574
+ if (command !== 'open') {
575
+ error(
576
+ `No server running for namespace '${namespace}' (resolved from ${namespaceSource}). Run 'opensteer open' first or use 'opensteer sessions' to see active sessions.`
577
+ )
578
+ }
518
579
  if (!existsSync(SERVER_SCRIPT)) {
519
580
  error(
520
581
  `Server script not found: ${SERVER_SCRIPT}. Run the build script first.`
@@ -532,6 +593,9 @@ async function main() {
532
593
  const response = await sendCommand(socketPath, request)
533
594
 
534
595
  if (response.ok) {
596
+ if (command === 'open') {
597
+ writeActiveNamespace(namespace)
598
+ }
535
599
  output({ ok: true, ...response.result })
536
600
  } else {
537
601
  process.stderr.write(
@@ -9856,7 +9856,7 @@ async function handleRequest(request, socket) {
9856
9856
  sendResponse(socket, {
9857
9857
  id,
9858
9858
  ok: false,
9859
- error: "No browser session. Call 'opensteer open' first."
9859
+ error: `No browser session in namespace '${namespace}'. Call 'opensteer open --name ${namespace}' first, or use 'opensteer sessions' to list active sessions.`
9860
9860
  });
9861
9861
  return;
9862
9862
  }
@@ -365,7 +365,7 @@ async function handleRequest(request, socket) {
365
365
  sendResponse(socket, {
366
366
  id,
367
367
  ok: false,
368
- error: "No browser session. Call 'opensteer open' first."
368
+ error: `No browser session in namespace '${namespace}'. Call 'opensteer open --name ${namespace}' first, or use 'opensteer sessions' to list active sessions.`
369
369
  });
370
370
  return;
371
371
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opensteer",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
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",