pw-repl 0.11.0 → 0.11.2

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/AGENTS.md CHANGED
@@ -25,7 +25,7 @@ If you hit a limitation or write a workaround, consider adding the capability to
25
25
  runs commands one at a time (`quit` and `dialog` skip its queue); `lib/server.js` is the command
26
26
  server; `lib/send.js` and `lib/client.js` are `send` and `where`; `lib/background.js` is
27
27
  `serve --background`, `attach` and `stop`; `lib/launch.js` is `--launch`; `lib/syntax.js` is how a
28
- command line's words are read, shared with `send`; `lib/cli-names.js` maps playwright-cli's command
28
+ command line's words are read, shared with `send`, and its `{{ PW_NAME }}` variables; `lib/cli-names.js` maps playwright-cli's command
29
29
  names to these; `lib/state.js` holds the session state, with each client's selected tab;
30
30
  `lib/output.js` routes all output so the server can return it; `lib/skill.js` stamps the skill
31
31
  `pw-repl skill` prints, and gives `where` its hash.
package/README.md CHANGED
@@ -88,7 +88,7 @@ the Docker bridge gateway, `ip route | awk '/default/ {print $3}'`).
88
88
 
89
89
  | I want to… | Commands |
90
90
  | ------------------------------------ | ------------------------------------------------------------------------------------- |
91
- | see where I am | `tab` (it marks the tab you are looking at `(in front)`), `info` |
91
+ | see where I am | `tab` (it marks the tab you are looking at `(visible)`), `info` |
92
92
  | see what is on the page | `snapshot`, `screenshot` |
93
93
  | do something on it | `click`, `fill`, `press` |
94
94
  | wait for a page or element | `wait load`, `wait <selector>`, `wait <selector> --gone` |
