ego-jev 0.0.0-stage → 0.3.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ ## 0.3.1 (2026-10-12)
4
+
5
+ - `ego-jev proxy` receives the key on stdin instead of its environment. A process's initial environment stays
6
+ readable with `ps eww` by the same user for the process's whole life; 0.3.0 put the key there.
7
+
8
+ ## 0.3.0 (2026-10-12)
9
+
10
+ - Security: the API key no longer goes into the ego runtime's `process.env`. The runtime keeps one `process.env` for
11
+ its whole life and shares it with every script, so 0.2.0 left the key readable by later ego scripts. It now lives
12
+ only in the Jev client's module scope.
13
+ - `ego-jev proxy [--idle MINUTES]`, `proxy status`, `proxy stop`: a background process holds the key and forwards Jev
14
+ requests over a unix socket (mode 600). While it runs, `do`, `check` and `read` need no key, which saves the 2 to 4 s
15
+ a secret manager takes per call.
16
+ - Settle waits for same-origin `fetch`/XHR requests as well as DOM mutations, so single-page apps that re-render
17
+ after their own request are not read too early.
18
+ - Custom checkboxes whose transparent input covers the label's left edge are clicked on the input.
19
+ - Requirements count as reached only in order: a later one is not latched while an earlier one is still open (a
20
+ "filter is selected" score on the home page no longer counts as progress).
21
+ - With requirements, a confident target (0.6 or more) is acted on when the focused requirement scores under 0.2, even
22
+ if Jev's tool answer is "no action".
23
+ - Enter in the field just typed into no longer stops as `ambiguous` when its target confidence is low.
24
+ - `docs/AGENT_GUIDE.md`: when an agent should hand work to ego-jev, from six rounds of agent experiments.
25
+ - `experiments/agent/`: agent-with-ego-jev against agent-alone runs (tasks, grader, summariser, 122 recorded runs).
26
+ With the final guide: 20.5 s against 29.0 s on average over six tasks, 25 s against 75 s on Danawa.
27
+ - `experiments/`: G마켓 and YES24 tasks; Danawa checks read all three filter layouts. With requirements, 29 of 30
28
+ regression runs were correct and none reported `done` for a wrong result.
29
+
30
+ ## 0.2.0 (2026-10-12)
31
+
32
+ - `--require "<page state>"` (repeatable): states the run has to reach, in order. Each round Jev scores them and works
33
+ on the first one not reached yet. With requirements, outcome-only goals went from 8/12 to 12/12 correct in the
34
+ experiments.
35
+ - Batch fill: when the next action types a value and more values have empty fields, all are typed in the same round.
36
+ The httpbin form dropped from 14.3 to 9.3 Jev requests.
37
+ - Requirement scores ride in the action request on pages that fit one stage (`--no-merge-require` turns it off).
38
+ - Click errors name the element that covers the target; blocked clicks retry after Escape.
39
+ - Elements that only open a popup or help text carry `opens`.
40
+ - Enter is pressed only on fields and buttons, otherwise on the field typed last.
41
+ - The done check accepts what an action leads to (the page after logging in) and rejects a filled but unsent form
42
+ for goals that order, send or submit.
43
+ - With requirements, a weak `none` next to a confident target becomes an action.
44
+ - Pages open at `domcontentloaded` instead of `load`.
45
+ - The CLI writes its own lines to stdout even when piped (the ego runtime sends `console.log` to stderr then).
46
+ - Results report `jev_tokens`.
47
+ - `experiments/`: task definitions, runner, summariser and 156 recorded runs.
48
+
49
+ ## 0.1.0 (2026-10-11)
50
+
51
+ - First release: Jev decision loop from jev-browser 0.1.2 on top of ego lite, `do`, `check`, `read`, done
52
+ verification, label-edge clicks, state-preserving page fitting, FIFO key hand-off.
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kim Jisub
4
+
5
+ Portions of src/ego-jev.mjs and the files in src/jev/ are derived from jev-browser 0.1.2,
6
+ Copyright (c) 2026 Ying-Kai Liao, also under the MIT License (see src/jev/LICENSE).
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,221 @@
1
- # Temporary Holding Version
1
+ # ego-jev
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Let **Jev** click through multi-step browser flows inside **ego lite**, your real browser, while your agent keeps the
4
+ goal and the values.
5
+
6
+ - [ego lite](https://github.com/citrolabs/ego-lite) runs a Chromium browser on your Mac with your logins, split into
7
+ isolated TaskSpaces that agents can share.
8
+ - **Jev** (TypeSafe System One) is a judgment model: it answers yes/no, multiple-choice and score questions over a
9
+ state object. **It never generates text.**
10
+ - **ego-jev** reads the page, asks Jev "what next, on which element, with which of the given values", and performs
11
+ the action through ego. Strings to type always come from the caller as `key=value`; Jev only decides where they go.
12
+
13
+ Your LLM agent stays in charge of what to do and why. ego-jev takes the clicking off its hands and keeps page
14
+ snapshots out of its context.
15
+
16
+ **Measured with Claude Opus 5.5 as the agent** (fresh subagents, paired runs, all 24 correct,
17
+ [details](experiments/agent/README.md)): on a 1,000-element shopping page with filters and sorting the agent finished
18
+ in 25 s with ego-jev against 75 s alone; across six tasks, 20.5 s against 29.0 s on average, with 40 % fewer tool
19
+ calls. On tasks where the agent did not hand anything to ego-jev, both took the same time.
20
+
21
+ ## Requirements
22
+
23
+ - macOS with ego lite installed and onboarded (`ego-browser` on your PATH or in `~/.local/bin`)
24
+ - Node.js 20 or later
25
+ - A TypeSafe API key in `TYPESAFE_API_KEY`, or an `ego-jev proxy` started with it
26
+
27
+ No other dependencies: no Playwright, no bundled browser.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm install -g ego-jev
33
+ # or from source
34
+ git clone https://github.com/kimjisub/ego-jev.git && cd ego-jev && npm link
35
+ ```
36
+
37
+ ## Quick start
38
+
39
+ ```bash
40
+ export TYPESAFE_API_KEY=...
41
+
42
+ ego-jev do "I want to buy the Sauce Labs Backpack and the Sauce Labs Bike Light" \
43
+ username=standard_user password=secret_sauce first=Ada last=Lovelace zip=12345 \
44
+ --url https://www.saucedemo.com/ --max 18 --allow-irreversible \
45
+ --require "Logged in to the store" \
46
+ --require "Sauce Labs Backpack and Sauce Labs Bike Light are both in the cart" \
47
+ --require "The checkout form has the given first name, last name and zip code" \
48
+ --require "The order is complete (a thank-you confirmation is shown)"
49
+ ```
50
+
51
+ One line per round goes to stdout, and the last line is the result:
52
+
53
+ ```text
54
+ r0: require=[0.04, 0.03, 0.03, 0.02]
55
+ r0: type(1) -> #0 input:text "Username" (0.99) value=username done=0.03 ...
56
+ also typed password -> #1 input:password "Password" (0.99)
57
+ ...
58
+ EGO_JEV_RESULT {"status":"likely_done","url":".../checkout-complete.html","actions":[...],"requirements":[...],"jev_calls":15,"space":12,"page":"p1"}
59
+ ```
60
+
61
+ ## Usage
62
+
63
+ ```bash
64
+ ego-jev do "<goal>" [key=value ...] [--require "<page state>" ...] [--url U] [--space N] [--page p1] [--max 10] [--allow-irreversible] [--finish] [--name NAME]
65
+ ego-jev check "<yes/no question>" [--space N] [--page p1]
66
+ ego-jev read "<question>" [--space N] [--page p1]
67
+ ego-jev proxy [--idle MINUTES] | ego-jev proxy status | ego-jev proxy stop
68
+ ```
69
+
70
+ - `do` works toward a goal. `key=value` pairs are the only strings Jev may type or select.
71
+ - `check` answers a yes/no question about the current page as a probability.
72
+ - `read` returns the page text relevant to a question (Jev ranks blocks, code keeps the best).
73
+ - `space` in the result is the ego TaskSpace id. Pass it to `--space` to continue in the same tab, or open it from
74
+ your own `ego-browser nodejs` script with `taskSpace(12)` and take over by hand.
75
+ - `--finish` closes the TaskSpace when the command ends. Otherwise close it with `task.finish({ keep: [] })`.
76
+ - With a secret manager, start the proxy once under it and call ego-jev without the key afterwards:
77
+
78
+ ```bash
79
+ op run --env-file keys.env -- ego-jev proxy # holds the key for 2 hours of inactivity (--idle MINUTES)
80
+ ego-jev do ... # no TYPESAFE_API_KEY needed while the proxy runs
81
+ ego-jev proxy status
82
+ ego-jev proxy stop
83
+ ```
84
+
85
+ The proxy receives the key on stdin (not in its environment, which `ps eww` would show), keeps it in memory and
86
+ forwards Jev requests that arrive on a unix socket (`~/.cache/ego-jev/jev.sock`, mode 600, in a mode 700
87
+ directory). It never sends the key back. Any process of your
88
+ user can send requests through it while it runs. A secret manager call costs 2 to 4 s, so this matters when an
89
+ agent calls ego-jev many times.
90
+
91
+ ## Using it from an agent
92
+
93
+ Give your agent [docs/AGENT_GUIDE.md](docs/AGENT_GUIDE.md) (1.8 KB), ideally inside the skill or instructions it
94
+ already reads for browser work. The rule it teaches came out of six rounds of agent experiments:
95
+
96
+ > On a site whose markup you do not know, hand filters, sorting and multi-screen flows (cart, checkout, multi-step
97
+ > forms) to ego-jev from your first browser call. Do everything else yourself: sites you know, URLs you have seen
98
+ > work, a single form or click, and reading the result.
99
+
100
+ Two rules that sounded right and lost time: "use ego-jev whenever you would have to look at the page first" (agents
101
+ handed it demo sites they already knew), and "fall back to ego-jev when your own attempt fails" (agents keep
102
+ debugging instead of handing over).
103
+
104
+ ## How to phrase a task
105
+
106
+ Jev picks one action at a time from what is on the screen. It does not plan. How you phrase the task decides whether
107
+ it finishes. Measured on six sites, 12 runs per row ([experiments](experiments/README.md)):
108
+
109
+ | Phrasing | Example | Correct | Mean time |
110
+ |---|---|---|---|
111
+ | outcome only | "I want the cheapest 3-person IH pressure cookers" | 8/12 | 7.4 s |
112
+ | procedure | "search, tick IH and 3-person in the filters, sort by lowest price" | 12/12 | 8.9 s |
113
+ | **outcome + `--require`** | the outcome, plus "the IH filter is selected", "the list is sorted by lowest price", ... | **12/12** | 10.4 s |
114
+
115
+ None of the 36 runs reported `done` for a wrong result.
116
+
117
+ Rules that came out of the runs:
118
+
119
+ 1. **Give the parts as `--require` page states, in order.** Write what you could see on the page when that part is
120
+ done ("the 3-person capacity filter is selected"), not the click ("click 3-person"). Each round Jev scores every
121
+ requirement and works on the first one not reached yet. You do not need to know the site.
122
+ 2. **Make the last requirement the end state** ("the order is complete", "the list is sorted by lowest price").
123
+ The run ends when every requirement has been reached and the last one is in effect.
124
+ 3. **Pass every string as a value.** Search terms, names, codes: Jev cannot write them. Several values for one form
125
+ are typed in the same round.
126
+ 4. **Outcome-only goals are fine for one-part tasks** (open an article, log in). For anything with filters, a cart
127
+ or several screens, they stop early.
128
+ 5. **Split long flows at points you want to check yourself**, especially before payments or deletes. Keep `--max`
129
+ near the number of steps you expect.
130
+
131
+ ## Statuses
132
+
133
+ | status | meaning | what the caller should do |
134
+ |---|---|---|
135
+ | `done` | the goal (or every requirement) is in effect on the page | move on |
136
+ | `likely_done` | probably reached, but not every part is visible now; with `--require`, `requirements` lists the scores | look at `requirements` or the page. After a flow whose earlier states disappear (an emptied cart), this is the expected result |
137
+ | `needs_confirmation` | the next click looks hard to undo (order, payment, delete, send) | rerun with `--allow-irreversible` only if that step is intended |
138
+ | `needs_login` | the page asks for sign-in and no credentials were given | log in in ego (or hand off to a human), then rerun |
139
+ | `blocked` | captcha, access denied, error page | hand off to a human |
140
+ | `ambiguous` | Jev could not pick a target with confidence | act on one of the returned `candidates` yourself |
141
+ | `stuck` | the same action repeated without progress, or Jev found nothing to do while the goal is not in effect | inspect the page and take over |
142
+ | `error` | the page shows an error after the last action | read `page_text` |
143
+ | `max_actions` | the action budget ran out | raise `--max` or split the goal |
144
+
145
+ ## How it works
146
+
147
+ Each round:
148
+
149
+ 1. Wait until the DOM has been quiet for 400 ms and no same-origin `fetch` or XHR is in flight (pages open at
150
+ `domcontentloaded`, so slow third-party resources do not block; single-page apps that re-render after their own
151
+ request are waited for).
152
+ 2. List the interactive elements of the top document with their label, state (checked, value, expanded, sorted,
153
+ selected) and whether they only open a popup or help text, and tag them with `data-jev-i`.
154
+ 3. Ask Jev one request with several questions: is the goal done, is the page blocked, is an error shown, is this a
155
+ login wall, would the next step be hard to undo, which tool, which element, which value. With `--require`, the
156
+ requirement scores ride in the same request. Pages with more than 240 elements go through two stages: pick a
157
+ group of 30 consecutive elements, then the element inside it.
158
+ 4. Turn the answers into one action (`click`, `type`, `press_enter`, `press_key`, `select`, `hover`, `drag`,
159
+ `upload`, `scroll`, `wait`) and run it through the ego Page API. If the action types a value and more values
160
+ have empty fields on the page, one more request maps them to fields and all are typed in that round.
161
+ 5. Stop on done, on a guard (login, irreversible, error, repetition) or on the action budget.
162
+
163
+ Before returning `done` without requirements, ego-jev asks a stricter question: judging from the page, is every part
164
+ of the goal in effect, either shown (a selected filter, an entered value) or evidenced (a confirmation, the page after
165
+ submitting)? A goal that orders or submits is not in effect while the form is merely filled.
166
+
167
+ Clicks on checkboxes and radios land on the left edge of their label, because labels often hold links in the middle;
168
+ when a custom checkbox lays its transparent input over that edge, the input itself is clicked.
169
+ A blocked click is retried after scrolling the target to the centre and once more after Escape; if it stays blocked,
170
+ Jev is told what covers it (`dialog "..." covers the target; close or dismiss it first`).
171
+
172
+ The API key reaches the ego runtime through a one-shot FIFO (mode 600), or stays in the `ego-jev proxy` process. It
173
+ is never written to disk and never put into the runtime's `process.env`, which the ego runtime shares between all
174
+ scripts it runs.
175
+
176
+ ## When to use it
177
+
178
+ Fresh Claude Opus 5.5 subagents, the same task with and without ego-jev, two paired runs each, wall clock including
179
+ the agent's own thinking ([agent experiments](experiments/agent/README.md)):
180
+
181
+ | Task | Agent alone | Agent + ego-jev | ego-jev used |
182
+ |---|---|---|---|
183
+ | Danawa: search, two filters, sort, top 3 (about 1,000 elements, hidden checkbox inputs) | 75.2 s | **25.2 s** | yes |
184
+ | G마켓: search, brand and delivery filters, sort, top 3 (single-page app) | 38.9 s | **31.0 s** | yes |
185
+ | YES24: search, category, sort, three titles | 22.3 s | 23.8 s | yes |
186
+ | httpbin form: 7 fields, submit | 13.1 s | 12.5 s | no |
187
+ | Wikipedia: one value from an article | 10.0 s | 10.8 s | no |
188
+ | 국가법령정보센터: one definition from a statute in an iframe | 14.6 s | 19.9 s | no (agent variance) |
189
+
190
+ - **Use ego-jev** for filters, sorting and multi-screen flows on sites your agent does not know, pages with hundreds of
191
+ elements or awkward markup, and when you want to keep page snapshots out of your agent's context.
192
+ - **Drive ego directly** for sites the model already knows (popular demo apps are in its training data), a URL you
193
+ can build, and one or two actions. An ego-jev call costs about 0.5 s to start plus 1 to 3 s per action.
194
+ - **Read results with your own script** in the same TaskSpace after ego-jev; `ego-jev read` ranks blocks by relevance
195
+ and can surface recommendation boxes above the list you sorted.
196
+
197
+ ## Limits
198
+
199
+ - Only the top document is read; elements inside iframes are not listed.
200
+ - Settling watches DOM mutations and same-origin `fetch`/XHR requests, not WebSockets or cross-origin data calls.
201
+ - New tabs are not followed automatically.
202
+ - Controls whose selected state is not in the DOM (no `checked`, `aria-pressed`, `aria-selected` or a
203
+ `selected`/`active` class) cannot be scored as requirements reliably.
204
+ - The irreversible-step guard is a probability with a 0.6 threshold. On sites with real payments, deletes or
205
+ messages, keep `--max` small and split the goal into steps you can check.
206
+ - A requirement counts as reached at a score of 0.5, once every earlier requirement has been reached, and stays
207
+ reached. If a later action undoes an earlier filter,
208
+ the run ends as `likely_done` and lists it; it does not go back to fix it.
209
+
210
+ ## Credits
211
+
212
+ The decision loop, page script, page model, content pruning and Jev client are derived from
213
+ [jev-browser](https://github.com/Ying-Kai-Liao/jev-browser) 0.1.2 by Ying-Kai Liao (MIT). ego-jev replaces its
214
+ Playwright and MCP layers with ego lite and adds requirements, batch filling, the done check, blocker-aware clicks
215
+ and state-preserving page fitting.
216
+
217
+ ego-jev is not affiliated with Citro Labs (ego lite) or TypeSafe (Jev).
218
+
219
+ ## License
220
+
221
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ // ego-jev: let Jev pick the next action inside an ego lite TaskSpace. The caller (you, or an LLM agent) supplies
3
+ // the goal and the values to type; Jev only chooses what to click and where each value goes.
4
+ //
5
+ // ego-jev do "<goal>" [key=value ...] [--url U] [--space N] [--page p1] [--max 10] [--require R ...] [--allow-irreversible] [--finish] [--name NAME]
6
+ // ego-jev check "<yes/no question>" [--space N] [--page p1]
7
+ // ego-jev read "<question>" [--space N] [--page p1]
8
+ // ego-jev proxy [--idle MINUTES] | proxy stop | proxy status
9
+ //
10
+ // The last line printed is `EGO_JEV_RESULT {json}`. Pass its `space` to --space to continue in the same TaskSpace.
11
+ //
12
+ // The ego runtime does not inherit environment variables, so the API key is handed over through a one-shot FIFO
13
+ // (mode 600) that the runtime reads once. The key is never written to disk.
14
+ import { spawn, spawnSync } from "node:child_process";
15
+ import { createWriteStream, existsSync, realpathSync, rmSync } from "node:fs";
16
+ import { tmpdir, homedir } from "node:os";
17
+ import { join, dirname, delimiter } from "node:path";
18
+ import { randomBytes } from "node:crypto";
19
+ import { fileURLToPath, pathToFileURL } from "node:url";
20
+ import { createInterface } from "node:readline";
21
+ import { request } from "node:http";
22
+ import { SOCKET, proxyAlive } from "../src/proxy.mjs";
23
+
24
+ const ROOT = join(dirname(realpathSync(fileURLToPath(import.meta.url))), "..");
25
+
26
+ const USAGE = `usage: ego-jev do|check|read "<text>" [key=value ...] [--url U] [--space N] [--page p1] [--max N] [--require R ...] [--allow-irreversible] [--finish] [--name NAME]
27
+ ego-jev proxy [--idle MINUTES] | proxy status | proxy stop
28
+
29
+ do work toward a goal; key=value pairs are the values Jev may type or select
30
+ check yes/no question about the current page, answered as a probability
31
+ read the page text relevant to a question (Jev ranks blocks, code keeps the best)
32
+ proxy start a background process that holds TYPESAFE_API_KEY and forwards Jev requests over a unix socket
33
+ (mode 600). While it runs, do/check/read need no key. It exits after --idle minutes without requests
34
+ (default 120). "proxy stop" ends it, "proxy status" reports it
35
+
36
+ --url U open U first (otherwise continue on the current page)
37
+ --space N reuse TaskSpace N (default: create one named --name or "ego-jev")
38
+ --page p1 page label inside the TaskSpace (default p1)
39
+ --max N action budget for do (default 10)
40
+ --require R a page state do has to reach; repeat for several, in the order they should be reached.
41
+ Each round works on the first one not reached yet
42
+ --allow-irreversible let do click through steps that look hard to undo (orders, payments, deletes)
43
+ --no-batch-fill type one value per round instead of filling every matching empty field at once
44
+ --no-merge-require score requirements in a separate request every round instead of with the action
45
+ --finish close the TaskSpace afterwards
46
+
47
+ environment: TYPESAFE_API_KEY (required unless a proxy runs), EGO_BROWSER (path to ego-browser, optional),
48
+ EGO_JEV_SOCKET (proxy socket, default ~/.cache/ego-jev/jev.sock)`;
49
+
50
+ const fail = msg => { console.error(msg); process.exit(2); };
51
+ const argv = process.argv.slice(2);
52
+ if (!argv.length || ["-h", "--help", "help"].includes(argv[0])) { console.log(USAGE); process.exit(0); }
53
+ const cmd = argv.shift();
54
+ if (cmd === "proxy") await proxyCommand(argv);
55
+ if (!["do", "check", "read"].includes(cmd)) fail(USAGE);
56
+ const A = { cmd, values: {} };
57
+ for (let k = 0; k < argv.length; k++) {
58
+ const a = argv[k];
59
+ if (a === "--url") A.url = argv[++k];
60
+ else if (a === "--space") A.space = argv[++k];
61
+ else if (a === "--page") A.page = argv[++k];
62
+ else if (a === "--max") A.max = Number(argv[++k]);
63
+ else if (a === "--name") A.name = argv[++k];
64
+ else if (a === "--allow-irreversible") A.allowIrreversible = true;
65
+ else if (a === "--finish") A.finish = true;
66
+ else if (a === "--require") (A.require ??= []).push(argv[++k]);
67
+ else if (a === "--no-batch-fill") A.batchFill = false;
68
+ else if (a === "--no-merge-require") A.mergeRequire = false;
69
+ else if (a.startsWith("--")) fail(USAGE);
70
+ else if (A.goal == null) A.goal = a;
71
+ else { const i = a.indexOf("="); if (i < 1) fail(USAGE); A.values[a.slice(0, i)] = a.slice(i + 1); }
72
+ }
73
+ if (!A.goal) fail(USAGE);
74
+
75
+ const key = process.env.TYPESAFE_API_KEY;
76
+ if (!key) {
77
+ if (await proxyAlive()) A.proxy = SOCKET;
78
+ else fail("TYPESAFE_API_KEY is not set and no ego-jev proxy is running. Export the key, wrap the call with your secret manager, or start `ego-jev proxy` once under it.");
79
+ }
80
+
81
+ const which = name => (process.env.PATH ?? "").split(delimiter).map(d => join(d, name)).find(p => existsSync(p));
82
+ const egoBrowser = [process.env.EGO_BROWSER, which("ego-browser"), join(homedir(), ".local/bin/ego-browser")].find(p => p && existsSync(p));
83
+ if (!egoBrowser) fail("ego-browser was not found. Install ego lite (https://github.com/citrolabs/ego-lite) and finish its onboarding.");
84
+
85
+ if (!A.proxy) {
86
+ A.keyFifo = join(tmpdir(), `ego-jev-${randomBytes(6).toString("hex")}.fifo`);
87
+ if (spawnSync("mkfifo", ["-m", "600", A.keyFifo]).status !== 0) fail("could not create a FIFO for the API key");
88
+ }
89
+ const cleanup = () => A.keyFifo && rmSync(A.keyFifo, { force: true });
90
+
91
+ const script = [
92
+ `globalThis.EGO_JEV_ARGS = ${JSON.stringify(A)};`,
93
+ `globalThis.EGO_JEV_EGO = { taskSpace };`,
94
+ `await import(${JSON.stringify(pathToFileURL(join(ROOT, "src/run.mjs")).href)});`,
95
+ ].join("\n");
96
+
97
+ const env = { ...process.env }; delete env.TYPESAFE_API_KEY;
98
+ const child = spawn(egoBrowser, ["nodejs"], { stdio: ["pipe", "pipe", "pipe"], env });
99
+ child.stdin.end(script);
100
+ // The ego runtime writes console.log to stderr when its stdout is not a terminal. Route ego-jev's own lines (progress
101
+ // and the result) to stdout and everything else (runtime errors) to stderr, so `ego-jev ... | tail -1` works.
102
+ const OURS = /^(EGO_JEV_RESULT |\s+r\d+:|\s+!|\s+(verify|confirm)=|\s+also typed )/;
103
+ for (const s of [child.stdout, child.stderr]) {
104
+ createInterface({ input: s }).on("line", l => (OURS.test(l) ? process.stdout : process.stderr).write(l + "\n"));
105
+ }
106
+ // Opening a FIFO for writing waits until the runtime opens it for reading.
107
+ if (A.keyFifo) {
108
+ const w = createWriteStream(A.keyFifo);
109
+ w.on("error", () => {});
110
+ w.end(key);
111
+ }
112
+ child.on("close", code => { cleanup(); process.exitCode = code ?? 1; });
113
+ for (const sig of ["SIGINT", "SIGTERM"]) process.on(sig, () => { child.kill(sig); cleanup(); process.exit(130); });
114
+
115
+ async function proxyCommand(args) {
116
+ const sub = args[0] && !args[0].startsWith("--") ? args.shift() : "start";
117
+ const alive = await proxyAlive();
118
+ if (sub === "status") { console.log(alive ? `running on ${SOCKET}` : "not running"); process.exit(alive ? 0 : 1); }
119
+ if (sub === "stop") {
120
+ if (!alive) { console.log("not running"); process.exit(0); }
121
+ await new Promise(res => { const r = request({ socketPath: SOCKET, path: "/stop", method: "POST" }, s => { s.resume(); s.on("end", res); }); r.on("error", res); r.end(); });
122
+ console.log("stopped"); process.exit(0);
123
+ }
124
+ if (sub !== "start") fail(USAGE);
125
+ if (alive) { console.log(`already running on ${SOCKET}`); process.exit(0); }
126
+ if (!process.env.TYPESAFE_API_KEY) fail("TYPESAFE_API_KEY is not set; start the proxy under your secret manager, e.g. op run --env-file keys.env -- ego-jev proxy");
127
+ const k = args.indexOf("--idle");
128
+ const idle = (k >= 0 ? Number(args[k + 1]) : 120) * 60_000;
129
+ const code = `const { serveFromStdin } = await import(${JSON.stringify(pathToFileURL(join(ROOT, "src/proxy.mjs")).href)}); await serveFromStdin({ idle: ${idle} });`;
130
+ // the key goes over stdin; the child's environment (readable with ps eww) does not carry it
131
+ const env = { ...process.env }; delete env.TYPESAFE_API_KEY;
132
+ const child = spawn(process.execPath, ["--input-type=module", "-e", code], { detached: true, stdio: ["pipe", "ignore", "ignore"], env });
133
+ child.stdin.end(process.env.TYPESAFE_API_KEY);
134
+ child.unref();
135
+ for (let i = 0; i < 30 && !(await proxyAlive()); i++) await new Promise(r => setTimeout(r, 100));
136
+ if (!(await proxyAlive())) fail("the proxy did not start");
137
+ console.log(`running on ${SOCKET}`); process.exit(0);
138
+ }
@@ -0,0 +1,27 @@
1
+ # ego-jev for agents
2
+
3
+ ego-jev drives a stretch of a site inside your ego TaskSpace: you give the goal, the strings to type and the page
4
+ states to reach; Jev picks every click (about 1 to 3 s per action, no tokens of yours).
5
+
6
+ **Use it for one thing: on a site whose markup you do not know, filters and sorting on a results page, or a flow
7
+ over several screens (cart, checkout, multi-step forms).** Make it your first browser call, with `--url`, instead of
8
+ looking at the page yourself. Do everything else with your own `ego-browser nodejs` scripts: sites you know, a URL
9
+ you have seen work, a single form or click on one page, and reading the result afterwards.
10
+
11
+ ```bash
12
+ ego-jev do "<goal>" key=value ... --url <start url> --name <taskspace> --max <actions + 3> \
13
+ --require "<page state 1>" --require "<page state 2>" ... [--allow-irreversible]
14
+ ```
15
+
16
+ - `key=value`: every string to type (search terms, names). Jev cannot write text.
17
+ - `--require`: what the page shows when each part is done, in order ("the 로지텍 brand filter is selected",
18
+ "the list is sorted by lowest price"). Not clicks.
19
+ - `--allow-irreversible` only when the task says to order, submit, send or delete.
20
+ - `--url`: start as close to the work as you can. If you know the site's search URL pattern, start on the results
21
+ page; every search Jev does from the home page costs two or three rounds.
22
+ - `--name` is the TaskSpace your scripts open with `taskSpace("<name>")`.
23
+
24
+ The last line is `EGO_JEV_RESULT {json}`. On `done`, the page is in that state: read what the task asks for and
25
+ finish, or finish right away if there is nothing to read. On anything else, the `requirements` scores show which
26
+ part is missing (under 0.5); the page is often already right, so check that part with one script and do only what
27
+ is left.
package/package.json CHANGED
@@ -1,6 +1,17 @@
1
1
  {
2
2
  "name": "ego-jev",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.1",
4
+ "description": "Let Jev (TypeSafe System One) drive multi-step flows inside ego lite, your real browser. The agent supplies goals, values and target page states; Jev picks every click.",
5
+ "keywords": ["ego-lite", "jev", "typesafe", "browser-automation", "agent", "claude-code"],
6
+ "license": "MIT",
7
+ "author": "Kim Jisub",
8
+ "repository": { "type": "git", "url": "git+https://github.com/kimjisub/ego-jev.git" },
9
+ "homepage": "https://github.com/kimjisub/ego-jev#readme",
10
+ "bugs": { "url": "https://github.com/kimjisub/ego-jev/issues" },
11
+ "type": "module",
12
+ "bin": { "ego-jev": "bin/ego-jev.mjs" },
13
+ "exports": { ".": "./src/ego-jev.mjs" },
14
+ "files": ["bin", "src", "docs", "README.md", "CHANGELOG.md", "LICENSE"],
15
+ "engines": { "node": ">=20" },
16
+ "os": ["darwin"]
17
+ }