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.
- package/README.md +256 -184
- package/dist/chunk-PQYA6IX2.js +32571 -0
- package/dist/chunk-PQYA6IX2.js.map +1 -0
- package/dist/cli/bin.cjs +38201 -0
- package/dist/cli/bin.cjs.map +1 -0
- package/dist/cli/bin.d.cts +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +5612 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/index.cjs +31309 -16009
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4440 -670
- package/dist/index.d.ts +4440 -670
- package/dist/index.js +438 -378
- package/dist/index.js.map +1 -0
- package/package.json +56 -62
- package/skills/README.md +21 -20
- package/skills/opensteer/SKILL.md +60 -194
- package/skills/opensteer/references/cli-reference.md +69 -113
- package/skills/opensteer/references/request-workflow.md +81 -0
- package/skills/opensteer/references/sdk-reference.md +101 -154
- package/CHANGELOG.md +0 -75
- package/bin/opensteer.mjs +0 -1423
- package/dist/browser-profile-client-CGXc0-P9.d.cts +0 -228
- package/dist/browser-profile-client-DHLzMf-K.d.ts +0 -228
- package/dist/chunk-2ES46WCO.js +0 -1437
- package/dist/chunk-3H5RRIMZ.js +0 -69
- package/dist/chunk-AVXUMEDG.js +0 -62
- package/dist/chunk-DN3GI5CH.js +0 -57
- package/dist/chunk-FAHE5DB2.js +0 -190
- package/dist/chunk-HBTSQ2V4.js +0 -15259
- package/dist/chunk-K5CL76MG.js +0 -81
- package/dist/chunk-U724TBY6.js +0 -1262
- package/dist/chunk-ZRCFF546.js +0 -77
- package/dist/cli/auth.cjs +0 -2022
- package/dist/cli/auth.d.cts +0 -114
- package/dist/cli/auth.d.ts +0 -114
- package/dist/cli/auth.js +0 -15
- package/dist/cli/local-profile.cjs +0 -197
- package/dist/cli/local-profile.d.cts +0 -18
- package/dist/cli/local-profile.d.ts +0 -18
- package/dist/cli/local-profile.js +0 -97
- package/dist/cli/profile.cjs +0 -18548
- package/dist/cli/profile.d.cts +0 -79
- package/dist/cli/profile.d.ts +0 -79
- package/dist/cli/profile.js +0 -1328
- package/dist/cli/server.cjs +0 -17232
- package/dist/cli/server.d.cts +0 -2
- package/dist/cli/server.d.ts +0 -2
- package/dist/cli/server.js +0 -977
- package/dist/cli/skills-installer.cjs +0 -230
- package/dist/cli/skills-installer.d.cts +0 -28
- package/dist/cli/skills-installer.d.ts +0 -28
- package/dist/cli/skills-installer.js +0 -201
- package/dist/extractor-4Q3TFZJB.js +0 -8
- package/dist/resolver-MGN64KCP.js +0 -7
- package/dist/types-Cr10igF3.d.cts +0 -345
- package/dist/types-Cr10igF3.d.ts +0 -345
- package/skills/electron/SKILL.md +0 -87
- package/skills/electron/references/opensteer-electron-recipes.md +0 -88
- package/skills/electron/references/opensteer-electron-workflow.md +0 -85
- package/skills/opensteer/references/examples.md +0 -118
package/dist/chunk-2ES46WCO.js
DELETED
|
@@ -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
|
-
};
|