npm package: @notilens/notilens
Description: NotiLens SDK + CLI — send AI agent task lifecycle notifications
Author: NotiLens
Latest version: 0.4.1

# NotiLens

Send notifications from AI agents and any Node.js project to [NotiLens](https://www.notilens.com).

Two ways to use it — pick one or both:

- **CLI** — for shell scripts, bash pipelines, Claude Code hooks, any terminal workflow
- **SDK** — for Node.js/TypeScript projects (import and call directly in code)

## Installation

```bash
npm install -g @notilens/notilens
```

Or per-project:

```bash
npm install @notilens/notilens
```

---

# CLI

## Setup

Get your token and secret from the [NotiLens dashboard](https://www.notilens.com).

```bash
notilens init --agent my-agent --token YOUR_TOKEN --secret YOUR_SECRET
```

This saves credentials to `~/.notilens_config.json`. All future commands read from there — no need to pass token/secret again.

**Multiple agents** (each agent notifies a different topic):
```bash
notilens init --agent scraper --token TOKEN_A --secret SECRET_A
notilens init --agent mailer  --token TOKEN_B --secret SECRET_B
```

**List / remove agents:**
```bash
notilens agents
notilens remove-agent my-agent
```

---

## Commands

`--task` is a semantic label (e.g. `email`, `report`). Each `task.start` creates an isolated run internally — concurrent executions of the same label never conflict.

### Task Lifecycle

```bash
notilens queue    --agent my-agent --task email
notilens start    --agent my-agent --task email
notilens progress "Fetching data"  --agent my-agent --task email
notilens loop     "Step 3 of 10"   --agent my-agent --task email
notilens retry    --agent my-agent --task email
notilens pause    "Rate limited"   --agent my-agent --task email
notilens resume   "Resuming"       --agent my-agent --task email
notilens wait     "Awaiting tool"  --agent my-agent --task email
notilens stop     --agent my-agent --task email
notilens complete "All done"       --agent my-agent --task email
notilens error    "Step 3 failed"  --agent my-agent --task email
notilens fail     "Unrecoverable"  --agent my-agent --task email
notilens timeout  "Took too long"  --agent my-agent --task email
notilens cancel   "User cancelled" --agent my-agent --task email
notilens terminate "Out of memory" --agent my-agent --task email
```

`task.start` prints the internal `run_id` to stdout. You can capture it if needed — but for sequential scripts, just use `--task LABEL` and the SDK handles the rest automatically.

### Output Events

```bash
notilens output.generate "Report ready"     --agent my-agent --task email
notilens output.fail     "Model unavailable" --agent my-agent --task email
```

### Input / Human-in-the-loop

```bash
notilens input.required "Please confirm the output" --agent my-agent --task email
notilens input.approve  "Confirmed"                 --agent my-agent --task email
notilens input.reject   "Rejected"                  --agent my-agent --task email
```

### Generic Event

```bash
notilens track order.placed "Order #1234" --agent my-agent --meta amount=99.99
notilens track disk.space.full "Only 1GB remaining" --agent my-agent --type warning
```

### Metrics

Pass any key=value pairs — numeric values accumulate across calls:

```bash
notilens metric tokens=512 cost=0.003 --agent my-agent --task email
notilens metric records=1500          --agent my-agent --task email

# Reset one metric
notilens metric.reset tokens --agent my-agent --task email

# Reset all metrics
notilens metric.reset --agent my-agent --task email
```

---

## Claude Code Hooks Example

```json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Task",
      "hooks": [{
        "type": "command",
        "command": "notilens start --agent claude --task $TASK_ID"
      }]
    }],
    "PostToolUse": [{
      "matcher": "Task",
      "hooks": [{
        "type": "command",
        "command": "notilens complete \"Task done\" --agent claude --task $TASK_ID"
      }]
    }],
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "notilens complete \"Session ended\" --agent claude --task session"
      }]
    }],
    "Notification": [{
      "hooks": [{
        "type": "command",
        "command": "notilens input.required \"Claude needs input\" --agent claude --task session"
      }]
    }]
  }
}
```

---

## Options

| Flag | Description |
|------|-------------|
| `--agent <name>` | Agent name **(required)** |
| `--task <label>` | Task label (e.g. `email`, `report`) |
| `--type` | Override type: `info` `success` `warning` `urgent` |
| `--meta key=value` | Custom metadata (repeatable) |
| `--image_url <url>` | Attach an image |
| `--open_url <url>` | Link to open |
| `--download_url <url>` | Link to download |
| `--tags "tag1,tag2"` | Comma-separated tags |
| `--is_actionable true\|false` | Override actionable flag |

---

## Full Example

```bash
notilens init --agent summarizer --token MY_TOKEN --secret MY_SECRET

notilens start --agent summarizer --task report
notilens metric tokens=1024 cost=0.004 --agent summarizer --task report
notilens complete "Summary ready" --agent summarizer --task report \
  --meta input_file=report.pdf \
  --open_url https://example.com/summary.pdf
```

---

# SDK

Use the SDK to send notifications directly from your Node.js or TypeScript code.

## 1. Setup

```typescript
import { NotiLens } from '@notilens/notilens';

// Option A — pass credentials directly
const agent = NotiLens.init('my-agent', { token: 'YOUR_TOKEN', secret: 'YOUR_SECRET' });

// Option B — read from environment variables
// NOTILENS_TOKEN=xxx NOTILENS_SECRET=yyy
const agent = NotiLens.init('my-agent');

// Option C — read from saved CLI config (~/.notilens_config.json)
const agent = NotiLens.init('my-agent');
```

All init options:
```typescript
const agent = NotiLens.init('my-agent', {
  token:    'YOUR_TOKEN',   // required (or env var)
  secret:   'YOUR_SECRET',  // required (or env var)
  stateTtl: 86400,          // optional — orphaned state TTL in seconds (default: 86400 / 24h)
});
```

## 2. Task Lifecycle

`agent.task(label)` creates a `Run` — an isolated execution context with its own state. Multiple concurrent runs of the same label never conflict.

```typescript
const run = agent.task('email');   // create a run for the "email" task
run.queue();                        // optional — pre-start signal
run.start();                        // begin the run

run.progress('Fetching records');
run.loop('Processing batch 2');
run.retry();
run.pause('Waiting for rate limit');    // non-terminal warning
run.resume('Resuming work');            // non-terminal info
run.wait('Waiting for tool response');  // non-terminal warning

run.error('Non-fatal error');           // task continues

run.complete('All done');           // terminal — clears state
run.fail('Unrecoverable');          // terminal
run.timeout('Exceeded 30s');        // terminal
run.cancel('User cancelled');       // terminal
run.terminate('OOM');               // terminal
run.stop();
```

## 3. Input / Human-in-the-loop

```typescript
run.inputRequired('Please confirm the output');
run.inputApproved('User confirmed');
run.inputRejected('User rejected');
```

## 4. Output Events

```typescript
run.outputGenerated('Summary ready');
run.outputFailed('Model timeout');
```

## 5. Metrics

Track any numeric or string values per run — accumulated automatically and included in every notification.

```typescript
run.metric('tokens', 350);    // set
run.metric('tokens', 210);    // now 560 (numeric values accumulate)
run.metric('cost', 0.0012);
run.metric('model', 'gpt-4'); // strings are replaced, not accumulated

run.resetMetrics('tokens');   // reset one metric
run.resetMetrics();           // reset all metrics
```

## Automatic Timing

NotiLens automatically tracks task timing. These fields are included in every notification's `meta` payload when non-zero:

| Field | Description |
|-------|-------------|
| `total_duration_ms` | Wall-clock time since `start` |
| `queue_ms` | Time between `queue` and `start` |
| `pause_ms` | Cumulative time spent paused |
| `wait_ms` | Cumulative time spent waiting |
| `active_ms` | Active time (`total − pause − wait`) |

---

## 6. Generic Events

```typescript
run.track('order.placed', 'Order #1234', { meta: { amount: 99.99 } });
run.track('disk.space.full', 'Only 1GB remaining', { level: 'warning' });
agent.track('user.registered', 'New signup', { meta: { plan: 'pro' } });
```

## Full Example

```typescript
import { NotiLens } from '@notilens/notilens';

const agent = NotiLens.init('summarizer', { token: 'TOKEN', secret: 'SECRET' });
const run   = agent.task('report');
run.start();

try {
  run.progress('Fetching PDF');

  const result = await llm.complete(prompt);
  run.metric('tokens', result.usage.totalTokens);
  run.metric('cost', result.usage.cost);

  run.outputGenerated('Summary ready');
  run.complete('All done');
} catch (err) {
  run.fail((err as Error).message);
}
```

---

## Requirements

- Node.js >= 18.0.0

## License

MIT — [notilens.com](https://www.notilens.com)