@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 CHANGED
@@ -1,6 +1,11 @@
1
1
  # NotiLens
2
2
 
3
- CLI tool for sending AI agent task lifecycle notifications to [NotiLens](https://www.notilens.com).
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
- ## Setup
12
-
13
- Register your agent with its endpoint and secret from the NotiLens dashboard:
16
+ Or per-project:
14
17
 
15
18
  ```bash
16
- notilens add-agent <agent-name> http <endpoint> <secret>
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
- ## Usage
24
+ # CLI
29
25
 
30
- ### Task Lifecycle
26
+ ## Setup
27
+
28
+ Get your token and secret from the [NotiLens dashboard](https://www.notilens.com).
31
29
 
32
30
  ```bash
33
- # Start a task (auto-generates task ID if omitted)
34
- notilens task.start --agent my-agent --task task_001
31
+ notilens init --agent my-agent --token YOUR_TOKEN --secret YOUR_SECRET
32
+ ```
35
33
 
36
- # Mark in progress
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
- # Complete successfully
40
- notilens task.complete "Processed 100 records" --agent my-agent --task task_001
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
- # Report an error (non-terminal, task continues)
43
- notilens task.error "Connection timeout, will retry" --agent my-agent --task task_001
42
+ **List / remove agents:**
43
+ ```bash
44
+ notilens agents
45
+ notilens remove-agent my-agent
46
+ ```
44
47
 
45
- # Retry
46
- notilens task.retry --agent my-agent --task task_001
48
+ ---
47
49
 
48
- # Loop iteration
49
- notilens task.loop "Processing batch 3/10" --agent my-agent --task task_001
50
+ ## Commands
50
51
 
51
- # Fail (terminal)
52
- notilens task.fail "Max retries exceeded" --agent my-agent --task task_001
52
+ ### Task Lifecycle
53
53
 
54
- # Timeout (terminal)
55
- notilens task.timeout "Exceeded 30s limit" --agent my-agent --task task_001
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
- # Cancel (terminal)
58
- notilens task.cancel "User cancelled" --agent my-agent --task task_001
71
+ ### AI Response Events
59
72
 
60
- # Terminate (terminal)
61
- notilens task.terminate "Out of memory" --agent my-agent --task task_001
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
- ### AI Response Events
78
+ ### Input / Human-in-the-loop
65
79
 
66
80
  ```bash
67
- notilens ai.response.generate "Summary generated" --agent my-agent --task task_001
68
- notilens ai.response.fail "Model unavailable" --agent my-agent --task task_001
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
- ### Input Events
86
+ ### Generic Event
72
87
 
73
88
  ```bash
74
- notilens input.required "Please confirm the output" --agent my-agent --task task_001
75
- notilens input.approve "User approved" --agent my-agent --task task_001
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
- # set.metrics <prompt_tokens> <completion_tokens> [confidence] --agent ... --task ...
85
- notilens set.metrics 512 128 0.95 --agent my-agent --task task_001
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
- ### Generic Event
89
-
90
- Emit any custom event:
103
+ ---
91
104
 
92
- ```bash
93
- notilens emit "data.processed" "Ingested 500 rows" --agent my-agent --task task_001
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 notification type: `info` `success` `warning` `urgent` `important` |
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
- Events are automatically mapped to notification types:
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
- # Register agent
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)
@@ -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
- switch (event) {
82
- case 'task.completed':
83
- case 'response.generated':
84
- case 'input.approved':
85
- return 'success';
86
- case 'task.failed':
87
- case 'task.timeout':
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', 'ai.validation.failed', 'ai.guardrail.triggered',
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', 'important'].includes(t)
113
- ? t
114
- : '';
95
+ return ['info', 'success', 'warning', 'urgent'].includes(t) ? t : '';
115
96
  }
116
97
  // ---- Core send ----
