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/bin/opensteer.mjs CHANGED
@@ -27,6 +27,13 @@ const SKILLS_INSTALLER_SCRIPT = join(
27
27
  'skills-installer.js'
28
28
  )
29
29
  const PROFILE_CLI_SCRIPT = join(__dirname, '..', 'dist', 'cli', 'profile.js')
30
+ const LOCAL_PROFILE_CLI_SCRIPT = join(
31
+ __dirname,
32
+ '..',
33
+ 'dist',
34
+ 'cli',
35
+ 'local-profile.js'
36
+ )
30
37
  const AUTH_CLI_SCRIPT = join(__dirname, '..', 'dist', 'cli', 'auth.js')
31
38
  const SKILLS_HELP_TEXT = `Usage: opensteer skills <install|add> [options]
32
39
 
@@ -60,6 +67,15 @@ Commands:
60
67
 
61
68
  Run "opensteer profile --help" after building for full command details.
62
69
  `
70
+ const LOCAL_PROFILE_HELP_TEXT = `Usage: opensteer local-profile <command> [options]
71
+
72
+ Inspect local Chrome profiles for real-browser mode.
73
+
74
+ Commands:
75
+ list
76
+
77
+ Run "opensteer local-profile --help" after building for full command details.
78
+ `
63
79
  const AUTH_HELP_TEXT = `Usage: opensteer auth <command> [options]
64
80
 
65
81
  Authenticate Opensteer CLI with Opensteer Cloud.
@@ -87,6 +103,7 @@ const CLOSE_ALL_REQUEST = { id: 1, command: 'close', args: {} }
87
103
  const PING_REQUEST = { id: 1, command: 'ping', args: {} }
88
104
  const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/
89
105
  const RUNTIME_SESSION_PREFIX = 'sc-'
106
+ const BOOLEAN_FLAGS = new Set(['all', 'headless', 'headed', 'json'])
90
107
 
91
108
  function getVersion() {
92
109
  try {
@@ -118,7 +135,14 @@ function parseArgs(argv) {
118
135
  if (arg.startsWith('--')) {
119
136
  const key = arg.slice(2)
120
137
  const next = args[i + 1]
121
- if (next !== undefined && !next.startsWith('--')) {
138
+ if (
139
+ BOOLEAN_FLAGS.has(key) &&
140
+ next !== undefined &&
141
+ next !== 'true' &&
142
+ next !== 'false'
143
+ ) {
144
+ flags[key] = true
145
+ } else if (next !== undefined && !next.startsWith('--')) {
122
146
  flags[key] = parseValue(next)
123
147
  i++
124
148
  } else {
@@ -334,9 +358,11 @@ function buildRequest(command, flags, positional) {
334
358
  for (const key of [
335
359
  'headless',
336
360
  'json',
337
- 'connect-url',
338
- 'channel',
339
- 'profile-dir',
361
+ 'browser',
362
+ 'profile',
363
+ 'cdp-url',
364
+ 'user-data-dir',
365
+ 'browser-path',
340
366
  'cloud-profile-id',
341
367
  'cloud-profile-reuse-if-active',
342
368
  'cursor',
@@ -348,6 +374,11 @@ function buildRequest(command, flags, positional) {
348
374
  }
349
375
 
350
376
  const args = { ...globalFlags, ...flags }
377
+ if ('headed' in flags) {
378
+ const headed = flags.headed === false ? false : Boolean(flags.headed)
379
+ args.headless = args.headless ?? !headed
380
+ delete args.headed
381
+ }
351
382
 
352
383
  switch (command) {
353
384
  case 'open':
@@ -975,6 +1006,30 @@ async function runProfileSubcommand(args) {
975
1006
  }
976
1007
  }
977
1008
 
1009
+ async function runLocalProfileSubcommand(args) {
1010
+ if (
1011
+ args.length === 0 ||
1012
+ args.includes('--help') ||
1013
+ args.includes('-h')
1014
+ ) {
1015
+ process.stdout.write(LOCAL_PROFILE_HELP_TEXT)
1016
+ return
1017
+ }
1018
+
1019
+ if (!existsSync(LOCAL_PROFILE_CLI_SCRIPT)) {
1020
+ throw new Error(
1021
+ `Local profile CLI module was not found at "${LOCAL_PROFILE_CLI_SCRIPT}". Run "npm run build" to generate dist artifacts.`
1022
+ )
1023
+ }
1024
+
1025
+ const moduleUrl = pathToFileURL(LOCAL_PROFILE_CLI_SCRIPT).href
1026
+ const { runOpensteerLocalProfileCli } = await import(moduleUrl)
1027
+ const exitCode = await runOpensteerLocalProfileCli(args)
1028
+ if (exitCode !== 0) {
1029
+ process.exit(exitCode)
1030
+ }
1031
+ }
1032
+
978
1033
  async function runAuthSubcommand(args) {
979
1034
  if (isAuthHelpRequest(args)) {
980
1035
  process.stdout.write(AUTH_HELP_TEXT)
@@ -1119,6 +1174,7 @@ Skills:
1119
1174
  skills add [options] Alias for "skills install"
1120
1175
  skills --help Show skills installer help
1121
1176
  profile <command> Manage cloud browser profiles and cookie sync
1177
+ local-profile <command> Inspect local Chrome profiles for real-browser mode
1122
1178
  auth <command> Manage cloud login credentials (login/status/logout)
1123
1179
  login Alias for "auth login"
1124
1180
  logout Alias for "auth logout"
@@ -1126,10 +1182,13 @@ Skills:
1126
1182
  Global Flags:
1127
1183
  --session <id> Logical session id (scoped by canonical cwd)
1128
1184
  --name <namespace> Selector namespace for cache storage on 'open'
1129
- --headless Launch browser in headless mode
1130
- --connect-url <url> Connect to a running browser (e.g. http://localhost:9222)
1131
- --channel <browser> Use installed browser (chrome, chrome-beta, msedge)
1132
- --profile-dir <path> Browser profile directory for logged-in sessions
1185
+ --headless Launch chromium mode in headless mode
1186
+ --browser <mode> Browser mode: chromium or real
1187
+ --profile <name> Browser profile directory name for real-browser mode
1188
+ --headed Launch real-browser mode with a visible window
1189
+ --cdp-url <url> Connect to a running browser (e.g. http://localhost:9222)
1190
+ --user-data-dir <path> Browser user-data root for real-browser mode
1191
+ --browser-path <path> Override Chrome executable path for real-browser mode
1133
1192
  --cloud-profile-id <id> Launch cloud session with a specific browser profile
1134
1193
  --cloud-profile-reuse-if-active <true|false>
1135
1194
  Reuse active cloud session for that browser profile
@@ -1151,6 +1210,10 @@ Environment:
1151
1210
  OPENSTEER_BASE_URL Override cloud control-plane base URL
1152
1211
  OPENSTEER_AUTH_SCHEME Cloud auth scheme: api-key (default) or bearer
1153
1212
  OPENSTEER_REMOTE_ANNOUNCE Cloud session announcement policy: always (default), off, tty
1213
+ OPENSTEER_BROWSER Local browser mode: chromium or real
1214
+ OPENSTEER_CDP_URL Connect to a running browser (e.g. http://localhost:9222)
1215
+ OPENSTEER_USER_DATA_DIR Browser user-data root for real-browser mode
1216
+ OPENSTEER_PROFILE_DIRECTORY Browser profile directory for real-browser mode
1154
1217
  `)
