opensteer 0.8.9 → 0.8.11
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/dist/{chunk-RO6WAWWG.js → chunk-33FDEOQY.js} +1973 -55
- package/dist/chunk-33FDEOQY.js.map +1 -0
- package/dist/cli/bin.cjs +2474 -169
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +398 -10
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.cjs +206 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -20
- package/dist/index.d.ts +82 -20
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/skills/opensteer/SKILL.md +33 -14
- package/skills/opensteer/references/request-workflow.md +312 -193
- package/skills/opensteer/references/sdk-reference.md +102 -14
- package/skills/recorder/SKILL.md +54 -0
- package/skills/recorder/references/recorder-reference.md +71 -0
- package/dist/chunk-RO6WAWWG.js.map +0 -1
package/dist/cli/bin.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { assertProviderSupportsEngine, createOpensteerSemanticRuntime, OpensteerBrowserManager, dispatchSemanticOperation, loadEnvironment, normalizeOpensteerProviderMode, discoverLocalCdpBrowsers, inspectCdpEndpoint, resolveOpensteerRuntimeConfig, resolveOpensteerEngineName, resolveOpensteerProvider, resolveFilesystemWorkspacePath, pathExists, readPersistedLocalBrowserSessionRecord, readPersistedCloudSessionRecord, OpensteerCloudClient, isProcessRunning } from '../chunk-
|
|
2
|
+
import { assertProviderSupportsEngine, createOpensteerSemanticRuntime, OpensteerBrowserManager, dispatchSemanticOperation, loadEnvironment, normalizeOpensteerProviderMode, discoverLocalCdpBrowsers, inspectCdpEndpoint, resolveOpensteerRuntimeConfig, resolveOpensteerEngineName, resolveOpensteerProvider, resolveFilesystemWorkspacePath, CloudSessionProxy, FlowRecorderCollector, generateReplayScript, pathExists, readPersistedLocalBrowserSessionRecord, readPersistedCloudSessionRecord, OpensteerCloudClient, isProcessRunning } from '../chunk-33FDEOQY.js';
|
|
3
3
|
import process2 from 'process';
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
5
|
import { existsSync } from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
9
10
|
|
|
10
11
|
// package.json
|
|
11
12
|
var package_default = {
|
|
12
|
-
version: "0.8.
|
|
13
|
+
version: "0.8.10"};
|
|
13
14
|
|
|
14
15
|
// src/cli/env-loader.ts
|
|
15
16
|
async function loadCliEnvironment(cwd) {
|
|
@@ -214,9 +215,11 @@ async function describeCloudLane(input) {
|
|
|
214
215
|
status: "connected",
|
|
215
216
|
current: input.current,
|
|
216
217
|
summary: input.record.sessionId,
|
|
217
|
-
detail: input.record.baseUrl,
|
|
218
218
|
sessionId: input.record.sessionId,
|
|
219
|
-
|
|
219
|
+
...input.cloudConfig === void 0 ? {} : {
|
|
220
|
+
detail: input.cloudConfig.baseUrl,
|
|
221
|
+
baseUrl: input.cloudConfig.baseUrl
|
|
222
|
+
}
|
|
220
223
|
};
|
|
221
224
|
if (input.cloudConfig === void 0) {
|
|
222
225
|
return base;
|
|
@@ -260,6 +263,214 @@ function formatLaneRow(input) {
|
|
|
260
263
|
const summary = input.summary.padEnd(16, " ");
|
|
261
264
|
return `${input.marker} ${provider} ${status} ${summary}${input.detail ?? ""}`.trimEnd();
|
|
262
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
|
+
async function runOpensteerCloudRecordCommand(input) {
|
|
317
|
+
const stdout = input.stdout ?? process.stdout;
|
|
318
|
+
const stderr = input.stderr ?? process.stderr;
|
|
319
|
+
const outputPath = resolveRecordOutputPath({
|
|
320
|
+
rootDir: input.rootDir,
|
|
321
|
+
workspace: input.workspace,
|
|
322
|
+
...input.outputPath === void 0 ? {} : { outputPath: input.outputPath }
|
|
323
|
+
});
|
|
324
|
+
let cloud;
|
|
325
|
+
const resolveCloud = () => {
|
|
326
|
+
cloud ??= new OpensteerCloudClient(input.cloudConfig);
|
|
327
|
+
return cloud;
|
|
328
|
+
};
|
|
329
|
+
const runtime = input.runtime ?? new CloudSessionProxy(resolveCloud(), {
|
|
330
|
+
rootDir: input.rootDir,
|
|
331
|
+
workspace: input.workspace
|
|
332
|
+
});
|
|
333
|
+
const client = input.client ?? resolveCloud();
|
|
334
|
+
const sleep = input.sleep ?? delay;
|
|
335
|
+
let closed = false;
|
|
336
|
+
try {
|
|
337
|
+
await runtime.open({
|
|
338
|
+
url: input.url,
|
|
339
|
+
...input.browser === void 0 ? {} : { browser: input.browser },
|
|
340
|
+
...input.launch === void 0 ? {} : { launch: input.launch },
|
|
341
|
+
...input.context === void 0 ? {} : { context: input.context }
|
|
342
|
+
});
|
|
343
|
+
const sessionId = await resolveCloudRecordingSessionId(runtime);
|
|
344
|
+
const sessionUrl = buildCloudRecordingSessionUrl(input.cloudConfig, sessionId);
|
|
345
|
+
await client.startSessionRecording(sessionId);
|
|
346
|
+
stderr.write(
|
|
347
|
+
`Recording browser actions for workspace "${input.workspace}". Open ${sessionUrl} and click "Stop recording" in the browser session toolbar when you're done.
|
|
348
|
+
`
|
|
349
|
+
);
|
|
350
|
+
const completed = await waitForCloudRecordingCompletion({
|
|
351
|
+
client,
|
|
352
|
+
sessionId,
|
|
353
|
+
...input.pollIntervalMs === void 0 ? {} : { pollIntervalMs: input.pollIntervalMs },
|
|
354
|
+
sleep
|
|
355
|
+
});
|
|
356
|
+
if (completed.result === void 0) {
|
|
357
|
+
throw new Error("Cloud recording completed without a replay script.");
|
|
358
|
+
}
|
|
359
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
360
|
+
await writeFile(outputPath, completed.result.script, "utf8");
|
|
361
|
+
await runtime.close();
|
|
362
|
+
closed = true;
|
|
363
|
+
stdout.write(`${outputPath}
|
|
364
|
+
`);
|
|
365
|
+
stderr.write(`Cloud browser session: ${sessionUrl}
|
|
366
|
+
`);
|
|
367
|
+
stderr.write(`Wrote replay script to ${outputPath}
|
|
368
|
+
`);
|
|
369
|
+
} finally {
|
|
370
|
+
if (!closed) {
|
|
371
|
+
await runtime.close().catch(() => void 0);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function resolveRecordOutputPath(input) {
|
|
376
|
+
if (input.outputPath !== void 0) {
|
|
377
|
+
return path.resolve(input.rootDir, input.outputPath);
|
|
378
|
+
}
|
|
379
|
+
return path.join(
|
|
380
|
+
resolveFilesystemWorkspacePath({
|
|
381
|
+
rootDir: input.rootDir,
|
|
382
|
+
workspace: input.workspace
|
|
383
|
+
}),
|
|
384
|
+
"recorded-flow.ts"
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
function createRecorderRuntimeAdapter(runtime) {
|
|
388
|
+
return {
|
|
389
|
+
addInitScript: (input) => runtime.addInitScript(input),
|
|
390
|
+
evaluate: async (input) => {
|
|
391
|
+
const output = await runtime.evaluate({
|
|
392
|
+
script: input.script,
|
|
393
|
+
...input.pageRef === void 0 ? {} : { pageRef: input.pageRef }
|
|
394
|
+
});
|
|
395
|
+
return output.value;
|
|
396
|
+
},
|
|
397
|
+
listPages: async () => {
|
|
398
|
+
const output = await runtime.listPages();
|
|
399
|
+
return {
|
|
400
|
+
pages: output.pages.map((page) => ({
|
|
401
|
+
pageRef: page.pageRef,
|
|
402
|
+
url: page.url,
|
|
403
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef }
|
|
404
|
+
}))
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function buildCloudRecordingSessionUrl(cloudConfig, sessionId) {
|
|
410
|
+
const baseUrl = cloudConfig.appBaseUrl;
|
|
411
|
+
if (!baseUrl || baseUrl.trim().length === 0) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
'record with provider=cloud requires OPENSTEER_CLOUD_APP_BASE_URL or "--cloud-app-base-url".'
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return `${baseUrl.replace(/\/+$/, "")}/browsers/${encodeURIComponent(sessionId)}`;
|
|
417
|
+
}
|
|
418
|
+
async function resolveCloudRecordingSessionId(runtime) {
|
|
419
|
+
const info = await runtime.info();
|
|
420
|
+
if (typeof info.sessionId !== "string" || info.sessionId.length === 0) {
|
|
421
|
+
throw new Error("Cloud recording could not resolve the created session id.");
|
|
422
|
+
}
|
|
423
|
+
return info.sessionId;
|
|
424
|
+
}
|
|
425
|
+
async function waitForCloudRecordingCompletion(input) {
|
|
426
|
+
const pollIntervalMs = input.pollIntervalMs ?? 1e3;
|
|
427
|
+
for (; ; ) {
|
|
428
|
+
const state = await input.client.getSessionRecording(input.sessionId);
|
|
429
|
+
if (state.status === "completed") {
|
|
430
|
+
return state;
|
|
431
|
+
}
|
|
432
|
+
if (state.status === "failed") {
|
|
433
|
+
throw new Error(state.error ?? "Cloud recording failed.");
|
|
434
|
+
}
|
|
435
|
+
await input.sleep(pollIntervalMs);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function formatRecordedAction(action) {
|
|
439
|
+
const time = new Date(action.timestamp).toISOString().slice(11, 19);
|
|
440
|
+
switch (action.kind) {
|
|
441
|
+
case "click":
|
|
442
|
+
return `[${time}] click ${action.pageId} -> ${action.selector ?? "<unknown>"}`;
|
|
443
|
+
case "dblclick":
|
|
444
|
+
return `[${time}] dblclick ${action.pageId} -> ${action.selector ?? "<unknown>"}`;
|
|
445
|
+
case "type":
|
|
446
|
+
return `[${time}] type ${action.pageId} -> ${action.selector ?? "<unknown>"} -> ${JSON.stringify(action.detail.text)}`;
|
|
447
|
+
case "keypress":
|
|
448
|
+
return `[${time}] keypress ${action.pageId} -> ${action.detail.key}`;
|
|
449
|
+
case "scroll":
|
|
450
|
+
return `[${time}] scroll ${action.pageId} -> (${String(action.detail.deltaX)}, ${String(action.detail.deltaY)})`;
|
|
451
|
+
case "select-option":
|
|
452
|
+
return `[${time}] select ${action.pageId} -> ${action.selector ?? "<unknown>"} -> ${JSON.stringify(action.detail.value)}`;
|
|
453
|
+
case "navigate":
|
|
454
|
+
return `[${time}] navigate ${action.pageId} -> ${action.detail.url}`;
|
|
455
|
+
case "new-tab":
|
|
456
|
+
return `[${time}] new-tab ${action.pageId} -> ${action.detail.initialUrl}`;
|
|
457
|
+
case "close-tab":
|
|
458
|
+
return `[${time}] close-tab ${action.pageId}`;
|
|
459
|
+
case "switch-tab":
|
|
460
|
+
return `[${time}] switch-tab -> ${action.detail.toPageId}`;
|
|
461
|
+
case "go-back":
|
|
462
|
+
return `[${time}] go-back ${action.pageId}`;
|
|
463
|
+
case "go-forward":
|
|
464
|
+
return `[${time}] go-forward ${action.pageId}`;
|
|
465
|
+
case "reload":
|
|
466
|
+
return `[${time}] reload ${action.pageId}`;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function delay(ms) {
|
|
470
|
+
return new Promise((resolve) => {
|
|
471
|
+
setTimeout(resolve, ms);
|
|
472
|
+
});
|
|
473
|
+
}
|
|
263
474
|
|
|
264
475
|
// src/cli/bin.ts
|
|
265
476
|
var OPERATION_ALIASES = /* @__PURE__ */ new Map([
|
|
@@ -351,6 +562,10 @@ async function main() {
|
|
|
351
562
|
await handleStatusCommand(parsed);
|
|
352
563
|
return;
|
|
353
564
|
}
|
|
565
|
+
if (parsed.command[0] === "record") {
|
|
566
|
+
await handleRecordCommandEntry(parsed);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
354
569
|
const operation = parsed.command[0] === "run" ? parsed.rest[0] : resolveOperation(parsed.command);
|
|
355
570
|
if (!operation) {
|
|
356
571
|
throw new Error(`Unknown command: ${parsed.command.join(" ")}`);
|
|
@@ -495,6 +710,100 @@ async function handleBrowserCommand(parsed) {
|
|
|
495
710
|
throw new Error(`Unknown browser command: ${parsed.command.join(" ")}`);
|
|
496
711
|
}
|
|
497
712
|
}
|
|
713
|
+
async function handleRecordCommandEntry(parsed) {
|
|
714
|
+
if (parsed.options.workspace === void 0) {
|
|
715
|
+
throw new Error('record requires "--workspace <id>".');
|
|
716
|
+
}
|
|
717
|
+
const url = parsed.options.url ?? parsed.rest[0];
|
|
718
|
+
if (url === void 0) {
|
|
719
|
+
throw new Error('record requires "--url <value>" or a positional URL.');
|
|
720
|
+
}
|
|
721
|
+
const provider = resolveCliProvider(parsed);
|
|
722
|
+
assertCloudCliOptionsMatchProvider(parsed, provider.mode);
|
|
723
|
+
const engineName = resolveCliEngineName(parsed);
|
|
724
|
+
if (engineName !== "playwright") {
|
|
725
|
+
throw new Error("record requires engine=playwright.");
|
|
726
|
+
}
|
|
727
|
+
const rootDir = process2.cwd();
|
|
728
|
+
const recordBrowser = parsed.options.browser;
|
|
729
|
+
if (provider.mode === "cloud") {
|
|
730
|
+
if (typeof recordBrowser === "object") {
|
|
731
|
+
throw new Error('record does not support browser.mode="attach".');
|
|
732
|
+
}
|
|
733
|
+
const runtimeProvider = buildCliRuntimeProvider(parsed, provider.mode);
|
|
734
|
+
const runtimeConfig = resolveOpensteerRuntimeConfig({
|
|
735
|
+
...runtimeProvider === void 0 ? {} : { provider: runtimeProvider },
|
|
736
|
+
environment: process2.env
|
|
737
|
+
});
|
|
738
|
+
await runOpensteerCloudRecordCommand({
|
|
739
|
+
cloudConfig: runtimeConfig.cloud,
|
|
740
|
+
workspace: parsed.options.workspace,
|
|
741
|
+
url,
|
|
742
|
+
rootDir,
|
|
743
|
+
...recordBrowser === void 0 ? {} : { browser: recordBrowser },
|
|
744
|
+
...parsed.options.launch === void 0 ? {} : { launch: parsed.options.launch },
|
|
745
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context },
|
|
746
|
+
...parsed.options.output === void 0 ? {} : { outputPath: parsed.options.output }
|
|
747
|
+
});
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
if (parsed.options.launch?.headless === true) {
|
|
751
|
+
throw new Error('record requires a headed browser. Remove "--headless true".');
|
|
752
|
+
}
|
|
753
|
+
if (recordBrowser !== void 0 && recordBrowser !== "persistent") {
|
|
754
|
+
throw new Error('record only supports "--browser persistent".');
|
|
755
|
+
}
|
|
756
|
+
const launch = {
|
|
757
|
+
...parsed.options.launch ?? {},
|
|
758
|
+
headless: false
|
|
759
|
+
};
|
|
760
|
+
const browserManager = new OpensteerBrowserManager({
|
|
761
|
+
rootDir,
|
|
762
|
+
workspace: parsed.options.workspace,
|
|
763
|
+
engineName,
|
|
764
|
+
browser: "persistent",
|
|
765
|
+
launch,
|
|
766
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context }
|
|
767
|
+
});
|
|
768
|
+
const runtime = createOpensteerSemanticRuntime({
|
|
769
|
+
provider: {
|
|
770
|
+
mode: "local"
|
|
771
|
+
},
|
|
772
|
+
engine: engineName,
|
|
773
|
+
runtimeOptions: {
|
|
774
|
+
rootPath: browserManager.rootPath,
|
|
775
|
+
cleanupRootOnClose: browserManager.cleanupRootOnDisconnect,
|
|
776
|
+
workspace: parsed.options.workspace,
|
|
777
|
+
browser: "persistent",
|
|
778
|
+
launch,
|
|
779
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context }
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
await runOpensteerRecordCommand({
|
|
783
|
+
runtime,
|
|
784
|
+
closeSession: () => closeOwnedLocalBrowserSession(runtime, browserManager),
|
|
785
|
+
workspace: parsed.options.workspace,
|
|
786
|
+
url,
|
|
787
|
+
rootDir,
|
|
788
|
+
...parsed.options.output === void 0 ? {} : { outputPath: parsed.options.output }
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
async function closeOwnedLocalBrowserSession(runtime, browserManager) {
|
|
792
|
+
let closeError;
|
|
793
|
+
try {
|
|
794
|
+
await runtime.close();
|
|
795
|
+
} catch (error) {
|
|
796
|
+
closeError = error;
|
|
797
|
+
}
|
|
798
|
+
try {
|
|
799
|
+
await browserManager.close();
|
|
800
|
+
} catch (error) {
|
|
801
|
+
closeError ??= error;
|
|
802
|
+
}
|
|
803
|
+
if (closeError !== void 0) {
|
|
804
|
+
throw closeError;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
498
807
|
function buildOperationInput(operation, parsed) {
|
|
499
808
|
if (parsed.options.inputJson !== void 0) {
|
|
500
809
|
return parsed.options.inputJson;
|
|
@@ -575,6 +884,46 @@ function resolveOperation(command) {
|
|
|
575
884
|
}
|
|
576
885
|
return void 0;
|
|
577
886
|
}
|
|
887
|
+
var CLI_OPTION_SPECS = {
|
|
888
|
+
workspace: { kind: "value" },
|
|
889
|
+
url: { kind: "value" },
|
|
890
|
+
output: { kind: "value" },
|
|
891
|
+
engine: { kind: "value" },
|
|
892
|
+
provider: { kind: "value" },
|
|
893
|
+
"cloud-base-url": { kind: "value" },
|
|
894
|
+
"cloud-api-key": { kind: "value" },
|
|
895
|
+
"cloud-app-base-url": { kind: "value" },
|
|
896
|
+
"cloud-profile-id": { kind: "value" },
|
|
897
|
+
"cloud-profile-reuse-if-active": { kind: "boolean" },
|
|
898
|
+
json: { kind: "boolean" },
|
|
899
|
+
agent: { kind: "value", multiple: true },
|
|
900
|
+
skill: { kind: "value", multiple: true },
|
|
901
|
+
global: { kind: "boolean" },
|
|
902
|
+
yes: { kind: "boolean" },
|
|
903
|
+
copy: { kind: "boolean" },
|
|
904
|
+
all: { kind: "boolean" },
|
|
905
|
+
list: { kind: "boolean" },
|
|
906
|
+
browser: { kind: "value" },
|
|
907
|
+
"attach-endpoint": { kind: "value" },
|
|
908
|
+
"attach-header": { kind: "value", multiple: true },
|
|
909
|
+
"fresh-tab": { kind: "boolean" },
|
|
910
|
+
headless: { kind: "boolean" },
|
|
911
|
+
"executable-path": { kind: "value" },
|
|
912
|
+
arg: { kind: "value", multiple: true },
|
|
913
|
+
"timeout-ms": { kind: "value" },
|
|
914
|
+
"context-json": { kind: "value" },
|
|
915
|
+
"input-json": { kind: "value" },
|
|
916
|
+
"schema-json": { kind: "value" },
|
|
917
|
+
"source-user-data-dir": { kind: "value" },
|
|
918
|
+
"source-profile-directory": { kind: "value" },
|
|
919
|
+
selector: { kind: "value" },
|
|
920
|
+
description: { kind: "value" },
|
|
921
|
+
element: { kind: "value" },
|
|
922
|
+
text: { kind: "value" },
|
|
923
|
+
"press-enter": { kind: "boolean" },
|
|
924
|
+
direction: { kind: "value" },
|
|
925
|
+
amount: { kind: "value" }
|
|
926
|
+
};
|
|
578
927
|
function parseCommandLine(argv) {
|
|
579
928
|
const leadingTokens = [];
|
|
580
929
|
let index = 0;
|
|
@@ -588,18 +937,43 @@ function parseCommandLine(argv) {
|
|
|
588
937
|
const rawOptions = /* @__PURE__ */ new Map();
|
|
589
938
|
while (index < argv.length) {
|
|
590
939
|
const token = argv[index];
|
|
940
|
+
if (token === "--") {
|
|
941
|
+
rest.push(...argv.slice(index + 1));
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
591
944
|
if (!token.startsWith("--")) {
|
|
592
945
|
rest.push(token);
|
|
593
946
|
index += 1;
|
|
594
947
|
continue;
|
|
595
948
|
}
|
|
596
|
-
const
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
949
|
+
const separator = token.indexOf("=");
|
|
950
|
+
const key = token.slice(2, separator === -1 ? void 0 : separator);
|
|
951
|
+
const spec = CLI_OPTION_SPECS[key];
|
|
952
|
+
if (spec === void 0) {
|
|
953
|
+
throw new Error(`Unknown option: --${key}.`);
|
|
954
|
+
}
|
|
955
|
+
if (separator !== -1) {
|
|
956
|
+
const value = token.slice(separator + 1);
|
|
957
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], value]);
|
|
600
958
|
index += 1;
|
|
601
959
|
continue;
|
|
602
960
|
}
|
|
961
|
+
const next = argv[index + 1];
|
|
962
|
+
if (spec.kind === "boolean") {
|
|
963
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
964
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], "true"]);
|
|
965
|
+
index += 1;
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], next]);
|
|
969
|
+
index += 2;
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
973
|
+
throw new Error(
|
|
974
|
+
`Option "--${key}" requires a value.${next?.startsWith("--") === true ? ` Use "--${key}=<value>" when the value begins with "--".` : ``}`
|
|
975
|
+
);
|
|
976
|
+
}
|
|
603
977
|
rawOptions.set(key, [...rawOptions.get(key) ?? [], next]);
|
|
604
978
|
index += 2;
|
|
605
979
|
}
|
|
@@ -630,6 +1004,8 @@ function parseCommandLine(argv) {
|
|
|
630
1004
|
...timeoutMs === void 0 ? {} : { timeoutMs }
|
|
631
1005
|
};
|
|
632
1006
|
const workspace = readSingle(rawOptions, "workspace");
|
|
1007
|
+
const url = readSingle(rawOptions, "url");
|
|
1008
|
+
const output = readSingle(rawOptions, "output");
|
|
633
1009
|
const sourceUserDataDir = readSingle(rawOptions, "source-user-data-dir");
|
|
634
1010
|
const sourceProfileDirectory = readSingle(rawOptions, "source-profile-directory");
|
|
635
1011
|
const selector = readSingle(rawOptions, "selector");
|
|
@@ -646,6 +1022,7 @@ function parseCommandLine(argv) {
|
|
|
646
1022
|
const provider = providerValue === void 0 ? void 0 : normalizeOpensteerProviderMode(providerValue, "--provider");
|
|
647
1023
|
const cloudBaseUrl = readSingle(rawOptions, "cloud-base-url");
|
|
648
1024
|
const cloudApiKey = readSingle(rawOptions, "cloud-api-key");
|
|
1025
|
+
const cloudAppBaseUrl = readSingle(rawOptions, "cloud-app-base-url");
|
|
649
1026
|
const cloudProfileId = readSingle(rawOptions, "cloud-profile-id");
|
|
650
1027
|
const cloudProfileReuseIfActive = readOptionalBoolean(
|
|
651
1028
|
rawOptions,
|
|
@@ -661,10 +1038,13 @@ function parseCommandLine(argv) {
|
|
|
661
1038
|
const list = readOptionalBoolean(rawOptions, "list");
|
|
662
1039
|
const options = {
|
|
663
1040
|
...workspace === void 0 ? {} : { workspace },
|
|
1041
|
+
...url === void 0 ? {} : { url },
|
|
1042
|
+
...output === void 0 ? {} : { output },
|
|
664
1043
|
...requestedEngineName === void 0 ? {} : { requestedEngineName },
|
|
665
1044
|
...provider === void 0 ? {} : { provider },
|
|
666
1045
|
...cloudBaseUrl === void 0 ? {} : { cloudBaseUrl },
|
|
667
1046
|
...cloudApiKey === void 0 ? {} : { cloudApiKey },
|
|
1047
|
+
...cloudAppBaseUrl === void 0 ? {} : { cloudAppBaseUrl },
|
|
668
1048
|
...cloudProfileId === void 0 ? {} : { cloudProfileId },
|
|
669
1049
|
...cloudProfileReuseIfActive === void 0 ? {} : { cloudProfileReuseIfActive },
|
|
670
1050
|
...json === void 0 ? {} : { json },
|
|
@@ -749,7 +1129,7 @@ function buildCliRuntimeProvider(parsed, providerMode) {
|
|
|
749
1129
|
return explicitProvider?.mode === "local" ? explicitProvider : void 0;
|
|
750
1130
|
}
|
|
751
1131
|
const browserProfile = buildCliBrowserProfile(parsed);
|
|
752
|
-
const hasCloudOverrides = parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || browserProfile !== void 0;
|
|
1132
|
+
const hasCloudOverrides = parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudAppBaseUrl !== void 0 || browserProfile !== void 0;
|
|
753
1133
|
if (!hasCloudOverrides && explicitProvider?.mode !== "cloud") {
|
|
754
1134
|
return void 0;
|
|
755
1135
|
}
|
|
@@ -757,11 +1137,12 @@ function buildCliRuntimeProvider(parsed, providerMode) {
|
|
|
757
1137
|
mode: "cloud",
|
|
758
1138
|
...parsed.options.cloudBaseUrl === void 0 ? {} : { baseUrl: parsed.options.cloudBaseUrl },
|
|
759
1139
|
...parsed.options.cloudApiKey === void 0 ? {} : { apiKey: parsed.options.cloudApiKey },
|
|
1140
|
+
...parsed.options.cloudAppBaseUrl === void 0 ? {} : { appBaseUrl: parsed.options.cloudAppBaseUrl },
|
|
760
1141
|
...browserProfile === void 0 ? {} : { browserProfile }
|
|
761
1142
|
};
|
|
762
1143
|
}
|
|
763
1144
|
function assertCloudCliOptionsMatchProvider(parsed, providerMode) {
|
|
764
|
-
if (providerMode !== "cloud" && (parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudProfileId !== void 0 || parsed.options.cloudProfileReuseIfActive === true)) {
|
|
1145
|
+
if (providerMode !== "cloud" && (parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudAppBaseUrl !== void 0 || parsed.options.cloudProfileId !== void 0 || parsed.options.cloudProfileReuseIfActive === true)) {
|
|
765
1146
|
throw new Error(
|
|
766
1147
|
'Cloud-specific options require provider=cloud. Set "--provider cloud" or OPENSTEER_PROVIDER=cloud.'
|
|
767
1148
|
);
|
|
@@ -860,6 +1241,9 @@ function resolveCommandLength(tokens) {
|
|
|
860
1241
|
if (tokens[0] === "status") {
|
|
861
1242
|
return 1;
|
|
862
1243
|
}
|
|
1244
|
+
if (tokens[0] === "record") {
|
|
1245
|
+
return 1;
|
|
1246
|
+
}
|
|
863
1247
|
for (let length = Math.min(3, tokens.length); length >= 1; length -= 1) {
|
|
864
1248
|
if (OPERATION_ALIASES.has(tokens.slice(0, length).join(" "))) {
|
|
865
1249
|
return length;
|
|
@@ -877,6 +1261,7 @@ Usage:
|
|
|
877
1261
|
opensteer click --workspace <id> (--element <n> | --selector <css> | --description <text>)
|
|
878
1262
|
opensteer input --workspace <id> --text <value> (--element <n> | --selector <css> | --description <text>)
|
|
879
1263
|
opensteer extract --workspace <id> --description <text> [--schema-json <json>]
|
|
1264
|
+
opensteer record --workspace <id> --url <url> [--output <path>]
|
|
880
1265
|
opensteer close --workspace <id>
|
|
881
1266
|
opensteer status [--workspace <id>] [--json]
|
|
882
1267
|
|
|
@@ -894,9 +1279,12 @@ Common options:
|
|
|
894
1279
|
--help
|
|
895
1280
|
--version
|
|
896
1281
|
--workspace <id>
|
|
1282
|
+
--url <url>
|
|
1283
|
+
--output <path>
|
|
897
1284
|
--provider local|cloud
|
|
898
1285
|
--cloud-base-url <url>
|
|
899
1286
|
--cloud-api-key <key>
|
|
1287
|
+
--cloud-app-base-url <url>
|
|
900
1288
|
--cloud-profile-id <id>
|
|
901
1289
|
--cloud-profile-reuse-if-active <true|false>
|
|
902
1290
|
--json <true|false>
|