@notilens/notilens 0.1.1 → 0.2.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/README.md +186 -64
- package/dist/agent.d.ts +33 -0
- package/dist/agent.js +166 -0
- package/dist/cli.js +107 -93
- package/dist/config.d.ts +3 -1
- package/dist/config.js +16 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +11 -0
- package/dist/notify.d.ts +1 -1
- package/dist/notify.js +11 -15
- package/dist/types.d.ts +5 -5
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# NotiLens
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Send notifications from AI agents and any Node.js project to [NotiLens](https://www.notilens.com).
|
|
4
|
+
|
|
5
|
+
Two ways to use it — pick one or both:
|
|
6
|
+
|
|
7
|
+
- **CLI** — for shell scripts, bash pipelines, Claude Code hooks, any terminal workflow
|
|
8
|
+
- **SDK** — for Node.js/TypeScript projects (import and call directly in code)
|
|
4
9
|
|
|
5
10
|
## Installation
|
|
6
11
|
|
|
@@ -8,89 +13,128 @@ CLI tool for sending AI agent task lifecycle notifications to [NotiLens](https:/
|
|
|
8
13
|
npm install -g @notilens/notilens
|
|
9
14
|
```
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Register your agent with its endpoint and secret from the NotiLens dashboard:
|
|
16
|
+
Or per-project:
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
|
-
|
|
19
|
+
npm install @notilens/notilens
|
|
17
20
|
```
|
|
18
21
|
|
|
19
|
-
**Example:**
|
|
20
|
-
```bash
|
|
21
|
-
notilens add-agent my-agent http https://www.notilens.com/nlapi/nl/v1/notify abc123secret
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Config is saved to `~/.notilens_config.json`.
|
|
25
|
-
|
|
26
22
|
---
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
# CLI
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
Get your token and secret from the [NotiLens dashboard](https://www.notilens.com).
|
|
31
29
|
|
|
32
30
|
```bash
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
notilens init --agent my-agent --token YOUR_TOKEN --secret YOUR_SECRET
|
|
32
|
+
```
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
notilens task.in_progress --agent my-agent --task task_001
|
|
34
|
+
This saves credentials to `~/.notilens_config.json`. All future commands read from there — no need to pass token/secret again.
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
**Multiple agents** (each agent notifies a different topic):
|
|
37
|
+
```bash
|
|
38
|
+
notilens init --agent scraper --token TOKEN_A --secret SECRET_A
|
|
39
|
+
notilens init --agent mailer --token TOKEN_B --secret SECRET_B
|
|
40
|
+
```
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
**List / remove agents:**
|
|
43
|
+
```bash
|
|
44
|
+
notilens agents
|
|
45
|
+
notilens remove-agent my-agent
|
|
46
|
+
```
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
notilens task.retry --agent my-agent --task task_001
|
|
48
|
+
---
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
notilens task.loop "Processing batch 3/10" --agent my-agent --task task_001
|
|
50
|
+
## Commands
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
notilens task.fail "Max retries exceeded" --agent my-agent --task task_001
|
|
52
|
+
### Task Lifecycle
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
```bash
|
|
55
|
+
# required: --agent
|
|
56
|
+
# optional: --task (auto-generated if omitted)
|
|
57
|
+
|
|
58
|
+
notilens task.start --agent my-agent --task job_001
|
|
59
|
+
notilens task.progress "Fetching data" --agent my-agent --task job_001
|
|
60
|
+
notilens task.loop "Step 3 of 10" --agent my-agent --task job_001
|
|
61
|
+
notilens task.retry --agent my-agent --task job_001
|
|
62
|
+
notilens task.stop --agent my-agent --task job_001
|
|
63
|
+
notilens task.complete "All done" --agent my-agent --task job_001
|
|
64
|
+
notilens task.error "Step 3 failed" --agent my-agent --task job_001
|
|
65
|
+
notilens task.fail "Unrecoverable" --agent my-agent --task job_001
|
|
66
|
+
notilens task.timeout "Took too long" --agent my-agent --task job_001
|
|
67
|
+
notilens task.cancel "User cancelled" --agent my-agent --task job_001
|
|
68
|
+
notilens task.terminate "Out of memory" --agent my-agent --task job_001
|
|
69
|
+
```
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
notilens task.cancel "User cancelled" --agent my-agent --task task_001
|
|
71
|
+
### AI Response Events
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
notilens
|
|
73
|
+
```bash
|
|
74
|
+
notilens ai.response.generate "Summary generated" --agent my-agent --task job_001
|
|
75
|
+
notilens ai.response.fail "Model unavailable" --agent my-agent --task job_001
|
|
62
76
|
```
|
|
63
77
|
|
|
64
|
-
###
|
|
78
|
+
### Input / Human-in-the-loop
|
|
65
79
|
|
|
66
80
|
```bash
|
|
67
|
-
notilens
|
|
68
|
-
notilens
|
|
81
|
+
notilens input.required "Please confirm the output" --agent my-agent --task job_001
|
|
82
|
+
notilens input.approve "Confirmed" --agent my-agent --task job_001
|
|
83
|
+
notilens input.reject "Rejected" --agent my-agent --task job_001
|
|
69
84
|
```
|
|
70
85
|
|
|
71
|
-
###
|
|
86
|
+
### Generic Event
|
|
72
87
|
|
|
73
88
|
```bash
|
|
74
|
-
notilens
|
|
75
|
-
notilens
|
|
76
|
-
notilens input.reject "User rejected" --agent my-agent --task task_001
|
|
89
|
+
notilens emit order.placed "Order #1234" --agent my-agent --meta amount=99.99
|
|
90
|
+
notilens emit disk.space.full "Only 1GB remaining" --agent my-agent --type warning
|
|
77
91
|
```
|
|
78
92
|
|
|
79
93
|
### Metrics
|
|
80
94
|
|
|
81
|
-
Track token usage and confidence after an AI call:
|
|
82
|
-
|
|
83
95
|
```bash
|
|
84
|
-
|
|
85
|
-
|
|
96
|
+
notilens set.metrics 512 128 0.95 --agent my-agent --task job_001
|
|
97
|
+
# ^ ^ ^
|
|
98
|
+
# | | confidence score (optional)
|
|
99
|
+
# | completion_tokens
|
|
100
|
+
# prompt_tokens
|
|
86
101
|
```
|
|
87
102
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
Emit any custom event:
|
|
103
|
+
---
|
|
91
104
|
|
|
92
|
-
|
|
93
|
-
|
|
105
|
+
## Claude Code Hooks Example
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"hooks": {
|
|
110
|
+
"PreToolUse": [{
|
|
111
|
+
"matcher": "Task",
|
|
112
|
+
"hooks": [{
|
|
113
|
+
"type": "command",
|
|
114
|
+
"command": "notilens task.start --agent claude --task $TASK_ID"
|
|
115
|
+
}]
|
|
116
|
+
}],
|
|
117
|
+
"PostToolUse": [{
|
|
118
|
+
"matcher": "Task",
|
|
119
|
+
"hooks": [{
|
|
120
|
+
"type": "command",
|
|
121
|
+
"command": "notilens task.complete \"Task done\" --agent claude --task $TASK_ID"
|
|
122
|
+
}]
|
|
123
|
+
}],
|
|
124
|
+
"Stop": [{
|
|
125
|
+
"hooks": [{
|
|
126
|
+
"type": "command",
|
|
127
|
+
"command": "notilens task.complete \"Session ended\" --agent claude"
|
|
128
|
+
}]
|
|
129
|
+
}],
|
|
130
|
+
"Notification": [{
|
|
131
|
+
"hooks": [{
|
|
132
|
+
"type": "command",
|
|
133
|
+
"command": "notilens input.required \"Claude needs input\" --agent claude"
|
|
134
|
+
}]
|
|
135
|
+
}]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
94
138
|
```
|
|
95
139
|
|
|
96
140
|
---
|
|
@@ -99,9 +143,9 @@ notilens emit "data.processed" "Ingested 500 rows" --agent my-agent --task task_
|
|
|
99
143
|
|
|
100
144
|
| Flag | Description |
|
|
101
145
|
|------|-------------|
|
|
102
|
-
| `--agent <name>` | Agent name (required) |
|
|
146
|
+
| `--agent <name>` | Agent name **(required)** |
|
|
103
147
|
| `--task <id>` | Task ID (auto-generated if omitted) |
|
|
104
|
-
| `--type` | Override
|
|
148
|
+
| `--type` | Override type: `info` `success` `warning` `urgent` |
|
|
105
149
|
| `--meta key=value` | Custom metadata (repeatable) |
|
|
106
150
|
| `--image_url <url>` | Attach an image |
|
|
107
151
|
| `--open_url <url>` | Link to open |
|
|
@@ -114,13 +158,12 @@ notilens emit "data.processed" "Ingested 500 rows" --agent my-agent --task task_
|
|
|
114
158
|
|
|
115
159
|
## Notification Types
|
|
116
160
|
|
|
117
|
-
|
|
161
|
+
Assigned automatically based on the event — can be overridden with `--type`.
|
|
118
162
|
|
|
119
163
|
| Type | Events |
|
|
120
164
|
|------|--------|
|
|
121
|
-
| `success` | `task.completed`, `response.generated`, `input.approved` |
|
|
122
|
-
| `urgent` | `task.failed`, `task.timeout`, `response.failed` |
|
|
123
|
-
| `important` | `task.error`, `task.terminated` |
|
|
165
|
+
| `success` | `task.completed`, `ai.response.generated`, `input.approved` |
|
|
166
|
+
| `urgent` | `task.failed`, `task.timeout`, `task.error`, `task.terminated`, `ai.response.failed` |
|
|
124
167
|
| `warning` | `task.retrying`, `task.cancelled`, `input.required`, `input.rejected` |
|
|
125
168
|
| `info` | All others |
|
|
126
169
|
|
|
@@ -129,16 +172,10 @@ Events are automatically mapped to notification types:
|
|
|
129
172
|
## Full Example
|
|
130
173
|
|
|
131
174
|
```bash
|
|
132
|
-
|
|
133
|
-
notilens add-agent summarizer http https://www.notilens.com/nlapi/nl/v1/notify mysecret
|
|
175
|
+
notilens init --agent summarizer --token MY_TOKEN --secret MY_SECRET
|
|
134
176
|
|
|
135
|
-
# Start task
|
|
136
177
|
notilens task.start --agent summarizer --task job_42
|
|
137
|
-
|
|
138
|
-
# Update token metrics mid-task
|
|
139
|
-
notilens set.metrics 1024 256 --agent summarizer --task job_42
|
|
140
|
-
|
|
141
|
-
# Add custom metadata
|
|
178
|
+
notilens set.metrics 1024 256 0.95 --agent summarizer --task job_42
|
|
142
179
|
notilens task.complete "Summary ready" --agent summarizer --task job_42 \
|
|
143
180
|
--meta input_file=report.pdf \
|
|
144
181
|
--meta pages=12 \
|
|
@@ -147,6 +184,91 @@ notilens task.complete "Summary ready" --agent summarizer --task job_42 \
|
|
|
147
184
|
|
|
148
185
|
---
|
|
149
186
|
|
|
187
|
+
# SDK
|
|
188
|
+
|
|
189
|
+
Use the SDK to send notifications directly from your Node.js or TypeScript code.
|
|
190
|
+
|
|
191
|
+
## 1. Setup
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { NotiLensAgent } from '@notilens/notilens';
|
|
195
|
+
|
|
196
|
+
// Option A — pass credentials directly (required on first use)
|
|
197
|
+
const agent = NotiLensAgent.init('my-agent', { token: 'YOUR_TOKEN', secret: 'YOUR_SECRET' });
|
|
198
|
+
|
|
199
|
+
// Option B — read from environment variables
|
|
200
|
+
// NOTILENS_TOKEN=xxx NOTILENS_SECRET=yyy
|
|
201
|
+
const agent = NotiLensAgent.init('my-agent');
|
|
202
|
+
|
|
203
|
+
// Option C — read from saved CLI config (~/.notilens_config.json)
|
|
204
|
+
// after running: notilens init --agent my-agent --token TOKEN --secret SECRET
|
|
205
|
+
const agent = NotiLensAgent.init('my-agent');
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 2. Task Lifecycle
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const taskId = agent.taskStart('job_001'); // optional: auto-generates ID if omitted
|
|
212
|
+
|
|
213
|
+
agent.taskProgress('Fetching records', taskId);
|
|
214
|
+
agent.taskLoop('Processing batch 2', taskId);
|
|
215
|
+
agent.taskRetry(taskId);
|
|
216
|
+
agent.taskError('Non-fatal error', taskId); // task continues
|
|
217
|
+
agent.taskComplete('All done', taskId); // terminal — clears state
|
|
218
|
+
agent.taskFail('Unrecoverable', taskId); // terminal
|
|
219
|
+
agent.taskTimeout('Exceeded 30s', taskId); // terminal
|
|
220
|
+
agent.taskCancel('User cancelled', taskId); // terminal
|
|
221
|
+
agent.taskTerminate('OOM', taskId); // terminal
|
|
222
|
+
agent.taskStop(taskId);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## 3. Input / Human-in-the-loop
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
agent.inputRequired('Please confirm the output', taskId);
|
|
229
|
+
agent.inputApproved('User confirmed', taskId);
|
|
230
|
+
agent.inputRejected('User rejected', taskId);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## 4. AI Response Events
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
agent.aiResponseGenerated('Summary: the document is about X', taskId);
|
|
237
|
+
agent.aiResponseFailed('Model timeout', taskId);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## 5. Generic Events
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// Free-form events for anything beyond task lifecycle
|
|
244
|
+
agent.emit('order.placed', 'Order #1234', { meta: { amount: 99.99 } });
|
|
245
|
+
agent.emit('disk.space.full', 'Only 1GB remaining', { level: 'warning' });
|
|
246
|
+
agent.emit('user.registered', 'New signup', { meta: { plan: 'pro' } });
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Full Example
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { NotiLensAgent } from '@notilens/notilens';
|
|
253
|
+
|
|
254
|
+
const agent = NotiLensAgent.init('summarizer', { token: 'TOKEN', secret: 'SECRET' });
|
|
255
|
+
const taskId = agent.taskStart();
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
agent.taskProgress('Fetching PDF', taskId);
|
|
259
|
+
// ... your logic ...
|
|
260
|
+
agent.taskComplete('Summary ready', taskId);
|
|
261
|
+
} catch (err) {
|
|
262
|
+
agent.taskFail((err as Error).message, taskId);
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Requirements
|
|
269
|
+
|
|
270
|
+
- Node.js >= 18.0.0
|
|
271
|
+
|
|
150
272
|
## License
|
|
151
273
|
|
|
152
274
|
MIT — [notilens.com](https://www.notilens.com)
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare class NotiLensAgent {
|
|
2
|
+
readonly agent: string;
|
|
3
|
+
private readonly token;
|
|
4
|
+
private readonly secret;
|
|
5
|
+
private constructor();
|
|
6
|
+
static init(agentName: string, options?: {
|
|
7
|
+
token?: string;
|
|
8
|
+
secret?: string;
|
|
9
|
+
}): NotiLensAgent;
|
|
10
|
+
taskStart(taskId?: string): string;
|
|
11
|
+
taskProgress(message: string, taskId: string): void;
|
|
12
|
+
taskLoop(message: string, taskId: string): void;
|
|
13
|
+
taskRetry(taskId: string): void;
|
|
14
|
+
taskError(message: string, taskId: string): void;
|
|
15
|
+
taskComplete(message: string, taskId: string): void;
|
|
16
|
+
taskFail(message: string, taskId: string): void;
|
|
17
|
+
taskTimeout(message: string, taskId: string): void;
|
|
18
|
+
taskCancel(message: string, taskId: string): void;
|
|
19
|
+
taskStop(taskId: string): void;
|
|
20
|
+
taskTerminate(message: string, taskId: string): void;
|
|
21
|
+
inputRequired(message: string, taskId: string): void;
|
|
22
|
+
inputApproved(message: string, taskId: string): void;
|
|
23
|
+
inputRejected(message: string, taskId: string): void;
|
|
24
|
+
aiResponseGenerated(message: string, taskId: string): void;
|
|
25
|
+
aiResponseFailed(message: string, taskId: string): void;
|
|
26
|
+
emit(event: string, message: string, options?: {
|
|
27
|
+
meta?: Record<string, unknown>;
|
|
28
|
+
level?: string;
|
|
29
|
+
}): void;
|
|
30
|
+
private send;
|
|
31
|
+
private calcDuration;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=agent.d.ts.map
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NotiLensAgent = void 0;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const state_1 = require("./state");
|
|
6
|
+
const notify_1 = require("./notify");
|
|
7
|
+
const SUCCESS_EVENTS = new Set([
|
|
8
|
+
'task.completed', 'ai.response.generated', 'input.approved',
|
|
9
|
+
]);
|
|
10
|
+
const ACTIONABLE_EVENTS = new Set([
|
|
11
|
+
'task.error', 'task.failed', 'task.timeout', 'task.retrying', 'task.loop',
|
|
12
|
+
'ai.response.failed', 'input.required', 'input.rejected',
|
|
13
|
+
]);
|
|
14
|
+
const LEVEL_TO_TYPE = {
|
|
15
|
+
debug: 'info', info: 'info', warning: 'warning', error: 'urgent', critical: 'urgent',
|
|
16
|
+
};
|
|
17
|
+
function eventType(event, level) {
|
|
18
|
+
if (SUCCESS_EVENTS.has(event))
|
|
19
|
+
return 'success';
|
|
20
|
+
if (['task.failed', 'task.timeout', 'task.error', 'task.terminated', 'ai.response.failed'].includes(event))
|
|
21
|
+
return 'urgent';
|
|
22
|
+
if (['task.retrying', 'task.cancelled', 'input.required', 'input.rejected'].includes(event))
|
|
23
|
+
return 'warning';
|
|
24
|
+
return LEVEL_TO_TYPE[level] ?? 'info';
|
|
25
|
+
}
|
|
26
|
+
class NotiLensAgent {
|
|
27
|
+
constructor(agent, token, secret) {
|
|
28
|
+
this.agent = agent;
|
|
29
|
+
this.token = token;
|
|
30
|
+
this.secret = secret;
|
|
31
|
+
}
|
|
32
|
+
// ── Factory ──────────────────────────────────────────────────────────────
|
|
33
|
+
static init(agentName, options) {
|
|
34
|
+
let token = options?.token ?? process.env.NOTILENS_TOKEN;
|
|
35
|
+
let secret = options?.secret ?? process.env.NOTILENS_SECRET;
|
|
36
|
+
if (!token || !secret) {
|
|
37
|
+
const conf = (0, config_1.getAgentConfig)(agentName);
|
|
38
|
+
token ?? (token = conf?.token);
|
|
39
|
+
secret ?? (secret = conf?.secret);
|
|
40
|
+
}
|
|
41
|
+
if (!token || !secret) {
|
|
42
|
+
throw new Error(`NotiLens: token and secret are required. Pass them directly, set ` +
|
|
43
|
+
`NOTILENS_TOKEN/NOTILENS_SECRET env vars, or run: ` +
|
|
44
|
+
`notilens init --agent ${agentName} --token TOKEN --secret SECRET`);
|
|
45
|
+
}
|
|
46
|
+
return new NotiLensAgent(agentName, token, secret);
|
|
47
|
+
}
|
|
48
|
+
// ── Task lifecycle ───────────────────────────────────────────────────────
|
|
49
|
+
taskStart(taskId) {
|
|
50
|
+
const id = taskId ?? `task_${Date.now()}`;
|
|
51
|
+
const sf = (0, state_1.getStateFile)(this.agent, id);
|
|
52
|
+
(0, state_1.writeState)(sf, { agent: this.agent, task: id, start_time: Date.now(), retry_count: 0, loop_count: 0 });
|
|
53
|
+
this.send('task.started', 'Task started', id);
|
|
54
|
+
return id;
|
|
55
|
+
}
|
|
56
|
+
taskProgress(message, taskId) {
|
|
57
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
58
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf) });
|
|
59
|
+
this.send('task.progress', message, taskId);
|
|
60
|
+
}
|
|
61
|
+
taskLoop(message, taskId) {
|
|
62
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
63
|
+
const state = (0, state_1.readState)(sf);
|
|
64
|
+
const count = (state.loop_count ?? 0) + 1;
|
|
65
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf), loop_count: count });
|
|
66
|
+
this.send('task.loop', message, taskId);
|
|
67
|
+
}
|
|
68
|
+
taskRetry(taskId) {
|
|
69
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
70
|
+
const state = (0, state_1.readState)(sf);
|
|
71
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf), retry_count: (state.retry_count ?? 0) + 1 });
|
|
72
|
+
this.send('task.retrying', 'Retrying task', taskId);
|
|
73
|
+
}
|
|
74
|
+
taskError(message, taskId) {
|
|
75
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
76
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf), last_error: message });
|
|
77
|
+
this.send('task.error', message, taskId);
|
|
78
|
+
}
|
|
79
|
+
taskComplete(message, taskId) {
|
|
80
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
81
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf) });
|
|
82
|
+
this.send('task.completed', message, taskId);
|
|
83
|
+
(0, state_1.deleteState)(sf);
|
|
84
|
+
}
|
|
85
|
+
taskFail(message, taskId) {
|
|
86
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
87
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf) });
|
|
88
|
+
this.send('task.failed', message, taskId);
|
|
89
|
+
(0, state_1.deleteState)(sf);
|
|
90
|
+
}
|
|
91
|
+
taskTimeout(message, taskId) {
|
|
92
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
93
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf) });
|
|
94
|
+
this.send('task.timeout', message, taskId);
|
|
95
|
+
(0, state_1.deleteState)(sf);
|
|
96
|
+
}
|
|
97
|
+
taskCancel(message, taskId) {
|
|
98
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
99
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf) });
|
|
100
|
+
this.send('task.cancelled', message, taskId);
|
|
101
|
+
(0, state_1.deleteState)(sf);
|
|
102
|
+
}
|
|
103
|
+
taskStop(taskId) {
|
|
104
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
105
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf) });
|
|
106
|
+
this.send('task.stopped', 'Task stopped', taskId);
|
|
107
|
+
}
|
|
108
|
+
taskTerminate(message, taskId) {
|
|
109
|
+
const sf = (0, state_1.getStateFile)(this.agent, taskId);
|
|
110
|
+
(0, state_1.updateState)(sf, { duration_ms: this.calcDuration(sf) });
|
|
111
|
+
this.send('task.terminated', message, taskId);
|
|
112
|
+
(0, state_1.deleteState)(sf);
|
|
113
|
+
}
|
|
114
|
+
// ── Input events ─────────────────────────────────────────────────────────
|
|
115
|
+
inputRequired(message, taskId) {
|
|
116
|
+
this.send('input.required', message, taskId);
|
|
117
|
+
}
|
|
118
|
+
inputApproved(message, taskId) {
|
|
119
|
+
this.send('input.approved', message, taskId);
|
|
120
|
+
}
|
|
121
|
+
inputRejected(message, taskId) {
|
|
122
|
+
this.send('input.rejected', message, taskId);
|
|
123
|
+
}
|
|
124
|
+
// ── AI response events ───────────────────────────────────────────────────
|
|
125
|
+
aiResponseGenerated(message, taskId) {
|
|
126
|
+
this.send('ai.response.generated', message, taskId);
|
|
127
|
+
}
|
|
128
|
+
aiResponseFailed(message, taskId) {
|
|
129
|
+
this.send('ai.response.failed', message, taskId);
|
|
130
|
+
}
|
|
131
|
+
// ── Generic emit ─────────────────────────────────────────────────────────
|
|
132
|
+
emit(event, message, options) {
|
|
133
|
+
this.send(event, message, undefined, options?.meta, options?.level);
|
|
134
|
+
}
|
|
135
|
+
// ── Internals ────────────────────────────────────────────────────────────
|
|
136
|
+
send(event, message, taskId, meta = {}, level = 'info') {
|
|
137
|
+
const title = taskId
|
|
138
|
+
? `${this.agent} | ${taskId} | ${event}`
|
|
139
|
+
: `${this.agent} | ${event}`;
|
|
140
|
+
const { image_url = '', open_url = '', download_url = '', tags = '', ...restMeta } = meta;
|
|
141
|
+
const payload = {
|
|
142
|
+
event,
|
|
143
|
+
title,
|
|
144
|
+
message,
|
|
145
|
+
type: eventType(event, level),
|
|
146
|
+
agent: this.agent,
|
|
147
|
+
task_id: taskId ?? '',
|
|
148
|
+
is_actionable: ACTIONABLE_EVENTS.has(event),
|
|
149
|
+
image_url,
|
|
150
|
+
open_url,
|
|
151
|
+
download_url,
|
|
152
|
+
tags,
|
|
153
|
+
ts: Date.now() / 1000,
|
|
154
|
+
meta: restMeta,
|
|
155
|
+
};
|
|
156
|
+
(0, notify_1.sendNotification)(this.token, this.secret, payload).catch(() => {
|
|
157
|
+
// silent fail
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
calcDuration(stateFile) {
|
|
161
|
+
const state = (0, state_1.readState)(stateFile);
|
|
162
|
+
return Date.now() - (state.start_time ?? 0);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
exports.NotiLensAgent = NotiLensAgent;
|
|
166
|
+
//# sourceMappingURL=agent.js.map
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,6 @@ const state_1 = require("./state");
|
|
|
6
6
|
const notify_1 = require("./notify");
|
|
7
7
|
const VERSION = require('../package.json').version;
|
|
8
8
|
// ---- Arg helpers ----
|
|
9
|
-
/** Split args into positional (before first --flag) and the full list for flag parsing. */
|
|
10
9
|
function positionalArgs(args) {
|
|
11
10
|
const result = [];
|
|
12
11
|
for (const a of args) {
|
|
@@ -78,47 +77,29 @@ function parseFlags(args) {
|
|
|
78
77
|
}
|
|
79
78
|
// ---- Event type / actionable mapping ----
|
|
80
79
|
function getEventType(event) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
case 'response.failed':
|
|
89
|
-
case 'validation.failed':
|
|
90
|
-
return 'urgent';
|
|
91
|
-
case 'task.error':
|
|
92
|
-
case 'task.terminated':
|
|
93
|
-
case 'guardrail.triggered':
|
|
94
|
-
return 'important';
|
|
95
|
-
case 'task.retrying':
|
|
96
|
-
case 'task.cancelled':
|
|
97
|
-
case 'input.required':
|
|
98
|
-
case 'input.rejected':
|
|
99
|
-
return 'warning';
|
|
100
|
-
default:
|
|
101
|
-
return 'info';
|
|
102
|
-
}
|
|
80
|
+
if (['task.completed', 'ai.response.generated', 'input.approved'].includes(event))
|
|
81
|
+
return 'success';
|
|
82
|
+
if (['task.failed', 'task.timeout', 'task.error', 'task.terminated', 'ai.response.failed'].includes(event))
|
|
83
|
+
return 'urgent';
|
|
84
|
+
if (['task.retrying', 'task.cancelled', 'input.required', 'input.rejected'].includes(event))
|
|
85
|
+
return 'warning';
|
|
86
|
+
return 'info';
|
|
103
87
|
}
|
|
104
88
|
function getActionableDefault(event) {
|
|
105
89
|
return [
|
|
106
|
-
'task.error', 'task.failed', 'task.timeout', 'task.retrying',
|
|
107
|
-
'ai.response.failed', '
|
|
108
|
-
'input.required', 'input.rejected',
|
|
90
|
+
'task.error', 'task.failed', 'task.timeout', 'task.retrying', 'task.loop',
|
|
91
|
+
'ai.response.failed', 'input.required', 'input.rejected',
|
|
109
92
|
].includes(event);
|
|
110
93
|
}
|
|
111
94
|
function validateType(t) {
|
|
112
|
-
return ['info', 'success', 'warning', 'urgent'
|
|
113
|
-
? t
|
|
114
|
-
: '';
|
|
95
|
+
return ['info', 'success', 'warning', 'urgent'].includes(t) ? t : '';
|
|
115
96
|
}
|
|
116
97
|
// ---- Core send ----
|
|
117
|
-
async function sendNotify(event,
|
|
98
|
+
async function sendNotify(event, message, flags) {
|
|
118
99
|
const conf = (0, config_1.getAgentConfig)(flags.agent);
|
|
119
|
-
if (!conf?.
|
|
120
|
-
console.error(
|
|
121
|
-
|
|
100
|
+
if (!conf?.token || !conf?.secret) {
|
|
101
|
+
console.error(`❌ Agent '${flags.agent}' not configured. Run: notilens init --agent ${flags.agent} --token TOKEN --secret SECRET`);
|
|
102
|
+
process.exit(1);
|
|
122
103
|
}
|
|
123
104
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
124
105
|
const state = (0, state_1.readState)(stateFile);
|
|
@@ -134,6 +115,7 @@ async function sendNotify(event, title, message, flags) {
|
|
|
134
115
|
loop_count: state.loop_count ?? 0,
|
|
135
116
|
...flags.meta,
|
|
136
117
|
};
|
|
118
|
+
const title = `${flags.agent} | ${flags.taskId} | ${event}`;
|
|
137
119
|
const finalType = validateType(flags.type) || getEventType(event);
|
|
138
120
|
const finalActionable = flags.isActionable !== ''
|
|
139
121
|
? flags.isActionable === 'true'
|
|
@@ -145,19 +127,20 @@ async function sendNotify(event, title, message, flags) {
|
|
|
145
127
|
type: finalType,
|
|
146
128
|
agent: flags.agent,
|
|
147
129
|
task_id: flags.taskId,
|
|
148
|
-
|
|
130
|
+
is_actionable: finalActionable,
|
|
149
131
|
image_url: flags.imageUrl,
|
|
150
132
|
open_url: flags.openUrl,
|
|
151
133
|
download_url: flags.downloadUrl,
|
|
152
134
|
tags: flags.tags,
|
|
153
|
-
|
|
135
|
+
ts: Date.now() / 1000,
|
|
136
|
+
meta,
|
|
154
137
|
};
|
|
155
138
|
try {
|
|
156
|
-
await (0, notify_1.sendNotification)(conf.
|
|
157
|
-
await new Promise(r => setTimeout(r, 300));
|
|
139
|
+
await (0, notify_1.sendNotification)(conf.token, conf.secret, payload);
|
|
140
|
+
await new Promise(r => setTimeout(r, 300));
|
|
158
141
|
}
|
|
159
142
|
catch {
|
|
160
|
-
//
|
|
143
|
+
// silent fail
|
|
161
144
|
}
|
|
162
145
|
}
|
|
163
146
|
// ---- Duration helper ----
|
|
@@ -171,35 +154,62 @@ async function main() {
|
|
|
171
154
|
const command = args[0];
|
|
172
155
|
const rest = args.slice(1);
|
|
173
156
|
switch (command) {
|
|
174
|
-
case '
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
157
|
+
case 'init': {
|
|
158
|
+
let agent = '', token = '', secret = '';
|
|
159
|
+
for (let i = 0; i < rest.length; i++) {
|
|
160
|
+
if (rest[i] === '--agent')
|
|
161
|
+
agent = rest[++i] ?? '';
|
|
162
|
+
if (rest[i] === '--token')
|
|
163
|
+
token = rest[++i] ?? '';
|
|
164
|
+
if (rest[i] === '--secret')
|
|
165
|
+
secret = rest[++i] ?? '';
|
|
166
|
+
}
|
|
167
|
+
if (!agent || !token || !secret) {
|
|
168
|
+
console.error('Usage: notilens init --agent <name> --token <token> --secret <secret>');
|
|
178
169
|
process.exit(1);
|
|
179
170
|
}
|
|
180
|
-
(0, config_1.
|
|
181
|
-
console.log(`✔ Agent '${agent}'
|
|
171
|
+
(0, config_1.saveAgent)(agent, token, secret);
|
|
172
|
+
console.log(`✔ Agent '${agent}' saved`);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
case 'agents': {
|
|
176
|
+
const agents = (0, config_1.listAgents)();
|
|
177
|
+
if (!agents.length)
|
|
178
|
+
console.log('No agents configured.');
|
|
179
|
+
else
|
|
180
|
+
agents.forEach(a => console.log(` ${a}`));
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case 'remove-agent': {
|
|
184
|
+
const agent = rest[0];
|
|
185
|
+
if (!agent) {
|
|
186
|
+
console.error('Usage: notilens remove-agent <agent>');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
(0, config_1.removeAgent)(agent)
|
|
190
|
+
? console.log(`✔ Agent '${agent}' removed`)
|
|
191
|
+
: console.error(`Agent '${agent}' not found`);
|
|
182
192
|
break;
|
|
183
193
|
}
|
|
184
194
|
case 'task.start': {
|
|
185
195
|
const flags = parseFlags(rest);
|
|
186
196
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
187
197
|
(0, state_1.writeState)(stateFile, {
|
|
188
|
-
agent: flags.agent,
|
|
189
|
-
|
|
190
|
-
start_time: Date.now(),
|
|
191
|
-
retry_count: 0,
|
|
198
|
+
agent: flags.agent, task: flags.taskId,
|
|
199
|
+
start_time: Date.now(), retry_count: 0, loop_count: 0,
|
|
192
200
|
});
|
|
193
|
-
await sendNotify('task.started',
|
|
201
|
+
await sendNotify('task.started', 'Task started', flags);
|
|
194
202
|
console.log(`▶️ Started: ${flags.agent} | ${flags.taskId}`);
|
|
195
203
|
break;
|
|
196
204
|
}
|
|
197
|
-
case 'task.
|
|
205
|
+
case 'task.progress': {
|
|
206
|
+
const pos = positionalArgs(rest);
|
|
207
|
+
const msg = pos[0] ?? '';
|
|
198
208
|
const flags = parseFlags(rest);
|
|
199
209
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
200
210
|
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile) });
|
|
201
|
-
await sendNotify('task.
|
|
202
|
-
console.log(`⏳
|
|
211
|
+
await sendNotify('task.progress', msg, flags);
|
|
212
|
+
console.log(`⏳ Progress: ${flags.agent} | ${flags.taskId}`);
|
|
203
213
|
break;
|
|
204
214
|
}
|
|
205
215
|
case 'task.stop': {
|
|
@@ -207,7 +217,7 @@ async function main() {
|
|
|
207
217
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
208
218
|
const dur = calcDuration(stateFile);
|
|
209
219
|
(0, state_1.updateState)(stateFile, { duration_ms: dur });
|
|
210
|
-
await sendNotify('task.stopped',
|
|
220
|
+
await sendNotify('task.stopped', 'Task stopped', flags);
|
|
211
221
|
console.log(`⏹ Stopped: ${flags.agent} | ${flags.taskId} (${dur} ms)`);
|
|
212
222
|
break;
|
|
213
223
|
}
|
|
@@ -219,7 +229,7 @@ async function main() {
|
|
|
219
229
|
duration_ms: calcDuration(stateFile),
|
|
220
230
|
retry_count: (state.retry_count ?? 0) + 1,
|
|
221
231
|
});
|
|
222
|
-
await sendNotify('task.retrying',
|
|
232
|
+
await sendNotify('task.retrying', 'Retrying task', flags);
|
|
223
233
|
console.log(`🔁 Retry: ${flags.agent} | ${flags.taskId}`);
|
|
224
234
|
break;
|
|
225
235
|
}
|
|
@@ -231,7 +241,7 @@ async function main() {
|
|
|
231
241
|
const state = (0, state_1.readState)(stateFile);
|
|
232
242
|
const loopCount = (state.loop_count ?? 0) + 1;
|
|
233
243
|
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile), loop_count: loopCount });
|
|
234
|
-
await sendNotify('task.loop',
|
|
244
|
+
await sendNotify('task.loop', msg, flags);
|
|
235
245
|
console.log(`🔄 Loop (${loopCount}): ${flags.agent} | ${flags.taskId}`);
|
|
236
246
|
break;
|
|
237
247
|
}
|
|
@@ -241,7 +251,7 @@ async function main() {
|
|
|
241
251
|
const flags = parseFlags(rest);
|
|
242
252
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
243
253
|
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile), last_error: msg });
|
|
244
|
-
await sendNotify('task.error',
|
|
254
|
+
await sendNotify('task.error', msg, flags);
|
|
245
255
|
console.error(`❌ Error: ${msg}`);
|
|
246
256
|
break;
|
|
247
257
|
}
|
|
@@ -250,8 +260,8 @@ async function main() {
|
|
|
250
260
|
const msg = pos[0] ?? '';
|
|
251
261
|
const flags = parseFlags(rest);
|
|
252
262
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
253
|
-
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile)
|
|
254
|
-
await sendNotify('task.failed',
|
|
263
|
+
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile) });
|
|
264
|
+
await sendNotify('task.failed', msg, flags);
|
|
255
265
|
(0, state_1.deleteState)(stateFile);
|
|
256
266
|
console.log(`💥 Failed: ${flags.agent} | ${flags.taskId}`);
|
|
257
267
|
break;
|
|
@@ -261,8 +271,8 @@ async function main() {
|
|
|
261
271
|
const msg = pos[0] ?? '';
|
|
262
272
|
const flags = parseFlags(rest);
|
|
263
273
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
264
|
-
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile)
|
|
265
|
-
await sendNotify('task.timeout',
|
|
274
|
+
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile) });
|
|
275
|
+
await sendNotify('task.timeout', msg, flags);
|
|
266
276
|
(0, state_1.deleteState)(stateFile);
|
|
267
277
|
console.log(`⏰ Timeout: ${flags.agent} | ${flags.taskId}`);
|
|
268
278
|
break;
|
|
@@ -272,8 +282,8 @@ async function main() {
|
|
|
272
282
|
const msg = pos[0] ?? '';
|
|
273
283
|
const flags = parseFlags(rest);
|
|
274
284
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
275
|
-
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile)
|
|
276
|
-
await sendNotify('task.cancelled',
|
|
285
|
+
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile) });
|
|
286
|
+
await sendNotify('task.cancelled', msg, flags);
|
|
277
287
|
(0, state_1.deleteState)(stateFile);
|
|
278
288
|
console.log(`🚫 Cancelled: ${flags.agent} | ${flags.taskId}`);
|
|
279
289
|
break;
|
|
@@ -283,8 +293,8 @@ async function main() {
|
|
|
283
293
|
const msg = pos[0] ?? '';
|
|
284
294
|
const flags = parseFlags(rest);
|
|
285
295
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
286
|
-
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile)
|
|
287
|
-
await sendNotify('task.terminated',
|
|
296
|
+
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile) });
|
|
297
|
+
await sendNotify('task.terminated', msg, flags);
|
|
288
298
|
(0, state_1.deleteState)(stateFile);
|
|
289
299
|
console.log(`⚠️ Terminated: ${flags.agent} | ${flags.taskId}`);
|
|
290
300
|
break;
|
|
@@ -295,7 +305,7 @@ async function main() {
|
|
|
295
305
|
const flags = parseFlags(rest);
|
|
296
306
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
297
307
|
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile) });
|
|
298
|
-
await sendNotify('task.completed',
|
|
308
|
+
await sendNotify('task.completed', msg, flags);
|
|
299
309
|
(0, state_1.deleteState)(stateFile);
|
|
300
310
|
console.log(`✅ Completed: ${flags.agent} | ${flags.taskId}`);
|
|
301
311
|
break;
|
|
@@ -317,35 +327,35 @@ async function main() {
|
|
|
317
327
|
const pos = positionalArgs(rest);
|
|
318
328
|
const msg = pos[0] ?? '';
|
|
319
329
|
const flags = parseFlags(rest);
|
|
320
|
-
await sendNotify('ai.response.generated',
|
|
330
|
+
await sendNotify('ai.response.generated', msg, flags);
|
|
321
331
|
break;
|
|
322
332
|
}
|
|
323
333
|
case 'ai.response.fail': {
|
|
324
334
|
const pos = positionalArgs(rest);
|
|
325
335
|
const msg = pos[0] ?? '';
|
|
326
336
|
const flags = parseFlags(rest);
|
|
327
|
-
await sendNotify('ai.response.failed',
|
|
337
|
+
await sendNotify('ai.response.failed', msg, flags);
|
|
328
338
|
break;
|
|
329
339
|
}
|
|
330
340
|
case 'input.required': {
|
|
331
341
|
const pos = positionalArgs(rest);
|
|
332
342
|
const msg = pos[0] ?? '';
|
|
333
343
|
const flags = parseFlags(rest);
|
|
334
|
-
await sendNotify('input.required',
|
|
344
|
+
await sendNotify('input.required', msg, flags);
|
|
335
345
|
break;
|
|
336
346
|
}
|
|
337
347
|
case 'input.approve': {
|
|
338
348
|
const pos = positionalArgs(rest);
|
|
339
349
|
const msg = pos[0] ?? '';
|
|
340
350
|
const flags = parseFlags(rest);
|
|
341
|
-
await sendNotify('input.approved',
|
|
351
|
+
await sendNotify('input.approved', msg, flags);
|
|
342
352
|
break;
|
|
343
353
|
}
|
|
344
354
|
case 'input.reject': {
|
|
345
355
|
const pos = positionalArgs(rest);
|
|
346
356
|
const msg = pos[0] ?? '';
|
|
347
357
|
const flags = parseFlags(rest);
|
|
348
|
-
await sendNotify('input.rejected',
|
|
358
|
+
await sendNotify('input.rejected', msg, flags);
|
|
349
359
|
break;
|
|
350
360
|
}
|
|
351
361
|
case 'emit': {
|
|
@@ -355,7 +365,7 @@ async function main() {
|
|
|
355
365
|
const flags = parseFlags(rest.slice(2));
|
|
356
366
|
const stateFile = (0, state_1.getStateFile)(flags.agent, flags.taskId);
|
|
357
367
|
(0, state_1.updateState)(stateFile, { duration_ms: calcDuration(stateFile) });
|
|
358
|
-
await sendNotify(event,
|
|
368
|
+
await sendNotify(event, msg, flags);
|
|
359
369
|
console.log(`📡 Event emitted: ${event}`);
|
|
360
370
|
break;
|
|
361
371
|
}
|
|
@@ -368,43 +378,47 @@ async function main() {
|
|
|
368
378
|
}
|
|
369
379
|
function printUsage() {
|
|
370
380
|
console.log(`Usage:
|
|
371
|
-
notilens
|
|
381
|
+
notilens init --agent <name> --token <token> --secret <secret>
|
|
382
|
+
notilens agents
|
|
383
|
+
notilens remove-agent <agent>
|
|
372
384
|
|
|
373
|
-
|
|
374
|
-
notilens task.start
|
|
375
|
-
notilens task.
|
|
376
|
-
notilens task.
|
|
377
|
-
notilens task.retry
|
|
378
|
-
notilens task.
|
|
379
|
-
notilens task.error
|
|
380
|
-
notilens task.fail
|
|
381
|
-
notilens task.timeout
|
|
382
|
-
notilens task.cancel
|
|
385
|
+
Task Lifecycle:
|
|
386
|
+
notilens task.start --agent <agent> [--task <id>]
|
|
387
|
+
notilens task.progress "msg" --agent <agent> [--task <id>]
|
|
388
|
+
notilens task.loop "msg" --agent <agent> [--task <id>]
|
|
389
|
+
notilens task.retry --agent <agent> [--task <id>]
|
|
390
|
+
notilens task.stop --agent <agent> [--task <id>]
|
|
391
|
+
notilens task.error "msg" --agent <agent> [--task <id>]
|
|
392
|
+
notilens task.fail "msg" --agent <agent> [--task <id>]
|
|
393
|
+
notilens task.timeout "msg" --agent <agent> [--task <id>]
|
|
394
|
+
notilens task.cancel "msg" --agent <agent> [--task <id>]
|
|
383
395
|
notilens task.terminate "msg" --agent <agent> [--task <id>]
|
|
384
|
-
notilens task.complete "msg"
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
notilens
|
|
388
|
-
notilens
|
|
389
|
-
notilens input.
|
|
396
|
+
notilens task.complete "msg" --agent <agent> [--task <id>]
|
|
397
|
+
|
|
398
|
+
AI / Input:
|
|
399
|
+
notilens ai.response.generate "msg" --agent <agent> [--task <id>]
|
|
400
|
+
notilens ai.response.fail "msg" --agent <agent> [--task <id>]
|
|
401
|
+
notilens input.required "msg" --agent <agent> [--task <id>]
|
|
402
|
+
notilens input.approve "msg" --agent <agent> [--task <id>]
|
|
403
|
+
notilens input.reject "msg" --agent <agent> [--task <id>]
|
|
390
404
|
|
|
391
|
-
Generic
|
|
405
|
+
Generic:
|
|
392
406
|
notilens emit <event> "msg" --agent <agent>
|
|
393
407
|
|
|
394
408
|
Metrics:
|
|
395
409
|
notilens set.metrics <prompt_tokens> <completion_tokens> [confidence] --agent <agent> [--task <id>]
|
|
396
410
|
|
|
397
411
|
Options:
|
|
398
|
-
--agent <
|
|
412
|
+
--agent <name>
|
|
399
413
|
--task <id>
|
|
400
|
-
--type success|warning|urgent|
|
|
401
|
-
--meta key=value
|
|
414
|
+
--type success|warning|urgent|info
|
|
415
|
+
--meta key=value (repeatable)
|
|
402
416
|
--image_url <url>
|
|
403
417
|
--open_url <url>
|
|
404
418
|
--download_url <url>
|
|
405
419
|
--tags "tag1,tag2"
|
|
406
420
|
--is_actionable true|false
|
|
407
|
-
--confidence <0-
|
|
421
|
+
--confidence <0-1>
|
|
408
422
|
|
|
409
423
|
Other:
|
|
410
424
|
notilens version`);
|
package/dist/config.d.ts
CHANGED
|
@@ -2,5 +2,7 @@ import { AgentConfig, NotiLensConfig } from './types';
|
|
|
2
2
|
export declare function readConfig(): NotiLensConfig;
|
|
3
3
|
export declare function writeConfig(config: NotiLensConfig): void;
|
|
4
4
|
export declare function getAgentConfig(agent: string): AgentConfig | null;
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function saveAgent(agent: string, token: string, secret: string): void;
|
|
6
|
+
export declare function removeAgent(agent: string): boolean;
|
|
7
|
+
export declare function listAgents(): string[];
|
|
6
8
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
CHANGED
|
@@ -36,7 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.readConfig = readConfig;
|
|
37
37
|
exports.writeConfig = writeConfig;
|
|
38
38
|
exports.getAgentConfig = getAgentConfig;
|
|
39
|
-
exports.
|
|
39
|
+
exports.saveAgent = saveAgent;
|
|
40
|
+
exports.removeAgent = removeAgent;
|
|
41
|
+
exports.listAgents = listAgents;
|
|
40
42
|
const fs = __importStar(require("fs"));
|
|
41
43
|
const os = __importStar(require("os"));
|
|
42
44
|
const path = __importStar(require("path"));
|
|
@@ -58,9 +60,20 @@ function getAgentConfig(agent) {
|
|
|
58
60
|
const config = readConfig();
|
|
59
61
|
return config[agent] || null;
|
|
60
62
|
}
|
|
61
|
-
function
|
|
63
|
+
function saveAgent(agent, token, secret) {
|
|
62
64
|
const config = readConfig();
|
|
63
|
-
config[agent] = {
|
|
65
|
+
config[agent] = { token, secret };
|
|
64
66
|
writeConfig(config);
|
|
65
67
|
}
|
|
68
|
+
function removeAgent(agent) {
|
|
69
|
+
const config = readConfig();
|
|
70
|
+
if (!config[agent])
|
|
71
|
+
return false;
|
|
72
|
+
delete config[agent];
|
|
73
|
+
writeConfig(config);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
function listAgents() {
|
|
77
|
+
return Object.keys(readConfig());
|
|
78
|
+
}
|
|
66
79
|
//# sourceMappingURL=config.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listAgents = exports.removeAgent = exports.getAgentConfig = exports.saveAgent = exports.NotiLensAgent = void 0;
|
|
4
|
+
var agent_1 = require("./agent");
|
|
5
|
+
Object.defineProperty(exports, "NotiLensAgent", { enumerable: true, get: function () { return agent_1.NotiLensAgent; } });
|
|
6
|
+
var config_1 = require("./config");
|
|
7
|
+
Object.defineProperty(exports, "saveAgent", { enumerable: true, get: function () { return config_1.saveAgent; } });
|
|
8
|
+
Object.defineProperty(exports, "getAgentConfig", { enumerable: true, get: function () { return config_1.getAgentConfig; } });
|
|
9
|
+
Object.defineProperty(exports, "removeAgent", { enumerable: true, get: function () { return config_1.removeAgent; } });
|
|
10
|
+
Object.defineProperty(exports, "listAgents", { enumerable: true, get: function () { return config_1.listAgents; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
package/dist/notify.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { NotifyPayload } from './types';
|
|
2
|
-
export declare function sendNotification(
|
|
2
|
+
export declare function sendNotification(token: string, secret: string, payload: NotifyPayload): Promise<void>;
|
|
3
3
|
//# sourceMappingURL=notify.d.ts.map
|
package/dist/notify.js
CHANGED
|
@@ -35,55 +35,51 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.sendNotification = sendNotification;
|
|
37
37
|
const https = __importStar(require("https"));
|
|
38
|
-
const http = __importStar(require("http"));
|
|
39
38
|
const VERSION = require('../package.json').version;
|
|
39
|
+
const WEBHOOK_URL = 'https://hook.notilens.com/webhook/%s/send';
|
|
40
40
|
const MAX_RETRIES = 2;
|
|
41
41
|
const TIMEOUT_MS = 10000;
|
|
42
|
-
async function attempt(
|
|
42
|
+
async function attempt(token, secret, body) {
|
|
43
43
|
return new Promise((resolve, reject) => {
|
|
44
|
+
const endpoint = WEBHOOK_URL.replace('%s', token);
|
|
44
45
|
const url = new URL(endpoint);
|
|
45
|
-
const lib = url.protocol === 'https:' ? https : http;
|
|
46
46
|
const options = {
|
|
47
47
|
hostname: url.hostname,
|
|
48
|
-
port: url.port ||
|
|
48
|
+
port: url.port || 443,
|
|
49
49
|
path: url.pathname + url.search,
|
|
50
50
|
method: 'POST',
|
|
51
51
|
headers: {
|
|
52
52
|
'Content-Type': 'application/json',
|
|
53
53
|
'X-NOTILENS-KEY': secret,
|
|
54
|
-
'User-Agent': `NotiLens-
|
|
54
|
+
'User-Agent': `NotiLens-SDK/${VERSION}`,
|
|
55
55
|
'Content-Length': Buffer.byteLength(body),
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
|
-
const req =
|
|
58
|
+
const req = https.request(options, (res) => {
|
|
59
59
|
res.resume();
|
|
60
60
|
resolve();
|
|
61
61
|
});
|
|
62
62
|
const timer = setTimeout(() => {
|
|
63
63
|
req.destroy(new Error('Request timeout'));
|
|
64
64
|
}, TIMEOUT_MS);
|
|
65
|
-
req.on('error', (err) => {
|
|
66
|
-
clearTimeout(timer);
|
|
67
|
-
reject(err);
|
|
68
|
-
});
|
|
65
|
+
req.on('error', (err) => { clearTimeout(timer); reject(err); });
|
|
69
66
|
req.on('close', () => clearTimeout(timer));
|
|
70
67
|
req.write(body);
|
|
71
68
|
req.end();
|
|
72
69
|
});
|
|
73
70
|
}
|
|
74
|
-
async function sendNotification(
|
|
71
|
+
async function sendNotification(token, secret, payload) {
|
|
75
72
|
const body = JSON.stringify(payload);
|
|
76
73
|
let lastErr;
|
|
77
74
|
for (let i = 0; i <= MAX_RETRIES; i++) {
|
|
78
75
|
try {
|
|
79
|
-
await attempt(
|
|
76
|
+
await attempt(token, secret, body);
|
|
80
77
|
return;
|
|
81
78
|
}
|
|
82
79
|
catch (err) {
|
|
83
80
|
lastErr = err;
|
|
84
|
-
if (i < MAX_RETRIES)
|
|
85
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
86
|
-
}
|
|
81
|
+
if (i < MAX_RETRIES)
|
|
82
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
throw lastErr;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export interface AgentConfig {
|
|
2
|
-
|
|
3
|
-
endpoint: string;
|
|
2
|
+
token: string;
|
|
4
3
|
secret: string;
|
|
5
4
|
}
|
|
6
5
|
export interface NotiLensConfig {
|
|
@@ -28,14 +27,15 @@ export interface NotifyPayload {
|
|
|
28
27
|
type: string;
|
|
29
28
|
agent: string;
|
|
30
29
|
task_id: string;
|
|
31
|
-
|
|
30
|
+
is_actionable: boolean;
|
|
32
31
|
image_url: string;
|
|
33
32
|
open_url: string;
|
|
34
33
|
download_url: string;
|
|
35
34
|
tags: string;
|
|
36
|
-
|
|
35
|
+
ts: number;
|
|
36
|
+
meta: Record<string, unknown>;
|
|
37
37
|
}
|
|
38
|
-
export type NotificationType = 'info' | 'success' | 'warning' | 'urgent'
|
|
38
|
+
export type NotificationType = 'info' | 'success' | 'warning' | 'urgent';
|
|
39
39
|
export interface ParsedFlags {
|
|
40
40
|
agent: string;
|
|
41
41
|
taskId: string;
|
package/package.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notilens/notilens",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "NotiLens CLI — send AI agent task lifecycle notifications",
|
|
5
|
-
"keywords": ["notilens", "ai-agent", "notifications", "monitoring", "cli"],
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "NotiLens SDK + CLI — send AI agent task lifecycle notifications",
|
|
5
|
+
"keywords": ["notilens", "ai-agent", "notifications", "monitoring", "cli", "sdk"],
|
|
6
6
|
"homepage": "https://www.notilens.com",
|
|
7
7
|
"author": "NotiLens",
|
|
8
8
|
"license": "MIT",
|
|
9
|
-
"main": "dist/
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
10
11
|
"bin": {
|
|
11
12
|
"notilens": "dist/cli.js"
|
|
12
13
|
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": "./dist/index.js",
|
|
16
|
+
"./cli": "./dist/cli.js"
|
|
17
|
+
},
|
|
13
18
|
"scripts": {
|
|
14
19
|
"build": "tsc",
|
|
15
20
|
"prepublishOnly": "npm run build",
|