@@ -179,6 +179,14 @@ Several agents can share one REPL: `pw-repl send -c <name>` (or `PW_CLIENT=<name
179
179
  selected tab of its own, shown in the pane as `[server:<name>]`. `modes` says which client turned each mode on,
180
180
  and `modes off --mine` turns off only the sender's own.
181
181
 
182
+ A password can be typed as a variable: `PW_PASSWORD=... pw-repl send fill '#password' "{{ PW_PASSWORD }}"`,
183
+ or the same line in a file run with `send --file`, as `watch save` writes it. `send` takes the value of
184
+ `{{ PW_NAME }}` from its own environment and passes it along with the command; the REPL puts it in only as
185
+ it runs the command, so the file, and the command as the pane, the log and the prompt's history show it,
186
+ never hold it. Output that reads it back can: `snapshot`, `html` and `attrs` hide a password field's value,
187
+ but `eval` reading the field, a field a Show password button made plain text (`help snapshot`), or page
188
+ text that repeats it do not. Only `PW_` names are variables (`pw-repl --help` has the rest).
189
+
182
190
  ### The HTTP protocol
183
191
 
184
192
  With `pw-repl serve`, the server speaks HTTP with JSON, for clients of your own:
@@ -188,11 +196,12 @@ curl --unix-socket /tmp/playwright-repl.sock -H 'Content-Type: application/json'
188
196
  -d '{"command": "info"}' http://localhost/run
189
197
  ```
190
198
 
191
- It returns `{"status": "ok" | "error", "output": "..."}`, plus `"unconfirmed": true` when the command may or
192
- may not have done what it was sent to do (it timed out, or the REPL quit while it ran and it was not
193
- read-only). While the REPL starts, `/run` answers 503 with `"starting": true`: retry. `GET /health` says
194
- `"status": "starting"` until it serves, then `"ok"`. `pw-repl serve <port>` serves TCP on 127.0.0.1
195
- instead; other addresses are refused.
199
+ The body may also have `"client": "<name>"`, and `"vars": {"PW_NAME": "<value>"}` with the values of the
200
+ `{{ PW_NAME }}` variables in the command, one line each (else 400). It returns `{"status": "ok" | "error", "output": "..."}`, plus
201
+ `"unconfirmed": true` when the command may or may not have done what it was sent to do (it timed out, or
202
+ the REPL quit while it ran and it was not read-only). While the REPL starts, `/run` answers 503 with
203
+ `"starting": true`: retry. `GET /health` says `"status": "starting"` until it serves, then `"ok"`.
204
+ `pw-repl serve <port>` serves TCP on 127.0.0.1 instead; other addresses are refused.
196
205
 
197
206
  ## Tests
198
207
 
package/bin/pw-repl.js CHANGED
@@ -45,13 +45,14 @@ folder, as on send's command line, and # comments and blank lines skipped. It st
45
45
  command that fails, saying its line, with its exit status. -t is each command's own limit. Other
46
46
  clients' commands can run between its lines, on their own tabs.
47
47
 
48
- A {{ PW_NAME }} in a command, or in a line of the file, is the variable PW_NAME from send's environment,
49
- e.g. a password kept out of the file and the REPL's log. In a file: fill '#password' "{{ PW_PASSWORD }}",
48
+ A {{ PW_NAME }} in a command, or in a line of the file, is a variable: send takes its value from its own
49
+ environment (PW_NAME) and passes it along with the command, not in it, e.g. a password kept out of the
50
+ file and out of the command as the REPL logs it. In a file: fill '#password' "{{ PW_PASSWORD }}",
50
51
  run with PW_PASSWORD=... pw-repl send --file login.txt; or one command: PW_PASSWORD=... pw-repl send
51
52
  fill '#password' "{{ PW_PASSWORD }}". Only names starting PW_ are variables: any other {{ ... }}, as in a
52
53
  page's template text, is sent as written. In a file, put it in double quotes: there its value is
53
54
  escaped as they read it (in single quotes, which escape nothing, a value with a ' is refused). The REPL
54
- fills it in as it runs the command, and its pane and log show the command as written. A variable that
55
+ puts it in only as it runs the command, and its pane and log show the command as written. A variable that
55
56
  is not set stops the command, or the file at that line, before it is sent. --no-vars sends
56
57
  {{ PW_NAME }} as it is. Variables need the command server (pw-repl serve), not the tmux pane.
57
58
 
package/lib/help.js CHANGED
@@ -152,7 +152,7 @@ const COMMANDS = {
152
152
  usage: 'tab [<index>|<url-part>|new [url]|close [url-part]]',
153
153
  summary: 'list tabs (* is selected); select, open or close one',
154
154
  args: [
155
- ['(none)', 'lists the tabs: * marks the selected one, (in front) the one the browser shows, and each says who opened it with tab new.'],
155
+ ['(none)', 'lists the tabs: * marks the selected one, (visible) the one the browser shows, and each says who opened it with tab new.'],
156
156
  ['<index>', 'selects by the number in your latest listing (each client has its own); numbers change when tabs open or close.'],
157
157
  ['<url-part>', 'selects the one tab whose URL contains it; refused if none or several do.'],
158
158
  ['new [url]', 'opens a tab behind the one in front, so it does not take the window from whoever is using it, and selects it. The URL is read as goto reads it.'],
@@ -161,11 +161,11 @@ const COMMANDS = {
161
161
  detail: `Closing the selected tab goes back to the previous tab if tab new opened it; otherwise no tab is
162
162
  selected, and commands that need one refuse until one is.
163
163
 
164
- In front: (in front) marks the tab the browser shows in its window, the one the user sees; (in front,
164
+ Visible: (visible) marks the tab the browser shows in its window, the one the user sees; (visible,
165
165
  focused) when its window has the keyboard too, and it is not in the address bar. A tab a client has
166
166
  selected or opened is never marked: pw-repl tells it that it is in front, so that it keeps drawing and
167
167
  clicks in it work while it is behind another, and so cannot ask it. The user's other tabs are told
168
- nothing, and say truly whether they are seen. Each window has a tab in front, so with several windows
168
+ nothing, and say truly whether they are seen. Each window shows a tab, so with several windows
169
169
  (an incognito one, say) several tabs are marked; focused is the one with the keyboard.`,
170
170
  },
171
171
  goto: {
@@ -353,8 +353,8 @@ describing it: click, fill, select, check, press, and wait load after a step tha
353
353
  element is found by its id, data-testid or name, its text, or else its place in the page, which a change
354
354
  to the page can break. A select step chooses the option by its value. What was typed into a password
355
355
  field (or any field, with --no-values) is a variable named after the field, e.g. fill '#password'
356
- "{{ PW_PASSWORD }}", which pw-repl send --file fills in from its environment: PW_PASSWORD=... pw-repl
357
- send --file login.txt. A step that cannot be a command (a value with a line break, an element in a frame, or
356
+ "{{ PW_PASSWORD }}": run it with PW_PASSWORD=... pw-repl send --file login.txt, and send passes the
357
+ value along for the REPL to put in, which shows the line as written. A step that cannot be a command (a value with a line break, an element in a frame, or
358
358
  one no selector finds alone) stays a comment saying why, and the save says how many there are.
359
359
 
360
360
  Shared: every client sees the same steps, and watch new's place is shared.`,
package/lib/runner.js CHANGED
@@ -188,7 +188,7 @@ function enqueue(line) {
188
188
  // someone watching sees everything an agent does.
189
189
  // client names the sender: its commands show as [server:<client>] and use its own selected tab.
190
190
  // vars: values for the {{ PW_NAME }} variables in text (from send), filled in to run it; text is shown
191
- // as written, so a password in one stays out of the pane, the log and the prompt's history.
191
+ // as written, so a password in one is not in the command as the pane, the log and the prompt's history show it.
192
192
  function submit(text, client = null, vars = null) {
193
193
  let run = text;
194
194
  if (vars) {
package/lib/syntax.js CHANGED
@@ -70,7 +70,7 @@ function variablesIn(line) {
70
70
  // values: { NAME: value }. Throws on a variable it has no value for.
71
71
  function expandVariables(line, values) {
72
72
  const value = name => {
73
- if (typeof values[name] !== 'string') throw new Error(`{{ ${name} }} has no value: send --file fills it in from ${name} in its environment`);
73
+ if (typeof values[name] !== 'string') throw new Error(`{{ ${name} }} has no value: send passes it along from ${name} in its environment`);
74
74
  return values[name];
75
75
  };
76
76
  return line.replace(new RegExp(`"(?:[^"\\\\]|\\\\.)*"|'[^']*'|${VARIABLE.source}`, 'g'), (whole, name) => {
package/lib/tabs.js CHANGED
@@ -105,7 +105,7 @@ async function pageForTarget(ctx, targetId) {
105
105
  }
106
106
 
107
107
  // Every tab, URL first, with * on the selected one. The numbers are what tab <index> uses.
108
- // Whether a tab is the one in front in its window, as the tab itself says: 'in front', 'in front,
108
+ // Whether a tab is the one in front in its window, as the tab itself says: 'visible', 'visible,
109
109
  // focused' (its window has the keyboard, and not in the address bar), or null. Not for a tab a client
110
110
  // uses, which is told it is in front so that it draws (lib/cdp.js); nor for one that does not answer
111
111
  // at once (a dialog open, a frozen tab). Each window has one in front, an incognito one too.
@@ -114,7 +114,7 @@ async function inFront(p) {
114
114
  const read = p.evaluate(() => [document.visibilityState, document.hasFocus()]).catch(() => null);
115
115
  const seen = await Promise.race([read, new Promise(resolve => setTimeout(resolve, FRONT_READ_TIMEOUT, null))]);
116
116
  if (!seen || seen[0] !== 'visible') return null;
117
- return seen[1] ? 'in front, focused' : 'in front';
117
+ return seen[1] ? 'visible, focused' : 'visible';
118
118
  }
119
119
 
120
120
  async function listTabs() {
package/lib/watch.js CHANGED
@@ -553,7 +553,7 @@ function showWatch(watch, all) {
553
553
 
554
554
  // watch save: the steps as commands for send --file to run again, a step that cannot be one (a value
555
555
  // with a line break, an element in a frame) as a comment saying so. What was typed into a password field,
556
- // or with --no-values, is a {{ PW_NAME }} variable, named after the field, that send --file fills in. A
556
+ // or with --no-values, is a {{ PW_NAME }} variable, named after the field, set when send --file runs it. A
557
557
  // selector is quoted as lib/syntax.js reads one: single quotes, which escape nothing, unless it holds
558
558
  // one. A value is always double-quoted, so an empty one, or one that starts with -, stays the value.
559
559
  function savedCommands(watch, p) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pw-repl",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "Text REPL for driving an existing Chromium through Playwright over CDP",
5
5
  "keywords": [
6
6
  "playwright",
package/skill/SKILL.md CHANGED
@@ -129,12 +129,15 @@ REPL), or why neither is reachable, without running anything.
129
129
  `send --file take.txt` runs a file of commands, one per line, as typed at the prompt (no shell quoting;
130
130
  files relative to your folder, as on `send`'s command line; `#` comments), and stops at the first that
131
131
  fails, saying which line. It is the way to record a video without gaps (`help record`), and to keep a
132
- repro to run again. A `{{ PW_NAME }}` in a line is the variable PW_NAME from `send`'s environment, best
133
- in double quotes (`fill '#password' "{{ PW_PASSWORD }}"`): the REPL fills it in as it runs the line, and
134
- its pane and log show the line as written, so a secret stays out of both the file and the log. Only
135
- `PW_` names are variables; any other `{{ ... }}` (a page's template text) is sent as written. One that
136
- is not set stops the file; `--no-vars` sends it as written. A single `send` takes them too: to type a
137
- password, `PW_PASSWORD=... pw-repl send fill '#password' "{{ PW_PASSWORD }}"` keeps it out of the log.
132
+ repro to run again. A `{{ PW_NAME }}` in a line is a variable, best in double quotes (`fill '#password'
133
+ "{{ PW_PASSWORD }}"`): `send` takes its value from its own environment and passes it along with the
134
+ line, not in it; the REPL puts it in only as it runs the line, and its pane and log show the line as
135
+ written, so neither the file nor the line as logged holds the secret. Only `PW_` names are variables;
136
+ any other `{{ ... }}` (a page's template text) is sent as written. One that is not set stops the file;
137
+ `--no-vars` sends it as written. A single `send` takes them too: `PW_PASSWORD=... pw-repl send fill
138
+ '#password' "{{ PW_PASSWORD }}"` types a password without it in the command. What reads it back can
139
+ still show it: `snapshot`, `html` and `attrs` hide a password field's value, `eval` on the field does
140
+ not.
138
141
 
139
142
  `send` takes one command; chain several in your shell. Use `&&` when a step only makes sense if the one
140
143
  before it worked: it stops at any exit status but 0, a failure and an outcome not confirmed included.
@@ -163,8 +166,8 @@ shown yet. For a tab they have not opened yet, `watch on --next-tab [url-part]`
163
166
  watches it from its first page. `watch save <file>` writes the steps as commands that `send --file`
164
167
  runs again, a repro to keep. What is typed is recorded, except in password fields; `watch on
165
168
  --no-values` leaves it out. The file has a `{{ PW_PASSWORD }}` variable where a password was typed,
166
- which `send --file` fills in from its environment (`PW_PASSWORD=... pw-repl send --file login.txt`), so
167
- the file can be shared without it.
169
+ set when it runs (`PW_PASSWORD=... pw-repl send --file login.txt`), so the file can be shared without
170
+ it.
168
171
 
169
172
  When watching someone:
170
173
 
@@ -214,7 +217,7 @@ pw-repl send -c <name> close # your modes off; tab close
214
217
 
215
218
  - The browser may have tabs that are not yours, and someone may be using it. Whether to read or act in
216
219
  one of those tabs, or to open your own (`tab new <url>`), depends on the task; when that is not clear,
217
- ask. `tab` marks `(in front)` the tab the user sees: "this page" is usually that one. A tab you have
220
+ ask. `tab` marks `(visible)` the tab the user sees: "this page" is usually that one. A tab you have
218
221
  selected is never marked (help tab says why).
219
222
  - No tab is selected when the REPL starts. A start URL's tab, marked `(the start URL)` in `tab`, is
220
223
  selected at its prompt and for `send` without a client name; a named client (`-c`) selects it with