1155
1218
  }
1156
1219
 
@@ -1211,11 +1274,34 @@ async function main() {
1211
1274
  }
1212
1275
  return
1213
1276
  }
1277
+ if (rawArgs[0] === 'local-profile') {
1278
+ try {
1279
+ await runLocalProfileSubcommand(rawArgs.slice(1))
1280
+ } catch (err) {
1281
+ const message =
1282
+ err instanceof Error
1283
+ ? err.message
1284
+ : 'Failed to run local-profile command'
1285
+ process.stderr.write(`${message}\n`)
1286
+ process.exit(1)
1287
+ }
1288
+ return
1289
+ }
1214
1290
 
1215
1291
  const scopeDir = resolveScopeDir()
1216
1292
 
1217
1293
  const { command, flags, positional } = parseArgs(process.argv)
1218
1294
 
1295
+ if (
1296
+ flags['connect-url'] !== undefined ||
1297
+ flags.channel !== undefined ||
1298
+ flags['profile-dir'] !== undefined
1299
+ ) {
1300
+ error(
1301
+ '--connect-url, --channel, and --profile-dir are no longer supported. Use --cdp-url, --browser real, --profile, --user-data-dir, and --browser-path instead.'
1302
+ )
1303
+ }
1304
+
1219
1305
  if (command === 'sessions') {
1220
1306
  output({ ok: true, sessions: listSessions() })
1221
1307
  return
@@ -1,4 +1,4 @@
1
- import { O as OpensteerAuthScheme } from './types-BxiRblC7.cjs';
1
+ import { O as OpensteerAuthScheme } from './types-BWItZPl_.cjs';
2
2
 
3
3
  type ActionFailureCode = 'TARGET_NOT_FOUND' | 'TARGET_UNAVAILABLE' | 'TARGET_STALE' | 'TARGET_AMBIGUOUS' | 'BLOCKED_BY_INTERCEPTOR' | 'NOT_VISIBLE' | 'NOT_ENABLED' | 'NOT_EDITABLE' | 'INVALID_TARGET' | 'INVALID_OPTIONS' | 'ACTION_TIMEOUT' | 'UNKNOWN';
4
4
  type ActionFailureClassificationSource = 'typed_error' | 'playwright_call_log' | 'dom_probe' | 'message_heuristic' | 'unknown';
@@ -1,4 +1,4 @@
1
- import { O as OpensteerAuthScheme } from './types-BxiRblC7.js';
1
+ import { O as OpensteerAuthScheme } from './types-BWItZPl_.js';
2
2
 
3
3
  type ActionFailureCode = 'TARGET_NOT_FOUND' | 'TARGET_UNAVAILABLE' | 'TARGET_STALE' | 'TARGET_AMBIGUOUS' | 'BLOCKED_BY_INTERCEPTOR' | 'NOT_VISIBLE' | 'NOT_ENABLED' | 'NOT_EDITABLE' | 'INVALID_TARGET' | 'INVALID_OPTIONS' | 'ACTION_TIMEOUT' | 'UNKNOWN';
4
4
  type ActionFailureClassificationSource = 'typed_error' | 'playwright_call_log' | 'dom_probe' | 'message_heuristic' | 'unknown';
@@ -1,70 +1,32 @@
1
1
  import {
2
2
  DEFAULT_CLOUD_BASE_URL,
3
- createKeychainStore,
4
3
  normalizeCloudBaseUrl,
5
4
  resolveCloudSelection,
6
5
  resolveConfigWithEnv,
6
+ selectCloudCredential,
7
7
  stripTrailingSlashes
8
- } from "./chunk-WDRMHPWL.js";
8
+ } from "./chunk-KPPOTU3D.js";
9
9
 
