lmnr-cli 0.1.8 → 0.1.10
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 +30 -362
- package/dist/index.cjs +425 -1285
- package/dist/index.cjs.map +1 -1
- package/package.json +7 -19
package/README.md
CHANGED
|
@@ -1,395 +1,60 @@
|
|
|
1
1
|
# lmnr-cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
The Laminar CLI is a pure orchestrator that coordinates debugging sessions between your code and the Laminar backend. It is designed to be completely language-agnostic, delegating language-specific execution to worker processes.
|
|
3
|
+
CLI for the [Laminar](https://lmnr.ai) agent observability platform.
|
|
8
4
|
|
|
9
5
|
## Installation
|
|
10
6
|
|
|
11
7
|
```bash
|
|
12
8
|
# Run directly with npx
|
|
13
|
-
npx lmnr-cli@latest
|
|
9
|
+
npx lmnr-cli@latest <command>
|
|
14
10
|
|
|
15
11
|
# Or install globally
|
|
16
12
|
npm install -g lmnr-cli
|
|
17
|
-
lmnr-cli dev agent.ts
|
|
18
13
|
```
|
|
19
14
|
|
|
20
|
-
##
|
|
15
|
+
## Commands
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
lmnr-cli dev [file] [options]
|
|
24
|
-
# OR
|
|
25
|
-
lmnr-cli dev -m <python-module> [options]
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### Options
|
|
29
|
-
|
|
30
|
-
- `-m, --python-module <module>` - Python module path (e.g., `src.myfile`)
|
|
31
|
-
- `--function <name>` - Specific function to serve (if multiple entrypoint functions found)
|
|
32
|
-
- `--project-api-key <key>` - Project API key (or set `LMNR_PROJECT_API_KEY` env variable)
|
|
33
|
-
- `--base-url <url>` - Base URL for the Laminar API (default: https://api.lmnr.ai)
|
|
34
|
-
- `--port <port>` - Port for the Laminar API (default: 443)
|
|
35
|
-
- `--grpc-port <port>` - Port for the Laminar gRPC backend (default: 8443)
|
|
36
|
-
- `--frontend-port <port>` - Port for the Laminar frontend (default: 5667)
|
|
37
|
-
- `--external-packages <packages...>` - [ADVANCED] Packages to pass as external to esbuild
|
|
38
|
-
- `--dynamic-imports-to-skip <modules...>` - [ADVANCED] Dynamic imports to skip
|
|
39
|
-
- `--command <command>` - [ADVANCED] Custom worker command (e.g., `python3`, `node`)
|
|
40
|
-
- `--command-args <args...>` - [ADVANCED] Arguments for the custom command
|
|
17
|
+
### [`sql`](src/commands/sql/README.md) - SQL Queries
|
|
41
18
|
|
|
42
|
-
|
|
19
|
+
Run SQL queries against your Laminar project data (spans, traces, events, and more).
|
|
43
20
|
|
|
44
21
|
```bash
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# JavaScript (extracts parameter names at runtime)
|
|
49
|
-
npx lmnr-cli@latest dev agent.js
|
|
50
|
-
|
|
51
|
-
# Python - Script mode
|
|
52
|
-
npx lmnr-cli@latest dev agent.py
|
|
53
|
-
|
|
54
|
-
# Python - Module mode
|
|
55
|
-
npx lmnr-cli@latest dev -m src.agent
|
|
22
|
+
lmnr-cli sql query "SELECT * FROM spans LIMIT 10" --json
|
|
23
|
+
lmnr-cli sql schema # Show available tables
|
|
56
24
|
```
|
|
57
25
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
The CLI orchestrates three components:
|
|
26
|
+
### [`dev`](src/commands/dev/README.md) - Agent Debugger
|
|
61
27
|
|
|
62
|
-
|
|
63
|
-
2. **Cache Server**: Local HTTP server (port 35667) that provides cached spans to workers
|
|
64
|
-
3. **Worker Process**: Language-specific subprocess that executes your code
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
┌─────────────────┐
|
|
68
|
-
│ Laminar API │
|
|
69
|
-
└────────┬────────┘
|
|
70
|
-
│ SSE
|
|
71
|
-
│
|
|
72
|
-
┌────────▼────────┐ ┌──────────────┐
|
|
73
|
-
│ CLI Process │◄────►│ Cache Server │
|
|
74
|
-
│ (Orchestrator) │ │ (HTTP:35667)│
|
|
75
|
-
└────────┬────────┘ └──────▲───────┘
|
|
76
|
-
│ │
|
|
77
|
-
│ spawn │ HTTP
|
|
78
|
-
│ │
|
|
79
|
-
┌────────▼────────┐ ┌──────┴───────┐
|
|
80
|
-
│ Worker Process │─────►│ Your Code │
|
|
81
|
-
│ (TS/Python/etc) │ │ (agent.ts) │
|
|
82
|
-
└─────────────────┘ └──────────────┘
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Worker Protocol
|
|
86
|
-
|
|
87
|
-
The CLI communicates with workers using a simple JSON protocol over stdin/stdout.
|
|
88
|
-
|
|
89
|
-
### Input (via stdin)
|
|
90
|
-
|
|
91
|
-
The CLI sends a single JSON line to the worker's stdin containing the configuration:
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
{
|
|
95
|
-
filePath: string; // Path to the file to execute
|
|
96
|
-
functionName?: string; // Specific function to run
|
|
97
|
-
args: Record<string, any> | any[]; // Arguments for the function
|
|
98
|
-
env: Record<string, string>; // Environment variables
|
|
99
|
-
cacheServerPort: number; // Port of the cache server
|
|
100
|
-
baseUrl: string; // Laminar API base URL
|
|
101
|
-
projectApiKey?: string; // Project API key
|
|
102
|
-
httpPort: number; // HTTP port for Laminar API
|
|
103
|
-
grpcPort: number; // gRPC port for Laminar API
|
|
104
|
-
externalPackages?: string[]; // External packages (TS-specific)
|
|
105
|
-
dynamicImportsToSkip?: string[]; // Dynamic imports to skip (TS-specific)
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Output (via stdout)
|
|
110
|
-
|
|
111
|
-
Workers send messages prefixed with `__LMNR_WORKER__:` to stdout:
|
|
112
|
-
|
|
113
|
-
#### Log Message
|
|
114
|
-
```typescript
|
|
115
|
-
{
|
|
116
|
-
type: 'log';
|
|
117
|
-
level: 'info' | 'debug' | 'error' | 'warn';
|
|
118
|
-
message: string;
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
#### Result Message
|
|
123
|
-
```typescript
|
|
124
|
-
{
|
|
125
|
-
type: 'result';
|
|
126
|
-
data: any; // Function return value
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
#### Error Message
|
|
131
|
-
```typescript
|
|
132
|
-
{
|
|
133
|
-
type: 'error';
|
|
134
|
-
error: string; // Error message
|
|
135
|
-
stack?: string; // Stack trace
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### User Output
|
|
140
|
-
|
|
141
|
-
Any stdout without the `__LMNR_WORKER__:` prefix is considered user output (e.g., `console.log()`) and passed through transparently.
|
|
142
|
-
|
|
143
|
-
### Environment Variables
|
|
144
|
-
|
|
145
|
-
The CLI sets these environment variables for the worker:
|
|
146
|
-
|
|
147
|
-
- `LMNR_ROLLOUT_SESSION_ID`: Session ID for this debugger run
|
|
148
|
-
- `LMNR_ROLLOUT_STATE_SERVER_ADDRESS`: Cache server URL (http://localhost:35667)
|
|
149
|
-
|
|
150
|
-
### Cache Server API
|
|
151
|
-
|
|
152
|
-
Workers can query the cache server for cached spans:
|
|
153
|
-
|
|
154
|
-
**Endpoint**: `POST /cached`
|
|
155
|
-
|
|
156
|
-
**Request**:
|
|
157
|
-
```json
|
|
158
|
-
{
|
|
159
|
-
"path": "string", // Span path
|
|
160
|
-
"index": number // Span index
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**Response**:
|
|
165
|
-
```json
|
|
166
|
-
{
|
|
167
|
-
"span": {
|
|
168
|
-
"name": "string",
|
|
169
|
-
"input": "string", // JSON string
|
|
170
|
-
"output": "string", // JSON string
|
|
171
|
-
"attributes": {} // Parsed attributes
|
|
172
|
-
},
|
|
173
|
-
"pathToCount": {
|
|
174
|
-
"path1": 5,
|
|
175
|
-
"path2": 3
|
|
176
|
-
},
|
|
177
|
-
"overrides": {
|
|
178
|
-
"spanName": {
|
|
179
|
-
"system": "string or array",
|
|
180
|
-
"tools": [{
|
|
181
|
-
"name": "string",
|
|
182
|
-
"description": "string",
|
|
183
|
-
"parameters": {}
|
|
184
|
-
}]
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## Implementing a Worker
|
|
191
|
-
|
|
192
|
-
To support a new language, implement a worker that:
|
|
193
|
-
|
|
194
|
-
1. Reads JSON configuration from stdin
|
|
195
|
-
2. Sets environment variables from `config.env`
|
|
196
|
-
3. Loads and executes the specified function
|
|
197
|
-
4. Initializes your SDK's tracing (reads `LMNR_ROLLOUT_STATE_SERVER_ADDRESS`)
|
|
198
|
-
5. Sends protocol messages to stdout (prefixed with `__LMNR_WORKER__:`)
|
|
199
|
-
6. Exits with code 0 on success, non-zero on failure
|
|
200
|
-
|
|
201
|
-
### Example: TypeScript Worker
|
|
202
|
-
|
|
203
|
-
The TypeScript worker is included in `@lmnr-ai/lmnr`:
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
// Read config from stdin
|
|
207
|
-
const config = JSON.parse(await readStdin());
|
|
208
|
-
|
|
209
|
-
// Set environment variables
|
|
210
|
-
for (const [key, value] of Object.entries(config.env)) {
|
|
211
|
-
process.env[key] = value;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Initialize SDK
|
|
215
|
-
Laminar.initialize({
|
|
216
|
-
baseUrl: config.baseUrl,
|
|
217
|
-
projectApiKey: config.projectApiKey,
|
|
218
|
-
// ...
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// Load and execute function
|
|
222
|
-
const module = require(config.filePath);
|
|
223
|
-
const result = await module[config.functionName](...config.args);
|
|
224
|
-
|
|
225
|
-
// Send result
|
|
226
|
-
sendMessage({ type: 'result', data: result });
|
|
227
|
-
|
|
228
|
-
// Exit successfully
|
|
229
|
-
process.exit(0);
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Example: Python Worker (Future)
|
|
233
|
-
|
|
234
|
-
```python
|
|
235
|
-
# python -m lmnr.cli.worker
|
|
236
|
-
|
|
237
|
-
import json
|
|
238
|
-
import sys
|
|
239
|
-
import os
|
|
240
|
-
|
|
241
|
-
# Read config from stdin
|
|
242
|
-
config = json.loads(sys.stdin.readline())
|
|
243
|
-
|
|
244
|
-
# Set environment variables
|
|
245
|
-
for key, value in config['env'].items():
|
|
246
|
-
os.environ[key] = value
|
|
247
|
-
|
|
248
|
-
# Initialize SDK
|
|
249
|
-
import lmnr
|
|
250
|
-
lmnr.initialize(
|
|
251
|
-
base_url=config['baseUrl'],
|
|
252
|
-
project_api_key=config['projectApiKey'],
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
# Load and execute function
|
|
256
|
-
module = __import__(config['filePath'])
|
|
257
|
-
result = getattr(module, config['functionName'])(*config['args'])
|
|
258
|
-
|
|
259
|
-
# Send result
|
|
260
|
-
send_message({'type': 'result', 'data': result})
|
|
261
|
-
|
|
262
|
-
# Exit successfully
|
|
263
|
-
sys.exit(0)
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## Default Worker Resolution
|
|
267
|
-
|
|
268
|
-
The CLI automatically resolves workers based on file extension:
|
|
269
|
-
|
|
270
|
-
| Extension | Worker Command |
|
|
271
|
-
|-----------|----------------|
|
|
272
|
-
| `.ts`, `.tsx` | `node @lmnr-ai/lmnr/dist/cli/worker.cjs` |
|
|
273
|
-
| `.js`, `.mjs`, `.cjs` | `node @lmnr-ai/lmnr/dist/cli/worker.cjs` |
|
|
274
|
-
| `.py` | `python3 -m lmnr.cli.worker` |
|
|
275
|
-
|
|
276
|
-
Custom workers can be specified with `--command` and `--command-args`.
|
|
277
|
-
|
|
278
|
-
## Python Language Support
|
|
279
|
-
|
|
280
|
-
### Metadata Discovery
|
|
281
|
-
|
|
282
|
-
The CLI needs to discover function metadata (name and parameters) to provide a rich UI experience. For Python files, this is done by calling the `lmnr` Python package.
|
|
283
|
-
|
|
284
|
-
#### Required Command Interface
|
|
285
|
-
|
|
286
|
-
The Python `lmnr` package must implement the following CLI command:
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
lmnr discover --file <filepath> [--function <name>]
|
|
290
|
-
# OR
|
|
291
|
-
lmnr discover --module <module> [--function <name>]
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
**Arguments:**
|
|
295
|
-
|
|
296
|
-
- `--file <filepath>`: Path to the Python file containing entrypoint functions (script mode)
|
|
297
|
-
- `--module <module>`: Python module path like `src.myfile` (module mode)
|
|
298
|
-
- `--function <name>`: (Optional) Specific function name to discover. If omitted, discover all.
|
|
299
|
-
|
|
300
|
-
**Note:** Exactly one of `--file` or `--module` must be provided.
|
|
301
|
-
|
|
302
|
-
**Output Format (JSON to stdout):**
|
|
303
|
-
|
|
304
|
-
```json
|
|
305
|
-
{
|
|
306
|
-
"name": "my_agent",
|
|
307
|
-
"params": [
|
|
308
|
-
{
|
|
309
|
-
"name": "query",
|
|
310
|
-
"type": "string"
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
"name": "max_tokens",
|
|
314
|
-
"type": "number"
|
|
315
|
-
}
|
|
316
|
-
]
|
|
317
|
-
}
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
**Exit Codes:**
|
|
321
|
-
|
|
322
|
-
- `0`: Success
|
|
323
|
-
- Non-zero: Error (stderr contains error message)
|
|
324
|
-
|
|
325
|
-
#### Python Execution Modes
|
|
326
|
-
|
|
327
|
-
Python code can be executed in two modes, affecting how imports work:
|
|
328
|
-
|
|
329
|
-
**Script Mode (default):**
|
|
330
|
-
|
|
331
|
-
```bash
|
|
332
|
-
npx lmnr-cli dev src/myfile.py
|
|
333
|
-
# Internally executes as: python src/myfile.py
|
|
334
|
-
# Discovery uses: lmnr discover --file src/myfile.py
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
- Works with relative imports: `from .utils import helper`
|
|
338
|
-
- File is executed as a script
|
|
339
|
-
- Best for standalone files and simple packages
|
|
340
|
-
|
|
341
|
-
**Module Mode:**
|
|
28
|
+
Start a language-agnostic debugging session for your AI agents. Connects to the Laminar backend, spawns a worker process for your code, and orchestrates the debugging flow.
|
|
342
29
|
|
|
343
30
|
```bash
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
31
|
+
lmnr-cli dev agent.ts # TypeScript file
|
|
32
|
+
lmnr-cli dev agent.py # Python script mode
|
|
33
|
+
lmnr-cli dev -m src.agent # Python module mode
|
|
34
|
+
lmnr-cli dev agent.ts --function myAgent # Specific function
|
|
347
35
|
```
|
|
348
36
|
|
|
349
|
-
|
|
350
|
-
- Module is executed via Python's `-m` flag
|
|
351
|
-
- Best for packages with complex import structures
|
|
352
|
-
- Requires proper `__init__.py` files in package directories
|
|
37
|
+
### [`dataset`](src/commands/dataset/README.md) - Dataset Management
|
|
353
38
|
|
|
354
|
-
|
|
39
|
+
List, push, pull, and create datasets in your Laminar project.
|
|
355
40
|
|
|
356
41
|
```bash
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
npx lmnr-cli dev -m agents.customer_support
|
|
362
|
-
|
|
363
|
-
# Specify a particular function
|
|
364
|
-
npx lmnr-cli dev -m agents.customer_support --function run_agent
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
### Python Worker Requirements
|
|
368
|
-
|
|
369
|
-
The Python `lmnr` package must implement a worker that executes entrypoint functions:
|
|
370
|
-
|
|
371
|
-
```bash
|
|
372
|
-
python -m lmnr.cli.worker
|
|
42
|
+
lmnr-cli dataset list --json # List all datasets
|
|
43
|
+
lmnr-cli dataset push data.jsonl -n my-dataset --json # Push data to a dataset
|
|
44
|
+
lmnr-cli dataset pull output.jsonl -n my-dataset --json # Pull data from a dataset
|
|
45
|
+
lmnr-cli dataset create my-dataset data.jsonl -o out.jsonl
|
|
373
46
|
```
|
|
374
47
|
|
|
375
|
-
|
|
48
|
+
## Global Options
|
|
376
49
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
3. Initialize the `lmnr` SDK
|
|
380
|
-
4. Load and execute the specified function based on the mode
|
|
381
|
-
5. Send protocol messages to stdout (see [Worker Protocol](#worker-protocol))
|
|
382
|
-
6. Exit with appropriate code
|
|
50
|
+
- `-v, --version` - Display version number
|
|
51
|
+
- `-h, --help` - Display help
|
|
383
52
|
|
|
384
|
-
|
|
53
|
+
Each command also accepts:
|
|
385
54
|
|
|
386
|
-
|
|
387
|
-
-
|
|
388
|
-
-
|
|
389
|
-
|
|
390
|
-
The worker must detect which field is present and execute accordingly.
|
|
391
|
-
|
|
392
|
-
See the [Worker Protocol](#worker-protocol) section for detailed communication protocol.
|
|
55
|
+
- `--project-api-key <key>` - Project API key (or set `LMNR_PROJECT_API_KEY` env variable)
|
|
56
|
+
- `--base-url <url>` - Base URL for the Laminar API (default: https://api.lmnr.ai)
|
|
57
|
+
- `--port <port>` - Port for the Laminar API (default: 443)
|
|
393
58
|
|
|
394
59
|
## Development
|
|
395
60
|
|
|
@@ -401,7 +66,10 @@ pnpm install
|
|
|
401
66
|
pnpm build
|
|
402
67
|
|
|
403
68
|
# Run locally
|
|
404
|
-
node dist/index.cjs
|
|
69
|
+
node dist/index.cjs <command>
|
|
70
|
+
|
|
71
|
+
# Run tests
|
|
72
|
+
pnpm test
|
|
405
73
|
```
|
|
406
74
|
|
|
407
75
|
## License
|