opensteer 0.8.8 → 0.8.10
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 +4 -4
- package/dist/{chunk-3QJGBVWT.js → chunk-F3X6UOEN.js} +3338 -822
- package/dist/chunk-F3X6UOEN.js.map +1 -0
- package/dist/cli/bin.cjs +3591 -885
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +293 -78
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.cjs +1770 -855
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +206 -15
- package/dist/index.d.ts +206 -15
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/skills/opensteer/SKILL.md +47 -71
- package/skills/opensteer/references/cli-reference.md +2 -0
- package/skills/opensteer/references/request-workflow.md +309 -203
- package/skills/opensteer/references/sdk-reference.md +104 -14
- package/skills/recorder/SKILL.md +54 -0
- package/skills/recorder/references/recorder-reference.md +71 -0
- package/dist/chunk-3QJGBVWT.js.map +0 -1
package/dist/cli/bin.js
CHANGED
|
@@ -1,77 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { assertProviderSupportsEngine, createOpensteerSemanticRuntime, OpensteerBrowserManager, dispatchSemanticOperation,
|
|
2
|
+
import { assertProviderSupportsEngine, createOpensteerSemanticRuntime, OpensteerBrowserManager, dispatchSemanticOperation, loadEnvironment, normalizeOpensteerProviderMode, discoverLocalCdpBrowsers, inspectCdpEndpoint, resolveOpensteerRuntimeConfig, resolveOpensteerEngineName, resolveOpensteerProvider, resolveFilesystemWorkspacePath, FlowRecorderCollector, generateReplayScript, pathExists, readPersistedLocalBrowserSessionRecord, readPersistedCloudSessionRecord, OpensteerCloudClient, isProcessRunning } from '../chunk-F3X6UOEN.js';
|
|
3
3
|
import process2 from 'process';
|
|
4
|
-
import { readFile } from 'fs/promises';
|
|
5
|
-
import path2 from 'path';
|
|
6
4
|
import { spawn } from 'child_process';
|
|
7
5
|
import { existsSync } from 'fs';
|
|
6
|
+
import path from 'path';
|
|
8
7
|
import { createRequire } from 'module';
|
|
9
8
|
import { fileURLToPath } from 'url';
|
|
9
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
10
10
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
|
-
version: "0.8.
|
|
14
|
-
|
|
13
|
+
version: "0.8.9"};
|
|
14
|
+
|
|
15
|
+
// src/cli/env-loader.ts
|
|
15
16
|
async function loadCliEnvironment(cwd) {
|
|
16
|
-
|
|
17
|
-
const directories = collectDirectories(cwd);
|
|
18
|
-
for (const directory of directories) {
|
|
19
|
-
for (const filename of ENV_FILENAMES) {
|
|
20
|
-
const filePath = path2.join(directory, filename);
|
|
21
|
-
if (!await pathExists(filePath)) {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
const parsed = parseEnvFile(await readFile(filePath, "utf8"));
|
|
25
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
26
|
-
if (protectedKeys.has(key)) {
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
process.env[key] = value;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function collectDirectories(cwd) {
|
|
35
|
-
const directories = [];
|
|
36
|
-
let current = path2.resolve(cwd);
|
|
37
|
-
for (; ; ) {
|
|
38
|
-
directories.unshift(current);
|
|
39
|
-
const parent = path2.dirname(current);
|
|
40
|
-
if (parent === current) {
|
|
41
|
-
return directories;
|
|
42
|
-
}
|
|
43
|
-
current = parent;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function parseEnvFile(contents) {
|
|
47
|
-
const parsed = {};
|
|
48
|
-
for (const rawLine of contents.split(/\r?\n/u)) {
|
|
49
|
-
const trimmed = rawLine.trim();
|
|
50
|
-
if (!trimmed || trimmed.startsWith("#")) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const line = trimmed.startsWith("export ") ? trimmed.slice("export ".length) : trimmed;
|
|
54
|
-
const separatorIndex = line.indexOf("=");
|
|
55
|
-
if (separatorIndex <= 0) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
const key = line.slice(0, separatorIndex).trim();
|
|
59
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key)) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
const rawValue = line.slice(separatorIndex + 1).trim();
|
|
63
|
-
parsed[key] = parseEnvValue(rawValue);
|
|
64
|
-
}
|
|
65
|
-
return parsed;
|
|
66
|
-
}
|
|
67
|
-
function parseEnvValue(rawValue) {
|
|
68
|
-
if (rawValue.length >= 2 && rawValue.startsWith('"') && rawValue.endsWith('"')) {
|
|
69
|
-
return rawValue.slice(1, -1).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"');
|
|
70
|
-
}
|
|
71
|
-
if (rawValue.length >= 2 && rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
72
|
-
return rawValue.slice(1, -1);
|
|
73
|
-
}
|
|
74
|
-
return rawValue.replace(/\s+#.*$/u, "").trimEnd();
|
|
17
|
+
loadEnvironment(cwd);
|
|
75
18
|
}
|
|
76
19
|
function createOpensteerSkillsInvocation(input) {
|
|
77
20
|
const cliArgs = ["add", input.skillSourcePath];
|
|
@@ -106,22 +49,22 @@ function createOpensteerSkillsInvocation(input) {
|
|
|
106
49
|
function resolveOpensteerSkillsCliPath() {
|
|
107
50
|
const require2 = createRequire(import.meta.url);
|
|
108
51
|
const skillsPackagePath = require2.resolve("skills/package.json");
|
|
109
|
-
const skillsPackageDir =
|
|
110
|
-
const cliPath =
|
|
52
|
+
const skillsPackageDir = path.dirname(skillsPackagePath);
|
|
53
|
+
const cliPath = path.join(skillsPackageDir, "bin", "cli.mjs");
|
|
111
54
|
if (!existsSync(cliPath)) {
|
|
112
55
|
throw new Error(`skills CLI entrypoint was not found at "${cliPath}".`);
|
|
113
56
|
}
|
|
114
57
|
return cliPath;
|
|
115
58
|
}
|
|
116
59
|
function resolveOpensteerSkillSourcePath() {
|
|
117
|
-
let ancestor =
|
|
60
|
+
let ancestor = path.dirname(fileURLToPath(import.meta.url));
|
|
118
61
|
for (let index = 0; index < 6; index += 1) {
|
|
119
|
-
const candidate =
|
|
120
|
-
const skillManifest =
|
|
62
|
+
const candidate = path.join(ancestor, "skills");
|
|
63
|
+
const skillManifest = path.join(candidate, "opensteer", "SKILL.md");
|
|
121
64
|
if (existsSync(skillManifest)) {
|
|
122
65
|
return candidate;
|
|
123
66
|
}
|
|
124
|
-
ancestor =
|
|
67
|
+
ancestor = path.resolve(ancestor, "..");
|
|
125
68
|
}
|
|
126
69
|
throw new Error("Unable to find the packaged Opensteer skill source directory.");
|
|
127
70
|
}
|
|
@@ -246,7 +189,7 @@ function describeLocalLane(record, current) {
|
|
|
246
189
|
summary: "none"
|
|
247
190
|
};
|
|
248
191
|
}
|
|
249
|
-
const browser = record.executablePath ?
|
|
192
|
+
const browser = record.executablePath ? path.basename(record.executablePath).replace(/\.[A-Za-z0-9]+$/u, "") : void 0;
|
|
250
193
|
return {
|
|
251
194
|
provider: "local",
|
|
252
195
|
status: "active",
|
|
@@ -272,9 +215,11 @@ async function describeCloudLane(input) {
|
|
|
272
215
|
status: "connected",
|
|
273
216
|
current: input.current,
|
|
274
217
|
summary: input.record.sessionId,
|
|
275
|
-
detail: input.record.baseUrl,
|
|
276
218
|
sessionId: input.record.sessionId,
|
|
277
|
-
|
|
219
|
+
...input.cloudConfig === void 0 ? {} : {
|
|
220
|
+
detail: input.cloudConfig.baseUrl,
|
|
221
|
+
baseUrl: input.cloudConfig.baseUrl
|
|
222
|
+
}
|
|
278
223
|
};
|
|
279
224
|
if (input.cloudConfig === void 0) {
|
|
280
225
|
return base;
|
|
@@ -318,6 +263,121 @@ function formatLaneRow(input) {
|
|
|
318
263
|
const summary = input.summary.padEnd(16, " ");
|
|
319
264
|
return `${input.marker} ${provider} ${status} ${summary}${input.detail ?? ""}`.trimEnd();
|
|
320
265
|
}
|
|
266
|
+
async function runOpensteerRecordCommand(input) {
|
|
267
|
+
const stdout = input.stdout ?? process.stdout;
|
|
268
|
+
const stderr = input.stderr ?? process.stderr;
|
|
269
|
+
const outputPath = resolveRecordOutputPath({
|
|
270
|
+
rootDir: input.rootDir,
|
|
271
|
+
workspace: input.workspace,
|
|
272
|
+
...input.outputPath === void 0 ? {} : { outputPath: input.outputPath }
|
|
273
|
+
});
|
|
274
|
+
const runtime = input.runtime;
|
|
275
|
+
const collector = new FlowRecorderCollector(createRecorderRuntimeAdapter(runtime), {
|
|
276
|
+
...input.pollIntervalMs === void 0 ? {} : { pollIntervalMs: input.pollIntervalMs },
|
|
277
|
+
onAction: (action) => {
|
|
278
|
+
stderr.write(`${formatRecordedAction(action)}
|
|
279
|
+
`);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
stderr.write(
|
|
283
|
+
`Recording browser actions for workspace "${input.workspace}". Click "Stop recording" in the browser when you're done.
|
|
284
|
+
`
|
|
285
|
+
);
|
|
286
|
+
let closed = false;
|
|
287
|
+
try {
|
|
288
|
+
const opened = await runtime.open({
|
|
289
|
+
url: input.url
|
|
290
|
+
});
|
|
291
|
+
await collector.install();
|
|
292
|
+
collector.start();
|
|
293
|
+
await collector.waitForStop();
|
|
294
|
+
const actions = await collector.stop();
|
|
295
|
+
const script = generateReplayScript({
|
|
296
|
+
actions,
|
|
297
|
+
workspace: input.workspace,
|
|
298
|
+
startUrl: opened.url
|
|
299
|
+
});
|
|
300
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
301
|
+
await writeFile(outputPath, script, "utf8");
|
|
302
|
+
if (input.closeSession !== void 0) {
|
|
303
|
+
await input.closeSession();
|
|
304
|
+
closed = true;
|
|
305
|
+
}
|
|
306
|
+
stdout.write(`${outputPath}
|
|
307
|
+
`);
|
|
308
|
+
stderr.write(`Wrote replay script to ${outputPath}
|
|
309
|
+
`);
|
|
310
|
+
} finally {
|
|
311
|
+
if (!closed) {
|
|
312
|
+
await runtime.disconnect().catch(() => void 0);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function resolveRecordOutputPath(input) {
|
|
317
|
+
if (input.outputPath !== void 0) {
|
|
318
|
+
return path.resolve(input.rootDir, input.outputPath);
|
|
319
|
+
}
|
|
320
|
+
return path.join(
|
|
321
|
+
resolveFilesystemWorkspacePath({
|
|
322
|
+
rootDir: input.rootDir,
|
|
323
|
+
workspace: input.workspace
|
|
324
|
+
}),
|
|
325
|
+
"recorded-flow.ts"
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
function createRecorderRuntimeAdapter(runtime) {
|
|
329
|
+
return {
|
|
330
|
+
addInitScript: (input) => runtime.addInitScript(input),
|
|
331
|
+
evaluate: async (input) => {
|
|
332
|
+
const output = await runtime.evaluate({
|
|
333
|
+
script: input.script,
|
|
334
|
+
...input.pageRef === void 0 ? {} : { pageRef: input.pageRef }
|
|
335
|
+
});
|
|
336
|
+
return output.value;
|
|
337
|
+
},
|
|
338
|
+
listPages: async () => {
|
|
339
|
+
const output = await runtime.listPages();
|
|
340
|
+
return {
|
|
341
|
+
pages: output.pages.map((page) => ({
|
|
342
|
+
pageRef: page.pageRef,
|
|
343
|
+
url: page.url,
|
|
344
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef }
|
|
345
|
+
}))
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function formatRecordedAction(action) {
|
|
351
|
+
const time = new Date(action.timestamp).toISOString().slice(11, 19);
|
|
352
|
+
switch (action.kind) {
|
|
353
|
+
case "click":
|
|
354
|
+
return `[${time}] click ${action.pageId} -> ${action.selector ?? "<unknown>"}`;
|
|
355
|
+
case "dblclick":
|
|
356
|
+
return `[${time}] dblclick ${action.pageId} -> ${action.selector ?? "<unknown>"}`;
|
|
357
|
+
case "type":
|
|
358
|
+
return `[${time}] type ${action.pageId} -> ${action.selector ?? "<unknown>"} -> ${JSON.stringify(action.detail.text)}`;
|
|
359
|
+
case "keypress":
|
|
360
|
+
return `[${time}] keypress ${action.pageId} -> ${action.detail.key}`;
|
|
361
|
+
case "scroll":
|
|
362
|
+
return `[${time}] scroll ${action.pageId} -> (${String(action.detail.deltaX)}, ${String(action.detail.deltaY)})`;
|
|
363
|
+
case "select-option":
|
|
364
|
+
return `[${time}] select ${action.pageId} -> ${action.selector ?? "<unknown>"} -> ${JSON.stringify(action.detail.value)}`;
|
|
365
|
+
case "navigate":
|
|
366
|
+
return `[${time}] navigate ${action.pageId} -> ${action.detail.url}`;
|
|
367
|
+
case "new-tab":
|
|
368
|
+
return `[${time}] new-tab ${action.pageId} -> ${action.detail.initialUrl}`;
|
|
369
|
+
case "close-tab":
|
|
370
|
+
return `[${time}] close-tab ${action.pageId}`;
|
|
371
|
+
case "switch-tab":
|
|
372
|
+
return `[${time}] switch-tab -> ${action.detail.toPageId}`;
|
|
373
|
+
case "go-back":
|
|
374
|
+
return `[${time}] go-back ${action.pageId}`;
|
|
375
|
+
case "go-forward":
|
|
376
|
+
return `[${time}] go-forward ${action.pageId}`;
|
|
377
|
+
case "reload":
|
|
378
|
+
return `[${time}] reload ${action.pageId}`;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
321
381
|
|
|
322
382
|
// src/cli/bin.ts
|
|
323
383
|
var OPERATION_ALIASES = /* @__PURE__ */ new Map([
|
|
@@ -409,6 +469,10 @@ async function main() {
|
|
|
409
469
|
await handleStatusCommand(parsed);
|
|
410
470
|
return;
|
|
411
471
|
}
|
|
472
|
+
if (parsed.command[0] === "record") {
|
|
473
|
+
await handleRecordCommandEntry(parsed);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
412
476
|
const operation = parsed.command[0] === "run" ? parsed.rest[0] : resolveOperation(parsed.command);
|
|
413
477
|
if (!operation) {
|
|
414
478
|
throw new Error(`Unknown command: ${parsed.command.join(" ")}`);
|
|
@@ -553,6 +617,83 @@ async function handleBrowserCommand(parsed) {
|
|
|
553
617
|
throw new Error(`Unknown browser command: ${parsed.command.join(" ")}`);
|
|
554
618
|
}
|
|
555
619
|
}
|
|
620
|
+
async function handleRecordCommandEntry(parsed) {
|
|
621
|
+
if (parsed.options.workspace === void 0) {
|
|
622
|
+
throw new Error('record requires "--workspace <id>".');
|
|
623
|
+
}
|
|
624
|
+
const url = parsed.options.url ?? parsed.rest[0];
|
|
625
|
+
if (url === void 0) {
|
|
626
|
+
throw new Error('record requires "--url <value>" or a positional URL.');
|
|
627
|
+
}
|
|
628
|
+
const provider = resolveCliProvider(parsed);
|
|
629
|
+
assertCloudCliOptionsMatchProvider(parsed, provider.mode);
|
|
630
|
+
if (provider.mode !== "local") {
|
|
631
|
+
throw new Error(
|
|
632
|
+
'record requires provider=local. Set "--provider local" or clear OPENSTEER_PROVIDER.'
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
const engineName = resolveCliEngineName(parsed);
|
|
636
|
+
if (engineName !== "playwright") {
|
|
637
|
+
throw new Error("record requires engine=playwright.");
|
|
638
|
+
}
|
|
639
|
+
if (parsed.options.browser !== void 0 && parsed.options.browser !== "persistent") {
|
|
640
|
+
throw new Error('record only supports "--browser persistent".');
|
|
641
|
+
}
|
|
642
|
+
if (parsed.options.launch?.headless === true) {
|
|
643
|
+
throw new Error('record requires a headed browser. Remove "--headless true".');
|
|
644
|
+
}
|
|
645
|
+
const rootDir = process2.cwd();
|
|
646
|
+
const launch = {
|
|
647
|
+
...parsed.options.launch ?? {},
|
|
648
|
+
headless: false
|
|
649
|
+
};
|
|
650
|
+
const browserManager = new OpensteerBrowserManager({
|
|
651
|
+
rootDir,
|
|
652
|
+
workspace: parsed.options.workspace,
|
|
653
|
+
engineName,
|
|
654
|
+
browser: "persistent",
|
|
655
|
+
launch,
|
|
656
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context }
|
|
657
|
+
});
|
|
658
|
+
const runtime = createOpensteerSemanticRuntime({
|
|
659
|
+
provider: {
|
|
660
|
+
mode: "local"
|
|
661
|
+
},
|
|
662
|
+
engine: engineName,
|
|
663
|
+
runtimeOptions: {
|
|
664
|
+
rootPath: browserManager.rootPath,
|
|
665
|
+
cleanupRootOnClose: browserManager.cleanupRootOnDisconnect,
|
|
666
|
+
workspace: parsed.options.workspace,
|
|
667
|
+
browser: "persistent",
|
|
668
|
+
launch,
|
|
669
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context }
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
await runOpensteerRecordCommand({
|
|
673
|
+
runtime,
|
|
674
|
+
closeSession: () => closeOwnedLocalBrowserSession(runtime, browserManager),
|
|
675
|
+
workspace: parsed.options.workspace,
|
|
676
|
+
url,
|
|
677
|
+
rootDir,
|
|
678
|
+
...parsed.options.output === void 0 ? {} : { outputPath: parsed.options.output }
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
async function closeOwnedLocalBrowserSession(runtime, browserManager) {
|
|
682
|
+
let closeError;
|
|
683
|
+
try {
|
|
684
|
+
await runtime.close();
|
|
685
|
+
} catch (error) {
|
|
686
|
+
closeError = error;
|
|
687
|
+
}
|
|
688
|
+
try {
|
|
689
|
+
await browserManager.close();
|
|
690
|
+
} catch (error) {
|
|
691
|
+
closeError ??= error;
|
|
692
|
+
}
|
|
693
|
+
if (closeError !== void 0) {
|
|
694
|
+
throw closeError;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
556
697
|
function buildOperationInput(operation, parsed) {
|
|
557
698
|
if (parsed.options.inputJson !== void 0) {
|
|
558
699
|
return parsed.options.inputJson;
|
|
@@ -633,6 +774,45 @@ function resolveOperation(command) {
|
|
|
633
774
|
}
|
|
634
775
|
return void 0;
|
|
635
776
|
}
|
|
777
|
+
var CLI_OPTION_SPECS = {
|
|
778
|
+
workspace: { kind: "value" },
|
|
779
|
+
url: { kind: "value" },
|
|
780
|
+
output: { kind: "value" },
|
|
781
|
+
engine: { kind: "value" },
|
|
782
|
+
provider: { kind: "value" },
|
|
783
|
+
"cloud-base-url": { kind: "value" },
|
|
784
|
+
"cloud-api-key": { kind: "value" },
|
|
785
|
+
"cloud-profile-id": { kind: "value" },
|
|
786
|
+
"cloud-profile-reuse-if-active": { kind: "boolean" },
|
|
787
|
+
json: { kind: "boolean" },
|
|
788
|
+
agent: { kind: "value", multiple: true },
|
|
789
|
+
skill: { kind: "value", multiple: true },
|
|
790
|
+
global: { kind: "boolean" },
|
|
791
|
+
yes: { kind: "boolean" },
|
|
792
|
+
copy: { kind: "boolean" },
|
|
793
|
+
all: { kind: "boolean" },
|
|
794
|
+
list: { kind: "boolean" },
|
|
795
|
+
browser: { kind: "value" },
|
|
796
|
+
"attach-endpoint": { kind: "value" },
|
|
797
|
+
"attach-header": { kind: "value", multiple: true },
|
|
798
|
+
"fresh-tab": { kind: "boolean" },
|
|
799
|
+
headless: { kind: "boolean" },
|
|
800
|
+
"executable-path": { kind: "value" },
|
|
801
|
+
arg: { kind: "value", multiple: true },
|
|
802
|
+
"timeout-ms": { kind: "value" },
|
|
803
|
+
"context-json": { kind: "value" },
|
|
804
|
+
"input-json": { kind: "value" },
|
|
805
|
+
"schema-json": { kind: "value" },
|
|
806
|
+
"source-user-data-dir": { kind: "value" },
|
|
807
|
+
"source-profile-directory": { kind: "value" },
|
|
808
|
+
selector: { kind: "value" },
|
|
809
|
+
description: { kind: "value" },
|
|
810
|
+
element: { kind: "value" },
|
|
811
|
+
text: { kind: "value" },
|
|
812
|
+
"press-enter": { kind: "boolean" },
|
|
813
|
+
direction: { kind: "value" },
|
|
814
|
+
amount: { kind: "value" }
|
|
815
|
+
};
|
|
636
816
|
function parseCommandLine(argv) {
|
|
637
817
|
const leadingTokens = [];
|
|
638
818
|
let index = 0;
|
|
@@ -646,18 +826,43 @@ function parseCommandLine(argv) {
|
|
|
646
826
|
const rawOptions = /* @__PURE__ */ new Map();
|
|
647
827
|
while (index < argv.length) {
|
|
648
828
|
const token = argv[index];
|
|
829
|
+
if (token === "--") {
|
|
830
|
+
rest.push(...argv.slice(index + 1));
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
649
833
|
if (!token.startsWith("--")) {
|
|
650
834
|
rest.push(token);
|
|
651
835
|
index += 1;
|
|
652
836
|
continue;
|
|
653
837
|
}
|
|
654
|
-
const
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
838
|
+
const separator = token.indexOf("=");
|
|
839
|
+
const key = token.slice(2, separator === -1 ? void 0 : separator);
|
|
840
|
+
const spec = CLI_OPTION_SPECS[key];
|
|
841
|
+
if (spec === void 0) {
|
|
842
|
+
throw new Error(`Unknown option: --${key}.`);
|
|
843
|
+
}
|
|
844
|
+
if (separator !== -1) {
|
|
845
|
+
const value = token.slice(separator + 1);
|
|
846
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], value]);
|
|
658
847
|
index += 1;
|
|
659
848
|
continue;
|
|
660
849
|
}
|
|
850
|
+
const next = argv[index + 1];
|
|
851
|
+
if (spec.kind === "boolean") {
|
|
852
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
853
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], "true"]);
|
|
854
|
+
index += 1;
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], next]);
|
|
858
|
+
index += 2;
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
862
|
+
throw new Error(
|
|
863
|
+
`Option "--${key}" requires a value.${next?.startsWith("--") === true ? ` Use "--${key}=<value>" when the value begins with "--".` : ``}`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
661
866
|
rawOptions.set(key, [...rawOptions.get(key) ?? [], next]);
|
|
662
867
|
index += 2;
|
|
663
868
|
}
|
|
@@ -688,6 +893,8 @@ function parseCommandLine(argv) {
|
|
|
688
893
|
...timeoutMs === void 0 ? {} : { timeoutMs }
|
|
689
894
|
};
|
|
690
895
|
const workspace = readSingle(rawOptions, "workspace");
|
|
896
|
+
const url = readSingle(rawOptions, "url");
|
|
897
|
+
const output = readSingle(rawOptions, "output");
|
|
691
898
|
const sourceUserDataDir = readSingle(rawOptions, "source-user-data-dir");
|
|
692
899
|
const sourceProfileDirectory = readSingle(rawOptions, "source-profile-directory");
|
|
693
900
|
const selector = readSingle(rawOptions, "selector");
|
|
@@ -719,6 +926,8 @@ function parseCommandLine(argv) {
|
|
|
719
926
|
const list = readOptionalBoolean(rawOptions, "list");
|
|
720
927
|
const options = {
|
|
721
928
|
...workspace === void 0 ? {} : { workspace },
|
|
929
|
+
...url === void 0 ? {} : { url },
|
|
930
|
+
...output === void 0 ? {} : { output },
|
|
722
931
|
...requestedEngineName === void 0 ? {} : { requestedEngineName },
|
|
723
932
|
...provider === void 0 ? {} : { provider },
|
|
724
933
|
...cloudBaseUrl === void 0 ? {} : { cloudBaseUrl },
|
|
@@ -831,7 +1040,7 @@ async function handleStatusCommand(parsed) {
|
|
|
831
1040
|
const runtimeProvider = buildCliRuntimeProvider(parsed, provider.mode);
|
|
832
1041
|
const runtimeConfig = resolveOpensteerRuntimeConfig({
|
|
833
1042
|
...runtimeProvider === void 0 ? {} : { provider: runtimeProvider },
|
|
834
|
-
|
|
1043
|
+
environment: process2.env
|
|
835
1044
|
});
|
|
836
1045
|
const status = await collectOpensteerStatus({
|
|
837
1046
|
rootDir: process2.cwd(),
|
|
@@ -918,6 +1127,9 @@ function resolveCommandLength(tokens) {
|
|
|
918
1127
|
if (tokens[0] === "status") {
|
|
919
1128
|
return 1;
|
|
920
1129
|
}
|
|
1130
|
+
if (tokens[0] === "record") {
|
|
1131
|
+
return 1;
|
|
1132
|
+
}
|
|
921
1133
|
for (let length = Math.min(3, tokens.length); length >= 1; length -= 1) {
|
|
922
1134
|
if (OPERATION_ALIASES.has(tokens.slice(0, length).join(" "))) {
|
|
923
1135
|
return length;
|
|
@@ -935,6 +1147,7 @@ Usage:
|
|
|
935
1147
|
opensteer click --workspace <id> (--element <n> | --selector <css> | --description <text>)
|
|
936
1148
|
opensteer input --workspace <id> --text <value> (--element <n> | --selector <css> | --description <text>)
|
|
937
1149
|
opensteer extract --workspace <id> --description <text> [--schema-json <json>]
|
|
1150
|
+
opensteer record --workspace <id> --url <url> [--output <path>]
|
|
938
1151
|
opensteer close --workspace <id>
|
|
939
1152
|
opensteer status [--workspace <id>] [--json]
|
|
940
1153
|
|
|
@@ -952,6 +1165,8 @@ Common options:
|
|
|
952
1165
|
--help
|
|
953
1166
|
--version
|
|
954
1167
|
--workspace <id>
|
|
1168
|
+
--url <url>
|
|
1169
|
+
--output <path>
|
|
955
1170
|
--provider local|cloud
|
|
956
1171
|
--cloud-base-url <url>
|
|
957
1172
|
--cloud-api-key <key>
|