@vibecodr/cli 1.0.14 → 1.0.15
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 +7 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +28 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/dryrun-logpush-check/README.md +1 -0
- package/dist/dryrun-logpush-check/worker.js +41512 -0
- package/dist/dryrun-logpush-check/worker.js.map +8 -0
- package/dist/legacy/cli/run.d.ts.map +1 -1
- package/dist/legacy/cli/run.js +410 -13
- package/dist/legacy/cli/run.js.map +1 -1
- package/dist/legacy/core/contracts.d.ts +76 -4
- package/dist/legacy/core/contracts.d.ts.map +1 -1
- package/dist/legacy/core/contracts.js +53 -3
- package/dist/legacy/core/contracts.js.map +1 -1
- package/dist/legacy/core/validators.js +1 -1
- package/dist/legacy/core/validators.js.map +1 -1
- package/dist/legacy/core/version.d.ts +2 -2
- package/dist/legacy/core/version.js +1 -1
- package/docs/API-CONTRACT.md +73 -5
- package/docs/CLOUDFLARE-PRIMITIVE-FIT.md +59 -4
- package/docs/VALIDATION-MATRIX.md +3 -3
- package/docs/commands.md +14 -1
- package/docs/legacy/vc-tools-finetune.md +1 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/legacy/cli/run.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/legacy/cli/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAgDtD,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAoED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CzF"}
|
package/dist/legacy/cli/run.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
3
4
|
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { ConfigStore, DEFAULT_API_URL, resolveConfigDir } from "../config/store.js";
|
|
@@ -18,6 +19,7 @@ const DEFAULT_AUTH_API_URL = "https://api.vibecodr.space";
|
|
|
18
19
|
const GRANT_REFRESH_SKEW_SECONDS = 60;
|
|
19
20
|
const ARTIFACT_OUTPUT_WORKSPACE_MESSAGE = "Artifact output is workspace-bounded so downloaded bytes can only be written to files you intentionally target inside this workspace. Use --local for ./vibecodr-proof, --out ./artifacts, --out ./artifacts/report.pdf, or cd to the intended workspace and use --out .";
|
|
20
21
|
const ARTIFACT_INPUT_WORKSPACE_MESSAGE = "Artifact upload sources are workspace-bounded so the CLI only reads files you intentionally target inside this workspace. Move the file into this workspace, or cd to the workspace that contains it.";
|
|
22
|
+
const WORKSPACE_SCRATCH_PROOF_DIR = ".vibecodr/browser-artifacts";
|
|
21
23
|
export async function runCli(argv, options = {}) {
|
|
22
24
|
const stdout = options.stdout ?? process.stdout;
|
|
23
25
|
const stderr = options.stderr ?? process.stderr;
|
|
@@ -190,11 +192,19 @@ async function commandTry(context, parsed) {
|
|
|
190
192
|
const { profile } = await context.store.getProfile(context.globals.profile);
|
|
191
193
|
const client = createClient(context, profile, await resolveToken(context, true));
|
|
192
194
|
const proofDir = getStringFlag(parsed.flags, "out") ?? "vibecodr-proof";
|
|
195
|
+
const proofOutput = await prepareAutoProofOutput(context, {
|
|
196
|
+
positionals: [],
|
|
197
|
+
flags: {
|
|
198
|
+
...parsed.flags,
|
|
199
|
+
out: proofDir
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
const resolvedProofDir = getStringFlag(proofOutput.parsed.flags, "out") ?? proofDir;
|
|
193
203
|
const browserParsed = {
|
|
194
204
|
positionals: ["https://example.com"],
|
|
195
205
|
flags: {
|
|
196
206
|
...parsed.flags,
|
|
197
|
-
out:
|
|
207
|
+
out: resolvedProofDir,
|
|
198
208
|
filename: "browser-read.md",
|
|
199
209
|
pollIntervalMs: getStringFlag(parsed.flags, "pollIntervalMs") ?? "250"
|
|
200
210
|
}
|
|
@@ -204,7 +214,7 @@ async function commandTry(context, parsed) {
|
|
|
204
214
|
flags: {
|
|
205
215
|
...parsed.flags,
|
|
206
216
|
command: "node -e \"console.log('vibecodr computer ok')\"",
|
|
207
|
-
out:
|
|
217
|
+
out: resolvedProofDir,
|
|
208
218
|
filename: "computer-run.json",
|
|
209
219
|
pollIntervalMs: getStringFlag(parsed.flags, "pollIntervalMs") ?? "250"
|
|
210
220
|
}
|
|
@@ -217,7 +227,7 @@ async function commandTry(context, parsed) {
|
|
|
217
227
|
proof: "failed",
|
|
218
228
|
usage: "failed"
|
|
219
229
|
};
|
|
220
|
-
const warnings = [];
|
|
230
|
+
const warnings = [...proofOutput.warnings];
|
|
221
231
|
const browserPayload = buildToolTestPayload("browser.extract_markdown", browserParsed.positionals[0], browserParsed);
|
|
222
232
|
const browserWork = await client.request("POST", "tools/test", { body: browserPayload });
|
|
223
233
|
const browserResult = await followSubmittedWork(context, client, "browser.extract_markdown", browserWork, browserParsed);
|
|
@@ -253,12 +263,12 @@ async function commandTry(context, parsed) {
|
|
|
253
263
|
const ready = Object.values(checks).every((status) => status === "ok");
|
|
254
264
|
return {
|
|
255
265
|
message: ready
|
|
256
|
-
? `Vibecodr Agent Computer check passed.\nProof saved: ${path.resolve(context.cwd,
|
|
257
|
-
: `Vibecodr Agent Computer check finished with attention needed.\nProof path: ${path.resolve(context.cwd,
|
|
266
|
+
? `Vibecodr Agent Computer check passed.\nProof saved: ${path.resolve(context.cwd, resolvedProofDir)}`
|
|
267
|
+
: `Vibecodr Agent Computer check finished with attention needed.\nProof path: ${path.resolve(context.cwd, resolvedProofDir)}`,
|
|
258
268
|
data: {
|
|
259
269
|
ready,
|
|
260
270
|
checks,
|
|
261
|
-
proofPath: path.resolve(context.cwd,
|
|
271
|
+
proofPath: path.resolve(context.cwd, resolvedProofDir)
|
|
262
272
|
},
|
|
263
273
|
warnings,
|
|
264
274
|
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
@@ -655,8 +665,140 @@ async function commandBrowser(context, subcommand, rest) {
|
|
|
655
665
|
const normalized = normalizeBrowserNotesOptions(parsed);
|
|
656
666
|
return submitHostedCapability(context, "browser.notes", normalized, "Captured a hosted Browser snapshot with your note attached.", { autoFollow: true });
|
|
657
667
|
}
|
|
668
|
+
case "session":
|
|
669
|
+
return commandBrowserSession(context, rest[0], rest.slice(1));
|
|
658
670
|
default:
|
|
659
|
-
throw unknownSubcommandError("browser", subcommand, ["render", "screenshot", "read", "markdown", "pdf", "crawl", "snapshot", "notes"], "Use vibecodr browser screenshot <https-url>, browser read <https-url>,
|
|
671
|
+
throw unknownSubcommandError("browser", subcommand, ["render", "screenshot", "read", "markdown", "pdf", "crawl", "snapshot", "notes", "session"], "Use vibecodr browser screenshot <https-url>, browser read <https-url>, browser snapshot <https-url> --local, or browser session open <https-url>.");
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
async function commandBrowserSession(context, subcommand, rest) {
|
|
675
|
+
const parsed = parseCommandOptions(rest);
|
|
676
|
+
const { profile } = await context.store.getProfile(context.globals.profile);
|
|
677
|
+
const client = createClient(context, profile, await resolveToken(context, true));
|
|
678
|
+
switch (subcommand) {
|
|
679
|
+
case "open": {
|
|
680
|
+
const target = parsed.positionals[0];
|
|
681
|
+
if (!target) {
|
|
682
|
+
throw new CliError("input.url_required", "browser session open requires an HTTPS URL target.", 2);
|
|
683
|
+
}
|
|
684
|
+
const body = { url: validateBrowserUrl(target) };
|
|
685
|
+
const timeoutMs = validatePositiveInt(getStringFlag(parsed.flags, "timeoutMs"), "--timeout-ms", 1000, 3_600_000);
|
|
686
|
+
const idleTimeoutMs = validatePositiveInt(getStringFlag(parsed.flags, "idleTimeoutMs"), "--idle-timeout-ms", 1000, 600_000);
|
|
687
|
+
if (timeoutMs !== undefined)
|
|
688
|
+
body.timeoutMs = timeoutMs;
|
|
689
|
+
if (idleTimeoutMs !== undefined)
|
|
690
|
+
body.idleTimeoutMs = idleTimeoutMs;
|
|
691
|
+
const response = await client.request("POST", "browser/sessions", { body });
|
|
692
|
+
return {
|
|
693
|
+
message: browserSessionMessage(response, "Opened Agent Browser session."),
|
|
694
|
+
data: response,
|
|
695
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
case "observe": {
|
|
699
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
700
|
+
const response = await client.request("GET", `browser/sessions/${encodePathSegment(sessionId)}`);
|
|
701
|
+
return {
|
|
702
|
+
message: browserSessionMessage(response, "Observed Agent Browser session."),
|
|
703
|
+
data: response,
|
|
704
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
case "goto":
|
|
708
|
+
case "navigate":
|
|
709
|
+
case "click":
|
|
710
|
+
case "type":
|
|
711
|
+
case "scroll":
|
|
712
|
+
case "wait": {
|
|
713
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
714
|
+
const body = browserSessionActionBody(subcommand, parsed);
|
|
715
|
+
const response = await client.request("POST", `browser/sessions/${encodePathSegment(sessionId)}/actions`, { body });
|
|
716
|
+
return {
|
|
717
|
+
message: browserSessionMessage(response, `Ran Agent Browser ${subcommand}.`),
|
|
718
|
+
data: response,
|
|
719
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
case "auth":
|
|
723
|
+
case "auth-request": {
|
|
724
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
725
|
+
const response = await client.request("POST", `browser/sessions/${encodePathSegment(sessionId)}/auth${browserSessionLiveViewQuery(parsed, context.globals.debug)}`);
|
|
726
|
+
const handoffUrl = browserSessionHandoffUrlFromResponse(response);
|
|
727
|
+
const skipOpen = getBooleanFlag(parsed.flags, "noOpen") ||
|
|
728
|
+
parsed.flags.open === false ||
|
|
729
|
+
context.globals.json ||
|
|
730
|
+
context.globals.quiet ||
|
|
731
|
+
context.globals.noInput;
|
|
732
|
+
let opened = false;
|
|
733
|
+
if (handoffUrl && !skipOpen) {
|
|
734
|
+
opened = await maybeOpenBrowser(context, parsed, handoffUrl);
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
message: browserSessionMessage(response, opened ? "Opened Agent Browser for you to control." : "Agent Browser control link ready."),
|
|
738
|
+
data: response,
|
|
739
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
case "live":
|
|
743
|
+
case "watch":
|
|
744
|
+
case "view": {
|
|
745
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
746
|
+
const response = await client.request("POST", `browser/sessions/${encodePathSegment(sessionId)}/live${browserSessionLiveViewQuery(parsed, context.globals.debug)}`);
|
|
747
|
+
const handoffUrl = browserSessionHandoffUrlFromResponse(response);
|
|
748
|
+
const skipOpen = getBooleanFlag(parsed.flags, "noOpen") ||
|
|
749
|
+
parsed.flags.open === false ||
|
|
750
|
+
context.globals.json ||
|
|
751
|
+
context.globals.quiet ||
|
|
752
|
+
context.globals.noInput;
|
|
753
|
+
let opened = false;
|
|
754
|
+
if (handoffUrl && !skipOpen) {
|
|
755
|
+
opened = await maybeOpenBrowser(context, parsed, handoffUrl);
|
|
756
|
+
}
|
|
757
|
+
return {
|
|
758
|
+
message: browserSessionMessage(response, opened ? "Opened Agent Browser live view." : "Agent Browser live view ready."),
|
|
759
|
+
data: response,
|
|
760
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
case "auth-status": {
|
|
764
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
765
|
+
const response = await client.request("GET", `browser/sessions/${encodePathSegment(sessionId)}/auth`);
|
|
766
|
+
return {
|
|
767
|
+
message: browserSessionMessage(response, "Read Agent Browser live-control status."),
|
|
768
|
+
data: response,
|
|
769
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
case "auth-complete":
|
|
773
|
+
case "auth-done": {
|
|
774
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
775
|
+
const response = await client.request("POST", `browser/sessions/${encodePathSegment(sessionId)}/auth/complete`);
|
|
776
|
+
return {
|
|
777
|
+
message: browserSessionMessage(response, "Gave Agent Browser control back to the agent."),
|
|
778
|
+
data: response,
|
|
779
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
case "auth-revoke": {
|
|
783
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
784
|
+
const response = await client.request("POST", `browser/sessions/${encodePathSegment(sessionId)}/auth/revoke`);
|
|
785
|
+
return {
|
|
786
|
+
message: browserSessionMessage(response, "Ended Agent Browser live link."),
|
|
787
|
+
data: response,
|
|
788
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
case "close": {
|
|
792
|
+
const sessionId = validateBrowserSessionId(parsed.positionals[0]);
|
|
793
|
+
const response = await client.request("DELETE", `browser/sessions/${encodePathSegment(sessionId)}`);
|
|
794
|
+
return {
|
|
795
|
+
message: "Closed Agent Browser session.",
|
|
796
|
+
data: response,
|
|
797
|
+
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
default:
|
|
801
|
+
throw unknownSubcommandError("browser session", subcommand, ["open", "observe", "goto", "click", "type", "scroll", "wait", "live", "auth", "auth-status", "auth-complete", "auth-revoke", "close"], "Use vibecodr browser session open <https-url>, live <sessionId>, observe <sessionId>, click <sessionId> --selector <css>, or close <sessionId>.");
|
|
660
802
|
}
|
|
661
803
|
}
|
|
662
804
|
async function commandComputer(context, subcommand, rest) {
|
|
@@ -736,13 +878,16 @@ async function submitHostedCapability(context, capabilityInput, parsed, successM
|
|
|
736
878
|
throw new CliError("input.local_requires_wait", "--local saves the completed output, so it cannot be combined with --no-wait.", 2);
|
|
737
879
|
}
|
|
738
880
|
const payload = buildToolTestPayload(capability, parsed.positionals[0], parsed, context.globals.timeoutMs === 30_000 ? undefined : context.globals.timeoutMs);
|
|
881
|
+
const proofOutput = options.autoFollow === true && !shouldSkipWait(parsed)
|
|
882
|
+
? await prepareAutoProofOutput(context, parsed)
|
|
883
|
+
: { parsed, warnings: [] };
|
|
739
884
|
const { profile } = await context.store.getProfile(context.globals.profile);
|
|
740
885
|
const client = createClient(context, profile, await resolveToken(context, true));
|
|
741
886
|
const response = await client.request("POST", "tools/test", {
|
|
742
887
|
body: payload
|
|
743
888
|
});
|
|
744
889
|
if (options.autoFollow === true && !shouldSkipWait(parsed)) {
|
|
745
|
-
return followSubmittedWork(context, client, capability, response, parsed);
|
|
890
|
+
return followSubmittedWork(context, client, capability, response, proofOutput.parsed, proofOutput.warnings);
|
|
746
891
|
}
|
|
747
892
|
return {
|
|
748
893
|
message: successMessage ?? (capability === "usage.read" ? "Read usage and limits from hosted Vibecodr." : `Submitted ${capability} test to hosted Vibecodr.`),
|
|
@@ -752,12 +897,13 @@ async function submitHostedCapability(context, capabilityInput, parsed, successM
|
|
|
752
897
|
function shouldSkipWait(parsed) {
|
|
753
898
|
return getBooleanFlag(parsed.flags, "noWait") || parsed.flags.wait === false;
|
|
754
899
|
}
|
|
755
|
-
async function followSubmittedWork(context, client, capability, submitted, parsed) {
|
|
900
|
+
async function followSubmittedWork(context, client, capability, submitted, parsed, warnings = []) {
|
|
756
901
|
const jobId = jobIdFromWork(submitted);
|
|
757
902
|
if (!jobId) {
|
|
758
903
|
return {
|
|
759
904
|
message: completedCapabilityMessage(capability),
|
|
760
905
|
data: publicWorkResult(capability, submitted, parsed),
|
|
906
|
+
warnings,
|
|
761
907
|
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
762
908
|
};
|
|
763
909
|
}
|
|
@@ -771,21 +917,24 @@ async function followSubmittedWork(context, client, capability, submitted, parse
|
|
|
771
917
|
return {
|
|
772
918
|
message: formatCompletedWorkMessage(capability, terminal, proof),
|
|
773
919
|
data: publicWorkResult(capability, terminal, parsed, proof),
|
|
920
|
+
warnings,
|
|
774
921
|
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
775
922
|
};
|
|
776
923
|
}
|
|
777
924
|
async function commandWorkFollow(context, parsed) {
|
|
778
925
|
const jobId = validateEntityId(requiredPositional(parsed, 0, "work follow requires a job id."), "job id");
|
|
926
|
+
const proofOutput = await prepareAutoProofOutput(context, parsed);
|
|
779
927
|
const { profile } = await context.store.getProfile(context.globals.profile);
|
|
780
928
|
const client = createClient(context, profile, await resolveToken(context, true));
|
|
781
|
-
const job = await pollWorkUntilTerminal(client, jobId, parsed);
|
|
929
|
+
const job = await pollWorkUntilTerminal(client, jobId, proofOutput.parsed);
|
|
782
930
|
const artifactId = artifactIdFromWork(job);
|
|
783
|
-
const proof = artifactId && shouldSaveArtifact(parsed)
|
|
784
|
-
? await saveArtifact(context, client, artifactId,
|
|
931
|
+
const proof = artifactId && shouldSaveArtifact(proofOutput.parsed)
|
|
932
|
+
? await saveArtifact(context, client, artifactId, proofOutput.parsed)
|
|
785
933
|
: undefined;
|
|
786
934
|
return {
|
|
787
935
|
message: formatCompletedWorkMessage(undefined, job, proof),
|
|
788
|
-
data: publicWorkResult(undefined, job, parsed, proof),
|
|
936
|
+
data: publicWorkResult(undefined, job, proofOutput.parsed, proof),
|
|
937
|
+
warnings: proofOutput.warnings,
|
|
789
938
|
humanData: getBooleanFlag(parsed.flags, "details") ? "show" : "hide"
|
|
790
939
|
};
|
|
791
940
|
}
|
|
@@ -881,6 +1030,60 @@ function parsedWithLocalOutput(parsed) {
|
|
|
881
1030
|
}
|
|
882
1031
|
};
|
|
883
1032
|
}
|
|
1033
|
+
async function prepareAutoProofOutput(context, parsed) {
|
|
1034
|
+
if (!shouldSaveArtifact(parsed)) {
|
|
1035
|
+
return { parsed, warnings: [] };
|
|
1036
|
+
}
|
|
1037
|
+
const withLocalOutput = parsedWithLocalOutput(parsed);
|
|
1038
|
+
const out = getStringFlag(withLocalOutput.flags, "out") ?? ".";
|
|
1039
|
+
const outPath = path.resolve(context.cwd, out);
|
|
1040
|
+
try {
|
|
1041
|
+
await ensureOutputPathAllowed(context.cwd, outPath);
|
|
1042
|
+
return { parsed: withLocalOutput, warnings: [] };
|
|
1043
|
+
}
|
|
1044
|
+
catch (error) {
|
|
1045
|
+
const cliError = toCliError(error);
|
|
1046
|
+
if (cliError.code !== "file.outside_workspace" || !isPathOutside(path.resolve(context.cwd), outPath)) {
|
|
1047
|
+
throw error;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
const fallbackOut = await allocateScratchProofOutputDir(context.cwd);
|
|
1051
|
+
return {
|
|
1052
|
+
parsed: {
|
|
1053
|
+
...withLocalOutput,
|
|
1054
|
+
flags: {
|
|
1055
|
+
...withLocalOutput.flags,
|
|
1056
|
+
out: fallbackOut
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
warnings: [
|
|
1060
|
+
`Output path is outside this workspace, so hosted artifacts cannot be written there. Writing to ${formatCliPath(fallbackOut)} instead. This scratch folder is gitignored; use --out ./path-inside-workspace to choose another workspace path.`
|
|
1061
|
+
]
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
async function allocateScratchProofOutputDir(cwd) {
|
|
1065
|
+
const root = path.join(cwd, WORKSPACE_SCRATCH_PROOF_DIR);
|
|
1066
|
+
await fs.mkdir(root, { recursive: true });
|
|
1067
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
1068
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
1069
|
+
const relative = `${WORKSPACE_SCRATCH_PROOF_DIR}/${timestamp}-${randomUUID().slice(0, 8)}`;
|
|
1070
|
+
const absolute = path.resolve(cwd, relative);
|
|
1071
|
+
try {
|
|
1072
|
+
await fs.mkdir(absolute, { recursive: false });
|
|
1073
|
+
return relative;
|
|
1074
|
+
}
|
|
1075
|
+
catch (error) {
|
|
1076
|
+
if (error.code !== "EEXIST") {
|
|
1077
|
+
throw error;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
throw new CliError("file.scratch_output_failed", "Could not create a workspace scratch output directory for hosted artifacts.", 5);
|
|
1082
|
+
}
|
|
1083
|
+
function formatCliPath(input) {
|
|
1084
|
+
const normalized = input.split(path.sep).join("/");
|
|
1085
|
+
return normalized.startsWith("./") || normalized.startsWith("../") ? normalized : `./${normalized}`;
|
|
1086
|
+
}
|
|
884
1087
|
function formatCompletedWorkMessage(capability, work, proof) {
|
|
885
1088
|
const status = workStatus(work) ?? "completed";
|
|
886
1089
|
if (status === "completed") {
|
|
@@ -1027,6 +1230,125 @@ function normalizeBrowserNotesOptions(parsed) {
|
|
|
1027
1230
|
flags
|
|
1028
1231
|
};
|
|
1029
1232
|
}
|
|
1233
|
+
function validateBrowserSessionId(input) {
|
|
1234
|
+
return validateEntityId(input ?? "", "Agent Browser session id");
|
|
1235
|
+
}
|
|
1236
|
+
function browserSessionActionBody(subcommand, parsed) {
|
|
1237
|
+
if (subcommand === "goto" || subcommand === "navigate") {
|
|
1238
|
+
const target = getStringFlag(parsed.flags, "url") ?? parsed.positionals[1];
|
|
1239
|
+
if (!target) {
|
|
1240
|
+
throw new CliError("input.url_required", `browser session ${subcommand} requires an HTTPS URL target.`, 2);
|
|
1241
|
+
}
|
|
1242
|
+
return { action: "navigate", url: validateBrowserUrl(target) };
|
|
1243
|
+
}
|
|
1244
|
+
if (subcommand === "click") {
|
|
1245
|
+
return { action: "click", selector: requiredBrowserSessionSelector(parsed, "browser session click requires --selector <css>.") };
|
|
1246
|
+
}
|
|
1247
|
+
if (subcommand === "type") {
|
|
1248
|
+
const text = getStringFlag(parsed.flags, "text") ?? parsed.positionals.slice(2).join(" ").trim();
|
|
1249
|
+
if (!text) {
|
|
1250
|
+
throw new CliError("input.text_required", "browser session type requires --text <text>.", 2);
|
|
1251
|
+
}
|
|
1252
|
+
return {
|
|
1253
|
+
action: "type",
|
|
1254
|
+
selector: requiredBrowserSessionSelector(parsed, "browser session type requires --selector <css>."),
|
|
1255
|
+
text: text.slice(0, 2_000)
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
if (subcommand === "scroll") {
|
|
1259
|
+
const deltaY = validateIntegerRange(getStringFlag(parsed.flags, "deltaY") ?? parsed.positionals[1], "--delta-y", -10_000, 10_000);
|
|
1260
|
+
return deltaY === undefined ? { action: "scroll" } : { action: "scroll", deltaY };
|
|
1261
|
+
}
|
|
1262
|
+
if (subcommand === "wait") {
|
|
1263
|
+
const ms = validateIntegerRange(getStringFlag(parsed.flags, "ms") ?? parsed.positionals[1], "--ms", 1, 30_000);
|
|
1264
|
+
return ms === undefined ? { action: "wait" } : { action: "wait", ms };
|
|
1265
|
+
}
|
|
1266
|
+
throw new CliError("input.invalid_browser_session_action", "Unknown Agent Browser session action.", 2);
|
|
1267
|
+
}
|
|
1268
|
+
function browserSessionLiveViewQuery(parsed, globalDebug = false) {
|
|
1269
|
+
const mode = browserSessionLiveViewMode(parsed, globalDebug);
|
|
1270
|
+
return mode === "devtools" ? "?view=devtools" : "";
|
|
1271
|
+
}
|
|
1272
|
+
function browserSessionLiveViewMode(parsed, globalDebug = false) {
|
|
1273
|
+
const raw = (getStringFlag(parsed.flags, "view") ?? getStringFlag(parsed.flags, "mode") ?? "").trim().toLowerCase();
|
|
1274
|
+
const debug = globalDebug || getBooleanFlag(parsed.flags, "debug") || getBooleanFlag(parsed.flags, "devtools");
|
|
1275
|
+
if (debug && (raw === "tab" || raw === "browser")) {
|
|
1276
|
+
throw new CliError("input.invalid_live_view_mode", "Use either --debug/--devtools or --view tab, not both.", 2);
|
|
1277
|
+
}
|
|
1278
|
+
if (debug || raw === "devtools" || raw === "debug" || raw === "inspector") {
|
|
1279
|
+
return "devtools";
|
|
1280
|
+
}
|
|
1281
|
+
if (!raw || raw === "tab" || raw === "browser") {
|
|
1282
|
+
return "tab";
|
|
1283
|
+
}
|
|
1284
|
+
throw new CliError("input.invalid_live_view_mode", "Agent Browser live view mode must be tab or devtools.", 2);
|
|
1285
|
+
}
|
|
1286
|
+
function requiredBrowserSessionSelector(parsed, message) {
|
|
1287
|
+
const selector = getStringFlag(parsed.flags, "selector") ?? parsed.positionals[1];
|
|
1288
|
+
if (!selector || selector.trim().length === 0) {
|
|
1289
|
+
throw new CliError("input.selector_required", message, 2);
|
|
1290
|
+
}
|
|
1291
|
+
return selector.trim().slice(0, 500);
|
|
1292
|
+
}
|
|
1293
|
+
function validateIntegerRange(input, label, min, max) {
|
|
1294
|
+
if (input === undefined) {
|
|
1295
|
+
return undefined;
|
|
1296
|
+
}
|
|
1297
|
+
const value = Number(input);
|
|
1298
|
+
if (!Number.isInteger(value) || value < min || value > max) {
|
|
1299
|
+
throw new CliError("input.invalid_number", `${label} must be an integer from ${min} to ${max}.`, 2);
|
|
1300
|
+
}
|
|
1301
|
+
return value;
|
|
1302
|
+
}
|
|
1303
|
+
function browserSessionMessage(response, fallback) {
|
|
1304
|
+
if (!isRecord(response)) {
|
|
1305
|
+
return fallback;
|
|
1306
|
+
}
|
|
1307
|
+
const session = isRecord(response.session) ? response.session : undefined;
|
|
1308
|
+
const id = typeof session?.id === "string" ? session.id : typeof response.id === "string" ? response.id : undefined;
|
|
1309
|
+
const observation = isRecord(response.observation) ? response.observation : undefined;
|
|
1310
|
+
const screenshot = isRecord(observation?.screenshot) ? observation?.screenshot : undefined;
|
|
1311
|
+
const auth = isRecord(response.auth) ? response.auth : isRecord(session?.auth) ? session?.auth : undefined;
|
|
1312
|
+
const lines = [fallback];
|
|
1313
|
+
if (id) {
|
|
1314
|
+
lines.push(`Session: ${id}`);
|
|
1315
|
+
lines.push(`Observe: vibecodr browser session observe ${id}`);
|
|
1316
|
+
lines.push(`Watch live: vibecodr browser session live ${id}`);
|
|
1317
|
+
lines.push(`Need to sign in or steer? vibecodr browser session auth ${id}`);
|
|
1318
|
+
lines.push(`Close: vibecodr browser session close ${id}`);
|
|
1319
|
+
}
|
|
1320
|
+
if (auth && typeof auth.status === "string") {
|
|
1321
|
+
lines.push(`Control: ${browserSessionControlLabel(auth.status)}`);
|
|
1322
|
+
}
|
|
1323
|
+
const handoffUrl = browserSessionHandoffUrlFromResponse(response);
|
|
1324
|
+
if (handoffUrl) {
|
|
1325
|
+
lines.push(`Open live page: ${handoffUrl}`);
|
|
1326
|
+
}
|
|
1327
|
+
if (typeof screenshot?.id === "string") {
|
|
1328
|
+
lines.push(`Screenshot proof: vibecodr proof show ${screenshot.id}`);
|
|
1329
|
+
}
|
|
1330
|
+
return lines.join("\n");
|
|
1331
|
+
}
|
|
1332
|
+
function browserSessionControlLabel(status) {
|
|
1333
|
+
if (status === "human_control")
|
|
1334
|
+
return "you are controlling the browser";
|
|
1335
|
+
if (status === "auth_ready")
|
|
1336
|
+
return "agent can continue";
|
|
1337
|
+
if (status === "revoked")
|
|
1338
|
+
return "live link ended";
|
|
1339
|
+
if (status === "handoff_expired")
|
|
1340
|
+
return "watch link expired";
|
|
1341
|
+
if (status === "public")
|
|
1342
|
+
return "agent is browsing";
|
|
1343
|
+
return status;
|
|
1344
|
+
}
|
|
1345
|
+
function browserSessionHandoffUrlFromResponse(response) {
|
|
1346
|
+
if (!isRecord(response)) {
|
|
1347
|
+
return undefined;
|
|
1348
|
+
}
|
|
1349
|
+
const auth = isRecord(response.auth) ? response.auth : undefined;
|
|
1350
|
+
return typeof auth?.handoffUrl === "string" ? auth.handoffUrl : undefined;
|
|
1351
|
+
}
|
|
1030
1352
|
function normalizeComputerCommandOptions(parsed, missingMessage) {
|
|
1031
1353
|
const command = getStringFlag(parsed.flags, "command") ?? parsed.positionals.join(" ").trim();
|
|
1032
1354
|
if (!command) {
|
|
@@ -1895,6 +2217,26 @@ function usageBar(percent) {
|
|
|
1895
2217
|
return `[${"#".repeat(filled)}${"-".repeat(width - filled)}]`;
|
|
1896
2218
|
}
|
|
1897
2219
|
function buildToolTestPayload(capability, target, parsed, globalToolTimeoutMs) {
|
|
2220
|
+
if (capability.startsWith("browser.session_")) {
|
|
2221
|
+
const input = {};
|
|
2222
|
+
if (capability === "browser.session_open") {
|
|
2223
|
+
if (!target) {
|
|
2224
|
+
throw new CliError("input.url_required", "browser.session_open requires an HTTPS URL target.", 2);
|
|
2225
|
+
}
|
|
2226
|
+
input.url = validateBrowserUrl(target);
|
|
2227
|
+
const timeoutMs = validatePositiveInt(getStringFlag(parsed.flags, "timeoutMs"), "--timeout-ms", 1000, 3_600_000);
|
|
2228
|
+
const idleTimeoutMs = validatePositiveInt(getStringFlag(parsed.flags, "idleTimeoutMs"), "--idle-timeout-ms", 1000, 600_000);
|
|
2229
|
+
if (timeoutMs !== undefined)
|
|
2230
|
+
input.timeoutMs = timeoutMs;
|
|
2231
|
+
if (idleTimeoutMs !== undefined)
|
|
2232
|
+
input.idleTimeoutMs = idleTimeoutMs;
|
|
2233
|
+
}
|
|
2234
|
+
else {
|
|
2235
|
+
input.sessionId = validateBrowserSessionId(target);
|
|
2236
|
+
Object.assign(input, browserSessionToolTestActionInput(capability, parsed));
|
|
2237
|
+
}
|
|
2238
|
+
return { capability, input };
|
|
2239
|
+
}
|
|
1898
2240
|
if (capability.startsWith("browser.")) {
|
|
1899
2241
|
if (!target) {
|
|
1900
2242
|
throw new CliError("input.url_required", `${capability} requires an HTTPS URL target.`, 2);
|
|
@@ -1976,6 +2318,37 @@ function buildToolTestPayload(capability, target, parsed, globalToolTimeoutMs) {
|
|
|
1976
2318
|
}
|
|
1977
2319
|
return { capability, input: {} };
|
|
1978
2320
|
}
|
|
2321
|
+
function browserSessionToolTestActionInput(capability, parsed) {
|
|
2322
|
+
if (capability === "browser.session_observe" || capability === "browser.session_close") {
|
|
2323
|
+
return {};
|
|
2324
|
+
}
|
|
2325
|
+
if (capability === "browser.session_navigate") {
|
|
2326
|
+
const target = getStringFlag(parsed.flags, "url") ?? parsed.positionals[1];
|
|
2327
|
+
if (!target) {
|
|
2328
|
+
throw new CliError("input.url_required", "browser.session_navigate requires an HTTPS URL target.", 2);
|
|
2329
|
+
}
|
|
2330
|
+
return { url: validateBrowserUrl(target) };
|
|
2331
|
+
}
|
|
2332
|
+
if (capability === "browser.session_click") {
|
|
2333
|
+
return { selector: requiredBrowserSessionSelector(parsed, "browser.session_click requires --selector <css>.") };
|
|
2334
|
+
}
|
|
2335
|
+
if (capability === "browser.session_type") {
|
|
2336
|
+
const text = getStringFlag(parsed.flags, "text") ?? parsed.positionals.slice(2).join(" ").trim();
|
|
2337
|
+
if (!text) {
|
|
2338
|
+
throw new CliError("input.text_required", "browser.session_type requires --text <text>.", 2);
|
|
2339
|
+
}
|
|
2340
|
+
return { selector: requiredBrowserSessionSelector(parsed, "browser.session_type requires --selector <css>."), text };
|
|
2341
|
+
}
|
|
2342
|
+
if (capability === "browser.session_scroll") {
|
|
2343
|
+
const deltaY = validateIntegerRange(getStringFlag(parsed.flags, "deltaY") ?? parsed.positionals[1], "--delta-y", -10_000, 10_000);
|
|
2344
|
+
return deltaY === undefined ? {} : { deltaY };
|
|
2345
|
+
}
|
|
2346
|
+
if (capability === "browser.session_wait") {
|
|
2347
|
+
const ms = validateIntegerRange(getStringFlag(parsed.flags, "ms") ?? parsed.positionals[1], "--ms", 1, 30_000);
|
|
2348
|
+
return ms === undefined ? {} : { ms };
|
|
2349
|
+
}
|
|
2350
|
+
return {};
|
|
2351
|
+
}
|
|
1979
2352
|
function buildScheduledQaPayload(target, parsed, globalToolTimeoutMs) {
|
|
1980
2353
|
const capability = normalizeScheduledQaCliCapability(getStringFlag(parsed.flags, "capability") ?? getStringFlag(parsed.flags, "tool") ?? "browser.render_url");
|
|
1981
2354
|
const timeoutInput = getStringFlag(parsed.flags, "timeoutMs") ?? (globalToolTimeoutMs === undefined ? undefined : String(globalToolTimeoutMs));
|
|
@@ -2797,6 +3170,9 @@ Usage:
|
|
|
2797
3170
|
vibecodr computer status
|
|
2798
3171
|
vibecodr computer run "<command>" [--timeout-ms <ms>] [--network public|off] [--local|--out ./proof] [--no-wait] [--details]
|
|
2799
3172
|
vibecodr computer test "<command>" [--timeout-ms <ms>] [--network public|off] [--local|--out ./proof] [--no-wait] [--details]
|
|
3173
|
+
|
|
3174
|
+
Notes:
|
|
3175
|
+
Automatic output saves are workspace-bounded. If --out points outside this workspace, vibecodr writes to ./.vibecodr/browser-artifacts/<run> instead.
|
|
2800
3176
|
`;
|
|
2801
3177
|
case "browser":
|
|
2802
3178
|
return `vibecodr browser
|
|
@@ -2810,14 +3186,32 @@ Usage:
|
|
|
2810
3186
|
vibecodr browser pdf <https-url> [--local|--out ./proof] [--no-wait] [--details]
|
|
2811
3187
|
vibecodr browser crawl <https-url> [--max-pages n] [--max-depth n] [--local|--out ./proof]
|
|
2812
3188
|
vibecodr browser snapshot <https-url> [--local|--out ./proof]
|
|
3189
|
+
vibecodr browser session open <https-url> [--timeout-ms <ms>] [--idle-timeout-ms <ms>]
|
|
3190
|
+
vibecodr browser session observe <sessionId>
|
|
3191
|
+
vibecodr browser session goto <sessionId> <https-url>
|
|
3192
|
+
vibecodr browser session click <sessionId> --selector <css>
|
|
3193
|
+
vibecodr browser session type <sessionId> --selector <css> --text <text>
|
|
3194
|
+
vibecodr browser session scroll <sessionId> [--delta-y 800]
|
|
3195
|
+
vibecodr browser session wait <sessionId> [--ms 1000]
|
|
3196
|
+
vibecodr browser session live <sessionId> [--no-open] [--debug|--view devtools]
|
|
3197
|
+
vibecodr browser session auth <sessionId> [--no-open] [--debug|--view devtools]
|
|
3198
|
+
vibecodr browser session auth-status <sessionId>
|
|
3199
|
+
vibecodr browser session auth-complete <sessionId>
|
|
3200
|
+
vibecodr browser session auth-revoke <sessionId>
|
|
3201
|
+
vibecodr browser session close <sessionId>
|
|
2813
3202
|
|
|
2814
3203
|
Attach a note:
|
|
2815
3204
|
vibecodr browser notes <https-url> --note <text> [--local|--out ./proof]
|
|
2816
3205
|
|
|
2817
3206
|
Notes:
|
|
2818
3207
|
Add --local to save completed output into ./vibecodr-proof automatically.
|
|
3208
|
+
Automatic output saves are workspace-bounded. If --out points outside this workspace, vibecodr writes to ./.vibecodr/browser-artifacts/<run> instead.
|
|
2819
3209
|
browser snapshot captures page state; it does not prompt an agent or model.
|
|
2820
3210
|
browser notes saves your note with the snapshot.
|
|
3211
|
+
browser session opens a real hosted Agent Browser the agent can observe and control until it is closed or idle.
|
|
3212
|
+
browser session live opens the watch-and-intercede page without pausing the agent.
|
|
3213
|
+
browser session auth opens the same live page with human control already active for login, MFA, CAPTCHA, or other human-only steps.
|
|
3214
|
+
Add --debug or --view devtools only when an agent/developer needs the inspector panel instead of the plain browser tab.
|
|
2821
3215
|
`;
|
|
2822
3216
|
case "work":
|
|
2823
3217
|
return `vibecodr work
|
|
@@ -2829,6 +3223,9 @@ Usage:
|
|
|
2829
3223
|
vibecodr work show <jobId>
|
|
2830
3224
|
vibecodr work follow <jobId> [--local|--out ./proof] [--details]
|
|
2831
3225
|
vibecodr work cancel <jobId> --yes
|
|
3226
|
+
|
|
3227
|
+
Notes:
|
|
3228
|
+
Automatic output saves are workspace-bounded. If --out points outside this workspace, vibecodr writes to ./.vibecodr/browser-artifacts/<run> instead.
|
|
2832
3229
|
`;
|
|
2833
3230
|
case "proof":
|
|
2834
3231
|
return `vibecodr proof
|