opensteer 0.6.2 → 0.6.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.
Files changed (33) hide show
  1. package/bin/opensteer.mjs +94 -8
  2. package/dist/{browser-profile-client-DK9qa_Dj.d.cts → browser-profile-client-D6PuRefA.d.cts} +1 -1
  3. package/dist/{browser-profile-client-CaL-mwqs.d.ts → browser-profile-client-OUHaODro.d.ts} +1 -1
  4. package/dist/{chunk-7RMY26CM.js → chunk-54KNQTOL.js} +172 -55
  5. package/dist/{chunk-WJI7TGBQ.js → chunk-6B6LOYU3.js} +1 -1
  6. package/dist/{chunk-F2VDVOJO.js → chunk-G6V2DJRN.js} +451 -609
  7. package/dist/chunk-K5CL76MG.js +81 -0
  8. package/dist/{chunk-WDRMHPWL.js → chunk-KPPOTU3D.js} +159 -180
  9. package/dist/cli/auth.cjs +186 -95
  10. package/dist/cli/auth.d.cts +1 -1
  11. package/dist/cli/auth.d.ts +1 -1
  12. package/dist/cli/auth.js +2 -2
  13. package/dist/cli/local-profile.cjs +197 -0
  14. package/dist/cli/local-profile.d.cts +18 -0
  15. package/dist/cli/local-profile.d.ts +18 -0
  16. package/dist/cli/local-profile.js +97 -0
  17. package/dist/cli/profile.cjs +1747 -1279
  18. package/dist/cli/profile.d.cts +2 -2
  19. package/dist/cli/profile.d.ts +2 -2
  20. package/dist/cli/profile.js +469 -7
  21. package/dist/cli/server.cjs +759 -257
  22. package/dist/cli/server.js +69 -16
  23. package/dist/index.cjs +688 -238
  24. package/dist/index.d.cts +7 -5
  25. package/dist/index.d.ts +7 -5
  26. package/dist/index.js +4 -3
  27. package/dist/{types-BxiRblC7.d.cts → types-BWItZPl_.d.cts} +31 -13
  28. package/dist/{types-BxiRblC7.d.ts → types-BWItZPl_.d.ts} +31 -13
  29. package/package.json +2 -2
  30. package/skills/opensteer/SKILL.md +34 -14
  31. package/skills/opensteer/references/cli-reference.md +1 -1
  32. package/skills/opensteer/references/examples.md +5 -3
  33. package/skills/opensteer/references/sdk-reference.md +16 -14
package/dist/cli/auth.cjs CHANGED
@@ -94,15 +94,75 @@ function toNonEmptyString(value) {
94
94
  return normalized.length ? normalized : void 0;
95
95
  }
96
96
 
97
+ // src/cloud/credential-selection.ts
98
+ function selectCloudCredential(options) {
99
+ const apiKey = normalizeNonEmptyString(options.apiKey);
100
+ const accessToken = normalizeNonEmptyString(options.accessToken);
101
+ if (apiKey) {
102
+ if (options.authScheme === "bearer") {
103
+ return {
104
+ apiKey,
105
+ authScheme: "bearer",
106
+ kind: "access-token",
107
+ token: apiKey,
108
+ compatibilityBearerApiKey: true
109
+ };
110
+ }
111
+ return {
112
+ apiKey,
113
+ authScheme: "api-key",
114
+ kind: "api-key",
115
+ token: apiKey
116
+ };
117
+ }
118
+ if (accessToken) {
119
+ return {
120
+ accessToken,
121
+ authScheme: "bearer",
122
+ kind: "access-token",
123
+ token: accessToken
124
+ };
125
+ }
126
+ return null;
127
+ }
128
+ function selectCloudCredentialByPrecedence(layers, authScheme) {
129
+ for (const layer of layers) {
130
+ const hasApiKey = layer.hasApiKey ?? Object.prototype.hasOwnProperty.call(layer, "apiKey");
131
+ const hasAccessToken = layer.hasAccessToken ?? Object.prototype.hasOwnProperty.call(layer, "accessToken");
132
+ if (!hasApiKey && !hasAccessToken) {
133
+ continue;
134
+ }
135
+ return {
136
+ source: layer.source,
137
+ apiKey: layer.apiKey,
138
+ accessToken: layer.accessToken,
139
+ hasApiKey,
140
+ hasAccessToken,
141
+ credential: selectCloudCredential({
142
+ apiKey: layer.apiKey,
143
+ accessToken: layer.accessToken,
144
+ authScheme: layer.authScheme ?? authScheme
145
+ })
146
+ };
147
+ }
148
+ return null;
149
+ }
150
+ function normalizeNonEmptyString(value) {
151
+ if (typeof value !== "string") return void 0;
152
+ const normalized = value.trim();
153
+ return normalized.length ? normalized : void 0;
154
+ }
155
+
97
156
  // src/config.ts
