genaicode 2.18.0 → 2.18.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/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@
4
4
 
5
5
  ### Added
6
6
 
7
+ - Auto-approve in `genaicode chat` and `genaicode ui`: `a` on a permission request (or
8
+ `/auto`, or the Auto-approve chip in the browser) approves it and everything after it;
9
+ `/auto off` asks again. `-y` starts with it on.
10
+ - `genaicode embed` makes projects without a dev server work too: the agent writes a small
11
+ dev-only server that serves the pages with the panel injected, leaving the files and the
12
+ build untouched. It also keeps its shell commands few, since each one may need approval.
13
+
14
+ ## 2.18.0
15
+
16
+ ### Added
17
+
7
18
  - `genaicode embed [request]`: your own agent adds genaicode to the project in the current
8
19
  directory. It gets instructions (the Vite overlay; a development-only panel through
9
20
  `genaicode/ui` for webpack, Next.js, Express and other Node dev servers; the in-app agent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genaicode",
3
- "version": "2.18.0",
3
+ "version": "2.18.2",
4
4
  "description": "Drive the coding-agent CLIs you already have (Claude Code, Codex, Copilot, Cursor, Gemini, opencode...) behind one API and one CLI, plus a thin provider-neutral LLM layer.",
5
5
  "author": "Grzegorz Tańczyk",
6
6
  "repository": {
@@ -10,8 +10,9 @@ already has installed (Claude Code, Codex, Copilot CLI, Cursor, Gemini, opencode
10
10
  one API. Your job is to wire it into the project in the current directory so the
11
11
  project's developers can talk to their agent from inside the running app.
12
12
 
13
- There are three ways in, plus a fallback. Pick by what the project is, not by what is
14
- more impressive.
13
+ There are four ways in, plus a last resort. Pick by what the project is, not by what is
14
+ more impressive. Always end with a working way in: "it doesn't fit" is not a result while
15
+ the project has HTML pages a browser can open.
15
16
 
16
17
  1. **Vite dev overlay** (`genaicode/vite`): for any app built with Vite. One plugin line;
17
18
  in `vite dev` the page gets a wolf button that opens a chat with the agent working in
@@ -27,15 +28,25 @@ more impressive.
27
28
  data. The agent works the app through tools the app serves over MCP, every write waits
28
29
  for the user's Approve, and an optional `dev` mode lets the agent edit the app's own
29
30
  code and propose a commit.
30
- 4. **Fallback**: no Node process to host it (a Rails, Django, Go or static-site project,
31
- or a stack you cannot change safely): tell the user to run `npx genaicode ui` (browser)
32
- or `npx genaicode` (terminal) in the project next to their dev server.
31
+ 4. **A dev server of your own**: the project has pages but nothing serves them in
32
+ development (static HTML, files opened from `file://`, a build script that writes
33
+ `index.html`, a game jam entry with a size budget...). Write a small Node script that
34
+ serves those files on localhost and injects the panel into the HTML it serves, and an
35
+ npm script to start it. The files on disk and the build output stay untouched, so size
36
+ budgets and release builds are not affected.
37
+ 5. **Last resort**: no Node at all in the project and no HTML to serve (a Rails, Django or
38
+ Go app whose server you cannot change safely): tell the user to run `npx genaicode ui`
39
+ (browser) or `npx genaicode` (terminal) next to their dev server, and say why.
33
40
 
34
41
  The agent CLI always runs on the machine that runs the server, with that machine's
35
- login and files. Options 1 and 2 exist only in development. The in-app agent is for apps
42
+ login and files. Options 1, 2 and 4 exist only in development. The in-app agent is for apps
36
43
  that run on the developer's own machine (or a trusted internal host behind real auth).
37
44
  Never wire any of them into a public production site, and never expose them without auth.
38
45
 
46
+ Work with as few shell commands as you can: the user approves each one unless they turned
47
+ on auto-approve. Read files instead of listing them through the shell where your tools
48
+ allow, batch what must run into few commands, and do not run the dev server just to look.
49
+
39
50
  ## Steps
40
51
 
41
52
  1. **Look before you change anything.** Read `package.json` (dependencies, scripts,
@@ -50,10 +61,11 @@ Never wire any of them into a public production site, and never expose them with
50
61
  - The user wants the agent to use the app itself (its data, its actions), or the app
51
62
  is an internal tool with a Node server and a React UI: option 3, alone or next to
52
63
  option 1 or 2.
53
- - Otherwise: option 4. Say why.
64
+ - Pages but no dev server: option 4.
65
+ - Otherwise: option 5. Say why.
54
66
  - If genaicode is already wired in, extend or fix what is there instead of adding a
55
67
  second copy.
56
- 3. **Install** `genaicode` as a dev dependency for options 1 and 2, as a regular
68
+ 3. **Install** `genaicode` as a dev dependency for options 1, 2 and 4, as a regular
57
69
  dependency when the server imports `genaicode/app-agent`. Use the project's package
58
70
  manager (lockfile: `package-lock.json` npm, `pnpm-lock.yaml` pnpm, `yarn.lock` yarn,
59
71
  `bun.lockb`/`bun.lock` bun). If you may not run the install, add the dependency to
@@ -63,7 +75,7 @@ Never wire any of them into a public production site, and never expose them with
63
75
  4. **Wire it in** (recipes below), matching the project's style: TypeScript or
64
76
  JavaScript, ESM or CJS, its formatter, its folder layout.
65
77
  5. **Check it**: run the project's type check, lint and tests if it has them, and a
66
- production build. For options 1 and 2 the production output must not contain
78
+ production build. For options 1, 2 and 4 the production output must not contain
67
79
  genaicode. Fix what you broke; do not "fix" unrelated failures. Remove build output
68
80
  you created that the project does not track.
69
81
  6. **Report**: what you changed, file by file, and how to try it (the dev command, where
@@ -201,6 +213,90 @@ Not React? The same in the app's own framework or plain DOM: fetch the session,
201
213
  fixed button that toggles a fixed iframe. Keep the token URL out of logs, `localStorage`
202
214
  and anything the page sends elsewhere.
203
215
 
216
+ ## Recipe: a dev server of your own
217
+
218
+ For a project with pages but no dev server. One file, e.g. `tools/agent-dev.mjs` (match the
219
+ project's folder for scripts), plus `"agent": "node tools/agent-dev.mjs"` (or a name that
220
+ fits next to the existing scripts) in `package.json`:
221
+
222
+ ```js
223
+ // Development only: serves <dir> on 127.0.0.1 with the genaicode panel injected into HTML.
224
+ // The files on disk are not changed.
225
+ import { createServer } from 'node:http';
226
+ import { readFile, stat } from 'node:fs/promises';
227
+ import { extname, join, normalize, resolve, sep } from 'node:path';
228
+ import { startEmbeddedWeb } from 'genaicode/ui';
229
+
230
+ const root = resolve(process.argv[2] ?? 'build');
231
+ const port = Number(process.env.PORT ?? 5173);
232
+ const origins = new Set([`http://localhost:${port}`, `http://127.0.0.1:${port}`]);
233
+ const chat = await startEmbeddedWeb({ cwd: process.cwd(), frameAncestors: () => [...origins] });
234
+ const TYPES = {
235
+ '.html': 'text/html',
236
+ '.js': 'text/javascript',
237
+ '.mjs': 'text/javascript',
238
+ '.css': 'text/css',
239
+ '.json': 'application/json',
240
+ '.png': 'image/png',
241
+ '.svg': 'image/svg+xml',
242
+ '.wasm': 'application/wasm',
243
+ };
244
+ const PANEL = `<script type="module" src="/__genaicode/panel.js"></script>`;
245
+
246
+ createServer(async (req, res) => {
247
+ const url = new URL(req.url ?? '/', 'http://x');
248
+ if (url.pathname === '/__genaicode/session') {
249
+ // The token: only this machine (a loopback Host, which stops DNS rebinding), only the
250
+ // app's own pages.
251
+ if (!origins.has(`http://${req.headers.host}`) || req.headers['sec-fetch-site'] !== 'same-origin') {
252
+ return res.writeHead(403).end();
253
+ }
254
+ res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' });
255
+ return res.end(JSON.stringify({ url: chat.url }));
256
+ }
257
+ if (url.pathname === '/__genaicode/panel.js') {
258
+ res.writeHead(200, { 'content-type': 'text/javascript' });
259
+ return res.end(PANEL_SCRIPT);
260
+ }
261
+ let file;
262
+ try {
263
+ file = normalize(join(root, decodeURIComponent(url.pathname)));
264
+ } catch {
265
+ return res.writeHead(400).end();
266
+ }
267
+ if (file !== root && !file.startsWith(root + sep)) return res.writeHead(403).end();
268
+ if ((await stat(file).catch(() => null))?.isDirectory()) file = join(file, 'index.html');
269
+ const body = await readFile(file).catch(() => null);
270
+ if (!body) return res.writeHead(404).end('not found');
271
+ const type = TYPES[extname(file)] ?? 'application/octet-stream';
272
+ res.writeHead(200, { 'content-type': type, 'cache-control': 'no-store' });
273
+ res.end(type === 'text/html' ? injectPanel(body.toString()) : body);
274
+ }).listen(port, '127.0.0.1', () =>
275
+ console.log(`http://localhost:${port} (genaicode panel: the button in the corner)`),
276
+ );
277
+
278
+ function injectPanel(html) {
279
+ return html.includes('</body>') ? html.replace('</body>', `${PANEL}</body>`) : html + PANEL;
280
+ }
281
+ ```
282
+
283
+ `PANEL_SCRIPT` is the page side as plain DOM, the same as the React component in the
284
+ recipe above: fetch `/__genaicode/session`, then a fixed button in a corner that toggles a
285
+ fixed iframe with `src` set to the session `url`. Keep it out of the app's own code.
286
+
287
+ - Listen on `127.0.0.1` only, and hand out the token only for a loopback `Host` and
288
+ `Sec-Fetch-Site: same-origin`. Together they keep the token on this machine and away from
289
+ other sites, including DNS-rebinding pages.
290
+ - Serve what the developer actually opens: the build output folder if the project builds
291
+ one (start or chain its build or watch script when it has one, e.g. with `--watch` or a
292
+ second process), otherwise the source folder with the HTML.
293
+ - Optional, for the agent to see what breaks: in `PANEL_SCRIPT`, collect `error` and
294
+ `unhandledrejection` events and offer a "Fix N errors" button that POSTs them to a
295
+ `/__genaicode/fix` route (same checks as the session route) which calls
296
+ `chat.controller.submit('Fix these errors in the page …' + details)`.
297
+ - A page that reloads itself on change (a watch build) is the developer's own; do not
298
+ add a live-reload system for this.
299
+
204
300
  ## Recipe: in-app agent
205
301
 
206
302
  Server side, wherever the Node server is created: