opensteer 0.6.13 → 0.7.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.
Files changed (62) hide show
  1. package/README.md +256 -184
  2. package/dist/chunk-PQYA6IX2.js +32571 -0
  3. package/dist/chunk-PQYA6IX2.js.map +1 -0
  4. package/dist/cli/bin.cjs +38201 -0
  5. package/dist/cli/bin.cjs.map +1 -0
  6. package/dist/cli/bin.d.cts +1 -0
  7. package/dist/cli/bin.d.ts +1 -0
  8. package/dist/cli/bin.js +5612 -0
  9. package/dist/cli/bin.js.map +1 -0
  10. package/dist/index.cjs +31309 -16009
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +4440 -670
  13. package/dist/index.d.ts +4440 -670
  14. package/dist/index.js +438 -378
  15. package/dist/index.js.map +1 -0
  16. package/package.json +56 -62
  17. package/skills/README.md +21 -20
  18. package/skills/opensteer/SKILL.md +60 -194
  19. package/skills/opensteer/references/cli-reference.md +69 -113
  20. package/skills/opensteer/references/request-workflow.md +81 -0
  21. package/skills/opensteer/references/sdk-reference.md +101 -154
  22. package/CHANGELOG.md +0 -75
  23. package/bin/opensteer.mjs +0 -1423
  24. package/dist/browser-profile-client-CGXc0-P9.d.cts +0 -228
  25. package/dist/browser-profile-client-DHLzMf-K.d.ts +0 -228
  26. package/dist/chunk-2ES46WCO.js +0 -1437
  27. package/dist/chunk-3H5RRIMZ.js +0 -69
  28. package/dist/chunk-AVXUMEDG.js +0 -62
  29. package/dist/chunk-DN3GI5CH.js +0 -57
  30. package/dist/chunk-FAHE5DB2.js +0 -190
  31. package/dist/chunk-HBTSQ2V4.js +0 -15259
  32. package/dist/chunk-K5CL76MG.js +0 -81
  33. package/dist/chunk-U724TBY6.js +0 -1262
  34. package/dist/chunk-ZRCFF546.js +0 -77
  35. package/dist/cli/auth.cjs +0 -2022
  36. package/dist/cli/auth.d.cts +0 -114
  37. package/dist/cli/auth.d.ts +0 -114
  38. package/dist/cli/auth.js +0 -15
  39. package/dist/cli/local-profile.cjs +0 -197
  40. package/dist/cli/local-profile.d.cts +0 -18
  41. package/dist/cli/local-profile.d.ts +0 -18
  42. package/dist/cli/local-profile.js +0 -97
  43. package/dist/cli/profile.cjs +0 -18548
  44. package/dist/cli/profile.d.cts +0 -79
  45. package/dist/cli/profile.d.ts +0 -79
  46. package/dist/cli/profile.js +0 -1328
  47. package/dist/cli/server.cjs +0 -17232
  48. package/dist/cli/server.d.cts +0 -2
  49. package/dist/cli/server.d.ts +0 -2
  50. package/dist/cli/server.js +0 -977
  51. package/dist/cli/skills-installer.cjs +0 -230
  52. package/dist/cli/skills-installer.d.cts +0 -28
  53. package/dist/cli/skills-installer.d.ts +0 -28
  54. package/dist/cli/skills-installer.js +0 -201
  55. package/dist/extractor-4Q3TFZJB.js +0 -8
  56. package/dist/resolver-MGN64KCP.js +0 -7
  57. package/dist/types-Cr10igF3.d.cts +0 -345
  58. package/dist/types-Cr10igF3.d.ts +0 -345
  59. package/skills/electron/SKILL.md +0 -87
  60. package/skills/electron/references/opensteer-electron-recipes.md +0 -88
  61. package/skills/electron/references/opensteer-electron-workflow.md +0 -85
  62. package/skills/opensteer/references/examples.md +0 -118