98
157
  var DEFAULT_CONFIG = {
99
158
  browser: {
100
159
  headless: false,
101
160
  executablePath: void 0,
102
161
  slowMo: 0,
103
- connectUrl: void 0,
104
- channel: void 0,
105
- profileDir: void 0
162
+ mode: void 0,
163
+ cdpUrl: void 0,
164
+ userDataDir: void 0,
165
+ profileDirectory: void 0
106
166
  },
107
167
  storage: {
108
168
  rootDir: process.cwd()
@@ -386,11 +446,6 @@ function normalizeCloudOptions(value) {
386
446
  }
387
447
  return value;
388
448
  }
389
- function normalizeNonEmptyString(value) {
390
- if (typeof value !== "string") return void 0;
391
- const normalized = value.trim();
392
- return normalized.length ? normalized : void 0;
393
- }
394
449
  function parseCloudEnabled(value, source) {
395
450
  if (value == null) return void 0;
396
451
  if (typeof value === "boolean") return value;
@@ -399,6 +454,18 @@ function parseCloudEnabled(value, source) {
399
454
  `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
400
455
  );
401
456
  }
457
+ function resolveCloudCredentialFields(selectedLayer) {
458
+ const credential = selectedLayer?.credential;
459
+ if (!credential) return {};
460
+ if (credential.kind === "api-key" || credential.compatibilityBearerApiKey === true && selectedLayer?.source !== "env") {
461
+ return {
462
+ apiKey: credential.token
463
+ };
464
+ }
465
+ return {
466
+ accessToken: credential.token
467
+ };
468
+ }
402
469
  function resolveCloudSelection(config, env = process.env) {
403
470
  const configCloud = parseCloudEnabled(config.cloud, "cloud");
404
471
  if (configCloud !== void 0) {
@@ -435,7 +502,12 @@ function resolveConfigWithEnv(input = {}, options = {}) {
435
502
  });
436
503
  assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
437
504
  assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
505
+ const fileCloudOptions = normalizeCloudOptions(fileConfig.cloud);
506
+ const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
507
+ const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
438
508
  const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
509
+ assertNoRemovedBrowserConfig(input.browser, "Opensteer constructor config");
510
+ assertNoRemovedBrowserConfig(fileConfig.browser, ".opensteer/config.json");
439
511
  const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
440
512
  const env = resolveEnv(envRootDir, {
441
513
  debug: debugHint,
@@ -451,14 +523,30 @@ function resolveConfigWithEnv(input = {}, options = {}) {
451
523
  "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
452
524
  );
453
525
  }
526
+ if (env.OPENSTEER_CONNECT_URL != null) {
527
+ throw new Error(
528
+ "OPENSTEER_CONNECT_URL is no longer supported. Use OPENSTEER_CDP_URL instead."
529
+ );
530
+ }
531
+ if (env.OPENSTEER_CHANNEL != null) {
532
+ throw new Error(
533
+ "OPENSTEER_CHANNEL is no longer supported. Use OPENSTEER_BROWSER plus OPENSTEER_BROWSER_PATH when needed."
534
+ );
535
+ }
536
+ if (env.OPENSTEER_PROFILE_DIR != null) {
537
+ throw new Error(
538
+ "OPENSTEER_PROFILE_DIR is no longer supported. Use OPENSTEER_USER_DATA_DIR and OPENSTEER_PROFILE_DIRECTORY instead."
539
+ );
540
+ }
454
541
  const envConfig = {
455
542
  browser: {
456
543
  headless: parseBool(env.OPENSTEER_HEADLESS),
457
544
  executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
458
545
  slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
459
- connectUrl: env.OPENSTEER_CONNECT_URL || void 0,
460
- channel: env.OPENSTEER_CHANNEL || void 0,
461
- profileDir: env.OPENSTEER_PROFILE_DIR || void 0
546
+ mode: env.OPENSTEER_BROWSER === "real" || env.OPENSTEER_BROWSER === "chromium" ? env.OPENSTEER_BROWSER : void 0,
547
+ cdpUrl: env.OPENSTEER_CDP_URL || void 0,
548
+ userDataDir: env.OPENSTEER_USER_DATA_DIR || void 0,
549
+ profileDirectory: env.OPENSTEER_PROFILE_DIRECTORY || void 0
462
550
  },
463
551
  cursor: {
464
552
  enabled: parseBool(env.OPENSTEER_CURSOR)
@@ -469,17 +557,38 @@ function resolveConfigWithEnv(input = {}, options = {}) {
469
557
  const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
470
558
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
471
559
  const resolved = mergeDeep(mergedWithEnv, input);
560
+ const browserHeadlessExplicit = input.browser?.headless !== void 0 || fileConfig.browser?.headless !== void 0 || envConfig.browser?.headless !== void 0;
561
+ if (!browserHeadlessExplicit && resolved.browser?.mode === "real") {
562
+ resolved.browser = {
563
+ ...resolved.browser,
564
+ headless: true
565
+ };
566
+ }
567
+ function assertNoRemovedBrowserConfig(value, source) {
568
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
569
+ return;
570
+ }
571
+ const record = value;
572
+ if (record.connectUrl !== void 0) {
573
+ throw new Error(
574
+ `${source}.browser.connectUrl is no longer supported. Use browser.cdpUrl instead.`
575
+ );
576
+ }
577
+ if (record.channel !== void 0) {
578
+ throw new Error(
579
+ `${source}.browser.channel is no longer supported. Use browser.mode plus browser.executablePath instead.`
580
+ );
581
+ }
582
+ if (record.profileDir !== void 0) {
583
+ throw new Error(
584
+ `${source}.browser.profileDir is no longer supported. Use browser.userDataDir and browser.profileDirectory instead.`
585
+ );
586
+ }
587
+ }
472
588
  const envApiKey = resolveOpensteerApiKey(env);
473
589
  const envAccessTokenRaw = resolveOpensteerAccessToken(env);
474
590
  const envBaseUrl = resolveOpensteerBaseUrl(env);
475
591
  const envAuthScheme = resolveOpensteerAuthScheme(env);
476
- if (envApiKey && envAccessTokenRaw) {
477
- throw new Error(
478
- "OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
479
- );
480
- }
481
- const envAccessToken = envAccessTokenRaw || (envAuthScheme === "bearer" ? envApiKey : void 0);
482
- const envApiCredential = envAuthScheme === "bearer" && !envAccessTokenRaw ? void 0 : envApiKey;
483
592
  const envCloudProfileId = resolveOpensteerCloudProfileId(env);
484
593
  const envCloudProfileReuseIfActive = resolveOpensteerCloudProfileReuseIfActive(env);
485
594
  const envCloudAnnounce = parseCloudAnnounce(
@@ -508,11 +617,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
508
617
  const inputHasCloudBaseUrl = Boolean(
509
618
  inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
510
619
  );
511
- if (normalizeNonEmptyString(inputCloudOptions?.apiKey) && normalizeNonEmptyString(inputCloudOptions?.accessToken)) {
512
- throw new Error(
513
- "cloud.apiKey and cloud.accessToken are mutually exclusive. Set only one."
514
- );
515
- }
516
620
  const cloudSelection = resolveCloudSelection({
517
621
  cloud: resolved.cloud
518
622
  }, env);
@@ -523,11 +627,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
523
627
  accessToken: resolvedCloudAccessTokenRaw,
524
628
  ...resolvedCloudRest
525
629
  } = resolvedCloud;
526
- if (normalizeNonEmptyString(resolvedCloudApiKeyRaw) && normalizeNonEmptyString(resolvedCloudAccessTokenRaw)) {
527
- throw new Error(
528
- "Cloud config cannot include both apiKey and accessToken at the same time."
529
- );
530
- }
531
630
  const resolvedCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
532
631
  resolvedCloud.browserProfile,
533
632
  "resolved.cloud.browserProfile"
@@ -539,25 +638,40 @@ function resolveConfigWithEnv(input = {}, options = {}) {
539
638
  const browserProfile = inputCloudBrowserProfile ?? envCloudBrowserProfile ?? resolvedCloudBrowserProfile;
540
639
  let authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
541
640
  const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
542
- const credentialOverriddenByInput = inputHasCloudApiKey || inputHasCloudAccessToken;
543
- let apiKey = normalizeNonEmptyString(resolvedCloudApiKeyRaw);
544
- let accessToken = normalizeNonEmptyString(resolvedCloudAccessTokenRaw);
545
- if (!credentialOverriddenByInput) {
546
- if (envAccessToken) {
547
- accessToken = envAccessToken;
548
- apiKey = void 0;
549
- } else if (envApiCredential) {
550
- apiKey = envApiCredential;
551
- accessToken = void 0;
552
- }
553
- }
641
+ const selectedCredentialLayer = selectCloudCredentialByPrecedence(
642
+ [
643
+ {
644
+ source: "input",
645
+ apiKey: inputCloudOptions?.apiKey,
646
+ accessToken: inputCloudOptions?.accessToken,
647
+ hasApiKey: inputHasCloudApiKey,
648
+ hasAccessToken: inputHasCloudAccessToken
649
+ },
650
+ {
651
+ source: "env",
652
+ apiKey: envApiKey,
653
+ accessToken: envAccessTokenRaw,
654
+ hasApiKey: envApiKey !== void 0,
655
+ hasAccessToken: envAccessTokenRaw !== void 0
656
+ },
657
+ {
658
+ source: "file",
659
+ apiKey: fileCloudOptions?.apiKey,
660
+ accessToken: fileCloudOptions?.accessToken,
661
+ hasApiKey: fileHasCloudApiKey,
662
+ hasAccessToken: fileHasCloudAccessToken
663
+ }
664
+ ],
665
+ authScheme
666
+ );
667
+ const { apiKey, accessToken } = resolveCloudCredentialFields(selectedCredentialLayer);
554
668
  if (accessToken) {
555
669
  authScheme = "bearer";
556
670
  }
557
671
  resolved.cloud = {
558
672
  ...resolvedCloudRest,
559
- ...inputHasCloudApiKey ? { apiKey: resolvedCloudApiKeyRaw } : apiKey ? { apiKey } : {},
560
- ...inputHasCloudAccessToken ? { accessToken: resolvedCloudAccessTokenRaw } : accessToken ? { accessToken } : {},
673
+ ...apiKey ? { apiKey } : selectedCredentialLayer?.hasApiKey && !accessToken ? { apiKey: selectedCredentialLayer.apiKey } : {},
674
+ ...accessToken ? { accessToken } : selectedCredentialLayer?.hasAccessToken && !apiKey ? { accessToken: selectedCredentialLayer.accessToken } : {},
561
675
  authScheme,
562
676
  announce,
563
677
  ...browserProfile ? { browserProfile } : {}
@@ -577,59 +691,21 @@ function resolveConfigWithEnv(input = {}, options = {}) {
577
691
 
578
692
  // src/auth/credential-resolver.ts
579
693
  function resolveCloudCredential(options) {
580
- const flagApiKey = normalizeToken(options.apiKeyFlag);
581
- const flagAccessToken = normalizeToken(options.accessTokenFlag);
582
- if (flagApiKey && flagAccessToken) {
583
- throw new Error("--api-key and --access-token are mutually exclusive.");
584
- }
585
- if (flagAccessToken) {
586
- return {
587
- kind: "access-token",
588
- source: "flag",
589
- token: flagAccessToken,
590
- authScheme: "bearer"
591
- };
592
- }
593
- if (flagApiKey) {
594
- return {
595
- kind: "api-key",
596
- source: "flag",
597
- token: flagApiKey,
598
- authScheme: "api-key"
599
- };
694
+ const flagCredential = selectCloudCredential({
695
+ apiKey: options.apiKeyFlag,
696
+ accessToken: options.accessTokenFlag
697
+ });
698
+ if (flagCredential) {
699
+ return toResolvedCloudCredential("flag", flagCredential);
600
700
  }
601
701
  const envAuthScheme = parseEnvAuthScheme(options.env.OPENSTEER_AUTH_SCHEME);
602
- const envApiKey = normalizeToken(options.env.OPENSTEER_API_KEY);
603
- const envAccessToken = normalizeToken(options.env.OPENSTEER_ACCESS_TOKEN);
604
- if (envApiKey && envAccessToken) {
605
- throw new Error(
606
- "OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
607
- );
608
- }
609
- if (envAccessToken) {
610
- return {
611
- kind: "access-token",
612
- source: "env",
613
- token: envAccessToken,
614
- authScheme: "bearer"
615
- };
616
- }
617
- if (envApiKey) {
618
- if (envAuthScheme === "bearer") {
619
- return {
620
- kind: "access-token",
621
- source: "env",
622
- token: envApiKey,
623
- authScheme: "bearer",
624
- compatibilityBearerApiKey: true
625
- };
626
- }
627
- return {
628
- kind: "api-key",
629
- source: "env",
630
- token: envApiKey,
631
- authScheme: envAuthScheme ?? "api-key"
632
- };
702
+ const envCredential = selectCloudCredential({
703
+ apiKey: options.env.OPENSTEER_API_KEY,
704
+ accessToken: options.env.OPENSTEER_ACCESS_TOKEN,
705
+ authScheme: envAuthScheme
706
+ });
707
+ if (envCredential) {
708
+ return toResolvedCloudCredential("env", envCredential);
633
709
  }
634
710
  return null;
635
711
  }
@@ -659,6 +735,23 @@ function normalizeToken(value) {
659
735
  const normalized = value.trim();
660
736
  return normalized.length ? normalized : void 0;
661
737
  }
738
+ function toResolvedCloudCredential(source, credential) {
739
+ if (credential.compatibilityBearerApiKey) {
740
+ return {
741
+ kind: credential.kind,
742
+ source,
743
+ token: credential.token,
744
+ authScheme: credential.authScheme,
745
+ compatibilityBearerApiKey: true
746
+ };
747
+ }
748
+ return {
749
+ kind: credential.kind,
750
+ source,
751
+ token: credential.token,
752
+ authScheme: credential.authScheme
753
+ };
754
+ }
662
755
 
663
756
  // src/auth/machine-credential-store.ts
664
757
  var import_node_crypto = require("crypto");
@@ -1729,8 +1822,6 @@ async function runLogin(args, deps) {
1729
1822
  return 0;
1730
1823
  }
1731
1824
  writeHumanLine(deps, "Opensteer CLI login successful.");
1732
- writeHumanLine(deps, ` API Base URL: ${baseUrl}`);
1733
- writeHumanLine(deps, ` Expires At: ${new Date(login.expiresAt).toISOString()}`);
1734
1825
  return 0;
1735
1826
  }
1736
1827
  async function runStatus(args, deps) {
@@ -1,4 +1,4 @@
1
- import { O as OpensteerAuthScheme } from '../types-BxiRblC7.cjs';
1
+ import { O as OpensteerAuthScheme } from '../types-BWItZPl_.cjs';
2
2
  import 'playwright';
3
3
 
4
4
  interface StoredMachineCloudCredential {
@@ -1,4 +1,4 @@
1
- import { O as OpensteerAuthScheme } from '../types-BxiRblC7.js';
1
+ import { O as OpensteerAuthScheme } from '../types-BWItZPl_.js';
2
2
  import 'playwright';
3
3
 
4
4
  interface StoredMachineCloudCredential {
package/dist/cli/auth.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  isCloudModeEnabledForRootDir,
5
5
  parseOpensteerAuthArgs,
6
6
  runOpensteerAuthCli
7
- } from "../chunk-7RMY26CM.js";
8
- import "../chunk-WDRMHPWL.js";
7
+ } from "../chunk-54KNQTOL.js";
8
+ import "../chunk-KPPOTU3D.js";
9
9
  export {
10
10
  ensureCloudCredentialsForCommand,
11
11
  ensureCloudCredentialsForOpenCommand,
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/cli/local-profile.ts
21
+ var local_profile_exports = {};
22
+ __export(local_profile_exports, {
23
+ parseOpensteerLocalProfileArgs: () => parseOpensteerLocalProfileArgs,
24
+ runOpensteerLocalProfileCli: () => runOpensteerLocalProfileCli
25
+ });
26
+ module.exports = __toCommonJS(local_profile_exports);
27
+
28
+ // src/browser/chrome.ts
29
+ var import_os = require("os");
30
+ var import_path = require("path");
31
+ var import_fs = require("fs");
32
+ function detectChromePaths() {
33
+ const os = (0, import_os.platform)();
34
+ if (os === "darwin") {
35
+ const executable2 = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
36
+ return {
37
+ executable: (0, import_fs.existsSync)(executable2) ? executable2 : null,
38
+ defaultUserDataDir: (0, import_path.join)(
39
+ (0, import_os.homedir)(),
40
+ "Library",
41
+ "Application Support",
42
+ "Google",
43
+ "Chrome"
44
+ )
45
+ };
46
+ }
47
+ if (os === "win32") {
48
+ const executable2 = (0, import_path.join)(
49
+ process.env.PROGRAMFILES || "C:\\Program Files",
50
+ "Google",
51
+ "Chrome",
52
+ "Application",
53
+ "chrome.exe"
54
+ );
55
+ return {
56
+ executable: (0, import_fs.existsSync)(executable2) ? executable2 : null,
57
+ defaultUserDataDir: (0, import_path.join)(
58
+ process.env.LOCALAPPDATA || (0, import_path.join)((0, import_os.homedir)(), "AppData", "Local"),
59
+ "Google",
60
+ "Chrome",
61
+ "User Data"
62
+ )
63
+ };
64
+ }
65
+ const executable = "/usr/bin/google-chrome";
66
+ return {
67
+ executable: (0, import_fs.existsSync)(executable) ? executable : null,
68
+ defaultUserDataDir: (0, import_path.join)((0, import_os.homedir)(), ".config", "google-chrome")
69
+ };
70
+ }
71
+ function expandHome(p) {
72
+ if (p.startsWith("~/") || p === "~") {
73
+ return (0, import_path.join)((0, import_os.homedir)(), p.slice(1));
74
+ }
75
+ return p;
76
+ }
77
+ function listLocalChromeProfiles(userDataDir = detectChromePaths().defaultUserDataDir) {
78
+ const resolvedUserDataDir = expandHome(userDataDir);
79
+ const localStatePath = (0, import_path.join)(resolvedUserDataDir, "Local State");
80
+ if (!(0, import_fs.existsSync)(localStatePath)) {
81
+ return [];
82
+ }
83
+ try {
84
+ const raw = JSON.parse((0, import_fs.readFileSync)(localStatePath, "utf-8"));
85
+ const infoCache = raw && typeof raw === "object" && !Array.isArray(raw) && raw.profile && typeof raw.profile === "object" && !Array.isArray(raw.profile) ? raw.profile.info_cache : void 0;
86
+ if (!infoCache || typeof infoCache !== "object") {
87
+ return [];
88
+ }
89
+ return Object.entries(infoCache).map(([directory, info]) => {
90
+ const record = info && typeof info === "object" && !Array.isArray(info) ? info : {};
91
+ const name = typeof record.name === "string" && record.name.trim() ? record.name.trim() : directory;
92
+ return {
93
+ directory,
94
+ name
95
+ };
96
+ }).filter((profile) => profile.directory.trim().length > 0).sort(
97
+ (left, right) => left.directory.localeCompare(right.directory)
98
+ );
99
+ } catch {
100
+ return [];
101
+ }
102
+ }
103
+
104
+ // src/cli/local-profile.ts
105
+ var HELP_TEXT = `Usage: opensteer local-profile <command> [options]
106
+
107
+ Inspect local Chrome profiles for real-browser mode.
108
+
109
+ Commands:
110
+ list List available local Chrome profiles
111
+
112
+ Options:
113
+ --json JSON output
114
+ --user-data-dir <path> Override Chrome user-data root
115
+ -h, --help Show this help
116
+ `;
117
+ function parseOpensteerLocalProfileArgs(argv) {
118
+ const [command, ...rest] = argv;
119
+ if (!command || command === "help" || command === "--help" || command === "-h") {
120
+ return { mode: "help" };
121
+ }
122
+ if (command !== "list") {
123
+ return {
124
+ mode: "error",
125
+ error: `Unsupported local-profile command "${command}".`
126
+ };
127
+ }
128
+ let json = false;
129
+ let userDataDir;
130
+ for (let index = 0; index < rest.length; index += 1) {
131
+ const arg = rest[index];
132
+ if (arg === "--json") {
133
+ json = true;
134
+ continue;
135
+ }
136
+ if (arg === "--help" || arg === "-h") {
137
+ return { mode: "help" };
138
+ }
139
+ if (arg === "--user-data-dir") {
140
+ const value = rest[index + 1];
141
+ if (!value) {
142
+ return {
143
+ mode: "error",
144
+ error: "--user-data-dir requires a path value."
145
+ };
146
+ }
147
+ userDataDir = value;
148
+ index += 1;
149
+ continue;
150
+ }
151
+ return {
152
+ mode: "error",
153
+ error: `Unsupported option "${arg}" for "opensteer local-profile list".`
154
+ };
155
+ }
156
+ return {
157
+ mode: "list",
158
+ json,
159
+ userDataDir
160
+ };
161
+ }
162
+ async function runOpensteerLocalProfileCli(argv, overrides = {}) {
163
+ const deps = {
164
+ writeStdout: (message) => process.stdout.write(message),
165
+ writeStderr: (message) => process.stderr.write(message),
166
+ ...overrides
167
+ };
168
+ const parsed = parseOpensteerLocalProfileArgs(argv);
169
+ if (parsed.mode === "help") {
170
+ deps.writeStdout(HELP_TEXT);
171
+ return 0;
172
+ }
173
+ if (parsed.mode === "error") {
174
+ deps.writeStderr(`${parsed.error}
175
+ `);
176
+ return 1;
177
+ }
178
+ const profiles = listLocalChromeProfiles(parsed.userDataDir);
179
+ if (parsed.json) {
180
+ deps.writeStdout(JSON.stringify({ profiles }, null, 2));
181
+ return 0;
182
+ }
183
+ if (!profiles.length) {
184
+ deps.writeStdout("No local Chrome profiles found.\n");
185
+ return 0;
186
+ }
187
+ for (const profile of profiles) {
188
+ deps.writeStdout(`${profile.directory} ${profile.name}
189
+ `);
190
+ }
191
+ return 0;
192
+ }
193
+ // Annotate the CommonJS export names for ESM import in node:
194
+ 0 && (module.exports = {
195
+ parseOpensteerLocalProfileArgs,
196
+ runOpensteerLocalProfileCli
197
+ });
@@ -0,0 +1,18 @@
1
+ interface LocalProfileCliDeps {
2
+ writeStdout: (message: string) => void;
3
+ writeStderr: (message: string) => void;
4
+ }
5
+ type ParsedLocalProfileArgs = {
6
+ mode: 'help';
7
+ } | {
8
+ mode: 'error';
9
+ error: string;
10
+ } | {
11
+ mode: 'list';
12
+ json: boolean;
13
+ userDataDir?: string;
14
+ };
15
+ declare function parseOpensteerLocalProfileArgs(argv: string[]): ParsedLocalProfileArgs;
16
+ declare function runOpensteerLocalProfileCli(argv: string[], overrides?: Partial<LocalProfileCliDeps>): Promise<number>;
17
+
18
+ export { type LocalProfileCliDeps, type ParsedLocalProfileArgs, parseOpensteerLocalProfileArgs, runOpensteerLocalProfileCli };
@@ -0,0 +1,18 @@
1
+ interface LocalProfileCliDeps {
2
+ writeStdout: (message: string) => void;
3
+ writeStderr: (message: string) => void;
4
+ }
5
+ type ParsedLocalProfileArgs = {
6
+ mode: 'help';
7
+ } | {
8
+ mode: 'error';
9
+ error: string;
10
+ } | {
11
+ mode: 'list';
12
+ json: boolean;
13
+ userDataDir?: string;
14
+ };
15
+ declare function parseOpensteerLocalProfileArgs(argv: string[]): ParsedLocalProfileArgs;
16
+ declare function runOpensteerLocalProfileCli(argv: string[], overrides?: Partial<LocalProfileCliDeps>): Promise<number>;
17
+
18
+ export { type LocalProfileCliDeps, type ParsedLocalProfileArgs, parseOpensteerLocalProfileArgs, runOpensteerLocalProfileCli };