agenter-app 0.0.0-stage → 1.0.0
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/Agenter.vbs +9 -0
- package/LICENSE +21 -0
- package/README.md +168 -2
- package/cli.js +71 -0
- package/hook.js +49 -0
- package/install.js +73 -0
- package/lib/open-dashboard.js +26 -0
- package/open.js +29 -0
- package/package.json +47 -4
- package/public/app.js +1414 -0
- package/public/index.html +188 -0
- package/public/style.css +551 -0
- package/scripts/install-shortcuts.ps1 +36 -0
- package/server.js +1434 -0
package/Agenter.vbs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
' Double-click (or use the shortcut) to open Agenter without a console window.
|
|
2
|
+
' "Agenter.vbs server" only starts the background server (used at Windows login).
|
|
3
|
+
Set fso = CreateObject("Scripting.FileSystemObject")
|
|
4
|
+
dir = fso.GetParentFolderName(WScript.ScriptFullName)
|
|
5
|
+
cmd = "node """ & dir & "\open.js"""
|
|
6
|
+
If WScript.Arguments.Count > 0 Then
|
|
7
|
+
If WScript.Arguments(0) = "server" Then cmd = "node """ & dir & "\server.js"" --no-open"
|
|
8
|
+
End If
|
|
9
|
+
CreateObject("WScript.Shell").Run cmd, 0, False
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ahmad Pandaog Aquino
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,169 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Agenter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
One dashboard for every Claude Code and Codex session on this machine, across all your VS Code windows.
|
|
4
|
+
|
|
5
|
+
- See every session live: what it's doing, which command it's running, and the full conversation.
|
|
6
|
+
- Approve or deny permission prompts (PowerShell/Bash commands, file edits, web fetches…) from one place.
|
|
7
|
+
- Answer Claude's multiple-choice questions.
|
|
8
|
+
- **Message any session at any time**, drive it from VS Code or from Agenter, or start a new one.
|
|
9
|
+
- Get a sound and a desktop notification whenever a session needs you.
|
|
10
|
+
|
|
11
|
+
No dependencies, just Node 18+. Built for Windows.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
npm install -g agenter-app
|
|
17
|
+
agenter install # adds hooks to ~/.claude/settings.json (a backup is made) + Desktop/Start Menu shortcuts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then **reload each VS Code window** (or start new Claude sessions). Hooks are read when a session starts.
|
|
21
|
+
|
|
22
|
+
Install it globally (`-g`), not with `npx`: the hooks point at the installed files, so they need to stay put.
|
|
23
|
+
If you move Node (a new Node version manager install, say), run `agenter install` again; it replaces the old hooks.
|
|
24
|
+
|
|
25
|
+
From a clone instead: `npm run install-hooks` and `npm run shortcuts`, then `npm run open`.
|
|
26
|
+
|
|
27
|
+
## Running it
|
|
28
|
+
|
|
29
|
+
You mostly don't have to: **the server starts itself** the first time any Claude session fires a hook,
|
|
30
|
+
and keeps running in the background. Just open the window when you want it:
|
|
31
|
+
|
|
32
|
+
| Way | What it does |
|
|
33
|
+
|---|---|
|
|
34
|
+
| **Agenter shortcut** (Desktop / Start Menu / taskbar) | Opens the dashboard in its own app window and starts the server if needed. No console. |
|
|
35
|
+
| `agenter` | Same thing, from a terminal. |
|
|
36
|
+
| `agenter start` | Runs the server in the foreground with logs (handy for debugging). |
|
|
37
|
+
| `agenter shortcuts --startup` | Also starts the server silently when you log in to Windows. |
|
|
38
|
+
|
|
39
|
+
Set `AGENTER_AUTOSTART=0` to stop hooks from starting the server.
|
|
40
|
+
Dashboard settings are kept in `~/.agenter/settings.json`.
|
|
41
|
+
|
|
42
|
+
To remove everything: `agenter uninstall`, then `npm uninstall -g agenter-app`.
|
|
43
|
+
|
|
44
|
+
## Sessions you see
|
|
45
|
+
|
|
46
|
+
- **Live**: sessions talking to Agenter right now (working, waiting for you, or idle).
|
|
47
|
+
- **Earlier today**: every session you used today, found from Claude's transcripts in `~/.claude/projects`,
|
|
48
|
+
including ones started before Agenter was running and ones you've closed. You can read their full
|
|
49
|
+
conversation and click **Open in VS Code** to jump to the project. When you continue one in VS Code,
|
|
50
|
+
it moves to Live.
|
|
51
|
+
|
|
52
|
+
**Clear earlier** hides those until they become active again.
|
|
53
|
+
|
|
54
|
+
## Steer from Agenter
|
|
55
|
+
|
|
56
|
+
Every live session has a message box at the bottom, so you never have to switch to VS Code to tell Claude something.
|
|
57
|
+
|
|
58
|
+
| Claude is… | Your message |
|
|
59
|
+
|---|---|
|
|
60
|
+
| Waiting for your reply (finished turn) | Goes in right away and Claude continues. |
|
|
61
|
+
| Working | Is queued and reaches Claude after its current step (the next tool call finishes). If the turn ends first, Claude continues with it. |
|
|
62
|
+
| Idle, driven from VS Code | Is queued and goes along with your next prompt from VS Code. Claude Code can't be woken up from outside. |
|
|
63
|
+
| Idle, driven from Agenter | Starts a new turn right away, run by Agenter. |
|
|
64
|
+
|
|
65
|
+
Queued messages show above the box, with **Cancel**.
|
|
66
|
+
|
|
67
|
+
### Drive from VS Code or Agenter
|
|
68
|
+
|
|
69
|
+
Each session has a **Drive from** switch in its header.
|
|
70
|
+
|
|
71
|
+
- **VS Code** (default): Claude runs in the VS Code panel. Agenter answers inside its turns, so while you
|
|
72
|
+
reply from Agenter the VS Code panel shows its spinner, and an idle session waits for VS Code.
|
|
73
|
+
- **Agenter**: Agenter runs the turns itself with the Claude Code that ships with the VS Code extension
|
|
74
|
+
(`claude -p --resume <session>`), in the same session. Nothing ever waits on VS Code, idle and closed
|
|
75
|
+
sessions can be continued, permission prompts and questions come to Agenter, and **Stop** ends a running turn.
|
|
76
|
+
The VS Code panel doesn't show these turns until you reopen the session there (Claude panel → session
|
|
77
|
+
history), and don't type in VS Code's panel for that session at the same time.
|
|
78
|
+
|
|
79
|
+
If Claude Code isn't found automatically, set `AGENTER_CLAUDE` to the path of `claude.exe`.
|
|
80
|
+
|
|
81
|
+
### Start a session in Agenter
|
|
82
|
+
|
|
83
|
+
**New session** (sidebar) starts a Claude session without VS Code: pick the project folder (folders you've
|
|
84
|
+
used are suggested), type the first message, and it runs in Agenter, driven from Agenter. You can still open
|
|
85
|
+
it in VS Code later from the Claude panel's session history.
|
|
86
|
+
|
|
87
|
+
## Codex
|
|
88
|
+
|
|
89
|
+
Agenter also shows your [Codex](https://developers.openai.com/codex) sessions (VS Code extension, desktop app
|
|
90
|
+
and CLI), read from Codex's own session logs in `~/.codex/sessions`. Nothing is sent anywhere and Codex's
|
|
91
|
+
settings aren't changed.
|
|
92
|
+
|
|
93
|
+
- **Sessions**: today's Codex sessions appear in the list with a **Codex** tag: the conversation, the
|
|
94
|
+
commands it ran, and whether it's working right now. Codex's helper threads (like its auto-review) are left out.
|
|
95
|
+
- **Steering**: each Codex session has a **Drive from** switch and a message box.
|
|
96
|
+
- **Codex app** (default): your message is queued in Codex (`codex queue`) and runs as the session's next
|
|
97
|
+
turn when it's open in Codex (VS Code extension or desktop app).
|
|
98
|
+
- **Agenter**: Agenter runs the turns itself (`codex exec resume`), sandboxed to the project folder
|
|
99
|
+
(`workspace-write`, no network), since nobody is there to approve anything more. A message sent while a
|
|
100
|
+
turn is running goes next. **Stop** ends a running turn.
|
|
101
|
+
- **New session** can start a Codex session too (pick **Codex** in the dialog). It runs in Agenter and also
|
|
102
|
+
shows up in the Codex app.
|
|
103
|
+
|
|
104
|
+
Agenter uses the Codex that ships with the Codex VS Code extension, or `codex` on your PATH. Set
|
|
105
|
+
`AGENTER_CODEX_BIN` to point at another `codex.exe`.
|
|
106
|
+
- **Plan usage**: the sidebar shows your Codex **5-hour** and **weekly** limits with reset times, with the
|
|
107
|
+
same alerts at 80% and 95%. Codex records these with each turn, so they're as of your last Codex turn
|
|
108
|
+
(a window that has reset since shows 0%).
|
|
109
|
+
|
|
110
|
+
Set `CODEX_HOME` if Codex keeps its data somewhere else, or `AGENTER_CODEX=0` to turn this off.
|
|
111
|
+
|
|
112
|
+
## Preview the app side by side
|
|
113
|
+
|
|
114
|
+
Click **Preview** in a session's header to show the app you're building next to the conversation, so you
|
|
115
|
+
can steer Claude and watch the result in one window.
|
|
116
|
+
|
|
117
|
+
- Agenter finds web servers started from that project's folder (`npm run dev`, Vite, Next, etc.) and shows
|
|
118
|
+
the first page it finds. Other servers it finds appear as chips you can switch between.
|
|
119
|
+
Start the dev server later and it shows up within a few seconds.
|
|
120
|
+
- Or type any address in the bar (`5173` is short for `http://localhost:5173`). It's remembered per project;
|
|
121
|
+
clear it to go back to the detected app.
|
|
122
|
+
- Drag the divider to resize (double-click to reset). **Open** (↗) opens the app in your normal browser.
|
|
123
|
+
- Some apps refuse to be shown inside another page. If the preview stays blank, use **Open**.
|
|
124
|
+
- Finding apps works on Windows. Elsewhere, type the address.
|
|
125
|
+
|
|
126
|
+
## How it works
|
|
127
|
+
|
|
128
|
+
Claude Code runs [hooks](https://docs.claude.com/en/docs/claude-code/hooks) on events such as
|
|
129
|
+
"about to use a tool", "needs permission" and "finished its turn". Each hook runs `hook.js`, which
|
|
130
|
+
forwards the event to the local server (`server.js`) and prints the server's answer back to Claude.
|
|
131
|
+
|
|
132
|
+
| Event | What Agenter does |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `PermissionRequest` | Holds the prompt until you click **Allow / Always allow / Deny / Answer in VS Code**. A deny reason is passed to Claude, so you can say "no, do X instead". |
|
|
135
|
+
| `PreToolUse` (AskUserQuestion) | Shows the question with its options; your answers go back to Claude. |
|
|
136
|
+
| `Stop` | If **Hold for reply** is on, the session waits for your next instruction. **Send** continues it; **Release to VS Code** lets it stop normally. |
|
|
137
|
+
| Everything else | Activity feed and status only (never blocks). |
|
|
138
|
+
|
|
139
|
+
### Safety valves
|
|
140
|
+
|
|
141
|
+
- **Server not running?** Hooks exit instantly and Claude behaves as if Agenter didn't exist.
|
|
142
|
+
- **Working in VS Code?** Sessions are only held while the dashboard window has focus. Switch to VS Code
|
|
143
|
+
and nothing new gets held, and anything already waiting goes back to VS Code after 5 seconds.
|
|
144
|
+
- **Dashboard closed?** Same, after 15 seconds.
|
|
145
|
+
- **Interrupt in VS Code** while something is waiting? The dashboard item disappears.
|
|
146
|
+
- The server only listens on `127.0.0.1` and rejects requests from other websites.
|
|
147
|
+
|
|
148
|
+
## Plan usage
|
|
149
|
+
|
|
150
|
+
(Codex usage is covered under [Codex](#codex).) The sidebar shows how much of your Claude plan you've used: the **5-hour session** limit and the **weekly**
|
|
151
|
+
limit, each with when it resets. You get an alert at 80% and 95%, and a banner in the inbox when the session
|
|
152
|
+
limit is nearly used up. It refreshes every 2 minutes (or click ↻).
|
|
153
|
+
|
|
154
|
+
It asks Anthropic using the login Claude Code already saved on this machine (`~/.claude/.credentials.json`),
|
|
155
|
+
the same way Claude Code's `/usage` does. The token only goes to `api.anthropic.com`, and Agenter never
|
|
156
|
+
refreshes it itself. This endpoint isn't officially documented, so it could change. Set `AGENTER_USAGE=0` to turn it off.
|
|
157
|
+
|
|
158
|
+
## Tips and limits
|
|
159
|
+
|
|
160
|
+
- **Hold for reply** is the global default (sidebar) and can be turned off per session. While a session is
|
|
161
|
+
held, its VS Code panel shows the "Clauding…" spinner. That's expected. To get it back in VS Code:
|
|
162
|
+
click **Release to VS Code** in the dashboard, press **Esc** in VS Code, or just wait. Anything waiting is
|
|
163
|
+
handed back automatically after **Hand back to VS Code after** (3 min by default, with a countdown on the card).
|
|
164
|
+
- An idle session that has been *released* can't be woken up from the dashboard, because Claude Code only
|
|
165
|
+
hands control to hooks at events. A message you send then waits for your next prompt from VS Code.
|
|
166
|
+
- Answering `AskUserQuestion` from the dashboard depends on your Claude Code version accepting
|
|
167
|
+
hook-supplied answers. If it doesn't, use **Answer in VS Code**.
|
|
168
|
+
- Port: set `AGENTER_PORT` for both the server and your environment if 4317 is taken.
|
|
169
|
+
- `npm start -- --no-open` skips opening the browser.
|
package/cli.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The `agenter` command (npm i -g agenter-app). Each subcommand runs one of the existing scripts.
|
|
3
|
+
'use strict';
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { spawnSync } = require('node:child_process');
|
|
6
|
+
const { version } = require('./package.json');
|
|
7
|
+
|
|
8
|
+
const [cmd = 'open', ...rest] = process.argv.slice(2);
|
|
9
|
+
const isWindows = process.platform === 'win32';
|
|
10
|
+
|
|
11
|
+
const node = (script, args = []) =>
|
|
12
|
+
spawnSync(process.execPath, [path.join(__dirname, script), ...args], { stdio: 'inherit' }).status ?? 1;
|
|
13
|
+
|
|
14
|
+
const shortcuts = (args = []) => {
|
|
15
|
+
if (!isWindows) {
|
|
16
|
+
console.log('Shortcuts are Windows only. Skipped.');
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
const ps1 = path.join(__dirname, 'scripts', 'install-shortcuts.ps1');
|
|
20
|
+
return spawnSync('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ps1, ...args], { stdio: 'inherit' }).status ?? 1;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const help = `Agenter ${version}: one inbox for every Claude Code and Codex session.
|
|
24
|
+
|
|
25
|
+
Usage: agenter [command]
|
|
26
|
+
|
|
27
|
+
(none), open Open the dashboard (starts the server if needed)
|
|
28
|
+
install Add Agenter's hooks to ~/.claude/settings.json (backed up first)
|
|
29
|
+
and, on Windows, Desktop and Start Menu shortcuts
|
|
30
|
+
uninstall Remove the hooks and shortcuts
|
|
31
|
+
start Run the server in the foreground with logs
|
|
32
|
+
shortcuts (Re)create the Windows shortcuts; --startup also starts the server at login
|
|
33
|
+
version Print the version
|
|
34
|
+
|
|
35
|
+
After installing, reload your VS Code windows so Claude sessions pick up the hooks.`;
|
|
36
|
+
|
|
37
|
+
let status = 0;
|
|
38
|
+
switch (cmd) {
|
|
39
|
+
case 'open':
|
|
40
|
+
status = node('open.js', rest);
|
|
41
|
+
break;
|
|
42
|
+
case 'install':
|
|
43
|
+
status = node('install.js', rest);
|
|
44
|
+
if (status === 0) status = shortcuts();
|
|
45
|
+
break;
|
|
46
|
+
case 'uninstall':
|
|
47
|
+
status = node('install.js', ['--uninstall']);
|
|
48
|
+
shortcuts(['-Remove']);
|
|
49
|
+
break;
|
|
50
|
+
case 'start':
|
|
51
|
+
status = node('server.js', rest);
|
|
52
|
+
break;
|
|
53
|
+
case 'shortcuts':
|
|
54
|
+
status = shortcuts(rest.includes('--startup') ? ['-Startup'] : []);
|
|
55
|
+
break;
|
|
56
|
+
case 'version':
|
|
57
|
+
case '--version':
|
|
58
|
+
case '-v':
|
|
59
|
+
console.log(version);
|
|
60
|
+
break;
|
|
61
|
+
case 'help':
|
|
62
|
+
case '--help':
|
|
63
|
+
case '-h':
|
|
64
|
+
console.log(help);
|
|
65
|
+
break;
|
|
66
|
+
default:
|
|
67
|
+
console.error(`Unknown command: ${cmd}\n`);
|
|
68
|
+
console.log(help);
|
|
69
|
+
status = 1;
|
|
70
|
+
}
|
|
71
|
+
process.exit(status);
|
package/hook.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Claude Code hook bridge. Claude Code runs this for every hook event and pipes the
|
|
3
|
+
// event JSON on stdin. We forward it to the Agenter server and print whatever it
|
|
4
|
+
// answers. If the server isn't running we start it in the background (unless
|
|
5
|
+
// AGENTER_AUTOSTART=0) and exit silently so Claude behaves normally this time.
|
|
6
|
+
'use strict';
|
|
7
|
+
const http = require('node:http');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const { spawn } = require('node:child_process');
|
|
10
|
+
|
|
11
|
+
const PORT = Number(process.env.AGENTER_PORT) || 4317;
|
|
12
|
+
|
|
13
|
+
function startServer() {
|
|
14
|
+
if (process.env.AGENTER_AUTOSTART === '0') return;
|
|
15
|
+
try {
|
|
16
|
+
spawn(process.execPath, [path.join(__dirname, 'server.js'), '--no-open'], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
17
|
+
} catch {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let input = '';
|
|
21
|
+
process.stdin.setEncoding('utf8');
|
|
22
|
+
process.stdin.on('data', (chunk) => (input += chunk));
|
|
23
|
+
process.stdin.on('end', () => {
|
|
24
|
+
const req = http.request(
|
|
25
|
+
{
|
|
26
|
+
host: '127.0.0.1',
|
|
27
|
+
port: PORT,
|
|
28
|
+
path: '/hook',
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { 'content-type': 'application/json', 'content-length': Buffer.byteLength(input) },
|
|
31
|
+
},
|
|
32
|
+
(res) => {
|
|
33
|
+
let body = '';
|
|
34
|
+
res.setEncoding('utf8');
|
|
35
|
+
res.on('data', (chunk) => (body += chunk));
|
|
36
|
+
res.on('end', () => {
|
|
37
|
+
const out = body.trim();
|
|
38
|
+
if (res.statusCode === 200 && out && out !== '{}') process.stdout.write(out);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
// Server down or unreachable: fall back to Claude's normal behaviour.
|
|
44
|
+
req.on('error', (err) => {
|
|
45
|
+
if (err.code === 'ECONNREFUSED') startServer();
|
|
46
|
+
process.exit(0);
|
|
47
|
+
});
|
|
48
|
+
req.end(input);
|
|
49
|
+
});
|
package/install.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Adds (or with --uninstall removes) the Agenter hooks in ~/.claude/settings.json.
|
|
3
|
+
// Existing settings and other hooks are left untouched; a backup is written first.
|
|
4
|
+
'use strict';
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
|
|
9
|
+
const SETTINGS = path.join(os.homedir(), '.claude', 'settings.json');
|
|
10
|
+
const HOOK_SCRIPT = path.join(__dirname, 'hook.js').replace(/\\/g, '/');
|
|
11
|
+
const COMMAND = `node "${HOOK_SCRIPT}"`;
|
|
12
|
+
|
|
13
|
+
// timeout is in seconds. Events that can wait for you get an hour.
|
|
14
|
+
const EVENTS = {
|
|
15
|
+
SessionStart: { timeout: 10 },
|
|
16
|
+
SessionEnd: { timeout: 10 },
|
|
17
|
+
UserPromptSubmit: { timeout: 10 },
|
|
18
|
+
PreToolUse: { matcher: '*', timeout: 3600 },
|
|
19
|
+
PostToolUse: { matcher: '*', timeout: 10 },
|
|
20
|
+
PermissionRequest: { matcher: '*', timeout: 3600 },
|
|
21
|
+
Notification: { timeout: 10 },
|
|
22
|
+
Stop: { timeout: 3600 },
|
|
23
|
+
SubagentStop: { timeout: 10 },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const uninstall = process.argv.includes('--uninstall');
|
|
27
|
+
|
|
28
|
+
let settings = {};
|
|
29
|
+
if (fs.existsSync(SETTINGS)) {
|
|
30
|
+
const raw = fs.readFileSync(SETTINGS, 'utf8');
|
|
31
|
+
try {
|
|
32
|
+
settings = JSON.parse(raw);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error(`Could not parse ${SETTINGS}: ${err.message}\nFix the file and run again.`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
const backup = `${SETTINGS}.agenter-backup-${Date.now()}`;
|
|
38
|
+
fs.writeFileSync(backup, raw);
|
|
39
|
+
console.log(`Backed up settings to ${backup}`);
|
|
40
|
+
} else {
|
|
41
|
+
fs.mkdirSync(path.dirname(SETTINGS), { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Also matches hooks left behind by an older copy of Agenter at another path.
|
|
45
|
+
const isOurs = (h) =>
|
|
46
|
+
h && typeof h.command === 'string' && (h.command.includes(HOOK_SCRIPT) || /agenter[^"]*\/hook\.js"$/i.test(h.command));
|
|
47
|
+
|
|
48
|
+
settings.hooks = settings.hooks || {};
|
|
49
|
+
for (const [event, groups] of Object.entries(settings.hooks)) {
|
|
50
|
+
if (!Array.isArray(groups)) continue;
|
|
51
|
+
settings.hooks[event] = groups
|
|
52
|
+
.map((g) => ({ ...g, hooks: (g.hooks || []).filter((h) => !isOurs(h)) }))
|
|
53
|
+
.filter((g) => g.hooks.length > 0);
|
|
54
|
+
if (settings.hooks[event].length === 0) delete settings.hooks[event];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!uninstall) {
|
|
58
|
+
for (const [event, { matcher, timeout }] of Object.entries(EVENTS)) {
|
|
59
|
+
const group = { hooks: [{ type: 'command', command: COMMAND, timeout }] };
|
|
60
|
+
if (matcher) group.matcher = matcher;
|
|
61
|
+
(settings.hooks[event] = settings.hooks[event] || []).push(group);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
65
|
+
|
|
66
|
+
fs.writeFileSync(SETTINGS, JSON.stringify(settings, null, 2) + '\n');
|
|
67
|
+
console.log(
|
|
68
|
+
uninstall
|
|
69
|
+
? 'Agenter hooks removed.'
|
|
70
|
+
: `Agenter hooks installed for: ${Object.keys(EVENTS).join(', ')}\n` +
|
|
71
|
+
'Restart your Claude sessions (or reload the VS Code windows) so they pick up the hooks.\n' +
|
|
72
|
+
'Then run: agenter'
|
|
73
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Opens the dashboard in its own app window (Edge/Chrome --app) when possible,
|
|
3
|
+
// otherwise in the default browser.
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { spawn, exec } = require('node:child_process');
|
|
7
|
+
|
|
8
|
+
function findAppBrowser() {
|
|
9
|
+
if (process.platform !== 'win32') return null;
|
|
10
|
+
const roots = [process.env['ProgramFiles(x86)'], process.env.ProgramFiles, process.env.LOCALAPPDATA].filter(Boolean);
|
|
11
|
+
const rels = [path.join('Microsoft', 'Edge', 'Application', 'msedge.exe'), path.join('Google', 'Chrome', 'Application', 'chrome.exe')];
|
|
12
|
+
for (const rel of rels) for (const root of roots) if (fs.existsSync(path.join(root, rel))) return path.join(root, rel);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = function openDashboard(url) {
|
|
17
|
+
const browser = findAppBrowser();
|
|
18
|
+
if (browser) {
|
|
19
|
+
const child = spawn(browser, [`--app=${url}`, '--window-size=1280,860'], { detached: true, stdio: 'ignore' });
|
|
20
|
+
child.on('error', () => {});
|
|
21
|
+
child.unref();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const cmd = process.platform === 'win32' ? `start "" "${url}"` : process.platform === 'darwin' ? `open ${url}` : `xdg-open ${url}`;
|
|
25
|
+
exec(cmd, () => {});
|
|
26
|
+
};
|
package/open.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// One-click launcher: starts the server in the background if it isn't running,
|
|
3
|
+
// then opens the dashboard window. Safe to run any number of times.
|
|
4
|
+
'use strict';
|
|
5
|
+
const http = require('node:http');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { spawn } = require('node:child_process');
|
|
8
|
+
const openDashboard = require('./lib/open-dashboard');
|
|
9
|
+
|
|
10
|
+
const PORT = Number(process.env.AGENTER_PORT) || 4317;
|
|
11
|
+
const URL = `http://localhost:${PORT}`;
|
|
12
|
+
|
|
13
|
+
const isUp = () =>
|
|
14
|
+
new Promise((resolve) => {
|
|
15
|
+
const req = http.get({ host: '127.0.0.1', port: PORT, path: '/', headers: { host: `127.0.0.1:${PORT}` } }, (res) => {
|
|
16
|
+
res.resume();
|
|
17
|
+
resolve(res.statusCode === 200);
|
|
18
|
+
});
|
|
19
|
+
req.on('error', () => resolve(false));
|
|
20
|
+
req.setTimeout(1000, () => req.destroy());
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
(async () => {
|
|
24
|
+
if (!(await isUp())) {
|
|
25
|
+
spawn(process.execPath, [path.join(__dirname, 'server.js'), '--no-open'], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
26
|
+
for (let i = 0; i < 40 && !(await isUp()); i++) await new Promise((r) => setTimeout(r, 150));
|
|
27
|
+
}
|
|
28
|
+
openDashboard(URL);
|
|
29
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agenter-app",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "One local dashboard to watch, approve and steer every Claude Code and Codex session on your machine.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"codex",
|
|
9
|
+
"ai-agents",
|
|
10
|
+
"dashboard",
|
|
11
|
+
"hooks",
|
|
12
|
+
"vscode"
|
|
13
|
+
],
|
|
14
|
+
"author": "Ahmad Pandaog Aquino (https://ahmad-new-portfolio.vercel.app/)",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"homepage": "https://github.com/zxcoding123/agenter_app#readme",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/zxcoding123/agenter_app.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/zxcoding123/agenter_app/issues"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"agenter": "cli.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"cli.js",
|
|
29
|
+
"hook.js",
|
|
30
|
+
"install.js",
|
|
31
|
+
"open.js",
|
|
32
|
+
"server.js",
|
|
33
|
+
"Agenter.vbs",
|
|
34
|
+
"lib/",
|
|
35
|
+
"public/",
|
|
36
|
+
"scripts/"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"start": "node server.js",
|
|
40
|
+
"open": "node open.js",
|
|
41
|
+
"shortcuts": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/install-shortcuts.ps1",
|
|
42
|
+
"shortcuts:startup": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/install-shortcuts.ps1 -Startup",
|
|
43
|
+
"install-hooks": "node install.js",
|
|
44
|
+
"uninstall-hooks": "node install.js --uninstall"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
}
|
|
49
|
+
}
|