fix: harden trans playwright passthrough
This commit is contained in:
@@ -255,7 +255,7 @@ UniDesk 是一个以主 server 为统一入口的分布式工作平台;本文
|
||||
- `bun scripts/cli.ts codex judge <taskId> --attempt <n> [--dry-run]`:按指定 task/attempt 用与队列 worker 相同的上下文构建和 MiniMax judge 调用路径单步复现完成判定;`--dry-run` 只输出 prompt/payload 诊断。
|
||||
- `bun scripts/cli.ts codex steer <taskId> [prompt|--prompt-file path|--prompt-stdin] [--steer-id id] [--dry-run] [--no-retry|--retry-attempts N]` / `codex steer-confirm <taskId> --steer-id <id>`:向运行中的 active turn 注入纠偏提示并用 `steerId` 做幂等/trace 确认;真实输出不回显 prompt,遇到 `deliveryUnconfirmed` 先查确认命令,不重复发送同一纠偏。
|
||||
- `bun scripts/cli.ts codex interrupt|cancel <taskId>`:通过 Code Queue 私有代理中断运行任务或取消 queued/retry_wait 任务,规则见 `docs/reference/cli.md`。
|
||||
- `bun scripts/playwright-cli.ts screenshot|open|eval ...` / `trans D601 playwright <<'PW' ...`:UniDesk 仓库自带的 Playwright 指挥手测 wrapper 与跨 host 透传入口,默认 headless,支持 heredoc 执行并把截图等产物回传到本机 `/tmp`;不实现长驻浏览器 daemon,规则见 `docs/reference/cli.md`。
|
||||
- `bun scripts/playwright-cli.ts screenshot|open|eval ...` / `trans D601 playwright <<'PW' ...`:UniDesk 仓库自带的 Playwright 指挥手测 wrapper 与跨 host 透传入口,默认 headless,支持 heredoc 后台执行、短轮询和截图等产物回传到本机 `/tmp`;不实现长驻浏览器 daemon,规则见 `docs/reference/cli.md`。
|
||||
- `bun scripts/cli.ts server stop`:以异步 job 停止固定 Compose 项目中的全部 UniDesk 服务,停止后用 `server status` 复核。
|
||||
- `bun scripts/cli.ts job list [--limit N]` / `bun scripts/cli.ts job status latest [--tail-bytes N]`:分页查询 `.state/jobs/` 中的异步任务状态,状态输出只读日志尾部并保留完整日志路径,job 机制见 `docs/reference/cli.md`。
|
||||
- `bun scripts/cli.ts debug health` / `bun scripts/cli.ts debug dispatch` / `bun scripts/cli.ts debug task`:通过 Docker 内网 core、真实 HTTP、WebSocket、系统指标、Docker 状态和 Host SSH 维护桥流程调试健康检查、任务下发与任务结果,调试规则见 `docs/reference/cli.md`。
|
||||
|
||||
@@ -146,9 +146,9 @@ playwright-cli screenshot https://example.com "$UNIDESK_PLAYWRIGHT_SCREENSHOT" -
|
||||
PW
|
||||
```
|
||||
|
||||
`playwright` operation 读取 stdin heredoc,在目标 POSIX host/workload 上临时注入 `playwright-cli` wrapper 到 `PATH`。wrapper 优先使用远端用户的 `~/.agents/skills/playwright*/scripts/playwright-cli.ts`,其次使用 route workspace 中的 `./scripts/playwright-cli.ts`,最后才使用远端 `PATH` 中的 `playwright-cli`。命令会设置 `UNIDESK_PLAYWRIGHT_REMOTE_DIR` 和 `UNIDESK_PLAYWRIGHT_SCREENSHOT`,把远端 run 目录中的 `png/jpg/jpeg/webp/pdf` 产物回传到本机 `--local-dir`,默认 `/tmp`,并返回本地路径、远端路径、字节数、SHA-256、stdout/stderr tail 和 transfer verification。
|
||||
`playwright` operation 读取 stdin heredoc,在目标 POSIX host/workload 上临时注入 `playwright-cli` wrapper 到 `PATH`。wrapper 优先使用 route workspace 或目标 host 上已知 UniDesk workspace 的 `./scripts/playwright-cli.ts`,其次使用远端用户的 `~/.agents/skills/playwright*/scripts/playwright-cli.ts`,最后才使用远端 `PATH` 中的 `playwright-cli`。命令会设置 `UNIDESK_PLAYWRIGHT_REMOTE_DIR` 和 `UNIDESK_PLAYWRIGHT_SCREENSHOT`,把远端 run 目录中的 `png/jpg/jpeg/webp/pdf` 产物回传到本机 `--local-dir`,默认 `/tmp`,并返回本地路径、远端路径、字节数、SHA-256、stdout/stderr tail 和 transfer verification。
|
||||
|
||||
该入口只负责短生命周期 Playwright 执行和产物回传,不提供长驻浏览器 daemon。需要多步交互时,把步骤写在同一个 heredoc 内;需要保留远端证据时显式加 `--keep-remote`。
|
||||
该入口只负责短生命周期 Playwright 执行和产物回传,不提供长驻浏览器 daemon。需要多步交互时,把步骤写在同一个 heredoc 内;helper 会在远端后台提交 job,并用短连接轮询 manifest,避免单次 SSH 透传超过 60 秒硬限制。需要保留远端证据时显式加 `--keep-remote`。
|
||||
|
||||
## Async Job State
|
||||
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ export function sshHelp(): unknown {
|
||||
"For arbitrary stdin streams into a workload command, use a workload route plus `exec --stdin -- <command> ...`; this keeps the route as location-only and avoids heredoc/base64/tar shell wrapping.",
|
||||
"`apply-patch` is the default remote text patch entry and uses the v2 local line-based patch engine with remote read/write operations, including Windows routes such as `D601:win/c/test`, so long Unicode/Chinese lines and pure insertion hunks avoid the legacy remote shell hunk parser. Plain multi-file Update File patches on POSIX host/k3s and Windows workspace routes use bulk read/write operations to avoid per-file SSH round trips. Its stdout follows Codex apply_patch text output rather than UniDesk JSON output; stderr keeps Codex-style failure text and appends one `UNIDESK_APPLY_PATCH_TIMING` JSON summary with durationMs, patchBytes, fileCount, hunkCount, changedCount, remoteOperationCount, remoteOperationCounts and remoteElapsedMs so slow patch runs can be attributed without changing success stdout.",
|
||||
"`upload` and `download` are the default whole-file transfer entries for non-text and generated files. They write through remote temp files, verify byte count and SHA-256 on both sides, and return `verification.automatic=true`, `verification.verified=true`, and `verification.match.{bytes,sha256}=true`; this JSON is the transfer integrity proof, so callers do not need a separate manual `sha256sum` check. Downloads stream over `host.ssh.tcp-pool`, emit progress JSON, and may receive a caller-supplied `--inactivity-timeout-ms` from async artifact/deploy jobs so active large transfers are not killed by the generic short-command budget.",
|
||||
"`playwright` runs a stdin heredoc on the target POSIX host/workload with a temporary `playwright-cli` wrapper in PATH, sets `UNIDESK_PLAYWRIGHT_SCREENSHOT` under a remote `/tmp` run directory, then downloads image/pdf artifacts to local `/tmp` by default with the same verified SHA-256 transfer path. Canonical syntax is `trans D601 playwright <<'PW' ... PW`; workspace routes are optional and only affect wrapper discovery.",
|
||||
"`playwright` runs a stdin heredoc on the target POSIX host/workload with a temporary `playwright-cli` wrapper in PATH, submits the remote script as a background job, polls short status commands for the manifest, then downloads image/pdf artifacts to local `/tmp` by default with the same verified SHA-256 transfer path. Canonical syntax is `trans D601 playwright <<'PW' ... PW`; workspace and known UniDesk host workspaces are preferred for wrapper discovery before external skill passthroughs.",
|
||||
"`apply-patch-v1` is the only legacy fallback entry: it rejects low-context update hunks by default, reports the matched file:line for each hunk on stderr, and only accepts --allow-loose when the caller has manually reviewed an intentionally ambiguous insertion.",
|
||||
"script defaults to target /bin/sh and inherits provider proxy variables such as HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY; it is for host/k3s POSIX shell only. Use --shell bash only for bash syntax such as pipefail, arrays, or [[ ... ]], not as a proxy workaround.",
|
||||
"Route syntax is `{provider}:{plane}[:{scope...}] {operation} [operation-args...]`: the first argv token locates a distributed target only, and every following token belongs to the operation parser. Host workspace routes use `<provider>:/absolute/workspace`; WSL providers can use `<provider>:win ps` for Windows PowerShell and `<provider>:win cmd` for Windows cmd.exe, with `<provider>:win/c/test ...` mapping the Windows cwd to `C:\\test`; native k3s providers such as D601 and G14 use `<provider>:k3s` for the control plane and `<provider>:k3s:<namespace>:<workload>[:<container>]` for a workload/container. In k3s routes, `:` is the distributed route separator; `/...` is only an in-container filesystem cwd and never selects a container. Prefer operation `--cwd /path` when a container is also specified.",
|
||||
|
||||
+128
-14
@@ -13,6 +13,8 @@ interface SshPlaywrightOptions {
|
||||
remoteDir: string | null;
|
||||
keepRemote: boolean;
|
||||
inactivityTimeoutMs?: number;
|
||||
pollIntervalMs: number;
|
||||
waitTimeoutMs: number;
|
||||
}
|
||||
|
||||
interface RemotePlaywrightManifest {
|
||||
@@ -62,9 +64,12 @@ export async function runSshPlaywrightOperation(
|
||||
const runId = `unidesk-playwright-${safePathSegment(invocation.providerId)}-${Date.now()}-${randomBytes(4).toString("hex")}`;
|
||||
const remoteDir = options.remoteDir ?? `/tmp/${runId}`;
|
||||
const localDir = resolve(options.localDir);
|
||||
const remoteCommand = builders.buildRouteCommand(invocation.route, ["sh", "-c", remotePlaywrightRunnerScript(remoteDir), "unidesk-playwright"], { stdin: true });
|
||||
const result = await executor.runRemoteCommand(remoteCommand, userScript);
|
||||
const manifest = parseRemoteManifest(result, remoteDir, runId);
|
||||
const submitCommand = builders.buildRouteCommand(invocation.route, ["sh", "-c", remotePlaywrightSubmitScript(remoteDir), "unidesk-playwright-submit"], { stdin: true });
|
||||
const submit = await executor.runRemoteCommand(submitCommand, userScript);
|
||||
if (submit.exitCode !== 0) {
|
||||
throw new Error(`ssh playwright submit failed: exitCode=${submit.exitCode}; stdoutTail=${JSON.stringify(submit.stdout.slice(-1000))}; stderrTail=${JSON.stringify(submit.stderr.slice(-1000))}`);
|
||||
}
|
||||
const manifest = await pollRemoteManifest(invocation.route, executor, builders, remoteDir, runId, options);
|
||||
const artifacts: Array<SshVerifiedDownloadResult & { manifestBytes: number | null; manifestSha256: string | null }> = [];
|
||||
let downloadFailure: Record<string, unknown> | null = null;
|
||||
|
||||
@@ -117,9 +122,10 @@ export async function runSshPlaywrightOperation(
|
||||
cleanup,
|
||||
downloadFailure,
|
||||
remoteCommand: {
|
||||
exitCode: result.exitCode,
|
||||
stdoutBytes: Buffer.byteLength(result.stdout, "utf8"),
|
||||
stderrBytes: Buffer.byteLength(result.stderr, "utf8"),
|
||||
exitCode: manifest.exitCode,
|
||||
submitExitCode: submit.exitCode,
|
||||
submitStdoutBytes: Buffer.byteLength(submit.stdout, "utf8"),
|
||||
submitStderrBytes: Buffer.byteLength(submit.stderr, "utf8"),
|
||||
},
|
||||
};
|
||||
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
||||
@@ -132,6 +138,8 @@ function parseSshPlaywrightOptions(args: string[]): SshPlaywrightOptions {
|
||||
localDir: "/tmp",
|
||||
remoteDir: null,
|
||||
keepRemote: false,
|
||||
pollIntervalMs: 2_000,
|
||||
waitTimeoutMs: 180_000,
|
||||
};
|
||||
for (let index = 1; index < args.length; index += 1) {
|
||||
const arg = args[index] ?? "";
|
||||
@@ -171,6 +179,24 @@ function parseSshPlaywrightOptions(args: string[]): SshPlaywrightOptions {
|
||||
options.inactivityTimeoutMs = parsePositiveInteger("--inactivity-timeout-ms", arg.slice("--inactivity-timeout-ms=".length));
|
||||
continue;
|
||||
}
|
||||
if (arg === "--poll-interval-ms") {
|
||||
options.pollIntervalMs = parsePositiveInteger(arg, next);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("--poll-interval-ms=")) {
|
||||
options.pollIntervalMs = parsePositiveInteger("--poll-interval-ms", arg.slice("--poll-interval-ms=".length));
|
||||
continue;
|
||||
}
|
||||
if (arg === "--wait-timeout-ms") {
|
||||
options.waitTimeoutMs = parsePositiveInteger(arg, next);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("--wait-timeout-ms=")) {
|
||||
options.waitTimeoutMs = parsePositiveInteger("--wait-timeout-ms", arg.slice("--wait-timeout-ms=".length));
|
||||
continue;
|
||||
}
|
||||
throw new Error(`unsupported ssh playwright option: ${arg}`);
|
||||
}
|
||||
return options;
|
||||
@@ -190,7 +216,8 @@ function playwrightHelp(): Record<string, unknown> {
|
||||
],
|
||||
behavior: [
|
||||
"Reads a POSIX shell heredoc from stdin and runs it on the target route.",
|
||||
"Prepends a temporary playwright-cli wrapper to PATH. The wrapper prefers ~/.agents/skills/playwright*/scripts/playwright-cli.ts, then ./scripts/playwright-cli.ts in the route workspace, then a playwright-cli binary.",
|
||||
"Prepends a temporary playwright-cli wrapper to PATH. The wrapper prefers ./scripts/playwright-cli.ts in the route workspace or known UniDesk host workspaces, then ~/.agents/skills/playwright*/scripts/playwright-cli.ts, then a playwright-cli binary.",
|
||||
"Submits the remote script as a background job and polls short status commands for the manifest, so multi-step Playwright flows do not occupy one SSH connection past the 60s trans runtime limit.",
|
||||
"Sets UNIDESK_PLAYWRIGHT_REMOTE_DIR and UNIDESK_PLAYWRIGHT_SCREENSHOT. Files created under that remote dir with image/pdf extensions are downloaded to --local-dir with byte and sha256 verification.",
|
||||
],
|
||||
options: {
|
||||
@@ -198,6 +225,8 @@ function playwrightHelp(): Record<string, unknown> {
|
||||
"--remote-dir <absolute-path>": "Remote artifact directory. Default: /tmp/unidesk-playwright-<provider>-<timestamp>-<id>.",
|
||||
"--keep-remote": "Do not remove the remote artifact directory after the transfer.",
|
||||
"--inactivity-timeout-ms <n>": "Forwarded to verified artifact download when large screenshots are returned.",
|
||||
"--wait-timeout-ms <n>": "Maximum wall-clock time to wait for the remote Playwright job. Default: 180000.",
|
||||
"--poll-interval-ms <n>": "Short-query polling interval for the remote job. Default: 2000.",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -227,17 +256,94 @@ async function readAllStdin(): Promise<string> {
|
||||
return Buffer.concat(chunks).toString("utf8");
|
||||
}
|
||||
|
||||
async function pollRemoteManifest(
|
||||
route: ParsedSshRoute,
|
||||
executor: SshRemoteCommandExecutor,
|
||||
builders: SshFileTransferCommandBuilders,
|
||||
remoteDir: string,
|
||||
runId: string,
|
||||
options: SshPlaywrightOptions,
|
||||
): Promise<RemotePlaywrightManifest> {
|
||||
const deadline = Date.now() + options.waitTimeoutMs;
|
||||
let lastStatus: SshCaptureResult | null = null;
|
||||
while (Date.now() <= deadline) {
|
||||
const command = builders.buildRouteCommand(route, ["sh", "-c", remotePlaywrightStatusScript(), "unidesk-playwright-status", remoteDir]);
|
||||
const status = await executor.runRemoteCommand(command);
|
||||
lastStatus = status;
|
||||
if (status.exitCode === 0 && status.stdout.includes(manifestEnd)) return parseRemoteManifest(status, remoteDir, runId);
|
||||
if (status.exitCode !== 0 && !/status\t(?:pending|running)/u.test(status.stdout)) {
|
||||
throw new Error(`ssh playwright status failed: exitCode=${status.exitCode}; stdoutTail=${JSON.stringify(status.stdout.slice(-1000))}; stderrTail=${JSON.stringify(status.stderr.slice(-1000))}`);
|
||||
}
|
||||
await sleep(options.pollIntervalMs);
|
||||
}
|
||||
throw new Error(`ssh playwright timed out waiting for remote job after ${options.waitTimeoutMs}ms; lastStdoutTail=${JSON.stringify(lastStatus?.stdout.slice(-1000) ?? "")}; lastStderrTail=${JSON.stringify(lastStatus?.stderr.slice(-1000) ?? "")}`);
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
||||
}
|
||||
|
||||
function remotePlaywrightSubmitScript(remoteDir: string): string {
|
||||
const runner = Buffer.from(remotePlaywrightRunnerScript(remoteDir), "utf8").toString("base64");
|
||||
return [
|
||||
"set -eu",
|
||||
`remote_dir=${shellQuote(remoteDir)}`,
|
||||
'mkdir -p -- "$remote_dir"',
|
||||
'user_script="$remote_dir/user-script.sh"',
|
||||
'runner_script="$remote_dir/runner.sh"',
|
||||
'pid_file="$remote_dir/pid"',
|
||||
'submit_stdout="$remote_dir/submit.out"',
|
||||
'submit_stderr="$remote_dir/submit.err"',
|
||||
'if [ -f "$pid_file" ] && kill -0 "$(cat "$pid_file")" >/dev/null 2>&1; then',
|
||||
' printf "status\\trunning\\nremote_dir\\t%s\\npid\\t%s\\n" "$remote_dir" "$(cat "$pid_file")"',
|
||||
" exit 0",
|
||||
"fi",
|
||||
'cat > "$user_script"',
|
||||
'chmod 700 "$user_script"',
|
||||
`printf %s ${shellQuote(runner)} | base64 -d >"$runner_script"`,
|
||||
'chmod 700 "$runner_script"',
|
||||
'nohup sh "$runner_script" "$user_script" >"$submit_stdout" 2>"$submit_stderr" < /dev/null &',
|
||||
'pid=$!',
|
||||
'printf "%s\\n" "$pid" >"$pid_file"',
|
||||
'printf "status\\tsubmitted\\nremote_dir\\t%s\\npid\\t%s\\n" "$remote_dir" "$pid"',
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function remotePlaywrightStatusScript(): string {
|
||||
return [
|
||||
"set -eu",
|
||||
'remote_dir="$1"',
|
||||
'manifest="$remote_dir/manifest.tsv"',
|
||||
'pid_file="$remote_dir/pid"',
|
||||
'if [ -f "$manifest" ]; then cat "$manifest"; exit 0; fi',
|
||||
'if [ -f "$pid_file" ] && kill -0 "$(cat "$pid_file")" >/dev/null 2>&1; then',
|
||||
' printf "status\\trunning\\nremote_dir\\t%s\\npid\\t%s\\n" "$remote_dir" "$(cat "$pid_file")"',
|
||||
" exit 1",
|
||||
"fi",
|
||||
'if [ -f "$pid_file" ]; then',
|
||||
' printf "status\\texited-without-manifest\\nremote_dir\\t%s\\npid\\t%s\\n" "$remote_dir" "$(cat "$pid_file")"',
|
||||
' if [ -f "$remote_dir/submit.err" ]; then tail -c 1000 "$remote_dir/submit.err" >&2; fi',
|
||||
' if [ -f "$remote_dir/stderr.log" ]; then tail -c 1000 "$remote_dir/stderr.log" >&2; fi',
|
||||
" exit 2",
|
||||
"fi",
|
||||
'printf "status\\tpending\\nremote_dir\\t%s\\n" "$remote_dir"',
|
||||
'if [ -f "$remote_dir/submit.err" ]; then tail -c 1000 "$remote_dir/submit.err" >&2; fi',
|
||||
'if [ -f "$remote_dir/stderr.log" ]; then tail -c 1000 "$remote_dir/stderr.log" >&2; fi',
|
||||
"exit 1",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function remotePlaywrightRunnerScript(remoteDir: string): string {
|
||||
return [
|
||||
"set -eu",
|
||||
`remote_dir=${shellQuote(remoteDir)}`,
|
||||
'if [ "$#" -lt 1 ]; then printf "missing user script path\\n" >&2; exit 2; fi',
|
||||
'user_script="$1"',
|
||||
'mkdir -p -- "$remote_dir/bin"',
|
||||
'user_script="$remote_dir/user-script.sh"',
|
||||
'stdout_file="$remote_dir/stdout.log"',
|
||||
'stderr_file="$remote_dir/stderr.log"',
|
||||
'artifacts_file="$remote_dir/artifacts.tsv"',
|
||||
'cat > "$user_script"',
|
||||
'chmod 700 "$user_script"',
|
||||
'manifest_file="$remote_dir/manifest.tsv"',
|
||||
"sha256_file() {",
|
||||
" if command -v sha256sum >/dev/null 2>&1; then sha256sum -- \"$1\" | awk '{print $1}'; return; fi",
|
||||
" if command -v shasum >/dev/null 2>&1; then shasum -a 256 -- \"$1\" | awk '{print $1}'; return; fi",
|
||||
@@ -245,14 +351,21 @@ function remotePlaywrightRunnerScript(remoteDir: string): string {
|
||||
" printf 'missing sha256 tool\\n' >&2; return 127",
|
||||
"}",
|
||||
"resolve_playwright_cli() {",
|
||||
" if command -v bun >/dev/null 2>&1; then",
|
||||
" if [ -f ./scripts/playwright-cli.ts ]; then",
|
||||
" UNIDESK_PLAYWRIGHT_CLI_MODE=repo; UNIDESK_PLAYWRIGHT_CLI_CWD=$(pwd); UNIDESK_PLAYWRIGHT_CLI_BIN=; return 0",
|
||||
" fi",
|
||||
" for dir in \"$HOME/workspace/unidesk-dev\" \"$HOME/unidesk\" \"/home/ubuntu/workspace/unidesk-dev\" \"/root/unidesk\"; do",
|
||||
" if [ -f \"$dir/scripts/playwright-cli.ts\" ]; then",
|
||||
" UNIDESK_PLAYWRIGHT_CLI_MODE=repo; UNIDESK_PLAYWRIGHT_CLI_CWD=$dir; UNIDESK_PLAYWRIGHT_CLI_BIN=; return 0",
|
||||
" fi",
|
||||
" done",
|
||||
" fi",
|
||||
" for dir in \"$HOME/.agents/skills/playwright\" \"$HOME/.agents/skills/playwright-cli\" \"$HOME/.codex/skills/playwright\" \"$HOME/.codex/skills/playwright-cli\"; do",
|
||||
" if command -v bun >/dev/null 2>&1 && [ -f \"$dir/scripts/playwright-cli.ts\" ]; then",
|
||||
" UNIDESK_PLAYWRIGHT_CLI_MODE=skill; UNIDESK_PLAYWRIGHT_CLI_CWD=$dir; UNIDESK_PLAYWRIGHT_CLI_BIN=; return 0",
|
||||
" fi",
|
||||
" done",
|
||||
" if command -v bun >/dev/null 2>&1 && [ -f ./scripts/playwright-cli.ts ]; then",
|
||||
" UNIDESK_PLAYWRIGHT_CLI_MODE=repo; UNIDESK_PLAYWRIGHT_CLI_CWD=$(pwd); UNIDESK_PLAYWRIGHT_CLI_BIN=; return 0",
|
||||
" fi",
|
||||
" if command -v playwright-cli >/dev/null 2>&1; then",
|
||||
" UNIDESK_PLAYWRIGHT_CLI_MODE=bin; UNIDESK_PLAYWRIGHT_CLI_CWD=; UNIDESK_PLAYWRIGHT_CLI_BIN=$(command -v playwright-cli); return 0",
|
||||
" fi",
|
||||
@@ -286,7 +399,6 @@ function remotePlaywrightRunnerScript(remoteDir: string): string {
|
||||
"set +e",
|
||||
'sh "$user_script" >"$stdout_file" 2>"$stderr_file"',
|
||||
"user_rc=$?",
|
||||
"set -e",
|
||||
': > "$artifacts_file"',
|
||||
'find "$remote_dir" -type f | while IFS= read -r file; do',
|
||||
' case "$file" in "$user_script"|"$stdout_file"|"$stderr_file"|"$artifacts_file"|"$remote_dir/bin/"*) continue ;; esac',
|
||||
@@ -298,6 +410,7 @@ function remotePlaywrightRunnerScript(remoteDir: string): string {
|
||||
" esac",
|
||||
"done",
|
||||
"b64_tail() { if [ -f \"$1\" ]; then tail -c 4000 \"$1\" | base64 | tr -d '\\n'; fi; }",
|
||||
`{`,
|
||||
`printf '%s\\n' ${shellQuote(manifestBegin)}`,
|
||||
"printf 'run_id\\t%s\\n' \"${remote_dir##*/}\"",
|
||||
"printf 'exit_code\\t%s\\n' \"$user_rc\"",
|
||||
@@ -312,6 +425,7 @@ function remotePlaywrightRunnerScript(remoteDir: string): string {
|
||||
"printf 'stderr_tail_b64\\t%s\\n' \"$(b64_tail \"$stderr_file\")\"",
|
||||
'cat "$artifacts_file"',
|
||||
`printf '%s\\n' ${shellQuote(manifestEnd)}`,
|
||||
`} > "$manifest_file"`,
|
||||
'exit "$user_rc"',
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user