@@ -1,1437 +0,0 @@
1
- // src/error-normalization.ts
2
- function extractErrorMessage(error, fallback = "Unknown error.") {
3
- if (error instanceof Error) {
4
- const message = error.message.trim();
5
- if (message) return message;
6
- const name = error.name.trim();
7
- if (name) return name;
8
- }
9
- if (typeof error === "string" && error.trim()) {
10
- return error.trim();
11
- }
12
- const record = asRecord(error);
13
- const recordMessage = toNonEmptyString(record?.message) || toNonEmptyString(record?.error);
14
- if (recordMessage) {
15
- return recordMessage;
16
- }
17
- return fallback;
18
- }
19
- function normalizeError(error, fallback = "Unknown error.", maxCauseDepth = 2) {
20
- const seen = /* @__PURE__ */ new WeakSet();
21
- return normalizeErrorInternal(error, fallback, maxCauseDepth, seen);
22
- }
23
- function normalizeErrorInternal(error, fallback, depthRemaining, seen) {
24
- const record = asRecord(error);
25
- if (record) {
26
- if (seen.has(record)) {
27
- return {
28
- message: extractErrorMessage(error, fallback)
29
- };
30
- }
31
- seen.add(record);
32
- }
33
- const message = extractErrorMessage(error, fallback);
34
- const code = extractCode(error);
35
- const name = extractName(error);
36
- const details = extractDetails(error);
37
- if (depthRemaining <= 0) {
38
- return compactErrorInfo({
39
- message,
40
- ...code ? { code } : {},
41
- ...name ? { name } : {},
42
- ...details ? { details } : {}
43
- });
44
- }
45
- const cause = extractCause(error);
46
- if (!cause) {
47
- return compactErrorInfo({
48
- message,
49
- ...code ? { code } : {},
50
- ...name ? { name } : {},
51
- ...details ? { details } : {}
52
- });
53
- }
54
- const normalizedCause = normalizeErrorInternal(
55
- cause,
56
- "Caused by an unknown error.",
57
- depthRemaining - 1,
58
- seen
59
- );
60
- return compactErrorInfo({
61
- message,
62
- ...code ? { code } : {},
63
- ...name ? { name } : {},
64
- ...details ? { details } : {},
65
- cause: normalizedCause
66
- });
67
- }
68
- function compactErrorInfo(info) {
69
- const safeDetails = toJsonSafeRecord(info.details);
70
- return {
71
- message: info.message,
72
- ...info.code ? { code: info.code } : {},
73
- ...info.name ? { name: info.name } : {},
74
- ...safeDetails ? { details: safeDetails } : {},
75
- ...info.cause ? { cause: info.cause } : {}
76
- };
77
- }
78
- function extractCode(error) {
79
- const record = asRecord(error);
80
- const raw = record?.code;
81
- if (typeof raw === "string" && raw.trim()) {
82
- return raw.trim();
83
- }
84
- if (typeof raw === "number" && Number.isFinite(raw)) {
85
- return String(raw);
86
- }
87
- return void 0;
88
- }
89
- function extractName(error) {
90
- if (error instanceof Error && error.name.trim()) {
91
- return error.name.trim();
92
- }
93
- const record = asRecord(error);
94
- return toNonEmptyString(record?.name);
95
- }
96
- function extractDetails(error) {
97
- const record = asRecord(error);
98
- if (!record) return void 0;
99
- const details = {};
100
- const rawDetails = asRecord(record.details);
101
- if (rawDetails) {
102
- Object.assign(details, rawDetails);
103
- }
104
- const action = toNonEmptyString(record.action);
105
- if (action) {
106
- details.action = action;
107
- }
108
- const selectorUsed = toNonEmptyString(record.selectorUsed);
109
- if (selectorUsed) {
110
- details.selectorUsed = selectorUsed;
111
- }
112
- if (typeof record.status === "number" && Number.isFinite(record.status)) {
113
- details.status = record.status;
114
- }
115
- const failure = asRecord(record.failure);
116
- if (failure) {
117
- const failureCode = toNonEmptyString(failure.code);
118
- const classificationSource = toNonEmptyString(
119
- failure.classificationSource
120
- );
121
- const failureDetails = asRecord(failure.details);
122
- if (failureCode || classificationSource || failureDetails) {
123
- details.actionFailure = {
124
- ...failureCode ? { code: failureCode } : {},
125
- ...classificationSource ? { classificationSource } : {},
126
- ...failureDetails ? { details: failureDetails } : {}
127
- };
128
- }
129
- }
130
- return Object.keys(details).length ? details : void 0;
131
- }
132
- function extractCause(error) {
133
- if (error instanceof Error) {
134
- return error.cause;
135
- }
136
- const record = asRecord(error);
137
- return record?.cause;
138
- }
139
- function asRecord(value) {
140
- if (!value || typeof value !== "object" || Array.isArray(value)) {
141
- return null;
142
- }
143
- return value;
144
- }
145
- function toNonEmptyString(value) {
146
- if (typeof value !== "string") return void 0;
147
- const normalized = value.trim();
148
- return normalized.length ? normalized : void 0;
149
- }
150
- function toJsonSafeRecord(value) {
151
- if (!value) return void 0;
152
- const sanitized = toJsonSafeValue(value, /* @__PURE__ */ new WeakSet());
153
- if (!sanitized || typeof sanitized !== "object" || Array.isArray(sanitized)) {
154
- return void 0;
155
- }
156
- const record = sanitized;
157
- return Object.keys(record).length > 0 ? record : void 0;
158
- }
159
- function toJsonSafeValue(value, seen) {
160
- if (value === null) return null;
161
- if (typeof value === "string" || typeof value === "boolean") {
162
- return value;
163
- }
164
- if (typeof value === "number") {
165
- return Number.isFinite(value) ? value : null;
166
- }
167
- if (typeof value === "bigint") {
168
- return value.toString();
169
- }
170
- if (value === void 0 || typeof value === "function" || typeof value === "symbol") {
171
- return void 0;
172
- }
173
- if (value instanceof Date) {
174
- return Number.isNaN(value.getTime()) ? null : value.toISOString();
175
- }
176
- if (Array.isArray(value)) {
177
- if (seen.has(value)) return "[Circular]";
178
- seen.add(value);
179
- const output = value.map((item) => {
180
- const next = toJsonSafeValue(item, seen);
181
- return next === void 0 ? null : next;
182
- });
183
- seen.delete(value);
184
- return output;
185
- }
186
- if (value instanceof Set) {
187
- if (seen.has(value)) return "[Circular]";
188
- seen.add(value);
189
- const output = Array.from(value, (item) => {
190
- const next = toJsonSafeValue(item, seen);
191
- return next === void 0 ? null : next;
192
- });
193
- seen.delete(value);
194
- return output;
195
- }
196
- if (value instanceof Map) {
197
- if (seen.has(value)) return "[Circular]";
198
- seen.add(value);
199
- const output = {};
200
- for (const [key, item] of value.entries()) {
201
- const normalizedKey = String(key);
202
- const next = toJsonSafeValue(item, seen);
203
- if (next !== void 0) {
204
- output[normalizedKey] = next;
205
- }
206
- }
207
- seen.delete(value);
208
- return output;
209
- }
210
- if (typeof value === "object") {
211
- const objectValue = value;
212
- if (seen.has(objectValue)) return "[Circular]";
213
- seen.add(objectValue);
214
- const output = {};
215
- for (const [key, item] of Object.entries(objectValue)) {
216
- const next = toJsonSafeValue(item, seen);
217
- if (next !== void 0) {
218
- output[key] = next;
219
- }
220
- }
221
- seen.delete(objectValue);
222
- return output;
223
- }
224
- return void 0;
225
- }
226
-
227
- // src/storage/namespace.ts
228
- import path from "path";
229
- var DEFAULT_NAMESPACE = "default";
230
- function normalizeNamespace(input) {
231
- const raw = String(input || "").trim().replace(/\\/g, "/");
232
- if (!raw) return DEFAULT_NAMESPACE;
233
- const segments = raw.split("/").map((segment) => sanitizeNamespaceSegment(segment)).filter((segment) => Boolean(segment));
234
- if (!segments.length) return DEFAULT_NAMESPACE;
235
- return segments.join("/");
236
- }
237
- function resolveNamespaceDir(rootDir, namespace) {
238
- const selectorsRoot = path.resolve(rootDir, ".opensteer", "selectors");
239
- const normalizedNamespace = normalizeNamespace(namespace);
240
- const namespaceDir = path.resolve(selectorsRoot, normalizedNamespace);
241
- const relative = path.relative(selectorsRoot, namespaceDir);
242
- if (relative === "" || relative === ".") return namespaceDir;
243
- if (relative.startsWith("..") || path.isAbsolute(relative)) {
244
- throw new Error(
245
- `Namespace "${namespace}" resolves outside selectors root.`
246
- );
247
- }
248
- return namespaceDir;
249
- }
250
- function sanitizeNamespaceSegment(segment) {
251
- const trimmed = String(segment || "").trim();
252
- if (!trimmed || trimmed === "." || trimmed === "..") return "";
253
- const replaced = trimmed.replace(/[^a-zA-Z0-9_-]+/g, "_");
254
- const collapsed = replaced.replace(/_+/g, "_");
255
- const bounded = collapsed.replace(/^_+|_+$/g, "");
256
- return bounded || "";
257
- }
258
-
259
- // src/config.ts
260
- import fs from "fs";
261
- import path2 from "path";
262
- import { fileURLToPath } from "url";
263
- import { parse as parseDotenv } from "dotenv";
264
-
265
- // src/cloud/credential-selection.ts
266
- function selectCloudCredential(options) {
267
- const apiKey = normalizeNonEmptyString(options.apiKey);
268
- const accessToken = normalizeNonEmptyString(options.accessToken);
269
- if (apiKey) {
270
- if (options.authScheme === "bearer") {
271
- return {
272
- apiKey,
273
- authScheme: "bearer",
274
- kind: "access-token",
275
- token: apiKey,
276
- compatibilityBearerApiKey: true
277
- };
278
- }
279
- return {
280
- apiKey,
281
- authScheme: "api-key",
282
- kind: "api-key",
283
- token: apiKey
284
- };
285
- }
286
- if (accessToken) {
287
- return {
288
- accessToken,
289
- authScheme: "bearer",
290
- kind: "access-token",
291
- token: accessToken
292
- };
293
- }
294
- return null;
295
- }
296
- function selectCloudCredentialByPrecedence(layers, authScheme) {
297
- for (const layer of layers) {
298
- const hasApiKey = layer.hasApiKey ?? Object.prototype.hasOwnProperty.call(layer, "apiKey");
299
- const hasAccessToken = layer.hasAccessToken ?? Object.prototype.hasOwnProperty.call(layer, "accessToken");
300
- if (!hasApiKey && !hasAccessToken) {
301
- continue;
302
- }
303
- return {
304
- source: layer.source,
305
- apiKey: layer.apiKey,
306
- accessToken: layer.accessToken,
307
- hasApiKey,
308
- hasAccessToken,
309
- credential: selectCloudCredential({
310
- apiKey: layer.apiKey,
311
- accessToken: layer.accessToken,
312
- authScheme: layer.authScheme ?? authScheme
313
- })
314
- };
315
- }
316
- return null;
317
- }
318
- function normalizeNonEmptyString(value) {
319
- if (typeof value !== "string") return void 0;
320
- const normalized = value.trim();
321
- return normalized.length ? normalized : void 0;
322
- }
323
-
324
- // src/config.ts
325
- var DEFAULT_CONFIG = {
326
- browser: {
327
- headless: void 0,
328
- executablePath: void 0,
329
- slowMo: 0,
330
- mode: void 0,
331
- cdpUrl: void 0,
332
- userDataDir: void 0,
333
- profileDirectory: void 0
334
- },
335
- storage: {
336
- rootDir: process.cwd()
337
- },
338
- cursor: {
339
- enabled: false,
340
- profile: "snappy"
341
- },
342
- model: "gpt-5.1",
343
- debug: false
344
- };
345
- function dotenvFileOrder(nodeEnv) {
346
- const normalized = nodeEnv?.trim() || "";
347
- const files = [];
348
- if (normalized) {
349
- files.push(`.env.${normalized}.local`);
350
- }
351
- if (normalized !== "test") {
352
- files.push(".env.local");
353
- }
354
- if (normalized) {
355
- files.push(`.env.${normalized}`);
356
- }
357
- files.push(".env");
358
- return files;
359
- }
360
- function loadDotenvValues(rootDir, baseEnv, options = {}) {
361
- const values = {};
362
- if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
363
- return values;
364
- }
365
- const debug = options.debug ?? parseBool(baseEnv.OPENSTEER_DEBUG) === true;
366
- const baseDir = path2.resolve(rootDir);
367
- const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
368
- for (const filename of dotenvFileOrder(nodeEnv)) {
369
- const filePath = path2.join(baseDir, filename);
370
- if (!fs.existsSync(filePath)) continue;
371
- try {
372
- const raw = fs.readFileSync(filePath, "utf8");
373
- const parsed = parseDotenv(raw);
374
- for (const [key, value] of Object.entries(parsed)) {
375
- if (values[key] === void 0) {
376
- values[key] = value;
377
- }
378
- }
379
- } catch (error) {
380
- const message = extractErrorMessage(
381
- error,
382
- "Unable to read or parse dotenv file."
383
- );
384
- if (debug) {
385
- console.warn(
386
- `[opensteer] failed to load dotenv file "${filePath}": ${message}`
387
- );
388
- }
389
- continue;
390
- }
391
- }
392
- return values;
393
- }
394
- function resolveEnv(rootDir, options = {}) {
395
- const baseEnv = options.baseEnv ?? process.env;
396
- const dotenvValues = loadDotenvValues(rootDir, baseEnv, options);
397
- return {
398
- ...dotenvValues,
399
- ...baseEnv
400
- };
401
- }
402
- function hasOwn(config, key) {
403
- if (!config || typeof config !== "object") return false;
404
- return Object.prototype.hasOwnProperty.call(config, key);
405
- }
406
- function hasLegacyAiConfig(config) {
407
- return hasOwn(config, "ai");
408
- }
409
- function assertNoLegacyAiConfig(source, config) {
410
- if (hasLegacyAiConfig(config)) {
411
- throw new Error(
412
- `Legacy "ai" config is no longer supported in ${source}. Use top-level "model" instead.`
413
- );
414
- }
415
- }
416
- function assertNoLegacyRuntimeConfig(source, config) {
417
- if (!config || typeof config !== "object") return;
418
- const configRecord = config;
419
- if (hasOwn(configRecord, "runtime")) {
420
- throw new Error(
421
- `Legacy "runtime" config is no longer supported in ${source}. Use top-level "cloud" instead.`
422
- );
423
- }
424
- if (hasOwn(configRecord, "mode")) {
425
- throw new Error(
426
- `Top-level "mode" config is no longer supported in ${source}. Use "cloud: true" to enable cloud mode.`
427
- );
428
- }
429
- if (hasOwn(configRecord, "remote")) {
430
- throw new Error(
431
- `Top-level "remote" config is no longer supported in ${source}. Use "cloud" options instead.`
432
- );
433
- }
434
- if (hasOwn(configRecord, "apiKey")) {
435
- throw new Error(
436
- `Top-level "apiKey" config is not supported in ${source}. Use "cloud.apiKey" instead.`
437
- );
438
- }
439
- }
440
- function loadConfigFile(rootDir, options = {}) {
441
- const configPath = path2.join(rootDir, ".opensteer", "config.json");
442
- if (!fs.existsSync(configPath)) return {};
443
- try {
444
- const raw = fs.readFileSync(configPath, "utf8");
445
- return JSON.parse(raw);
446
- } catch (error) {
447
- const message = extractErrorMessage(
448
- error,
449
- "Unable to read or parse config file."
450
- );
451
- if (options.debug) {
452
- console.warn(
453
- `[opensteer] failed to load config file "${configPath}": ${message}`
454
- );
455
- }
456
- return {};
457
- }
458
- }
459
- function mergeDeep(base, patch) {
460
- const out = {
461
- ...base
462
- };
463
- for (const [key, value] of Object.entries(patch || {})) {
464
- const currentValue = out[key];
465
- if (value && typeof value === "object" && !Array.isArray(value) && currentValue && typeof currentValue === "object" && !Array.isArray(currentValue)) {
466
- out[key] = mergeDeep(
467
- currentValue,
468
- value
469
- );
470
- continue;
471
- }
472
- if (value !== void 0) {
473
- out[key] = value;
474
- }
475
- }
476
- return out;
477
- }
478
- function parseBool(value) {
479
- if (value == null) return void 0;
480
- const normalized = value.trim().toLowerCase();
481
- if (normalized === "true" || normalized === "1") return true;
482
- if (normalized === "false" || normalized === "0") return false;
483
- return void 0;
484
- }
485
- function parseNumber(value) {
486
- if (value == null || value.trim() === "") return void 0;
487
- const parsed = Number(value);
488
- if (!Number.isFinite(parsed)) return void 0;
489
- return parsed;
490
- }
491
- function parseRuntimeMode(value, source) {
492
- if (value == null) return void 0;
493
- if (typeof value !== "string") {
494
- throw new Error(
495
- `Invalid ${source} value "${String(value)}". Use "local" or "cloud".`
496
- );
497
- }
498
- const normalized = value.trim().toLowerCase();
499
- if (!normalized) return void 0;
500
- if (normalized === "local" || normalized === "cloud") {
501
- return normalized;
502
- }
503
- throw new Error(
504
- `Invalid ${source} value "${value}". Use "local" or "cloud".`
505
- );
506
- }
507
- function parseAuthScheme(value, source) {
508
- if (value == null) return void 0;
509
- if (typeof value !== "string") {
510
- throw new Error(
511
- `Invalid ${source} value "${String(value)}". Use "api-key" or "bearer".`
512
- );
513
- }
514
- const normalized = value.trim().toLowerCase();
515
- if (!normalized) return void 0;
516
- if (normalized === "api-key" || normalized === "bearer") {
517
- return normalized;
518
- }
519
- throw new Error(
520
- `Invalid ${source} value "${value}". Use "api-key" or "bearer".`
521
- );
522
- }
523
- function parseCloudAnnounce(value, source) {
524
- if (value == null) return void 0;
525
- if (typeof value !== "string") {
526
- throw new Error(
527
- `Invalid ${source} value "${String(value)}". Use "always", "off", or "tty".`
528
- );
529
- }
530
- const normalized = value.trim().toLowerCase();
531
- if (!normalized) return void 0;
532
- if (normalized === "always" || normalized === "off" || normalized === "tty") {
533
- return normalized;
534
- }
535
- throw new Error(
536
- `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
537
- );
538
- }
539
- function resolveOpensteerApiKey(env) {
540
- const value = env.OPENSTEER_API_KEY?.trim();
541
- if (!value) return void 0;
542
- return value;
543
- }
544
- function resolveOpensteerAccessToken(env) {
545
- const value = env.OPENSTEER_ACCESS_TOKEN?.trim();
546
- if (!value) return void 0;
547
- return value;
548
- }
549
- function resolveOpensteerBaseUrl(env) {
550
- const value = env.OPENSTEER_BASE_URL?.trim();
551
- if (!value) return void 0;
552
- return value;
553
- }
554
- function resolveOpensteerAuthScheme(env) {
555
- return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
556
- }
557
- function resolveOpensteerCloudProfileId(env) {
558
- const value = env.OPENSTEER_CLOUD_PROFILE_ID?.trim();
559
- if (!value) return void 0;
560
- return value;
561
- }
562
- function resolveOpensteerCloudProfileReuseIfActive(env) {
563
- return parseBool(env.OPENSTEER_CLOUD_PROFILE_REUSE_IF_ACTIVE);
564
- }
565
- function parseCloudBrowserProfileReuseIfActive(value) {
566
- if (value == null) return void 0;
567
- if (typeof value !== "boolean") {
568
- throw new Error(
569
- `Invalid cloud.browserProfile.reuseIfActive value "${String(
570
- value
571
- )}". Use true or false.`
572
- );
573
- }
574
- return value;
575
- }
576
- function normalizeCloudBrowserProfileOptions(value, source) {
577
- if (value == null) {
578
- return void 0;
579
- }
580
- if (typeof value !== "object" || Array.isArray(value)) {
581
- throw new Error(
582
- `Invalid ${source} value "${String(value)}". Use an object with profileId and optional reuseIfActive.`
583
- );
584
- }
585
- const record = value;
586
- const rawProfileId = record.profileId;
587
- if (typeof rawProfileId !== "string" || !rawProfileId.trim()) {
588
- throw new Error(
589
- `${source}.profileId must be a non-empty string when browserProfile is provided.`
590
- );
591
- }
592
- return {
593
- profileId: rawProfileId.trim(),
594
- reuseIfActive: parseCloudBrowserProfileReuseIfActive(record.reuseIfActive)
595
- };
596
- }
597
- function resolveEnvCloudBrowserProfile(profileId, reuseIfActive) {
598
- if (reuseIfActive !== void 0 && !profileId) {
599
- throw new Error(
600
- "OPENSTEER_CLOUD_PROFILE_REUSE_IF_ACTIVE requires OPENSTEER_CLOUD_PROFILE_ID."
601
- );
602
- }
603
- if (!profileId) {
604
- return void 0;
605
- }
606
- return {
607
- profileId,
608
- reuseIfActive
609
- };
610
- }
611
- function normalizeCloudOptions(value) {
612
- if (!value || typeof value !== "object" || Array.isArray(value)) {
613
- return void 0;
614
- }
615
- return value;
616
- }
617
- function parseCloudEnabled(value, source) {
618
- if (value == null) return void 0;
619
- if (typeof value === "boolean") return value;
620
- if (typeof value === "object" && !Array.isArray(value)) return true;
621
- throw new Error(
622
- `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
623
- );
624
- }
625
- function resolveCloudCredentialFields(selectedLayer) {
626
- const credential = selectedLayer?.credential;
627
- if (!credential) return {};
628
- if (credential.kind === "api-key" || credential.compatibilityBearerApiKey === true && selectedLayer?.source !== "env") {
629
- return {
630
- apiKey: credential.token
631
- };
632
- }
633
- return {
634
- accessToken: credential.token
635
- };
636
- }
637
- function resolveCloudSelection(config, env = process.env) {
638
- const configCloud = parseCloudEnabled(config.cloud, "cloud");
639
- if (configCloud !== void 0) {
640
- return {
641
- cloud: configCloud,
642
- source: "config.cloud"
643
- };
644
- }
645
- const envMode = parseRuntimeMode(env.OPENSTEER_MODE, "OPENSTEER_MODE");
646
- if (envMode) {
647
- return {
648
- cloud: envMode === "cloud",
649
- source: "env.OPENSTEER_MODE"
650
- };
651
- }
652
- return {
653
- cloud: false,
654
- source: "default"
655
- };
656
- }
657
- function resolveConfigWithEnv(input = {}, options = {}) {
658
- const processEnv = options.env ?? process.env;
659
- const debugHint = typeof input.debug === "boolean" ? input.debug : parseBool(processEnv.OPENSTEER_DEBUG) === true;
660
- const initialRootDir = input.storage?.rootDir ?? process.cwd();
661
- const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
662
- storage: {
663
- rootDir: initialRootDir
664
- }
665
- });
666
- assertNoLegacyAiConfig("Opensteer constructor config", input);
667
- assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
668
- const fileConfig = loadConfigFile(initialRootDir, {
669
- debug: debugHint
670
- });
671
- assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
672
- assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
673
- const fileCloudOptions = normalizeCloudOptions(fileConfig.cloud);
674
- const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
675
- const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
676
- const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
677
- assertNoRemovedBrowserConfig(input.browser, "Opensteer constructor config");
678
- assertNoRemovedBrowserConfig(fileConfig.browser, ".opensteer/config.json");
679
- const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
680
- const env = resolveEnv(envRootDir, {
681
- debug: debugHint,
682
- baseEnv: processEnv
683
- });
684
- if (env.OPENSTEER_AI_MODEL) {
685
- throw new Error(
686
- "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
687
- );
688
- }
689
- if (env.OPENSTEER_RUNTIME != null) {
690
- throw new Error(
691
- "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
692
- );
693
- }
694
- if (env.OPENSTEER_CONNECT_URL != null) {
695
- throw new Error(
696
- "OPENSTEER_CONNECT_URL is no longer supported. Use OPENSTEER_CDP_URL instead."
697
- );
698
- }
699
- if (env.OPENSTEER_CHANNEL != null) {
700
- throw new Error(
701
- "OPENSTEER_CHANNEL is no longer supported. Use OPENSTEER_BROWSER plus OPENSTEER_BROWSER_PATH when needed."
702
- );
703
- }
704
- if (env.OPENSTEER_PROFILE_DIR != null) {
705
- throw new Error(
706
- "OPENSTEER_PROFILE_DIR is no longer supported. Use OPENSTEER_USER_DATA_DIR and OPENSTEER_PROFILE_DIRECTORY instead."
707
- );
708
- }
709
- const envConfig = {
710
- browser: {
711
- headless: parseBool(env.OPENSTEER_HEADLESS),
712
- executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
713
- slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
714
- mode: env.OPENSTEER_BROWSER === "real" || env.OPENSTEER_BROWSER === "chromium" ? env.OPENSTEER_BROWSER : void 0,
715
- cdpUrl: env.OPENSTEER_CDP_URL || void 0,
716
- userDataDir: env.OPENSTEER_USER_DATA_DIR || void 0,
717
- profileDirectory: env.OPENSTEER_PROFILE_DIRECTORY || void 0
718
- },
719
- cursor: {
720
- enabled: parseBool(env.OPENSTEER_CURSOR)
721
- },
722
- model: env.OPENSTEER_MODEL || void 0,
723
- debug: parseBool(env.OPENSTEER_DEBUG)
724
- };
725
- const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
726
- const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
727
- const resolved = mergeDeep(mergedWithEnv, input);
728
- const browserHeadlessExplicit = input.browser?.headless !== void 0 || fileConfig.browser?.headless !== void 0 || envConfig.browser?.headless !== void 0;
729
- if (!browserHeadlessExplicit && resolved.browser?.mode === "real") {
730
- resolved.browser = {
731
- ...resolved.browser,
732
- headless: true
733
- };
734
- }
735
- function assertNoRemovedBrowserConfig(value, source) {
736
- if (!value || typeof value !== "object" || Array.isArray(value)) {
737
- return;
738
- }
739
- const record = value;
740
- if (record.connectUrl !== void 0) {
741
- throw new Error(
742
- `${source}.browser.connectUrl is no longer supported. Use browser.cdpUrl instead.`
743
- );
744
- }
745
- if (record.channel !== void 0) {
746
- throw new Error(
747
- `${source}.browser.channel is no longer supported. Use browser.mode plus browser.executablePath instead.`
748
- );
749
- }
750
- if (record.profileDir !== void 0) {
751
- throw new Error(
752
- `${source}.browser.profileDir is no longer supported. Use browser.userDataDir and browser.profileDirectory instead.`
753
- );
754
- }
755
- }
756
- const envApiKey = resolveOpensteerApiKey(env);
757
- const envAccessTokenRaw = resolveOpensteerAccessToken(env);
758
- const envBaseUrl = resolveOpensteerBaseUrl(env);
759
- const envAuthScheme = resolveOpensteerAuthScheme(env);
760
- const envCloudProfileId = resolveOpensteerCloudProfileId(env);
761
- const envCloudProfileReuseIfActive = resolveOpensteerCloudProfileReuseIfActive(env);
762
- const envCloudAnnounce = parseCloudAnnounce(
763
- env.OPENSTEER_REMOTE_ANNOUNCE,
764
- "OPENSTEER_REMOTE_ANNOUNCE"
765
- );
766
- const inputCloudOptions = normalizeCloudOptions(input.cloud);
767
- const inputCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
768
- inputCloudOptions?.browserProfile,
769
- "cloud.browserProfile"
770
- );
771
- const inputAuthScheme = parseAuthScheme(
772
- inputCloudOptions?.authScheme,
773
- "cloud.authScheme"
774
- );
775
- const inputCloudAnnounce = parseCloudAnnounce(
776
- inputCloudOptions?.announce,
777
- "cloud.announce"
778
- );
779
- const inputHasCloudApiKey = Boolean(
780
- inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
781
- );
782
- const inputHasCloudAccessToken = Boolean(
783
- inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "accessToken")
784
- );
785
- const inputHasCloudBaseUrl = Boolean(
786
- inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
787
- );
788
- const cloudSelection = resolveCloudSelection({
789
- cloud: resolved.cloud
790
- }, env);
791
- if (cloudSelection.cloud) {
792
- const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
793
- const {
794
- apiKey: resolvedCloudApiKeyRaw,
795
- accessToken: resolvedCloudAccessTokenRaw,
796
- ...resolvedCloudRest
797
- } = resolvedCloud;
798
- const resolvedCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
799
- resolvedCloud.browserProfile,
800
- "resolved.cloud.browserProfile"
801
- );
802
- const envCloudBrowserProfile = resolveEnvCloudBrowserProfile(
803
- envCloudProfileId,
804
- envCloudProfileReuseIfActive
805
- );
806
- const browserProfile = inputCloudBrowserProfile ?? envCloudBrowserProfile ?? resolvedCloudBrowserProfile;
807
- let authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
808
- const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
809
- const selectedCredentialLayer = selectCloudCredentialByPrecedence(
810
- [
811
- {
812
- source: "input",
813
- apiKey: inputCloudOptions?.apiKey,
814
- accessToken: inputCloudOptions?.accessToken,
815
- hasApiKey: inputHasCloudApiKey,
816
- hasAccessToken: inputHasCloudAccessToken
817
- },
818
- {
819
- source: "env",
820
- apiKey: envApiKey,
821
- accessToken: envAccessTokenRaw,
822
- hasApiKey: envApiKey !== void 0,
823
- hasAccessToken: envAccessTokenRaw !== void 0
824
- },
825
- {
826
- source: "file",
827
- apiKey: fileCloudOptions?.apiKey,
828
- accessToken: fileCloudOptions?.accessToken,
829
- hasApiKey: fileHasCloudApiKey,
830
- hasAccessToken: fileHasCloudAccessToken
831
- }
832
- ],
833
- authScheme
834
- );
835
- const { apiKey, accessToken } = resolveCloudCredentialFields(selectedCredentialLayer);
836
- if (accessToken) {
837
- authScheme = "bearer";
838
- }
839
- resolved.cloud = {
840
- ...resolvedCloudRest,
841
- ...apiKey ? { apiKey } : selectedCredentialLayer?.hasApiKey && !accessToken ? { apiKey: selectedCredentialLayer.apiKey } : {},
842
- ...accessToken ? { accessToken } : selectedCredentialLayer?.hasAccessToken && !apiKey ? { accessToken: selectedCredentialLayer.accessToken } : {},
843
- authScheme,
844
- announce,
845
- ...browserProfile ? { browserProfile } : {}
846
- };
847
- }
848
- if (envBaseUrl && cloudSelection.cloud && !inputHasCloudBaseUrl) {
849
- resolved.cloud = {
850
- ...normalizeCloudOptions(resolved.cloud) ?? {},
851
- baseUrl: envBaseUrl
852
- };
853
- }
854
- return {
855
- config: resolved,
856
- env
857
- };
858
- }
859
- function resolveNamespace(config, rootDir) {
860
- if (config.name && config.name.trim()) {
861
- return normalizeNamespace(config.name);
862
- }
863
- const caller = getCallerFilePath();
864
- if (!caller) return normalizeNamespace("default");
865
- const relative = path2.relative(rootDir, caller);
866
- const cleaned = relative.replace(/\\/g, "/").replace(/\.(ts|tsx|js|mjs|cjs)$/, "");
867
- return normalizeNamespace(cleaned || "default");
868
- }
869
- function getCallerFilePath() {
870
- const stack = new Error().stack;
871
- if (!stack) return null;
872
- const lines = stack.split("\n").slice(2);
873
- for (const line of lines) {
874
- const match = line.match(/\((.*):(\d+):(\d+)\)/) || line.match(/at\s+(.*):(\d+):(\d+)/);
875
- if (!match) continue;
876
- const rawPath = match[1];
877
- if (!rawPath) continue;
878
- if (rawPath.includes("node:internal")) continue;
879
- if (rawPath.includes("node_modules")) continue;
880
- if (rawPath.includes("/opensteer-oss/src/")) continue;
881
- try {
882
- if (rawPath.startsWith("file://")) {
883
- return fileURLToPath(rawPath);
884
- }
885
- return rawPath;
886
- } catch {
887
- continue;
888
- }
889
- }
890
- return null;
891
- }
892
-
893
- // src/cloud/contracts.ts
894
- var cloudActionMethods = [
895
- "goto",
896
- "snapshot",
897
- "screenshot",
898
- "state",
899
- "click",
900
- "dblclick",
901
- "rightclick",
902
- "hover",
903
- "input",
904
- "select",
905
- "scroll",
906
- "tabs",
907
- "newTab",
908
- "switchTab",
909
- "closeTab",
910
- "getCookies",
911
- "setCookie",
912
- "clearCookies",
913
- "pressKey",
914
- "type",
915
- "getElementText",
916
- "getElementValue",
917
- "getElementAttributes",
918
- "getElementBoundingBox",
919
- "getHtml",
920
- "getTitle",
921
- "uploadFile",
922
- "exportCookies",
923
- "importCookies",
924
- "waitForText",
925
- "extract",
926
- "extractFromPlan",
927
- "clearCache"
928
- ];
929
- var cloudErrorCodes = [
930
- "CLOUD_AUTH_FAILED",
931
- "CLOUD_SESSION_NOT_FOUND",
932
- "CLOUD_SESSION_CLOSED",
933
- "CLOUD_UNSUPPORTED_METHOD",
934
- "CLOUD_INVALID_REQUEST",
935
- "CLOUD_MODEL_NOT_ALLOWED",
936
- "CLOUD_ACTION_FAILED",
937
- "CLOUD_CAPACITY_EXHAUSTED",
938
- "CLOUD_RUNTIME_UNAVAILABLE",
939
- "CLOUD_RUNTIME_MISMATCH",
940
- "CLOUD_SESSION_STALE",
941
- "CLOUD_CONTROL_PLANE_ERROR",
942
- "CLOUD_CONTRACT_MISMATCH",
943
- "CLOUD_PROXY_UNAVAILABLE",
944
- "CLOUD_PROXY_REQUIRED",
945
- "CLOUD_BILLING_LIMIT_REACHED",
946
- "CLOUD_RATE_LIMITED",
947
- "CLOUD_BROWSER_PROFILE_NOT_FOUND",
948
- "CLOUD_BROWSER_PROFILE_BUSY",
949
- "CLOUD_BROWSER_PROFILE_DISABLED",
950
- "CLOUD_BROWSER_PROFILE_PROXY_UNAVAILABLE",
951
- "CLOUD_BROWSER_PROFILE_SYNC_FAILED",
952
- "CLOUD_INTERNAL"
953
- ];
954
- var cloudSessionStatuses = [
955
- "provisioning",
956
- "active",
957
- "closing",
958
- "closed",
959
- "failed"
960
- ];
961
- var cloudSessionContractVersion = "v3";
962
- var cloudSessionSourceTypes = [
963
- "agent-thread",
964
- "agent-run",
965
- "project-agent-run",
966
- "local-cloud",
967
- "manual"
968
- ];
969
- var cloudActionMethodSet = new Set(cloudActionMethods);
970
- var cloudErrorCodeSet = new Set(cloudErrorCodes);
971
- var cloudSessionSourceTypeSet = new Set(
972
- cloudSessionSourceTypes
973
- );
974
- var cloudSessionStatusSet = new Set(cloudSessionStatuses);
975
- function isCloudActionMethod(value) {
976
- return typeof value === "string" && cloudActionMethodSet.has(value);
977
- }
978
- function isCloudErrorCode(value) {
979
- return typeof value === "string" && cloudErrorCodeSet.has(value);
980
- }
981
- function isCloudSessionSourceType(value) {
982
- return typeof value === "string" && cloudSessionSourceTypeSet.has(value);
983
- }
984
- function isCloudSessionStatus(value) {
985
- return typeof value === "string" && cloudSessionStatusSet.has(value);
986
- }
987
-
988
- // src/cloud/errors.ts
989
- var OpensteerCloudError = class extends Error {
990
- code;
991
- status;
992
- details;
993
- constructor(code, message, status, details) {
994
- super(message);
995
- this.name = "OpensteerCloudError";
996
- this.code = code;
997
- this.status = status;
998
- this.details = details;
999
- }
1000
- };
1001
- function cloudUnsupportedMethodError(method, message) {
1002
- return new OpensteerCloudError(
1003
- "CLOUD_UNSUPPORTED_METHOD",
1004
- message || `${method} is not supported in cloud mode.`
1005
- );
1006
- }
1007
- function cloudNotLaunchedError() {
1008
- return new OpensteerCloudError(
1009
- "CLOUD_SESSION_NOT_FOUND",
1010
- "Cloud session is not connected. Call launch() first."
1011
- );
1012
- }
1013
-
1014
- // src/cloud/cdp-client.ts
1015
- import {
1016
- chromium
1017
- } from "playwright";
1018
-
1019
- // src/cloud/ws-url.ts
1020
- function withTokenQuery(wsUrl, token) {
1021
- const url = new URL(wsUrl);
1022
- url.searchParams.set("token", token);
1023
- return url.toString();
1024
- }
1025
-
1026
- // src/cloud/cdp-client.ts
1027
- var CloudCdpClient = class {
1028
- async connect(args) {
1029
- const endpoint = withTokenQuery(args.wsUrl, args.token);
1030
- let browser;
1031
- try {
1032
- browser = await chromium.connectOverCDP(endpoint);
1033
- } catch (error) {
1034
- const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
1035
- throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
1036
- }
1037
- const contexts = browser.contexts();
1038
- const context = contexts[0];
1039
- if (!context) {
1040
- await browser.close();
1041
- throw new OpensteerCloudError(
1042
- "CLOUD_INTERNAL",
1043
- "Cloud browser returned no context."
1044
- );
1045
- }
1046
- const preferred = selectPreferredContextPage(browser, contexts);
1047
- if (preferred) {
1048
- return preferred;
1049
- }
1050
- const page = context.pages()[0] || await context.newPage();
1051
- return { browser, context, page };
1052
- }
1053
- };
1054
- function selectPreferredContextPage(browser, contexts) {
1055
- let aboutBlankCandidate = null;
1056
- for (const context of contexts) {
1057
- for (const page of context.pages()) {
1058
- const url = safePageUrl(page);
1059
- if (!isInternalOrEmptyUrl(url)) {
1060
- return { browser, context, page };
1061
- }
1062
- if (!aboutBlankCandidate && url === "about:blank") {
1063
- aboutBlankCandidate = { browser, context, page };
1064
- }
1065
- }
1066
- }
1067
- return aboutBlankCandidate;
1068
- }
1069
- function safePageUrl(page) {
1070
- try {
1071
- return page.url();
1072
- } catch {
1073
- return "";
1074
- }
1075
- }
1076
- function isInternalOrEmptyUrl(url) {
1077
- if (!url) return true;
1078
- if (url === "about:blank") return true;
1079
- return url.startsWith("chrome://") || url.startsWith("devtools://") || url.startsWith("edge://");
1080
- }
1081
-
1082
- // src/utils/strip-trailing-slashes.ts
1083
- function stripTrailingSlashes(value) {
1084
- let end = value.length;
1085
- while (end > 0 && value.charCodeAt(end - 1) === 47) {
1086
- end -= 1;
1087
- }
1088
- return end === value.length ? value : value.slice(0, end);
1089
- }
1090
-
1091
- // src/cloud/http-client.ts
1092
- function normalizeCloudBaseUrl(baseUrl) {
1093
- return stripTrailingSlashes(baseUrl);
1094
- }
1095
- function cloudAuthHeaders(key, authScheme) {
1096
- if (authScheme === "bearer") {
1097
- return {
1098
- authorization: `Bearer ${key}`
1099
- };
1100
- }
1101
- return {
1102
- "x-api-key": key
1103
- };
1104
- }
1105
- async function parseCloudHttpError(response) {
1106
- let body = null;
1107
- try {
1108
- body = await response.json();
1109
- } catch {
1110
- body = null;
1111
- }
1112
- const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
1113
- const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
1114
- return new OpensteerCloudError(code, message, response.status, body?.details);
1115
- }
1116
- function toCloudErrorCode(code) {
1117
- return isCloudErrorCode(code) ? code : "CLOUD_TRANSPORT_ERROR";
1118
- }
1119
-
1120
- // src/cloud/session-client.ts
1121
- var CACHE_IMPORT_BATCH_SIZE = 200;
1122
- var CloudSessionClient = class {
1123
- baseUrl;
1124
- key;
1125
- authScheme;
1126
- constructor(baseUrl, key, authScheme = "api-key") {
1127
- this.baseUrl = normalizeCloudBaseUrl(baseUrl);
1128
- this.key = key;
1129
- this.authScheme = authScheme;
1130
- }
1131
- async create(request) {
1132
- const response = await fetch(`${this.baseUrl}/sessions`, {
1133
- method: "POST",
1134
- headers: {
1135
- "content-type": "application/json",
1136
- ...cloudAuthHeaders(this.key, this.authScheme)
1137
- },
1138
- body: JSON.stringify(request)
1139
- });
1140
- if (!response.ok) {
1141
- throw await parseCloudHttpError(response);
1142
- }
1143
- let body;
1144
- try {
1145
- body = await response.json();
1146
- } catch {
1147
- throw new OpensteerCloudError(
1148
- "CLOUD_CONTRACT_MISMATCH",
1149
- "Invalid cloud session create response: expected a JSON object.",
1150
- response.status
1151
- );
1152
- }
1153
- return parseCreateResponse(body, response.status);
1154
- }
1155
- async close(sessionId) {
1156
- const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
1157
- method: "DELETE",
1158
- headers: {
1159
- ...cloudAuthHeaders(this.key, this.authScheme)
1160
- }
1161
- });
1162
- if (response.status === 204) {
1163
- return;
1164
- }
1165
- if (!response.ok) {
1166
- throw await parseCloudHttpError(response);
1167
- }
1168
- }
1169
- async importSelectorCache(request) {
1170
- if (!request.entries.length) {
1171
- return zeroImportResponse();
1172
- }
1173
- let totals = zeroImportResponse();
1174
- for (let offset = 0; offset < request.entries.length; offset += CACHE_IMPORT_BATCH_SIZE) {
1175
- const batch = request.entries.slice(
1176
- offset,
1177
- offset + CACHE_IMPORT_BATCH_SIZE
1178
- );
1179
- const response = await this.importSelectorCacheBatch(batch);
1180
- totals = mergeImportResponse(totals, response);
1181
- }
1182
- return totals;
1183
- }
1184
- async importSelectorCacheBatch(entries) {
1185
- const response = await fetch(`${this.baseUrl}/selector-cache/import`, {
1186
- method: "POST",
1187
- headers: {
1188
- "content-type": "application/json",
1189
- ...cloudAuthHeaders(this.key, this.authScheme)
1190
- },
1191
- body: JSON.stringify({ entries })
1192
- });
1193
- if (!response.ok) {
1194
- throw await parseCloudHttpError(response);
1195
- }
1196
- return await response.json();
1197
- }
1198
- };
1199
- function parseCreateResponse(body, status) {
1200
- const root = requireObject(
1201
- body,
1202
- "Invalid cloud session create response: expected a JSON object.",
1203
- status
1204
- );
1205
- const sessionId = requireString(root, "sessionId", status);
1206
- const actionWsUrl = requireString(root, "actionWsUrl", status);
1207
- const cdpWsUrl = requireString(root, "cdpWsUrl", status);
1208
- const actionToken = requireString(root, "actionToken", status);
1209
- const cdpToken = requireString(root, "cdpToken", status);
1210
- const cloudSessionUrl = requireString(root, "cloudSessionUrl", status);
1211
- const cloudSessionRoot = requireObject(
1212
- root.cloudSession,
1213
- "Invalid cloud session create response: cloudSession must be an object.",
1214
- status
1215
- );
1216
- const cloudSession = {
1217
- sessionId: requireString(cloudSessionRoot, "sessionId", status, "cloudSession"),
1218
- workspaceId: requireString(
1219
- cloudSessionRoot,
1220
- "workspaceId",
1221
- status,
1222
- "cloudSession"
1223
- ),
1224
- state: requireSessionStatus(
1225
- cloudSessionRoot,
1226
- "state",
1227
- status,
1228
- "cloudSession"
1229
- ),
1230
- createdAt: requireNumber(cloudSessionRoot, "createdAt", status, "cloudSession"),
1231
- sourceType: requireSourceType(cloudSessionRoot, "sourceType", status, "cloudSession"),
1232
- sourceRef: optionalString(cloudSessionRoot, "sourceRef", status, "cloudSession"),
1233
- label: optionalString(cloudSessionRoot, "label", status, "cloudSession")
1234
- };
1235
- const expiresAt = optionalNumber(root, "expiresAt", status);
1236
- return {
1237
- sessionId,
1238
- actionWsUrl,
1239
- cdpWsUrl,
1240
- actionToken,
1241
- cdpToken,
1242
- expiresAt,
1243
- cloudSessionUrl,
1244
- cloudSession
1245
- };
1246
- }
1247
- function requireObject(value, message, status) {
1248
- if (!value || typeof value !== "object" || Array.isArray(value)) {
1249
- throw new OpensteerCloudError("CLOUD_CONTRACT_MISMATCH", message, status);
1250
- }
1251
- return value;
1252
- }
1253
- function requireString(source, field, status, parent) {
1254
- const value = source[field];
1255
- if (typeof value !== "string" || !value.trim()) {
1256
- throw new OpensteerCloudError(
1257
- "CLOUD_CONTRACT_MISMATCH",
1258
- `Invalid cloud session create response: ${formatFieldPath(
1259
- field,
1260
- parent
1261
- )} must be a non-empty string.`,
1262
- status
1263
- );
1264
- }
1265
- return value;
1266
- }
1267
- function requireNumber(source, field, status, parent) {
1268
- const value = source[field];
1269
- if (typeof value !== "number" || !Number.isFinite(value)) {
1270
- throw new OpensteerCloudError(
1271
- "CLOUD_CONTRACT_MISMATCH",
1272
- `Invalid cloud session create response: ${formatFieldPath(
1273
- field,
1274
- parent
1275
- )} must be a finite number.`,
1276
- status
1277
- );
1278
- }
1279
- return value;
1280
- }
1281
- function optionalString(source, field, status, parent) {
1282
- const value = source[field];
1283
- if (value == null) {
1284
- return void 0;
1285
- }
1286
- if (typeof value !== "string") {
1287
- throw new OpensteerCloudError(
1288
- "CLOUD_CONTRACT_MISMATCH",
1289
- `Invalid cloud session create response: ${formatFieldPath(
1290
- field,
1291
- parent
1292
- )} must be a string when present.`,
1293
- status
1294
- );
1295
- }
1296
- return value;
1297
- }
1298
- function optionalNumber(source, field, status, parent) {
1299
- const value = source[field];
1300
- if (value == null) {
1301
- return void 0;
1302
- }
1303
- if (typeof value !== "number" || !Number.isFinite(value)) {
1304
- throw new OpensteerCloudError(
1305
- "CLOUD_CONTRACT_MISMATCH",
1306
- `Invalid cloud session create response: ${formatFieldPath(
1307
- field,
1308
- parent
1309
- )} must be a finite number when present.`,
1310
- status
1311
- );
1312
- }
1313
- return value;
1314
- }
1315
- function requireSourceType(source, field, status, parent) {
1316
- const value = source[field];
1317
- if (isCloudSessionSourceType(value)) {
1318
- return value;
1319
- }
1320
- throw new OpensteerCloudError(
1321
- "CLOUD_CONTRACT_MISMATCH",
1322
- `Invalid cloud session create response: ${formatFieldPath(
1323
- field,
1324
- parent
1325
- )} must be one of ${formatAllowedValues(cloudSessionSourceTypes)}.`,
1326
- status
1327
- );
1328
- }
1329
- function requireSessionStatus(source, field, status, parent) {
1330
- const value = source[field];
1331
- if (isCloudSessionStatus(value)) {
1332
- return value;
1333
- }
1334
- throw new OpensteerCloudError(
1335
- "CLOUD_CONTRACT_MISMATCH",
1336
- `Invalid cloud session create response: ${formatFieldPath(
1337
- field,
1338
- parent
1339
- )} must be one of ${formatAllowedValues(cloudSessionStatuses)}.`,
1340
- status
1341
- );
1342
- }
1343
- function formatFieldPath(field, parent) {
1344
- return parent ? `"${parent}.${field}"` : `"${field}"`;
1345
- }
1346
- function formatAllowedValues(values) {
1347
- if (values.length === 0) {
1348
- return "";
1349
- }
1350
- if (values.length === 1) {
1351
- return `"${values[0]}"`;
1352
- }
1353
- if (values.length === 2) {
1354
- return `"${values[0]}" or "${values[1]}"`;
1355
- }
1356
- const quotedValues = values.map((value) => `"${value}"`);
1357
- return `${quotedValues.slice(0, -1).join(", ")}, or ${quotedValues[quotedValues.length - 1]}`;
1358
- }
1359
- function zeroImportResponse() {
1360
- return {
1361
- imported: 0,
1362
- inserted: 0,
1363
- updated: 0,
1364
- skipped: 0
1365
- };
1366
- }
1367
- function mergeImportResponse(first, second) {
1368
- return {
1369
- imported: first.imported + second.imported,
1370
- inserted: first.inserted + second.inserted,
1371
- updated: first.updated + second.updated,
1372
- skipped: first.skipped + second.skipped
1373
- };
1374
- }
1375
-
1376
- // src/cloud/runtime.ts
1377
- var DEFAULT_CLOUD_BASE_URL = "https://api.opensteer.com";
1378
- function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key") {
1379
- const normalizedBaseUrl = normalizeCloudBaseUrl(baseUrl);
1380
- return {
1381
- sessionClient: new CloudSessionClient(
1382
- normalizedBaseUrl,
1383
- key,
1384
- authScheme
1385
- ),
1386
- cdpClient: new CloudCdpClient(),
1387
- baseUrl: normalizedBaseUrl,
1388
- actionClient: null,
1389
- sessionId: null,
1390
- localRunId: null,
1391
- cloudSessionUrl: null
1392
- };
1393
- }
1394
- function resolveCloudBaseUrl() {
1395
- const value = process.env.OPENSTEER_BASE_URL?.trim();
1396
- if (!value) return DEFAULT_CLOUD_BASE_URL;
1397
- return normalizeCloudBaseUrl(value);
1398
- }
1399
- function readCloudActionDescription(payload) {
1400
- const description = payload.description;
1401
- if (typeof description !== "string") return void 0;
1402
- const normalized = description.trim();
1403
- return normalized.length ? normalized : void 0;
1404
- }
1405
-
1406
- export {
1407
- extractErrorMessage,
1408
- normalizeError,
1409
- selectCloudCredential,
1410
- normalizeNamespace,
1411
- resolveNamespaceDir,
1412
- resolveCloudSelection,
1413
- resolveConfigWithEnv,
1414
- resolveNamespace,
1415
- cloudActionMethods,
1416
- cloudErrorCodes,
1417
- cloudSessionStatuses,
1418
- cloudSessionContractVersion,
1419
- cloudSessionSourceTypes,
1420
- isCloudActionMethod,
1421
- isCloudErrorCode,
1422
- isCloudSessionSourceType,
1423
- isCloudSessionStatus,
1424
- OpensteerCloudError,
1425
- cloudUnsupportedMethodError,
1426
- cloudNotLaunchedError,
1427
- withTokenQuery,
1428
- CloudCdpClient,
1429
- stripTrailingSlashes,
1430
- normalizeCloudBaseUrl,
1431
- cloudAuthHeaders,
1432
- parseCloudHttpError,
1433
- CloudSessionClient,
1434
- DEFAULT_CLOUD_BASE_URL,
1435
- createCloudRuntimeState,
1436
- readCloudActionDescription
1437
- };