10
10
  // src/cli/auth.ts
11
11
  import open from "open";
12
12
 
13
13
  // src/auth/credential-resolver.ts
14
14
  function resolveCloudCredential(options) {
15
- const flagApiKey = normalizeToken(options.apiKeyFlag);
16
- const flagAccessToken = normalizeToken(options.accessTokenFlag);
17
- if (flagApiKey && flagAccessToken) {
18
- throw new Error("--api-key and --access-token are mutually exclusive.");
19
- }
20
- if (flagAccessToken) {
21
- return {
22
- kind: "access-token",
23
- source: "flag",
24
- token: flagAccessToken,
25
- authScheme: "bearer"
26
- };
27
- }
28
- if (flagApiKey) {
29
- return {
30
- kind: "api-key",
31
- source: "flag",
32
- token: flagApiKey,
33
- authScheme: "api-key"
34
- };
15
+ const flagCredential = selectCloudCredential({
16
+ apiKey: options.apiKeyFlag,
17
+ accessToken: options.accessTokenFlag
18
+ });
19
+ if (flagCredential) {
20
+ return toResolvedCloudCredential("flag", flagCredential);
35
21
  }
36
22
  const envAuthScheme = parseEnvAuthScheme(options.env.OPENSTEER_AUTH_SCHEME);
37
- const envApiKey = normalizeToken(options.env.OPENSTEER_API_KEY);
38
- const envAccessToken = normalizeToken(options.env.OPENSTEER_ACCESS_TOKEN);
39
- if (envApiKey && envAccessToken) {
40
- throw new Error(
41
- "OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
42
- );
43
- }
44
- if (envAccessToken) {
45
- return {
46
- kind: "access-token",
47
- source: "env",
48
- token: envAccessToken,
49
- authScheme: "bearer"
50
- };
51
- }
52
- if (envApiKey) {
53
- if (envAuthScheme === "bearer") {
54
- return {
55
- kind: "access-token",
56
- source: "env",
57
- token: envApiKey,
58
- authScheme: "bearer",
59
- compatibilityBearerApiKey: true
60
- };
61
- }
62
- return {
63
- kind: "api-key",
64
- source: "env",
65
- token: envApiKey,
66
- authScheme: envAuthScheme ?? "api-key"
67
- };
23
+ const envCredential = selectCloudCredential({
24
+ apiKey: options.env.OPENSTEER_API_KEY,
25
+ accessToken: options.env.OPENSTEER_ACCESS_TOKEN,
26
+ authScheme: envAuthScheme
27
+ });
28
+ if (envCredential) {
29
+ return toResolvedCloudCredential("env", envCredential);
68
30
  }
69
31
  return null;
70
32
  }
@@ -94,12 +56,168 @@ function normalizeToken(value) {
94
56
  const normalized = value.trim();
95
57
  return normalized.length ? normalized : void 0;
96
58
  }
59
+ function toResolvedCloudCredential(source, credential) {
60
+ if (credential.compatibilityBearerApiKey) {
61
+ return {
62
+ kind: credential.kind,
63
+ source,
64
+ token: credential.token,
65
+ authScheme: credential.authScheme,
66
+ compatibilityBearerApiKey: true
67
+ };
68
+ }
69
+ return {
70
+ kind: credential.kind,
71
+ source,
72
+ token: credential.token,
73
+ authScheme: credential.authScheme
74
+ };
75
+ }
97
76
 
98
77
  // src/auth/machine-credential-store.ts
99
78
  import { createHash } from "crypto";
100
79
  import fs from "fs";
101
80
  import os from "os";
102
81
  import path from "path";
82
+
83
+ // src/auth/keychain-store.ts
84
+ import { spawnSync } from "child_process";
85
+ function commandExists(command) {
86
+ const result = spawnSync(command, ["--help"], {
87
+ encoding: "utf8",
88
+ stdio: "ignore"
89
+ });
90
+ return result.error == null;
91
+ }
92
+ function commandFailed(result) {
93
+ return typeof result.status === "number" && result.status !== 0;
94
+ }
95
+ function sanitizeCommandArgs(command, args) {
96
+ if (command !== "security") {
97
+ return args;
98
+ }
99
+ const sanitized = [];
100
+ for (let index = 0; index < args.length; index += 1) {
101
+ const value = args[index];
102
+ sanitized.push(value);
103
+ if (value === "-w" && index + 1 < args.length) {
104
+ sanitized.push("[REDACTED]");
105
+ index += 1;
106
+ }
107
+ }
108
+ return sanitized;
109
+ }
110
+ function buildCommandError(command, args, result) {
111
+ const stderr = typeof result.stderr === "string" && result.stderr.trim() ? result.stderr.trim() : `Command "${command}" failed with status ${String(result.status)}.`;
112
+ const sanitizedArgs = sanitizeCommandArgs(command, args);
113
+ return new Error(
114
+ [
115
+ `Unable to persist credential via ${command}.`,
116
+ `${command} ${sanitizedArgs.join(" ")}`,
117
+ stderr
118
+ ].join(" ")
119
+ );
120
+ }
121
+ function createMacosSecurityStore() {
122
+ return {
123
+ backend: "macos-security",
124
+ get(service, account) {
125
+ const result = spawnSync(
126
+ "security",
127
+ ["find-generic-password", "-s", service, "-a", account, "-w"],
128
+ { encoding: "utf8" }
129
+ );
130
+ if (commandFailed(result)) {
131
+ return null;
132
+ }
133
+ const secret = result.stdout.trim();
134
+ return secret.length ? secret : null;
135
+ },
136
+ set(service, account, secret) {
137
+ const args = [
138
+ "add-generic-password",
139
+ "-U",
140
+ "-s",
141
+ service,
142
+ "-a",
143
+ account,
144
+ "-w",
145
+ secret
146
+ ];
147
+ const result = spawnSync("security", args, { encoding: "utf8" });
148
+ if (commandFailed(result)) {
149
+ throw buildCommandError("security", args, result);
150
+ }
151
+ },
152
+ delete(service, account) {
153
+ const args = ["delete-generic-password", "-s", service, "-a", account];
154
+ const result = spawnSync("security", args, { encoding: "utf8" });
155
+ if (commandFailed(result)) {
156
+ return;
157
+ }
158
+ }
159
+ };
160
+ }
161
+ function createLinuxSecretToolStore() {
162
+ return {
163
+ backend: "linux-secret-tool",
164
+ get(service, account) {
165
+ const result = spawnSync(
166
+ "secret-tool",
167
+ ["lookup", "service", service, "account", account],
168
+ {
169
+ encoding: "utf8"
170
+ }
171
+ );
172
+ if (commandFailed(result)) {
173
+ return null;
174
+ }
175
+ const secret = result.stdout.trim();
176
+ return secret.length ? secret : null;
177
+ },
178
+ set(service, account, secret) {
179
+ const args = [
180
+ "store",
181
+ "--label",
182
+ "Opensteer CLI",
183
+ "service",
184
+ service,
185
+ "account",
186
+ account
187
+ ];
188
+ const result = spawnSync("secret-tool", args, {
189
+ encoding: "utf8",
190
+ input: secret
191
+ });
192
+ if (commandFailed(result)) {
193
+ throw buildCommandError("secret-tool", args, result);
194
+ }
195
+ },
196
+ delete(service, account) {
197
+ const args = ["clear", "service", service, "account", account];
198
+ spawnSync("secret-tool", args, {
199
+ encoding: "utf8"
200
+ });
201
+ }
202
+ };
203
+ }
204
+ function createKeychainStore() {
205
+ if (process.platform === "darwin") {
206
+ if (!commandExists("security")) {
207
+ return null;
208
+ }
209
+ return createMacosSecurityStore();
210
+ }
211
+ if (process.platform === "linux") {
212
+ if (!commandExists("secret-tool")) {
213
+ return null;
214
+ }
215
+ return createLinuxSecretToolStore();
216
+ }
217
+ return null;
218
+ }
219
+
220
+ // src/auth/machine-credential-store.ts
103
221
  var METADATA_VERSION = 2;
104
222
  var ACTIVE_TARGET_VERSION = 2;
105
223
  var KEYCHAIN_SERVICE = "com.opensteer.cli.cloud";
@@ -1025,8 +1143,6 @@ async function runLogin(args, deps) {
1025
1143
  return 0;
1026
1144
  }
1027
1145
  writeHumanLine(deps, "Opensteer CLI login successful.");
1028
- writeHumanLine(deps, ` API Base URL: ${baseUrl}`);
1029
- writeHumanLine(deps, ` Expires At: ${new Date(login.expiresAt).toISOString()}`);
1030
1146
  return 0;
1031
1147
  }
1032
1148
  async function runStatus(args, deps) {
@@ -1122,6 +1238,7 @@ async function runOpensteerAuthCli(rawArgs, overrideDeps = {}) {
1122
1238
  }
1123
1239
 
1124
1240
  export {
1241
+ createKeychainStore,
1125
1242
  parseOpensteerAuthArgs,
1126
1243
  isCloudModeEnabledForRootDir,
1127
1244
  ensureCloudCredentialsForOpenCommand,
@@ -2,7 +2,7 @@ import {
2
2
  cloudAuthHeaders,
3
3
  normalizeCloudBaseUrl,
4
4
  parseCloudHttpError
5
- } from "./chunk-WDRMHPWL.js";
5
+ } from "./chunk-KPPOTU3D.js";
6
6
 
7
7
  // src/cloud/browser-profile-client.ts
8
8
  var BrowserProfileClient = class {