@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 +1 -1
- package/resources/media/make-ad.mjs +108 -0
- package/resources/media/make-ad.ps1 +98 -0
- package/src/daemon/liberd.js +3 -3
package/package.json
CHANGED
|
@@ -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"
|
package/src/daemon/liberd.js
CHANGED
|
@@ -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
|
}
|