agentclick 0.1.0 → 0.1.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/AGENTS.md +4 -2
- package/README.md +9 -0
- package/bin/agentclick.mjs +75 -1
- package/package.json +1 -1
- package/packages/server/dist/index.js +29 -0
- package/packages/server/src/index.ts +34 -0
- package/packages/web/dist/assets/index-CLd6pAEL.js +67 -0
- package/packages/web/dist/assets/index-DAT8x7ee.css +1 -0
- package/packages/web/dist/index.html +2 -2
- package/packages/web/dist/assets/index-D8TPv7du.css +0 -1
- package/packages/web/dist/assets/index-DABc1gsV.js +0 -67
package/AGENTS.md
CHANGED
|
@@ -132,10 +132,12 @@ Read `docs/dev-rules.md` before writing any code. Key rules:
|
|
|
132
132
|
- Minimal CI build workflow (GitHub Actions: `npm ci && npm run build`)
|
|
133
133
|
- Deployment guide (`docs/deployment.md`) for single-port runtime, env vars, reverse proxy, and Docker/OpenClaw notes
|
|
134
134
|
- Global CLI packaging entrypoint (`bin/agentclick.mjs`) for `agentclick` command startup
|
|
135
|
+
- npm package published: `agentclick@0.1.0`
|
|
136
|
+
- CC suggestions checkbox UI (returns selected suggestions in review completion payload)
|
|
137
|
+
- Summary view fetches `/api/sessions/:id/summary` and renders mock summary response (backend endpoint included)
|
|
135
138
|
|
|
136
139
|
**Pending:**
|
|
137
|
-
-
|
|
138
|
-
- CC suggestions as checkboxes (currently free-text input; agent can pass `ccSuggestions[]`)
|
|
140
|
+
- Replace mock summary endpoint with real agent-generated summaries
|
|
139
141
|
|
|
140
142
|
---
|
|
141
143
|
|
package/README.md
CHANGED
|
@@ -69,6 +69,15 @@ agentclick
|
|
|
69
69
|
|
|
70
70
|
This starts the AgentClick server on `http://localhost:3001` and serves the built web UI on the same port.
|
|
71
71
|
|
|
72
|
+
CLI options:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
agentclick --help
|
|
76
|
+
PORT=3002 agentclick
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
By default, `agentclick` starts on port `3001`. If that port is in use, it automatically tries `3002`, `3003`, and so on.
|
|
80
|
+
|
|
72
81
|
Optional server config (defaults shown in `.env.example`):
|
|
73
82
|
|
|
74
83
|
```bash
|
package/bin/agentclick.mjs
CHANGED
|
@@ -4,11 +4,39 @@ import { existsSync } from 'node:fs'
|
|
|
4
4
|
import { dirname, join } from 'node:path'
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
6
|
import { spawnSync } from 'node:child_process'
|
|
7
|
+
import net from 'node:net'
|
|
7
8
|
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url)
|
|
9
10
|
const rootDir = dirname(dirname(__filename))
|
|
10
11
|
const webDistIndex = join(rootDir, 'packages', 'web', 'dist', 'index.html')
|
|
11
12
|
const serverDistEntry = join(rootDir, 'packages', 'server', 'dist', 'index.js')
|
|
13
|
+
const args = process.argv.slice(2)
|
|
14
|
+
|
|
15
|
+
function printHelp() {
|
|
16
|
+
console.log(`AgentClick CLI
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
agentclick
|
|
20
|
+
agentclick --help
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--help, -h Show this help message
|
|
24
|
+
`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseArgs(argv) {
|
|
28
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
29
|
+
const arg = argv[i]
|
|
30
|
+
if (arg === '--help' || arg === '-h') {
|
|
31
|
+
printHelp()
|
|
32
|
+
process.exit(0)
|
|
33
|
+
}
|
|
34
|
+
console.error(`[agentclick] Unknown argument: ${arg}`)
|
|
35
|
+
console.error('[agentclick] Run "agentclick --help" for usage.')
|
|
36
|
+
process.exit(1)
|
|
37
|
+
}
|
|
38
|
+
return {}
|
|
39
|
+
}
|
|
12
40
|
|
|
13
41
|
function run(command, args) {
|
|
14
42
|
const result = spawnSync(command, args, {
|
|
@@ -25,6 +53,8 @@ function run(command, args) {
|
|
|
25
53
|
}
|
|
26
54
|
}
|
|
27
55
|
|
|
56
|
+
parseArgs(args)
|
|
57
|
+
|
|
28
58
|
if (!existsSync(webDistIndex) || !existsSync(serverDistEntry)) {
|
|
29
59
|
console.log('[agentclick] Build artifacts not found, running npm run build...')
|
|
30
60
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
@@ -36,4 +66,48 @@ if (!existsSync(serverDistEntry)) {
|
|
|
36
66
|
process.exit(1)
|
|
37
67
|
}
|
|
38
68
|
|
|
39
|
-
|
|
69
|
+
async function canListen(port) {
|
|
70
|
+
return await new Promise(resolve => {
|
|
71
|
+
const server = net.createServer()
|
|
72
|
+
server.once('error', err => {
|
|
73
|
+
if ((err).code === 'EADDRINUSE') {
|
|
74
|
+
resolve(false)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
console.error(`[agentclick] Port check failed for ${port}: ${err.message}`)
|
|
78
|
+
process.exit(1)
|
|
79
|
+
})
|
|
80
|
+
server.once('listening', () => {
|
|
81
|
+
server.close(() => resolve(true))
|
|
82
|
+
})
|
|
83
|
+
server.listen(port)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function resolvePort() {
|
|
88
|
+
if (process.env.PORT) return process.env.PORT
|
|
89
|
+
|
|
90
|
+
let port = 3001
|
|
91
|
+
while (true) {
|
|
92
|
+
const available = await canListen(port)
|
|
93
|
+
if (available) return String(port)
|
|
94
|
+
console.log(`[agentclick] Port ${port} in use, trying ${port + 1}...`)
|
|
95
|
+
port += 1
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const childEnv = { ...process.env }
|
|
100
|
+
childEnv.PORT = await resolvePort()
|
|
101
|
+
|
|
102
|
+
const result = spawnSync(process.execPath, [serverDistEntry], {
|
|
103
|
+
cwd: rootDir,
|
|
104
|
+
stdio: 'inherit',
|
|
105
|
+
env: childEnv,
|
|
106
|
+
})
|
|
107
|
+
if (result.error) {
|
|
108
|
+
console.error('[agentclick] Failed to start server:', result.error.message)
|
|
109
|
+
process.exit(1)
|
|
110
|
+
}
|
|
111
|
+
if (typeof result.status === 'number' && result.status !== 0) {
|
|
112
|
+
process.exit(result.status)
|
|
113
|
+
}
|
package/package.json
CHANGED
|
@@ -74,6 +74,35 @@ app.get('/api/sessions/:id', (req, res) => {
|
|
|
74
74
|
return res.status(404).json({ error: 'Session not found' });
|
|
75
75
|
res.json(session);
|
|
76
76
|
});
|
|
77
|
+
// Mock summary endpoint for inbox items (UI integration first)
|
|
78
|
+
app.get('/api/sessions/:id/summary', (req, res) => {
|
|
79
|
+
const session = getSession(req.params.id);
|
|
80
|
+
if (!session)
|
|
81
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
82
|
+
const payload = session.payload;
|
|
83
|
+
const inbox = payload?.inbox ?? [];
|
|
84
|
+
const emailId = req.query.emailId;
|
|
85
|
+
const email = inbox.find(item => item.id === emailId);
|
|
86
|
+
if (!email) {
|
|
87
|
+
return res.status(404).json({ error: 'Email not found in session' });
|
|
88
|
+
}
|
|
89
|
+
const preview = String(email.preview ?? '');
|
|
90
|
+
const summaryText = preview.length > 0
|
|
91
|
+
? `This email appears to be about: ${preview}`
|
|
92
|
+
: 'No preview text is available for this email yet.';
|
|
93
|
+
const from = String(email.from ?? 'Unknown sender');
|
|
94
|
+
const category = String(email.category ?? 'Unknown');
|
|
95
|
+
res.json({
|
|
96
|
+
emailId,
|
|
97
|
+
summary: summaryText,
|
|
98
|
+
bullets: [
|
|
99
|
+
`From: ${from}`,
|
|
100
|
+
`Category: ${category}`,
|
|
101
|
+
'Source: mock summary endpoint (replace with agent summary later)',
|
|
102
|
+
],
|
|
103
|
+
confidence: 'mock',
|
|
104
|
+
});
|
|
105
|
+
});
|
|
77
106
|
// Long-poll: agent calls this and blocks until user completes review (up to 5 min)
|
|
78
107
|
app.get('/api/sessions/:id/wait', async (req, res) => {
|
|
79
108
|
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -83,6 +83,40 @@ app.get('/api/sessions/:id', (req, res) => {
|
|
|
83
83
|
res.json(session)
|
|
84
84
|
})
|
|
85
85
|
|
|
86
|
+
// Mock summary endpoint for inbox items (UI integration first)
|
|
87
|
+
app.get('/api/sessions/:id/summary', (req, res) => {
|
|
88
|
+
const session = getSession(req.params.id)
|
|
89
|
+
if (!session) return res.status(404).json({ error: 'Session not found' })
|
|
90
|
+
|
|
91
|
+
const payload = session.payload as Record<string, unknown> | undefined
|
|
92
|
+
const inbox = (payload?.inbox as Array<Record<string, unknown>> | undefined) ?? []
|
|
93
|
+
const emailId = req.query.emailId as string | undefined
|
|
94
|
+
const email = inbox.find(item => item.id === emailId)
|
|
95
|
+
|
|
96
|
+
if (!email) {
|
|
97
|
+
return res.status(404).json({ error: 'Email not found in session' })
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const preview = String(email.preview ?? '')
|
|
101
|
+
const summaryText = preview.length > 0
|
|
102
|
+
? `This email appears to be about: ${preview}`
|
|
103
|
+
: 'No preview text is available for this email yet.'
|
|
104
|
+
|
|
105
|
+
const from = String(email.from ?? 'Unknown sender')
|
|
106
|
+
const category = String(email.category ?? 'Unknown')
|
|
107
|
+
|
|
108
|
+
res.json({
|
|
109
|
+
emailId,
|
|
110
|
+
summary: summaryText,
|
|
111
|
+
bullets: [
|
|
112
|
+
`From: ${from}`,
|
|
113
|
+
`Category: ${category}`,
|
|
114
|
+
'Source: mock summary endpoint (replace with agent summary later)',
|
|
115
|
+
],
|
|
116
|
+
confidence: 'mock',
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
86
120
|
// Long-poll: agent calls this and blocks until user completes review (up to 5 min)
|
|
87
121
|
app.get('/api/sessions/:id/wait', async (req, res) => {
|
|
88
122
|
const TIMEOUT_MS = 5 * 60 * 1000
|