@arthovlabs/liber 0.7.5 → 0.7.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arthovlabs/liber",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "LiBER — autonomous agent orchestrator with a machine-enforced grounding gate and a skill library",
5
5
  "bin": {
6
6
  "liber": "bin/liber",
@@ -0,0 +1,108 @@
1
+ // resources/media/make-ad.mjs — Instagram (1080x1920, 30 fps) ad for LiBER.
2
+ //
3
+ // node resources/media/make-ad.mjs [out.mp4]
4
+ // -> artifacts/media/liber-instagram-ad.mp4
5
+ //
6
+ // Everything is produced by ffmpeg's own sources: no stock footage, no assets, no
7
+ // fonts beyond Windows'. Written in JS rather than PowerShell because a filtergraph
8
+ // is one long string full of ':' and quotes — passing it as a single argv element
9
+ // avoids every layer of shell escaping.
10
+ import { spawnSync } from 'node:child_process';
11
+ import { mkdirSync } from 'node:fs';
12
+ import path from 'node:path';
13
+
14
+ const out = process.argv[2] || path.join('artifacts', 'media', 'liber-instagram-ad.mp4');
15
+ const ffmpeg = process.env.FFMPEG || 'ffmpeg';
16
+ mkdirSync(path.dirname(out), { recursive: true });
17
+
18
+ const F = {
19
+ bold: 'C\\:/Windows/Fonts/segoeuib.ttf',
20
+ semi: 'C\\:/Windows/Fonts/seguisb.ttf',
21
+ mono: 'C\\:/Windows/Fonts/consola.ttf'
22
+ };
23
+ const C = { gold: '0xE5B25C', cyan: '0x39C5F0', red: '0xF0616D', green: '0x2FD07A', white: '0xFFFFFF' };
24
+
25
+ const esc = (s) => String(s).replace(/\\/g, '\\\\').replace(/:/g, '\\:').replace(/'/g, "\\'");
26
+
27
+ /** A layer that fades in at `at` and is only drawn between at..until. */
28
+ function layer({ text, font = F.bold, size, y, color = `${C.white}@0.95`, at, until, x = '(w-text_w)/2', fade = 0.35 }) {
29
+ return [
30
+ `drawtext=fontfile='${font}'`,
31
+ `text='${esc(text)}'`,
32
+ `fontsize=${size}`,
33
+ `fontcolor=${color}`,
34
+ `x=${x}`,
35
+ `y=${y}`,
36
+ `enable='between(t,${at},${until})'`,
37
+ `alpha='if(lt(t,${at}),0,min(1,(t-${at})/${fade}))'`
38
+ ].join(':');
39
+ }
40
+
41
+ const panel = (x, y, w, h, color) =>
42
+ `drawbox=x=${x}:y=${y}:w=${w}:h=${h}:color=${color}:t=fill:enable='between(t,7.2,14.0)'`;
43
+
44
+ const draw = [
45
+ // Hook
46
+ layer({ text: 'your code', size: 88, y: 820, at: 1.4, until: 4.4 }),
47
+ layer({ text: 'just failed', size: 88, y: 926, at: 2.0, until: 4.4 }),
48
+ layer({ text: 'again.', size: 156, y: 800, color: C.red, at: 4.4, until: 7.0 }),
49
+
50
+ // Terminal card
51
+ panel(90, 560, 900, 360, '0x101216@0.88'),
52
+ panel(90, 560, 900, 2, `${C.cyan}@0.55`),
53
+ layer({ text: '$ liber fix', font: F.mono, size: 40, color: C.cyan, x: 130, y: 606, at: 7.4, until: 14.0 }),
54
+ layer({ text: '[E1] terminal OK (exit 0)', font: F.mono, size: 34, color: `${C.white}@0.9`, x: 130, y: 686, at: 8.6, until: 14.0 }),
55
+ layer({ text: '[E2] file patched', font: F.mono, size: 34, color: `${C.white}@0.9`, x: 130, y: 746, at: 9.4, until: 14.0 }),
56
+ layer({ text: 'GROUNDING PASS · 12 receipts', font: F.mono, size: 34, color: C.green, x: 130, y: 812, at: 10.6, until: 14.0 }),
57
+
58
+ // Payoff beats over the card
59
+ layer({ text: 'LiBER reads it.', font: F.semi, size: 56, color: `${C.white}@0.92`, y: 1000, at: 7.6, until: 9.6 }),
60
+ layer({ text: 'fixes it.', size: 104, y: 1000, at: 9.6, until: 11.6 }),
61
+ layer({ text: '337 tests · 0 failing', font: F.mono, size: 34, color: `${C.white}@0.8`, y: 1170, at: 9.9, until: 11.6, fade: 0.5 }),
62
+ layer({ text: 'proves it.', size: 104, y: 1000, color: C.cyan, at: 11.6, until: 14.0 }),
63
+ layer({ text: 'every claim on a tool receipt', font: F.mono, size: 34, color: `${C.white}@0.55`, y: 1170, at: 11.9, until: 14.0, fade: 0.5 }),
64
+
65
+ // Wordmark
66
+ layer({ text: 'L i B E R', size: 176, y: 806, at: 14.4, until: 20, fade: 0.5 }),
67
+ `drawbox=x=96:y=790:w=10:h=210:color=${C.gold}@0.95:t=fill:enable='between(t,14.4,20)'`,
68
+ `drawbox=x=974:y=790:w=10:h=210:color=${C.gold}@0.95:t=fill:enable='between(t,14.4,20)'`,
69
+ layer({ text: 'no claim without a receipt', font: F.semi, size: 50, color: `${C.white}@0.85`, y: 1050, at: 15.4, until: 20 }),
70
+
71
+ // CTA
72
+ layer({ text: 'npm i -g @arthovlabs/liber', size: 46, color: C.cyan, y: 1230, at: 17.8, until: 20 }),
73
+ layer({ text: 'autonomous engineering agent', font: F.semi, size: 44, color: `${C.white}@0.8`, y: 1310, at: 17.8, until: 20 }),
74
+ layer({ text: 'withmerlin.online', font: F.mono, size: 34, color: `${C.white}@0.45`, y: 1780, at: 6.0, until: 20, fade: 0.8 }),
75
+ `drawbox=x=0:y=1912:w='1080*t/20':h=8:color=${C.cyan}@0.55:t=fill`
76
+ ].join(',');
77
+
78
+ const filter = [
79
+ // procedural background: a slow, breathing gradient with a grid and vignette
80
+ `[0:v]scale=1080:1920,zoompan=z='min(zoom+0.0005,1.10)':d=1:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1080x1920:fps=30,`,
81
+ `drawgrid=w=90:h=90:t=1:color=white@0.025,vignette=PI/5,`,
82
+ `${draw},`,
83
+ `fade=t=in:st=0:d=0.6,fade=t=out:st=19.75:d=0.25,format=yuv420p[v];`,
84
+ `[1:a][2:a]amix=inputs=2:duration=longest,volume=0.12,tremolo=f=0.2:d=0.5,`,
85
+ `afade=t=in:st=0:d=1.2,afade=t=out:st=18.4:d=1.6[a]`
86
+ ].join('');
87
+
88
+ const args = [
89
+ '-hide_banner', '-y',
90
+ '-f', 'lavfi', '-i', 'gradients=s=1080x1920:c0=0x0B1B3A:c1=0x070A10:c2=0x151F36:n=3:speed=0.015:d=20:r=30',
91
+ '-f', 'lavfi', '-i', 'sine=frequency=55:duration=20:sample_rate=48000',
92
+ '-f', 'lavfi', '-i', 'sine=frequency=110:duration=20:sample_rate=48000',
93
+ '-filter_complex', filter,
94
+ '-map', '[v]', '-map', '[a]',
95
+ '-t', '20',
96
+ '-c:v', 'libx264', '-preset', 'slow', '-crf', '19', '-pix_fmt', 'yuv420p',
97
+ '-c:a', 'aac', '-b:a', '128k',
98
+ '-movflags', '+faststart',
99
+ out
100
+ ];
101
+
102
+ process.stdout.write(`rendering ${out} …\n`);
103
+ const r = spawnSync(ffmpeg, args, { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
104
+ if (r.status !== 0) {
105
+ process.stderr.write((r.stderr || '').split('\n').slice(-8).join('\n') + '\n');
106
+ process.exit(r.status || 1);
107
+ }
108
+ process.stdout.write(`done: ${out}\n`);
@@ -0,0 +1,98 @@
1
+ # resources/media/make-ad.ps1 — Instagram (Reels/Stories) ad for LiBER.
2
+ #
3
+ # powershell -ExecutionPolicy Bypass -File resources\media\make-ad.ps1
4
+ # -> artifacts\media\liber-instagram-ad.mp4 (1080x1920, 30 fps, H.264 + AAC)
5
+ #
6
+ # Everything is generated from ffmpeg's own sources: no assets, no stock footage,
7
+ # no fonts beyond Windows'. Motion is procedural, so the file is reproducible and
8
+ # the script is the source of truth for the design.
9
+ param(
10
+ [string]$Out = "artifacts\media\liber-instagram-ad.mp4",
11
+ [string]$Ffmpeg = "ffmpeg"
12
+ )
13
+
14
+ $ErrorActionPreference = 'Stop'
15
+ $root = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
16
+ Set-Location $root
17
+ New-Item -ItemType Directory -Force -Path (Split-Path $Out) | Out-Null
18
+
19
+ $bold = 'C\:/Windows/Fonts/segoeuib.ttf'
20
+ $semi = 'C\:/Windows/Fonts/seguisb.ttf'
21
+ $mono = 'C\:/Windows/Fonts/consola.ttf'
22
+
23
+ # Brand
24
+ $gold = '0xE5B25C'
25
+ $cyan = '0x39C5F0'
26
+ $red = '0xF0616D'
27
+ $green= '0x2FD07A'
28
+ $white= 'white'
29
+
30
+ function Fade($start, $dur = 0.35) {
31
+ # alpha rises over $dur seconds starting at $start
32
+ "alpha='if(lt(t,$start),0,min(1,(t-$start)/$dur))'"
33
+ }
34
+
35
+ # Each entry: text, font, size, y, colour, in, out
36
+ $lines = @(
37
+ @{ t = 'your code'; f = $bold; s = 86; y = 780; c = "$white@0.95"; a = 1.6; b = 4.2 },
38
+ @{ t = 'just failed'; f = $bold; s = 86; y = 880; c = "$white@0.95"; a = 2.2; b = 4.2 },
39
+ @{ t = 'again.'; f = $bold; s = 150; y = 720; c = $red; a = 4.2; b = 7.0 },
40
+ @{ t = 'LiBER reads the failure.'; f = $semi; s = 58; y = 800; c = "$white@0.92"; a = 7.2; b = 9.6 },
41
+ @{ t = 'fixes it.'; f = $bold; s = 104; y = 790; c = $white; a = 9.6; b = 11.6 },
42
+ @{ t = 'proves it.'; f = $bold; s = 104; y = 790; c = $cyan; a = 11.6; b = 14.0 },
43
+ @{ t = 'no claim without a receipt'; f = $semi; s = 50; y = 1500; c = "$white@0.85"; a = 14.4; b = 18.4 }
44
+ )
45
+
46
+ $draw = @()
47
+ foreach ($l in $lines) {
48
+ $t = $l.t -replace ':', '\:'
49
+ $draw += "drawtext=fontfile='$($l.f)':text='$t':fontsize=$($l.s):fontcolor=$($l.c):x=(w-text_w)/2:y=$($l.y):enable='between(t,$($l.a),$($l.b))':" + (Fade $l.a)
50
+ }
51
+
52
+ # Terminal card: a rounded panel with three lines that appear one after another.
53
+ $draw += "drawbox=x=90:y=560:w=900:h=360:color=0x101216@0.88:t=fill:enable='between(t,7.2,14.0)'"
54
+ $draw += "drawbox=x=90:y=560:w=900:h=2:color=$cyan@0.5:t=fill:enable='between(t,7.2,14.0)'"
55
+ $draw += "drawtext=fontfile='$mono':text='\$ liber fix':fontsize=40:fontcolor=$cyan:x=130:y=610:enable='between(t,7.4,14.0)':" + (Fade 7.4)
56
+ $draw += "drawtext=fontfile='$mono':text='[E1] terminal OK (exit 0)':fontsize=34:fontcolor=$white@0.9:x=130:y=690:enable='between(t,8.6,14.0)':" + (Fade 8.6)
57
+ $draw += "drawtext=fontfile='$mono':text='[E2] file patched':fontsize=34:fontcolor=$white@0.9:x=130:y=750:enable='between(t,9.4,14.0)':" + (Fade 9.4)
58
+ $draw += "drawtext=fontfile='$mono':text='GROUNDING PASS - 12 receipts':fontsize=34:fontcolor=$green:x=130:y=820:enable='between(t,10.6,14.0)':" + (Fade 10.6)
59
+
60
+ # Wordmark and CTA
61
+ $draw += "drawtext=fontfile='$bold':text='L i B E R':fontsize=190:fontcolor=$white:x=(w-text_w)/2:y=700:enable='between(t,14.4,18.6)':" + (Fade 14.4)
62
+ $draw += "drawtext=fontfile='$bold':text='<':fontsize=150:fontcolor=$gold:x=150:y=736:enable='between(t,14.4,18.6)':" + (Fade 14.4)
63
+ $draw += "drawtext=fontfile='$bold':text='>':fontsize=150:fontcolor=$gold:x=800:y=736:enable='between(t,14.4,18.6)':" + (Fade 14.4)
64
+
65
+ $draw += "drawtext=fontfile='$bold':text='npm i -g @arthovlabs/liber':fontsize=46:fontcolor=$cyan:x=(w-text_w)/2:y=1120:enable='between(t,18.4,20)':" + (Fade 18.4)
66
+ $draw += "drawtext=fontfile='$semi':text='autonomous engineering agent':fontsize=44:fontcolor=$white@0.8:x=(w-text_w)/2:y=1200:enable='between(t,0.4,4.2)+between(t,18.4,20)':" + (Fade 0.4)
67
+
68
+ $chain = ($draw -join ',')
69
+
70
+ $filter = @"
71
+ [0:v]scale=1080:1920,
72
+ zoompan=z='min(zoom+0.0006,1.12)':d=1:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1080x1920:fps=30,
73
+ drawgrid=w=90:h=90:t=1:color=white@0.03,
74
+ vignette=PI/5,
75
+ $chain,
76
+ fade=t=in:st=0:d=0.6,fade=t=out:st=19.2:d=0.8,
77
+ format=yuv420p[v];
78
+ [1:a]volume=0.16,tremolo=f=0.25:d=0.6,afade=t=in:st=0:d=1.2,afade=t=out:st=18.6:d=1.4[a]
79
+ "@
80
+
81
+ $args = @(
82
+ '-hide_banner', '-y',
83
+ '-f', 'lavfi', '-i', "gradients=s=1080x1920:c0=0x0B1B3A:c1=0x070A10:c2=0x121A2E:n=3:speed=0.02:d=20:r=30",
84
+ '-f', 'lavfi', '-i', "sine=frequency=55:duration=20:sample_rate=48000",
85
+ '-f', 'lavfi', '-i', "sine=frequency=110:duration=20:sample_rate=48000",
86
+ '-filter_complex', $filter,
87
+ '-map', '[v]', '-map', '[a]',
88
+ '-t', '20',
89
+ '-c:v', 'libx264', '-preset', 'slow', '-crf', '19', '-pix_fmt', 'yuv420p',
90
+ '-c:a', 'aac', '-b:a', '128k',
91
+ '-movflags', '+faststart',
92
+ $Out
93
+ )
94
+
95
+ Write-Host "rendering $Out …"
96
+ & $Ffmpeg @args 2>&1 | Select-Object -Last 4
97
+ if ($LASTEXITCODE -ne 0) { Write-Host "ffmpeg failed ($LASTEXITCODE)" -ForegroundColor Red; exit 1 }
98
+ Write-Host "done: $Out"
@@ -21,12 +21,12 @@ const unauth = guard.createThrottle();
21
21
  // A shell that polls without a token used to write one log line per request: 12,239
22
22
  // of them in a day. Keep the signal, drop the flood.
23
23
  const authFailLog = new Map();
24
- function logAuthFailure(endpoint, ip) {
24
+ function logAuthFailure(endpoint, ip, ua) {
25
25
  const key = endpoint + '|' + ip;
26
26
  const now = Date.now();
27
27
  const prev = authFailLog.get(key) || { at: 0, count: 0 };
28
28
  if (now - prev.at > 60000) {
29
- logger.warn('SYSTEM', 'AUTH_FAILED', { endpoint, ip, suppressed_since_last: prev.count });
29
+ logger.warn('SYSTEM', 'AUTH_FAILED', { endpoint, ip, ua: String(ua || '').slice(0, 60), suppressed_since_last: prev.count });
30
30
  authFailLog.set(key, { at: now, count: 0 });
31
31
  } else {
32
32
  prev.count += 1;
@@ -434,7 +434,7 @@ class LiberDaemon {
434
434
  success: false,
435
435
  error: 'Unauthorized. Send Authorization: Bearer <token>. The desktop token lives in .liber/daemon.token; other clients get their own from /clients (scope admin).'
436
436
  }));
437
- logAuthFailure(endpoint, req.socket.remoteAddress);
437
+ logAuthFailure(endpoint, req.socket.remoteAddress, req.headers['user-agent']);
438
438
  auth.audit({ client: null, ip: req.socket.remoteAddress, method: req.method, endpoint, decision: 'deny', reason: 'bad token' });
439
439
  return;
440
440
  }