lam-cli 0.1.2__tar.gz → 0.1.4__tar.gz

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.
lam_cli-0.1.4/PKG-INFO ADDED
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: lam-cli
3
+ Version: 0.1.4
4
+ Summary: Secure data transformation tool supporting JQ and JavaScript (Bun)
5
+ Home-page: https://github.com/laminar-run/lam
6
+ Author: Laminar Run, Inc.
7
+ Author-email: connect@laminar.run
8
+ License: GPLv3
9
+ Project-URL: Documentation, https://docs.laminar.run
10
+ Project-URL: Source, https://github.com/laminar-run/lam
11
+ Project-URL: Issue Tracker, https://github.com/laminar-run/lam/issues
12
+ Keywords: laminar,api,integration,transformation,json,jq,javascript,bun
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24
+ Classifier: Topic :: Software Development :: Build Tools
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: backoff>=2.2.1
29
+ Requires-Dist: certifi>=2024.12.14
30
+ Requires-Dist: charset-normalizer>=3.3.2
31
+ Requires-Dist: click>=8.1.7
32
+ Requires-Dist: idna>=3.7
33
+ Requires-Dist: logtail-python>=0.2.2
34
+ Requires-Dist: monotonic>=1.6
35
+ Requires-Dist: msgpack>=1.0.8
36
+ Requires-Dist: posthog>=3.4.0
37
+ Requires-Dist: psutil>=5.9.0
38
+ Requires-Dist: python-dateutil>=2.8.2
39
+ Requires-Dist: requests>=2.32.3
40
+ Requires-Dist: six>=1.16.0
41
+ Requires-Dist: urllib3>=2.2.2
42
+ Dynamic: author
43
+ Dynamic: author-email
44
+ Dynamic: classifier
45
+ Dynamic: description-content-type
46
+ Dynamic: home-page
47
+ Dynamic: keywords
48
+ Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: project-url
51
+ Dynamic: requires-dist
52
+ Dynamic: requires-python
53
+ Dynamic: summary
@@ -8,7 +8,7 @@ import socket
8
8
  import subprocess
9
9
  import sys
10
10
  import tempfile
11
- from datetime import datetime
11
+ from datetime import datetime, timezone
12
12
  from enum import Enum
13
13
  from pathlib import Path
14
14
  from typing import Any, Dict, Optional, Tuple, Union
@@ -22,9 +22,12 @@ from posthog import Posthog
22
22
  posthog = Posthog(project_api_key='phc_wfeHFG0p5yZIdBpjVYy00o5x1HbEpggdMzIuFYgNPSK',
23
23
  host='https://app.posthog.com')
24
24
 