117
- async function sendNotify(event, title, message, flags) {
98
+ async function sendNotify(event, message, flags) {
118
99
  const conf = (0, config_1.getAgentConfig)(flags.agent);
119
- if (!conf?.endpoint || !conf?.secret) {
120
- console.error('❌ Agent not configured');
121
- return;
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
- meta,
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
- is_actionable: finalActionable,
135
+ ts: Date.now() / 1000,
136
+ meta,
154
137
  };
155
138
  try {
156
- await (0, notify_1.sendNotification)(conf.endpoint, conf.secret, payload);
157
- await new Promise(r => setTimeout(r, 300)); // match bash sleep 0.3
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
- // Silent fail — mirrors bash curl behaviour
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 'add-agent': {
175
- const [agent, transport, endpoint, secret] = rest;
176
- if (!agent || !transport || !endpoint || !secret) {
177
- console.error('Usage: notilens add-agent <agent> <transport> <endpoint> <secret>');
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.addAgent)(agent, transport, endpoint, secret);
181
- console.log(`✔ Agent '${agent}' added`);
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
- task: flags.taskId,
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', `${flags.agent} | ${flags.taskId} started`, 'Task started', flags);
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.in_progress': {
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.in_progress', `${flags.agent} | ${flags.taskId} running`, 'Task in progress', flags);
202
- console.log(`⏳ In Progress: ${flags.agent} | ${flags.taskId}`);
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', `${flags.agent} | ${flags.taskId} stopped`, 'Task stopped', flags);
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', `${flags.agent} | ${flags.taskId} retry`, 'Retrying task', flags);
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', `${flags.agent} | ${flags.taskId} loop #${loopCount}`, msg, flags);
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', `${flags.agent} | ${flags.taskId} error`, msg, flags);
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), failed: true });
254
- await sendNotify('task.failed', `${flags.agent} | ${flags.taskId} failed`, msg, flags);
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), timeout: true });
265
- await sendNotify('task.timeout', `${flags.agent} | ${flags.taskId} timeout`, msg, flags);
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), cancelled: true });
276
- await sendNotify('task.cancelled', `${flags.agent} | ${flags.taskId} cancelled`, msg, flags);
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), terminated: true });
287
- await sendNotify('task.terminated', `${flags.agent} | ${flags.taskId} terminated`, msg, flags);
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', `${flags.agent} | ${flags.taskId} workflow`, msg, flags);
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', `${flags.agent} | ${flags.taskId} response`, msg, flags);
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', `${flags.agent} | ${flags.taskId} response failed`, msg, flags);
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', `${flags.agent} | ${flags.taskId} input required`, msg, flags);
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', `${flags.agent} | ${flags.taskId} input approved`, msg, flags);
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', `${flags.agent} | ${flags.taskId} input rejected`, msg, flags);
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, `${flags.agent} | ${flags.taskId} ${event}`, msg, flags);
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 add-agent <agent> <transport> <endpoint> <secret>
381
+ notilens init --agent <name> --token <token> --secret <secret>
382
+ notilens agents
383
+ notilens remove-agent <agent>
372
384
 
373
- Core Commands:
374
- notilens task.start --agent <agent> [--task <id>]
375
- notilens task.in_progress --agent <agent> [--task <id>]
376
- notilens task.stop --agent <agent> [--task <id>]
377
- notilens task.retry --agent <agent> [--task <id>]
378
- notilens task.loop "msg" --agent <agent>
379
- notilens task.error "msg" --agent <agent> [--task <id>]
380
- notilens task.fail "msg" --agent <agent> [--task <id>]
381
- notilens task.timeout "msg" --agent <agent> [--task <id>]
382
- notilens task.cancel "msg" --agent <agent> [--task <id>]
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" --agent <agent> [--task <id>]
385
- notilens ai.response.generate "msg" --agent <agent>
386
- notilens ai.response.fail "msg" --agent <agent>
387
- notilens input.required "msg" --agent <agent>
388
- notilens input.approve "msg" --agent <agent>
389
- notilens input.reject "msg" --agent <agent>
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 Event:
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 <agent>
412
+ --agent <name>
399
413
  --task <id>
400
- --type success|warning|urgent|important|info
401
- --meta key=value (repeatable)
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-100>
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 addAgent(agent: string, transport: string, endpoint: string, secret: string): void;
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.addAgent = addAgent;
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 addAgent(agent, transport, endpoint, secret) {
63
+ function saveAgent(agent, token, secret) {
62
64
  const config = readConfig();
63
- config[agent] = { transport, endpoint, secret };
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
@@ -0,0 +1,3 @@
1
+ export { NotiLensAgent } from './agent';
2
+ export { saveAgent, getAgentConfig, removeAgent, listAgents } from './config';
3
+ //# sourceMappingURL=index.d.ts.map
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(endpoint: string, secret: string, payload: NotifyPayload): Promise<void>;
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(endpoint, secret, body) {
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 || (url.protocol === 'https:' ? 443 : 80),
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-CLI/${VERSION}`,
54
+ 'User-Agent': `NotiLens-SDK/${VERSION}`,
55
55
  'Content-Length': Buffer.byteLength(body),
56
56
  },
57
57
  };
58
- const req = lib.request(options, (res) => {
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(endpoint, secret, payload) {
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(endpoint, secret, body);
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)); // 1s retry delay
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
- transport: string;
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
- meta: Record<string, unknown>;
30
+ is_actionable: boolean;
32
31
  image_url: string;
33
32
  open_url: string;
34
33
  download_url: string;
35
34
  tags: string;
36
- is_actionable: boolean;
35
+ ts: number;
36
+ meta: Record<string, unknown>;
37
37
  }
38
- export type NotificationType = 'info' | 'success' | 'warning' | 'urgent' | 'important';
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.1.1",
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/cli.js",
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",