opensteer 0.6.0 → 0.6.2

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.
@@ -1,7 +1,11 @@
1
1
  import {
2
- Opensteer,
3
- normalizeError
4
- } from "../chunk-SGZYTGY3.js";
2
+ Opensteer
3
+ } from "../chunk-F2VDVOJO.js";
4
+ import {
5
+ normalizeError,
6
+ resolveCloudSelection,
7
+ resolveConfigWithEnv
8
+ } from "../chunk-WDRMHPWL.js";
5
9
  import "../chunk-3H5RRIMZ.js";
6
10
 
7
11
  // src/cli/server.ts
@@ -57,6 +61,21 @@ var commands = {
57
61
  async state(opensteer) {
58
62
  return await opensteer.state();
59
63
  },
64
+ async cursor(opensteer, args) {
65
+ const mode = typeof args.mode === "string" ? args.mode : "status";
66
+ if (mode === "on") {
67
+ opensteer.setCursorEnabled(true);
68
+ } else if (mode === "off") {
69
+ opensteer.setCursorEnabled(false);
70
+ } else if (mode !== "status") {
71
+ throw new Error(
72
+ `Invalid cursor mode "${String(mode)}". Use "on", "off", or "status".`
73
+ );
74
+ }
75
+ return {
76
+ cursor: opensteer.getCursorState()
77
+ };
78
+ },
60
79
  async screenshot(opensteer, args) {
61
80
  const file = args.file || "screenshot.png";
62
81
  const type = file.endsWith(".jpg") || file.endsWith(".jpeg") ? "jpeg" : "png";
@@ -256,10 +275,175 @@ function getCommandHandler(name) {
256
275
  return commands[name];
257
276
  }
258
277
 
278
+ // src/cli/cloud-profile-binding.ts
279
+ function normalizeCloudProfileBinding(value) {
280
+ if (!value) {
281
+ return null;
282
+ }
283
+ const profileId = typeof value.profileId === "string" ? value.profileId.trim() : "";
284
+ if (!profileId) {
285
+ return null;
286
+ }
287
+ return {
288
+ profileId,
289
+ reuseIfActive: typeof value.reuseIfActive === "boolean" ? value.reuseIfActive : void 0
290
+ };
291
+ }
292
+ function resolveConfiguredCloudProfileBinding(config) {
293
+ if (!isCloudConfigured(config)) {
294
+ return null;
295
+ }
296
+ return normalizeCloudProfileBinding(config.cloud.browserProfile);
297
+ }
298
+ function resolveSessionCloudProfileBinding(config, requested) {
299
+ if (!isCloudConfigured(config)) {
300
+ return null;
301
+ }
302
+ return requested ?? resolveConfiguredCloudProfileBinding(config);
303
+ }
304
+ function assertCompatibleCloudProfileBinding(sessionId, active, requested) {
305
+ if (!requested) {
306
+ return;
307
+ }
308
+ if (!active) {
309
+ throw new Error(
310
+ [
311
+ `Session '${sessionId}' is already running without a bound cloud browser profile.`,
312
+ "Cloud browser profile selection only applies when the session is first opened.",
313
+ "Close this session or use a different --session to target another profile."
314
+ ].join(" ")
315
+ );
316
+ }
317
+ if (active.profileId === requested.profileId && active.reuseIfActive === requested.reuseIfActive) {
318
+ return;
319
+ }
320
+ throw new Error(
321
+ [
322
+ `Session '${sessionId}' is already bound to cloud browser profile ${formatCloudProfileBinding(active)}.`,
323
+ `Requested ${formatCloudProfileBinding(requested)} does not match.`,
324
+ "Use the same cloud profile for this session, or start a different --session."
325
+ ].join(" ")
326
+ );
327
+ }
328
+ function formatCloudProfileBinding(binding) {
329
+ if (binding.reuseIfActive === void 0) {
330
+ return `'${binding.profileId}'`;
331
+ }
332
+ return `'${binding.profileId}' (reuseIfActive=${String(
333
+ binding.reuseIfActive
334
+ )})`;
335
+ }
336
+ function isCloudConfigured(config) {
337
+ return Boolean(
338
+ config.cloud && typeof config.cloud === "object" && !Array.isArray(config.cloud)
339
+ );
340
+ }
341
+
342
+ // src/cli/open-cloud-auth.ts
343
+ function normalizeCliOpenCloudAuth(value) {
344
+ if (value == null) {
345
+ return null;
346
+ }
347
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
348
+ throw new Error("Invalid open request cloud auth payload.");
349
+ }
350
+ const record = value;
351
+ const apiKey = normalizeNonEmptyString(record.apiKey);
352
+ const accessToken = normalizeNonEmptyString(record.accessToken);
353
+ const baseUrl = normalizeNonEmptyString(record.baseUrl);
354
+ const authScheme = normalizeAuthScheme(record.authScheme);
355
+ if (!baseUrl) {
356
+ throw new Error("Open request cloud auth payload is missing baseUrl.");
357
+ }
358
+ if ((apiKey ? 1 : 0) + (accessToken ? 1 : 0) !== 1) {
359
+ throw new Error(
360
+ "Open request cloud auth payload must include exactly one credential."
361
+ );
362
+ }
363
+ if (accessToken && authScheme !== "bearer") {
364
+ throw new Error(
365
+ 'Open request cloud auth payload must use authScheme "bearer" with accessToken.'
366
+ );
367
+ }
368
+ return {
369
+ ...apiKey ? { apiKey } : {},
370
+ ...accessToken ? { accessToken } : {},
371
+ baseUrl,
372
+ authScheme
373
+ };
374
+ }
375
+ function buildServerOpenConfig(options) {
376
+ const config = {
377
+ name: options.name,
378
+ storage: {
379
+ rootDir: options.scopeDir
380
+ },
381
+ cursor: {
382
+ enabled: options.cursorEnabled
383
+ },
384
+ browser: {
385
+ headless: options.headless ?? false,
386
+ connectUrl: options.connectUrl,
387
+ channel: options.channel,
388
+ profileDir: options.profileDir
389
+ }
390
+ };
391
+ if (!options.cloudAuth) {
392
+ return config;
393
+ }
394
+ const resolved = resolveConfigWithEnv(
395
+ {
396
+ storage: {
397
+ rootDir: options.scopeDir
398
+ }
399
+ },
400
+ {
401
+ env: options.env
402
+ }
403
+ );
404
+ const cloudSelection = resolveCloudSelection(
405
+ {
406
+ cloud: resolved.config.cloud
407
+ },
408
+ resolved.env
409
+ );
410
+ if (!cloudSelection.cloud) {
411
+ return config;
412
+ }
413
+ config.cloud = toOpensteerCloudOptions(options.cloudAuth);
414
+ return config;
415
+ }
416
+ function toOpensteerCloudOptions(auth) {
417
+ return {
418
+ ...auth.apiKey ? { apiKey: auth.apiKey } : {},
419
+ ...auth.accessToken ? { accessToken: auth.accessToken } : {},
420
+ baseUrl: auth.baseUrl,
421
+ authScheme: auth.authScheme
422
+ };
423
+ }
424
+ function normalizeNonEmptyString(value) {
425
+ if (typeof value !== "string") {
426
+ return void 0;
427
+ }
428
+ const trimmed = value.trim();
429
+ return trimmed.length > 0 ? trimmed : void 0;
430
+ }
431
+ function normalizeAuthScheme(value) {
432
+ if (value === "api-key" || value === "bearer") {
433
+ return value;
434
+ }
435
+ throw new Error(
436
+ 'Open request cloud auth payload must use authScheme "api-key" or "bearer".'
437
+ );
438
+ }
439
+
259
440
  // src/cli/server.ts
260
441
  var instance = null;
261
442
  var launchPromise = null;
262
443
  var selectorNamespace = null;
444
+ var cloudProfileBinding = null;
445
+ var cloudAuthOverride = null;
446
+ var cursorEnabledPreference = readCursorPreferenceFromEnv();
263
447
  var requestQueue = Promise.resolve();
264
448
  var shuttingDown = false;
265
449
  function sanitizeNamespace(value) {
@@ -277,6 +461,36 @@ function invalidateInstance() {
277
461
  instance.close().catch(() => {
278
462
  });
279
463
  instance = null;
464
+ cloudProfileBinding = null;
465
+ }
466
+ function normalizeCursorFlag(value) {
467
+ if (value === void 0 || value === null) {
468
+ return null;
469
+ }
470
+ if (typeof value === "boolean") {
471
+ return value;
472
+ }
473
+ if (typeof value === "number") {
474
+ if (value === 1) return true;
475
+ if (value === 0) return false;
476
+ }
477
+ throw new Error(
478
+ '--cursor must be a boolean value ("true" or "false").'
479
+ );
480
+ }
481
+ function readCursorPreferenceFromEnv() {
482
+ const value = process.env.OPENSTEER_CURSOR;
483
+ if (typeof value !== "string") {
484
+ return null;
485
+ }
486
+ const normalized = value.trim().toLowerCase();
487
+ if (normalized === "true" || normalized === "1") {
488
+ return true;
489
+ }
490
+ if (normalized === "false" || normalized === "0") {
491
+ return false;
492
+ }
493
+ return null;
280
494
  }
281
495
  function attachLifecycleListeners(inst) {
282
496
  try {
@@ -386,7 +600,26 @@ async function handleRequest(request, socket) {
386
600
  const connectUrl = args["connect-url"];
387
601
  const channel = args.channel;
388
602
  const profileDir = args["profile-dir"];
603
+ const cloudProfileId = typeof args["cloud-profile-id"] === "string" ? args["cloud-profile-id"].trim() : void 0;
604
+ const cloudProfileReuseIfActive = typeof args["cloud-profile-reuse-if-active"] === "boolean" ? args["cloud-profile-reuse-if-active"] : void 0;
605
+ const requestedCloudProfileBinding = normalizeCloudProfileBinding({
606
+ profileId: cloudProfileId,
607
+ reuseIfActive: cloudProfileReuseIfActive
608
+ });
609
+ const requestedCloudAuth = normalizeCliOpenCloudAuth(
610
+ args["cloud-auth"]
611
+ );
612
+ if (cloudProfileReuseIfActive !== void 0 && !cloudProfileId) {
613
+ throw new Error(
614
+ "--cloud-profile-reuse-if-active requires --cloud-profile-id."
615
+ );
616
+ }
617
+ const requestedCursor = normalizeCursorFlag(args.cursor);
389
618
  const requestedName = typeof args.name === "string" && args.name.trim().length > 0 ? sanitizeNamespace(args.name) : null;
619
+ if (requestedCursor !== null) {
620
+ cursorEnabledPreference = requestedCursor;
621
+ }
622
+ const effectiveCursorEnabled = cursorEnabledPreference !== null ? cursorEnabledPreference : true;
390
623
  if (selectorNamespace && requestedName && requestedName !== selectorNamespace) {
391
624
  sendResponse(socket, {
392
625
  id,
@@ -410,6 +643,9 @@ async function handleRequest(request, socket) {
410
643
  selectorNamespace = requestedName ?? logicalSession;
411
644
  }
412
645
  const activeNamespace = selectorNamespace ?? logicalSession;
646
+ if (requestedCloudAuth) {
647
+ cloudAuthOverride = requestedCloudAuth;
648
+ }
413
649
  if (instance && !launchPromise) {
414
650
  try {
415
651
  if (instance.page.isClosed()) {
@@ -419,31 +655,59 @@ async function handleRequest(request, socket) {
419
655
  invalidateInstance();
420
656
  }
421
657
  }
658
+ if (instance && !launchPromise) {
659
+ assertCompatibleCloudProfileBinding(
660
+ logicalSession,
661
+ cloudProfileBinding,
662
+ requestedCloudProfileBinding
663
+ );
664
+ }
422
665
  if (!instance) {
423
- instance = new Opensteer({
424
- name: activeNamespace,
425
- browser: {
426
- headless: headless ?? false,
666
+ instance = new Opensteer(
667
+ buildServerOpenConfig({
668
+ scopeDir,
669
+ name: activeNamespace,
670
+ cursorEnabled: effectiveCursorEnabled,
671
+ headless,
427
672
  connectUrl,
428
673
  channel,
429
- profileDir
430
- }
431
- });
674
+ profileDir,
675
+ cloudAuth: cloudAuthOverride
676
+ })
677
+ );
678
+ const nextCloudProfileBinding = resolveSessionCloudProfileBinding(
679
+ instance.getConfig(),
680
+ requestedCloudProfileBinding
681
+ );
682
+ if (requestedCloudProfileBinding && !nextCloudProfileBinding) {
683
+ instance = null;
684
+ throw new Error(
685
+ "--cloud-profile-id can only be used when cloud mode is enabled for this session."
686
+ );
687
+ }
432
688
  launchPromise = instance.launch({
433
689
  headless: headless ?? false,
690
+ cloudBrowserProfile: cloudProfileId ? {
691
+ profileId: cloudProfileId,
692
+ reuseIfActive: cloudProfileReuseIfActive
693
+ } : void 0,
434
694
  timeout: connectUrl ? 12e4 : 3e4
435
695
  });
436
696
  try {
437
697
  await launchPromise;
438
698
  attachLifecycleListeners(instance);
699
+ cloudProfileBinding = nextCloudProfileBinding;
439
700
  } catch (err) {
440
701
  instance = null;
702
+ cloudProfileBinding = null;
441
703
  throw err;
442
704
  } finally {
443
705
  launchPromise = null;
444
706
  }
445
707
  } else if (launchPromise) {
446
708
  await launchPromise;
709
+ } else if (requestedCursor !== null) {
710
+ instance.setCursorEnabled(requestedCursor);
447
711
  }
448
712
  if (url) {
449
713
  await instance.goto(url);
@@ -458,6 +722,7 @@ async function handleRequest(request, socket) {
458
722
  runtimeSession: session,
459
723
  scopeDir,
460
724
  name: activeNamespace,
725
+ cursor: instance.getCursorState(),
461
726
  cloudSessionId: instance.getCloudSessionId() ?? void 0,
462
727
  cloudSessionUrl: instance.getCloudSessionUrl() ?? void 0
463
728
  }
@@ -470,6 +735,41 @@ async function handleRequest(request, socket) {
470
735
  }
471
736
  return;
472
737
  }
738
+ if (command === "cursor") {
739
+ try {
740
+ const mode = typeof args.mode === "string" ? args.mode : "status";
741
+ if (mode === "on") {
742
+ cursorEnabledPreference = true;
743
+ instance?.setCursorEnabled(true);
744
+ } else if (mode === "off") {
745
+ cursorEnabledPreference = false;
746
+ instance?.setCursorEnabled(false);
747
+ } else if (mode !== "status") {
748
+ throw new Error(
749
+ `Invalid cursor mode "${mode}". Use "on", "off", or "status".`
750
+ );
751
+ }
752
+ const defaultEnabled = cursorEnabledPreference !== null ? cursorEnabledPreference : true;
753
+ const cursor = instance ? instance.getCursorState() : {
754
+ enabled: defaultEnabled,
755
+ active: false,
756
+ reason: "session_not_open"
757
+ };
758
+ sendResponse(socket, {
759
+ id,
760
+ ok: true,
761
+ result: {
762
+ cursor
763
+ }
764
+ });
765
+ } catch (err) {
766
+ sendResponse(
767
+ socket,
768
+ buildErrorResponse(id, err, "Failed to update cursor mode.")
769
+ );
770
+ }
771
+ return;
772
+ }
473
773
  if (command === "close") {
474
774
  try {
475
775
  if (instance) {