25
- logging.basicConfig(level=logging.INFO,
25
+ # Configure logging with UTC timezone
26
+ logging.Formatter.converter = lambda *args: datetime.now(timezone.utc).timetuple()
27
+ logging.basicConfig(level=logging.DEBUG,
26
28
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
27
29
  logger = logging.getLogger(__name__)
30
+ logging.getLogger('urllib3').setLevel(logging.WARNING) # Suppress urllib3 logs
28
31
 
29
32
  handler = LogtailHandler(source_token="TYz3WrrvC8ehYjXdAEGGyiDp")
30
33
  logger.addHandler(handler)
@@ -47,20 +50,20 @@ class ResourceLimitError(LAMError):
47
50
 
48
51
  def check_resource_limits(modules_dir: Optional[Path] = None) -> None:
49
52
  """Check system resource availability"""
50
- # Check disk space
53
+ logger.debug("Checking system resource limits")
51
54
  disk = shutil.disk_usage(tempfile.gettempdir())
52
55
  if disk.free < 100 * 1024 * 1024: # 100MB minimum
56
+ logger.critical("Insufficient disk space: %dMB free", disk.free // (1024*1024))
53
57
  raise ResourceLimitError("Insufficient disk space")
54
58
 
55
- # Check shared modules size if provided
56
59
  if modules_dir and modules_dir.exists():
57
60
  modules_size = sum(
58
61
  os.path.getsize(os.path.join(dirpath, filename))
59
62
  for dirpath, _, filenames in os.walk(modules_dir)
60
63
  for filename in filenames
61
64
  )
62
- if modules_size > 500 * 1024 * 1024: # 500MB limit
63
- logger.warning("Shared modules exceeding size limit, cleaning up")
65
+ if modules_size > 500 * 1024 * 1024:
66
+ logger.warning("Cleaning oversized modules directory (%dMB)", modules_size//(1024*1024))
64
67
  shutil.rmtree(modules_dir)
65
68
  modules_dir.mkdir(exist_ok=True)
66
69
 
@@ -71,7 +74,6 @@ class Stats:
71
74
  self.memory_start = self.get_memory_usage()
72
75
 
73
76
  def get_memory_usage(self):
74
- import psutil
75
77
  process = psutil.Process()
76
78
  return process.memory_info().rss
77
79
 
@@ -120,17 +122,17 @@ class Engine:
120
122
  class JQEngine(Engine):
121
123
  """JQ execution engine"""
122
124
  def validate_environment(self) -> bool:
125
+ logger.debug("Validating JQ environment")
123
126
  return shutil.which("jq") is not None
124
127
 
125
128
  def execute(self, program_file: str, input_data: str) -> Tuple[Union[Dict, str], Optional[str]]:
126
129
  logger.info(f"Executing JQ script: {program_file}")
127
130
 
128
131
  try:
129
- # Parse JQ program
130
132
  with open(program_file, 'r') as file:
131
133
  jq_script = ''.join(line for line in file if not line.strip().startswith('#'))
134
+ logger.debug("Loaded JQ script: %d characters", len(jq_script))
132
135
 
133
- # Run JQ
134
136
  process = subprocess.Popen(
135
137
  ["jq", "-c", jq_script],
136
138
  stdin=subprocess.PIPE,
@@ -139,59 +141,82 @@ class JQEngine(Engine):
139
141
  text=True
140
142
  )
141
143
 
144
+ logger.debug("Starting JQ process PID %d", process.pid)
142
145
  output, error = process.communicate(input=input_data)
143
146
 
144
147
  if error:
148
+ logger.error("JQ error output: %s", error.strip())
145
149
  raise ProcessingError(error)
146
150
 
147
151
  # Handle output
148
152
  try:
149
- return json.loads(output), None
150
- except json.JSONDecodeError:
153
+ output_lines = [line.strip() for line in output.splitlines() if line.strip()]
154
+ logger.debug(f"Found {len(output_lines)} JSON objects in output")
155
+
156
+ if len(output_lines) > 1:
157
+ parsed = [json.loads(line) for line in output_lines]
158
+ logger.info(f"Processed {len(parsed)} JSON objects")
159
+ return {"lam.result": parsed}, None
160
+ elif len(output_lines) == 1:
161
+ result = json.loads(output_lines[0])
162
+ logger.info("Processed single JSON object")
163
+ return result, None
164
+ else:
165
+ logger.info("No JSON objects in output")
166
+ return {"lam.error": "No JSON objects in output"}, "No JSON objects in output"
167
+
168
+ except json.JSONDecodeError as e:
151
169
  return {"lam.result": output}, None
152
170
 
153
171
  except Exception as e:
172
+ logger.exception("JQ execution failed")
154
173
  self.track_event('lam.jq.error', {'error': str(e)})
155
174
  return {"lam.error": str(e)}, str(e)
156
175
 
157
176
  class BunEngine(Engine):
158
- """Bun JavaScript execution engine"""
177
+ """Bun JavaScript execution engine with enhanced logging"""
159
178
  def __init__(self, *args, **kwargs):
160
179
  super().__init__(*args, **kwargs)
161
- # Create a persistent temp directory for node_modules
162
180
  self.modules_dir = Path(tempfile.gettempdir()) / "lam_modules"
163
181
  self.modules_dir.mkdir(exist_ok=True)
164
182
  self._setup_shared_modules()
165
183
 
166
184
  self.runtime_template = '''
167
- // Secure runtime environment
168
- globalThis.process = undefined;
169
- globalThis.Deno = undefined;
170
- globalThis.fetch = undefined;
185
+ const logs = [];
186
+ const originalLog = console.log;
187
+ const originalError = console.error;
188
+ const originalWarn = console.warn;
171
189
 
172
- import _ from 'lodash';
173
- import { format, parseISO } from 'date-fns';
190
+ console.log = (...args) => logs.push({ type: 'log', message: args.map(String).join(' ') });
191
+ console.error = (...args) => {
192
+ originalError(...args); // Keep error output for debugging
193
+ logs.push({ type: 'error', message: args.map(String).join(' ') });
194
+ };
195
+ console.warn = (...args) => logs.push({ type: 'warn', message: args.map(String).join(' ') });
174
196
 
175
- // Safe console methods
176
- const secureConsole = {
177
- log: console.log,
178
- error: console.error,
179
- warn: console.warn
197
+ // Keep original stdout for result output
198
+ const writeResult = (obj) => {
199
+ console.error("Writing result:", JSON.stringify(obj, null, 2));
200
+ originalLog(JSON.stringify(obj));
180
201
  };
181
- globalThis.console = secureConsole;
182
202
 
183
- // Expose safe utilities
184
- globalThis._ = _;
185
- globalThis.format = format;
186
- globalThis.parseISO = parseISO;
203
+ const _ = require('lodash');
204
+ const { format, parseISO } = require('date-fns');
205
+
206
+ module.exports = {
207
+ _,
208
+ format,
209
+ parseISO,
210
+ logs,
211
+ writeResult
212
+ };
187
213
  '''
188
-
214
+
189
215
  def _setup_shared_modules(self):
190
216
  """Setup shared node_modules once"""
191
217
  if not (self.modules_dir / "node_modules").exists():
192
- # Create package.json
218
+ logger.info("Initializing shared modules directory")
193
219
  package_json = {
194
- "type": "module",
195
220
  "dependencies": {
196
221
  "lodash": "^4.17.21",
197
222
  "date-fns": "^2.30.0"
@@ -200,26 +225,25 @@ class BunEngine(Engine):
200
225
  with open(self.modules_dir / "package.json", "w") as f:
201
226
  json.dump(package_json, f, indent=2)
202
227
 
203
- # Install dependencies once
204
228
  try:
205
- subprocess.run(
229
+ logger.debug("Installing shared dependencies")
230
+ result = subprocess.run(
206
231
  [self.get_bun_path(), "install"],
207
232
  cwd=self.modules_dir,
208
233
  check=True,
209
234
  capture_output=True,
210
235
  text=True,
211
- timeout=30 # Reasonable timeout for installation
236
+ timeout=30
212
237
  )
238
+ logger.debug("Dependency install output: %s", result.stdout)
213
239
  except subprocess.CalledProcessError as e:
214
- logger.error(f"Failed to install shared dependencies: {e.stderr}")
215
- raise ProcessingError(
216
- f"Failed to set up JavaScript environment: {e.stderr}"
217
- ) from e
240
+ logger.error("Dependency install failed: %s", e.stderr)
241
+ raise ProcessingError(f"Environment setup failed: {e.stderr}") from e
218
242
 
219
243
  def create_wrapper(self, input_data: str, user_script: str) -> str:
220
244
  """Create the wrapper script with proper escaping"""
221
245
  return f'''
222
- import './runtime.js';
246
+ const {{ _, format, parseISO, logs, writeResult }} = require('./runtime.js');
223
247
 
224
248
  // Utility function to handle circular references in JSON.stringify
225
249
  function safeStringify(obj) {{
@@ -250,7 +274,7 @@ class BunEngine(Engine):
250
274
  // Parse input safely
251
275
  let input;
252
276
  try {{
253
- input = {input_data};
277
+ input = JSON.parse({json.dumps(input_data)});
254
278
  }} catch (e) {{
255
279
  throw new Error(`Failed to parse input data: ${{e.message}}`);
256
280
  }}
@@ -269,17 +293,15 @@ class BunEngine(Engine):
269
293
  // Execute transform
270
294
  const result = transform(input);
271
295
 
272
- // Validate result is serializable
273
- try {{
274
- const serialized = safeStringify(result);
275
- console.log(serialized);
276
- }} catch (e) {{
277
- throw new Error(`Result is not JSON serializable: ${{e.message}}`);
278
- }}
296
+ // Output result after transform
297
+ writeResult({{
298
+ result,
299
+ logs
300
+ }});
279
301
  }} catch (error) {{
280
302
  console.error(JSON.stringify({{
281
303
  error: error.message,
282
- stack: error.stack,
304
+ stack: error.stack?.split('\\n') || [],
283
305
  type: error.constructor.name
284
306
  }}));
285
307
  process.exit(1);
@@ -292,10 +314,11 @@ class BunEngine(Engine):
292
314
  runtime_path = temp_dir / "runtime.js"
293
315
  with open(runtime_path, "w") as f:
294
316
  f.write(self.runtime_template)
317
+ logger.debug("Runtime file written to: %s", runtime_path)
295
318
 
296
319
  # Symlink node_modules from shared directory
297
320
  os.symlink(self.modules_dir / "node_modules", temp_dir / "node_modules")
298
-
321
+ logger.debug("node_modules symlinked from: %s", self.modules_dir / "node_modules")
299
322
 
300
323
  def validate_environment(self) -> bool:
301
324
  # Check multiple locations for bun
@@ -335,22 +358,23 @@ class BunEngine(Engine):
335
358
  # Read user script
336
359
  with open(program_file, 'r') as f:
337
360
  user_script = f.read()
361
+ logger.debug("Loaded user script: %d characters", len(user_script))
338
362
 
339
363
  # Create wrapper script
340
364
  wrapper = self.create_wrapper(input_data, user_script)
341
-
342
365
  script_path = temp_dir / "script.js"
343
- with open(script_path, "w") as f:
366
+ with open(script_path, 'w') as f:
344
367
  f.write(wrapper)
368
+ logger.debug("Generated wrapper script: %s", script_path)
345
369
 
346
370
  # Execute with Bun
347
371
  process = subprocess.Popen(
348
372
  [
349
373
  self.get_bun_path(),
350
374
  "run",
351
- "--no-fetch", # Disable network
352
- "--smol", # Reduced memory
353
- "--silent", # Reduce Bun's own error noise
375
+ "--no-fetch",
376
+ "--smol",
377
+ "--silent",
354
378
  str(script_path)
355
379
  ],
356
380
  stdout=subprocess.PIPE,
@@ -358,36 +382,64 @@ class BunEngine(Engine):
358
382
  text=True,
359
383
  cwd=temp_dir
360
384
  )
385
+ logger.info("Started Bun process PID %d", process.pid)
361
386
 
362
387
  try:
363
- output, error = process.communicate(timeout=5) # 5 second timeout
388
+ output, error = process.communicate(timeout=5)
389
+ logger.debug("Process completed with code %d", process.returncode)
364
390
  except subprocess.TimeoutExpired as e:
391
+ logger.warning("Process timeout after 5 seconds")
365
392
  process.kill()
366
- raise ProcessingError("Script execution timed out") from e
393
+ return {"lam.error": "Script execution timed out"}, "Execution timed out after 5 seconds"
367
394
 
368
- if error:
395
+ # Handle process errors
396
+ if process.returncode != 0:
369
397
  try:
370
- error_data = json.loads(error)
398
+ # Try to parse structured error from stderr
399
+ error_data = json.loads(error.strip())
371
400
  error_msg = error_data.get('error', 'Unknown error')
372
- if error_data.get('stack'):
373
- error_msg = f"{error_msg}\nStack trace:\n{error_data['stack']}"
401
+ stack = error_data.get('stack', [])
402
+
403
+ # Format error message
404
+ error_details = {
405
+ "lam.error": error_msg,
406
+ "stack_trace": stack
407
+ }
408
+ return error_details, error_msg
409
+
374
410
  except json.JSONDecodeError:
375
- error_msg = error.split('\n')[0] # Just take the first line if not JSON
376
- raise ProcessingError(error_msg)
411
+ # Fallback to raw error output
412
+ error_msg = error.strip() or "Unknown error"
413
+ return {"lam.error": error_msg}, error_msg
377
414
 
415
+ # Handle successful output
378
416
  try:
379
- return json.loads(output), None
380
- except json.JSONDecodeError:
381
- return {"lam.result": output}, None
417
+ output_data = json.loads(output)
418
+
419
+ # Process JavaScript logs (if any)
420
+ if 'logs' in output_data:
421
+ for log_entry in output_data.get('logs', []):
422
+ if log_entry['type'] == 'error':
423
+ logger.error("[JS] %s", log_entry['message'])
424
+ else:
425
+ logger.debug("[JS] %s", log_entry['message'])
426
+
427
+ result = output_data.get('result', {})
428
+ return result, None
429
+
430
+ except json.JSONDecodeError as e:
431
+ logger.error("Failed to parse output: %s", str(e))
432
+ return {
433
+ "lam.error": "Invalid JSON output",
434
+ "raw_output": output.strip()
435
+ }, "Output format error"
382
436
 
383
437
  except Exception as e:
384
- stats_data = stats.finalize()
385
- self.track_event('lam.bun.error', {
386
- 'error': str(e),
387
- 'error_type': e.__class__.__name__,
388
- **stats_data
389
- })
390
- return {"lam.error": str(e)}, str(e)
438
+ logger.exception("Execution failed")
439
+ return {
440
+ "lam.error": str(e),
441
+ "type": e.__class__.__name__
442
+ }, str(e)
391
443
 
392
444
  def get_engine(engine_type: str, workspace_id: str, flow_id: str, execution_id: str) -> Engine:
393
445
  """Factory function to get the appropriate execution engine"""
@@ -409,13 +461,16 @@ def get_engine(engine_type: str, workspace_id: str, flow_id: str, execution_id:
409
461
  def process_input(input: str) -> Tuple[str, Optional[str]]:
410
462
  """Process and validate input data"""
411
463
  if os.path.isfile(input):
464
+ logger.debug("Loading input from file: %s", input)
412
465
  with open(input, 'r') as file:
413
466
  return file.read(), None
414
467
 
415
468
  try:
416
469
  json.loads(input)
470
+ logger.debug("Validated inline JSON input")
417
471
  return input, None
418
472
  except json.JSONDecodeError as e:
473
+ logger.error("Invalid JSON input: %s", str(e))
419
474
  return None, str(e)
420
475
 
421
476
  @click.group()
@@ -435,16 +490,14 @@ def lam():
435
490
  def run(program_file: str, input: str, language: str, workspace_id: str,
436
491
  flow_id: str, execution_id: str, as_json: bool):
437
492
  """Execute a LAM transformation script"""
438
- stats = Stats() # Start tracking stats at the top level
493
+ stats = Stats()
439
494
 
440
- # Initialize engine
441
495
  try:
442
496
  engine = get_engine(language, workspace_id, flow_id, execution_id)
443
497
  except (ValueError, EnvironmentError) as e:
444
498
  click.echo({"lam.error": str(e)}, err=True)
445
499
  return
446
500
 
447
- # Setup logging
448
501
  log_file = engine.get_log_file()
449
502
  result_file = engine.get_result_file()
450
503
 
@@ -452,25 +505,22 @@ def run(program_file: str, input: str, language: str, workspace_id: str,
452
505
  file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
453
506
  logger.addHandler(file_handler)
454
507
 
455
- logger.info(f"Starting LAM execution with {language} engine")
508
+ logger.info("Starting LAM execution with %s engine", language)
456
509
  engine.track_event('lam.run.start', {
457
510
  'language': language,
458
511
  'program_file': program_file
459
512
  })
460
513
 
461
514
  try:
462
- # Process input
463
515
  input_data, error = process_input(input)
464
516
  if error:
465
517
  raise ProcessingError(f"Invalid input: {error}")
466
518
 
467
- # Execute transformation
468
519
  result, error = engine.execute(program_file, input_data)
469
520
 
470
- # Get final stats
471
521
  stats_data = stats.finalize()
472
- logger.info(f"Execution stats: duration={stats_data['duration_ms']:.2f}ms, "
473
- f"memory_used={stats_data['memory_used_mb']:.2f}MB")
522
+ logger.info("Execution stats: duration=%.2fms, memory=%.2fMB",
523
+ stats_data['duration_ms'], stats_data['memory_used_mb'])
474
524
 
475
525
  if error:
476
526
  click.echo({"lam.error": error}, err=True)
@@ -480,15 +530,6 @@ def run(program_file: str, input: str, language: str, workspace_id: str,
480
530
  click.echo(output)
481
531
  engine.track_event('lam.run.success', stats_data)
482
532
 
483
- # Save result with stats
484
- result_with_stats = {
485
- 'result': result,
486
- 'stats': stats_data,
487
- 'error': error or None
488
- }
489
- logger.info(f"Final stats: {stats_data}")
490
-
491
- # If result is an array, wrap it in "lam.result"
492
533
  if isinstance(result, list):
493
534
  result = {"lam.result": result}
494
535
 
@@ -497,9 +538,9 @@ def run(program_file: str, input: str, language: str, workspace_id: str,
497
538
 
498
539
  except Exception as e:
499
540
  stats_data = stats.finalize()
500
- logger.error(f"Execution failed: {e}")
501
- logger.error(f"Final stats: duration={stats_data['duration_ms']:.2f}ms, "
502
- f"memory_used={stats_data['memory_used_mb']:.2f}MB")
541
+ logger.error("Execution failed: %s", str(e))
542
+ logger.error("Final stats: duration=%.2fms, memory=%.2fMB",
543
+ stats_data['duration_ms'], stats_data['memory_used_mb'])
503
544
  click.echo({"lam.error": str(e)}, err=True)
504
545
  engine.track_event('lam.run.error', {'error': str(e), **stats_data})
505
546
 
@@ -508,4 +549,4 @@ def run(program_file: str, input: str, language: str, workspace_id: str,
508
549
  logger.removeHandler(file_handler)
509
550
 
510
551
  if __name__ == '__main__':
511
- lam()
552
+ lam()
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: lam-cli
3
+ Version: 0.1.4
4
+ Summary: Secure data transformation tool supporting JQ and JavaScript (Bun)
5
+ Home-page: https://github.com/laminar-run/lam
6
+ Author: Laminar Run, Inc.
7
+ Author-email: connect@laminar.run
8
+ License: GPLv3
9
+ Project-URL: Documentation, https://docs.laminar.run
10
+ Project-URL: Source, https://github.com/laminar-run/lam
11
+ Project-URL: Issue Tracker, https://github.com/laminar-run/lam/issues
12
+ Keywords: laminar,api,integration,transformation,json,jq,javascript,bun
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24
+ Classifier: Topic :: Software Development :: Build Tools
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: backoff>=2.2.1
29
+ Requires-Dist: certifi>=2024.12.14
30
+ Requires-Dist: charset-normalizer>=3.3.2
31
+ Requires-Dist: click>=8.1.7
32
+ Requires-Dist: idna>=3.7
33
+ Requires-Dist: logtail-python>=0.2.2
34
+ Requires-Dist: monotonic>=1.6
35
+ Requires-Dist: msgpack>=1.0.8
36
+ Requires-Dist: posthog>=3.4.0
37
+ Requires-Dist: psutil>=5.9.0
38
+ Requires-Dist: python-dateutil>=2.8.2
39
+ Requires-Dist: requests>=2.32.3
40
+ Requires-Dist: six>=1.16.0
41
+ Requires-Dist: urllib3>=2.2.2
42
+ Dynamic: author
43
+ Dynamic: author-email
44
+ Dynamic: classifier
45
+ Dynamic: description-content-type
46
+ Dynamic: home-page
47
+ Dynamic: keywords
48
+ Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: project-url
51
+ Dynamic: requires-dist
52
+ Dynamic: requires-python
53
+ Dynamic: summary
lam_cli-0.1.4/setup.py ADDED
@@ -0,0 +1,62 @@
1
+ from pathlib import Path
2
+
3
+ from setuptools import find_packages, setup
4
+
5
+ # Read docs.md for long description
6
+ docs_path = Path(__file__).parent / "docs.md"
7
+ long_description = docs_path.read_text() if docs_path.exists() else ""
8
+
9
+ setup(
10
+ name="lam-cli",
11
+ version="0.1.4",
12
+ packages=find_packages(),
13
+ install_requires=[
14
+ "backoff>=2.2.1",
15
+ "certifi>=2024.12.14",
16
+ "charset-normalizer>=3.3.2",
17
+ "click>=8.1.7",
18
+ "idna>=3.7",
19
+ "logtail-python>=0.2.2",
20
+ "monotonic>=1.6",
21
+ "msgpack>=1.0.8",
22
+ "posthog>=3.4.0",
23
+ "psutil>=5.9.0",
24
+ "python-dateutil>=2.8.2",
25
+ "requests>=2.32.3",
26
+ "six>=1.16.0",
27
+ "urllib3>=2.2.2",
28
+ ],
29
+ entry_points={
30
+ "console_scripts": [
31
+ "laminar=lam.lam:lam",
32
+ ],
33
+ },
34
+ python_requires=">=3.9",
35
+ license="GPLv3",
36
+ author="Laminar Run, Inc.",
37
+ author_email="connect@laminar.run",
38
+ description="Secure data transformation tool supporting JQ and JavaScript (Bun)",
39
+ long_description=long_description,
40
+ long_description_content_type="text/markdown",
41
+ url="https://github.com/laminar-run/lam",
42
+ project_urls={
43
+ "Documentation": "https://docs.laminar.run",
44
+ "Source": "https://github.com/laminar-run/lam",
45
+ "Issue Tracker": "https://github.com/laminar-run/lam/issues",
46
+ },
47
+ classifiers=[
48
+ "Development Status :: 4 - Beta",
49
+ "Environment :: Console",
50
+ "Intended Audience :: Developers",
51
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
52
+ "Operating System :: OS Independent",
53
+ "Programming Language :: Python :: 3",
54
+ "Programming Language :: Python :: 3.9",
55
+ "Programming Language :: Python :: 3.10",
56
+ "Programming Language :: Python :: 3.11",
57
+ "Topic :: Software Development :: Libraries :: Python Modules",
58
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
59
+ "Topic :: Software Development :: Build Tools",
60
+ ],
61
+ keywords="laminar, api, integration, transformation, json, jq, javascript, bun",
62
+ )
lam_cli-0.1.2/PKG-INFO DELETED
@@ -1,240 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: lam-cli
3
- Version: 0.1.2
4
- Summary: Secure data transformation tool supporting JQ and JavaScript (Bun)
5
- Home-page: https://github.com/laminar-run/lam
6
- Author: Laminar Run, Inc.
7
- Author-email: connect@laminar.run
8
- License: GPLv3
9
- Project-URL: Documentation, https://docs.laminar.run
10
- Project-URL: Source, https://github.com/laminar-run/lam
11
- Project-URL: Issue Tracker, https://github.com/laminar-run/lam/issues
12
- Keywords: laminar,api,integration,transformation,json,jq,javascript,bun
13
- Classifier: Development Status :: 4 - Beta
14
- Classifier: Environment :: Console
15
- Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
- Classifier: Operating System :: OS Independent
18
- Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.9
20
- Classifier: Programming Language :: Python :: 3.10
21
- Classifier: Programming Language :: Python :: 3.11
22
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
- Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24
- Classifier: Topic :: Software Development :: Build Tools
25
- Requires-Python: >=3.9
26
- Description-Content-Type: text/markdown
27
- License-File: LICENSE
28
- Requires-Dist: backoff>=2.2.1
29
- Requires-Dist: certifi>=2024.12.14
30
- Requires-Dist: charset-normalizer>=3.3.2
31
- Requires-Dist: click>=8.1.7
32
- Requires-Dist: idna>=3.7
33
- Requires-Dist: logtail-python>=0.2.2
34
- Requires-Dist: monotonic>=1.6
35
- Requires-Dist: msgpack>=1.0.8
36
- Requires-Dist: posthog>=3.4.0
37
- Requires-Dist: psutil>=5.9.0
38
- Requires-Dist: python-dateutil>=2.8.2
39
- Requires-Dist: requests>=2.32.3
40
- Requires-Dist: six>=1.16.0
41
- Requires-Dist: urllib3>=2.2.2
42
- Dynamic: author
43
- Dynamic: author-email
44
- Dynamic: classifier
45
- Dynamic: description
46
- Dynamic: description-content-type
47
- Dynamic: home-page
48
- Dynamic: keywords
49
- Dynamic: license
50
- Dynamic: project-url
51
- Dynamic: requires-dist
52
- Dynamic: requires-python
53
- Dynamic: summary
54
-
55
- # lam
56
- Lam is a data transformation tool for Laminar that supports both `jq` and JavaScript transformations using Bun.
57
-
58
- ## Quickstart
59
- Install the dependencies:
60
- ```bash
61
- # For JQ support
62
- brew install jq # or sudo apt-get install jq
63
-
64
- # For JavaScript support
65
- curl -fsSL https://bun.sh/install | bash
66
-
67
- make setup
68
- ```
69
-
70
- Run the CLI tool:
71
- ```bash
72
- make cli ARGS="run <program> <input> [--language jq|js]"
73
- ```
74
-
75
- ## Features
76
- - JQ transformations (default)
77
- - JavaScript transformations with Bun runtime
78
- - Built-in utilities (lodash, date-fns)
79
- - Resource monitoring and limits
80
- - Detailed execution statistics
81
- - Secure execution environment
82
-
83
- ## Examples
84
-
85
- ### JQ Transform
86
- ```bash
87
- make cli ARGS="run examples/transform.jq data.json"
88
- ```
89
-
90
- ### JavaScript Transform
91
- ```bash
92
- make cli ARGS="run examples/transform.js data.json --language js"
93
- ```
94
-
95
- Example JavaScript transform:
96
- ```javascript
97
- (input) => {
98
- // Lodash available as _
99
- return _.map(input.data, item => ({
100
- value: item.value * 2
101
- }));
102
- }
103
- ```
104
-
105
- ## Installation
106
-
107
- ### Docker Installation
108
- ```dockerfile
109
- # Install lam-cli
110
- RUN pip3 install git+https://${GITHUB_USER}:${GITHUB_TOKEN}@github.com/user/project.git@{version}
111
-
112
- # Install dependencies
113
- RUN apt-get update && apt-get install -y jq
114
- RUN curl -fsSL https://bun.sh/install | bash
115
- ```
116
-
117
- ### Manual Setup
118
- Create a virtual environment and install dependencies:
119
- ```bash
120
- python3 -m venv ./venv
121
- source ./venv/bin/activate
122
- pip install -r requirements.txt
123
- ```
124
-
125
- ## Usage
126
- ```bash
127
- # Basic usage
128
- python3 ./lam/lam.py run <program> <input>
129
-
130
- # With JavaScript
131
- python3 ./lam/lam.py run script.js data.json --language js
132
-
133
- # Full options
134
- python3 ./lam/lam.py run <program> <input> \
135
- --language [jq|js] \
136
- --workspace_id <id> \
137
- --flow_id <id> \
138
- --execution_id <id> \
139
- [--as-json]
140
- ```
141
-
142
- ## Resource Limits
143
- - Maximum input size: 10MB
144
- - Execution timeout: 5 seconds
145
- - Memory limits enabled
146
- - Disk space monitoring
147
-
148
- ## Security
149
- - Sandboxed JavaScript execution
150
- - Network access disabled
151
- - Limited global scope
152
- - Resource monitoring
153
- - Secure dependency management
154
-
155
- ## Logging and Monitoring
156
- - Execution statistics (duration, memory usage)
157
- - Detailed error tracking
158
- - PostHog analytics integration
159
- - Log file generation
160
-
161
- ## Development
162
- ```bash
163
- # Run all tests
164
- make test
165
-
166
- # Run specific test suite
167
- make test-jq
168
- make test-js
169
- make test-js-edge-cases
170
-
171
- # Run single test
172
- make test-single TEST=test/js/example.js DATA=test/data/input.json
173
- ```
174
-
175
- ## Releases
176
- Update version in `setup.py`:
177
- ```python
178
- setup(
179
- name="lam-cli",
180
- version="0.0.<x>",
181
- ...
182
- )
183
- ```
184
-
185
- Create and push tag:
186
- ```bash
187
- git tag v<version>-<increment>
188
- git push origin v<version>-<increment>
189
- ```
190
-
191
- ## Dependencies
192
- Update dependencies:
193
- ```bash
194
- pip3 install <package>
195
- pip3 freeze > requirements.txt
196
- ```
197
-
198
- ## Install Locally
199
-
200
- ```bash
201
- pip3 install -e .
202
- ```
203
-
204
- ## Troubleshooting
205
-
206
- ### Package Installation Issues
207
- If you encounter issues installing the package with pip, particularly with the certifi dependency, you may see errors like:
208
-
209
- ```bash
210
- error: uninstall-no-record-file
211
- × Cannot uninstall certifi None
212
- ╰─> The package's contents are unknown: no RECORD file was found for certifi.
213
- ```
214
- This can happen when multiple Python versions are installed or when system packages have been modified. Try these steps:
215
-
216
- First, identify which Python version you're using:
217
-
218
- ```bash
219
- which python3
220
- python3 --version
221
- which pip3
222
- ```
223
-
224
- Remove the problematic certifi installation (adjust path based on your Python version):
225
-
226
- ```bash
227
- sudo rm -rf /usr/local/lib/python3.13/site-packages/certifi
228
- ```
229
-
230
- Install certifi directly:
231
-
232
- ```bash
233
- pip3 install --ignore-installed certifi --break-system-packages
234
- ```
235
-
236
- Then try installing lam-cli again:
237
-
238
- ```bash
239
- pip3 install . --break-system-packages
240
- ```
@@ -1,240 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: lam-cli
3
- Version: 0.1.2
4
- Summary: Secure data transformation tool supporting JQ and JavaScript (Bun)
5
- Home-page: https://github.com/laminar-run/lam
6
- Author: Laminar Run, Inc.
7
- Author-email: connect@laminar.run
8
- License: GPLv3
9
- Project-URL: Documentation, https://docs.laminar.run
10
- Project-URL: Source, https://github.com/laminar-run/lam
11
- Project-URL: Issue Tracker, https://github.com/laminar-run/lam/issues
12
- Keywords: laminar,api,integration,transformation,json,jq,javascript,bun
13
- Classifier: Development Status :: 4 - Beta
14
- Classifier: Environment :: Console
15
- Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
- Classifier: Operating System :: OS Independent
18
- Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.9
20
- Classifier: Programming Language :: Python :: 3.10
21
- Classifier: Programming Language :: Python :: 3.11
22
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
- Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24
- Classifier: Topic :: Software Development :: Build Tools
25
- Requires-Python: >=3.9
26
- Description-Content-Type: text/markdown
27
- License-File: LICENSE
28
- Requires-Dist: backoff>=2.2.1
29
- Requires-Dist: certifi>=2024.12.14
30
- Requires-Dist: charset-normalizer>=3.3.2
31
- Requires-Dist: click>=8.1.7
32
- Requires-Dist: idna>=3.7
33
- Requires-Dist: logtail-python>=0.2.2
34
- Requires-Dist: monotonic>=1.6
35
- Requires-Dist: msgpack>=1.0.8
36
- Requires-Dist: posthog>=3.4.0
37
- Requires-Dist: psutil>=5.9.0
38
- Requires-Dist: python-dateutil>=2.8.2
39
- Requires-Dist: requests>=2.32.3
40
- Requires-Dist: six>=1.16.0
41
- Requires-Dist: urllib3>=2.2.2
42
- Dynamic: author
43
- Dynamic: author-email
44
- Dynamic: classifier
45
- Dynamic: description
46
- Dynamic: description-content-type
47
- Dynamic: home-page
48
- Dynamic: keywords
49
- Dynamic: license
50
- Dynamic: project-url
51
- Dynamic: requires-dist
52
- Dynamic: requires-python
53
- Dynamic: summary
54
-
55
- # lam
56
- Lam is a data transformation tool for Laminar that supports both `jq` and JavaScript transformations using Bun.
57
-
58
- ## Quickstart
59
- Install the dependencies:
60
- ```bash
61
- # For JQ support
62
- brew install jq # or sudo apt-get install jq
63
-
64
- # For JavaScript support
65
- curl -fsSL https://bun.sh/install | bash
66
-
67
- make setup
68
- ```
69
-
70
- Run the CLI tool:
71
- ```bash
72
- make cli ARGS="run <program> <input> [--language jq|js]"
73
- ```
74
-
75
- ## Features
76
- - JQ transformations (default)
77
- - JavaScript transformations with Bun runtime
78
- - Built-in utilities (lodash, date-fns)
79
- - Resource monitoring and limits
80
- - Detailed execution statistics
81
- - Secure execution environment
82
-
83
- ## Examples
84
-
85
- ### JQ Transform
86
- ```bash
87
- make cli ARGS="run examples/transform.jq data.json"
88
- ```
89
-
90
- ### JavaScript Transform
91
- ```bash
92
- make cli ARGS="run examples/transform.js data.json --language js"
93
- ```
94
-
95
- Example JavaScript transform:
96
- ```javascript
97
- (input) => {
98
- // Lodash available as _
99
- return _.map(input.data, item => ({
100
- value: item.value * 2
101
- }));
102
- }
103
- ```
104
-
105
- ## Installation
106
-
107
- ### Docker Installation
108
- ```dockerfile
109
- # Install lam-cli
110
- RUN pip3 install git+https://${GITHUB_USER}:${GITHUB_TOKEN}@github.com/user/project.git@{version}
111
-
112
- # Install dependencies
113
- RUN apt-get update && apt-get install -y jq
114
- RUN curl -fsSL https://bun.sh/install | bash
115
- ```
116
-
117
- ### Manual Setup
118
- Create a virtual environment and install dependencies:
119
- ```bash
120
- python3 -m venv ./venv
121
- source ./venv/bin/activate
122
- pip install -r requirements.txt
123
- ```
124
-
125
- ## Usage
126
- ```bash
127
- # Basic usage
128
- python3 ./lam/lam.py run <program> <input>
129
-
130
- # With JavaScript
131
- python3 ./lam/lam.py run script.js data.json --language js
132
-
133
- # Full options
134
- python3 ./lam/lam.py run <program> <input> \
135
- --language [jq|js] \
136
- --workspace_id <id> \
137
- --flow_id <id> \
138
- --execution_id <id> \
139
- [--as-json]
140
- ```
141
-
142
- ## Resource Limits
143
- - Maximum input size: 10MB
144
- - Execution timeout: 5 seconds
145
- - Memory limits enabled
146
- - Disk space monitoring
147
-
148
- ## Security
149
- - Sandboxed JavaScript execution
150
- - Network access disabled
151
- - Limited global scope
152
- - Resource monitoring
153
- - Secure dependency management
154
-
155
- ## Logging and Monitoring
156
- - Execution statistics (duration, memory usage)
157
- - Detailed error tracking
158
- - PostHog analytics integration
159
- - Log file generation
160
-
161
- ## Development
162
- ```bash
163
- # Run all tests
164
- make test
165
-
166
- # Run specific test suite
167
- make test-jq
168
- make test-js
169
- make test-js-edge-cases
170
-
171
- # Run single test
172
- make test-single TEST=test/js/example.js DATA=test/data/input.json
173
- ```
174
-
175
- ## Releases
176
- Update version in `setup.py`:
177
- ```python
178
- setup(
179
- name="lam-cli",
180
- version="0.0.<x>",
181
- ...
182
- )
183
- ```
184
-
185
- Create and push tag:
186
- ```bash
187
- git tag v<version>-<increment>
188
- git push origin v<version>-<increment>
189
- ```
190
-
191
- ## Dependencies
192
- Update dependencies:
193
- ```bash
194
- pip3 install <package>
195
- pip3 freeze > requirements.txt
196
- ```
197
-
198
- ## Install Locally
199
-
200
- ```bash
201
- pip3 install -e .
202
- ```
203
-
204
- ## Troubleshooting
205
-
206
- ### Package Installation Issues
207
- If you encounter issues installing the package with pip, particularly with the certifi dependency, you may see errors like:
208
-
209
- ```bash
210
- error: uninstall-no-record-file
211
- × Cannot uninstall certifi None
212
- ╰─> The package's contents are unknown: no RECORD file was found for certifi.
213
- ```
214
- This can happen when multiple Python versions are installed or when system packages have been modified. Try these steps:
215
-
216
- First, identify which Python version you're using:
217
-
218
- ```bash
219
- which python3
220
- python3 --version
221
- which pip3
222
- ```
223
-
224
- Remove the problematic certifi installation (adjust path based on your Python version):
225
-
226
- ```bash
227
- sudo rm -rf /usr/local/lib/python3.13/site-packages/certifi
228
- ```
229
-
230
- Install certifi directly:
231
-
232
- ```bash
233
- pip3 install --ignore-installed certifi --break-system-packages
234
- ```
235
-
236
- Then try installing lam-cli again:
237
-
238
- ```bash
239
- pip3 install . --break-system-packages
240
- ```
lam_cli-0.1.2/setup.py DELETED
@@ -1,62 +0,0 @@
1
- from pathlib import Path
2
-
3
- from setuptools import find_packages, setup
4
-
5
- # Read the README for the long description
6
- this_directory = Path(__file__).parent
7
- long_description = (this_directory / "README.md").read_text()
8
-
9
- setup(
10
- name='lam-cli',
11
- version='0.1.2',
12
- packages=find_packages(),
13
- install_requires=[
14
- 'backoff>=2.2.1',
15
- 'certifi>=2024.12.14',
16
- 'charset-normalizer>=3.3.2',
17
- 'click>=8.1.7',
18
- 'idna>=3.7',
19
- 'logtail-python>=0.2.2',
20
- 'monotonic>=1.6',
21
- 'msgpack>=1.0.8',
22
- 'posthog>=3.4.0',
23
- 'psutil>=5.9.0',
24
- 'python-dateutil>=2.8.2',
25
- 'requests>=2.32.3',
26
- 'six>=1.16.0',
27
- 'urllib3>=2.2.2'
28
- ],
29
- entry_points={
30
- 'console_scripts': [
31
- 'laminar=lam.lam:lam',
32
- ],
33
- },
34
- python_requires='>=3.9',
35
- license='GPLv3',
36
- author='Laminar Run, Inc.',
37
- author_email='connect@laminar.run',
38
- description='Secure data transformation tool supporting JQ and JavaScript (Bun)',
39
- long_description=long_description,
40
- long_description_content_type='text/markdown',
41
- url='https://github.com/laminar-run/lam',
42
- project_urls={
43
- 'Documentation': 'https://docs.laminar.run',
44
- 'Source': 'https://github.com/laminar-run/lam',
45
- 'Issue Tracker': 'https://github.com/laminar-run/lam/issues',
46
- },
47
- classifiers=[
48
- 'Development Status :: 4 - Beta',
49
- 'Environment :: Console',
50
- 'Intended Audience :: Developers',
51
- 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
52
- 'Operating System :: OS Independent',
53
- 'Programming Language :: Python :: 3',
54
- 'Programming Language :: Python :: 3.9',
55
- 'Programming Language :: Python :: 3.10',
56
- 'Programming Language :: Python :: 3.11',
57
- 'Topic :: Software Development :: Libraries :: Python Modules',
58
- 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
59
- 'Topic :: Software Development :: Build Tools',
60
- ],
61
- keywords='laminar, api, integration, transformation, json, jq, javascript, bun',
62
- )
File without changes
File without changes
File without changes
File without changes