@barnaby.build/barnaby 0.0.233 → 0.0.234
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/bin/barnaby.cjs +19 -19
- package/dist/assets/{index-B2QCc9UL.js → index-92s3dAHv.js} +90 -90
- package/dist/debug-window.html +65 -65
- package/dist/index.html +1 -1
- package/dist/splash.html +46 -46
- package/dist-electron/main/index.js +6098 -10181
- package/package.json +1 -1
- package/public/debug-window.html +65 -65
- package/public/splash.html +46 -46
- package/scripts/bump-version.mjs +35 -35
- package/scripts/create-release-notes.mjs +47 -47
- package/scripts/generate-license-key.mjs +99 -99
- package/scripts/license-keypair-init.mjs +56 -56
- package/scripts/patch-nord-theme.mjs +14 -14
- package/scripts/rebuild-native.mjs +47 -47
- package/scripts/shortcut-win.mjs +57 -57
- package/scripts/sync-icon.mjs +20 -20
package/package.json
CHANGED
package/public/debug-window.html
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Barnaby Debug Output</title>
|
|
7
|
-
<style>
|
|
8
|
-
* { box-sizing: border-box; }
|
|
9
|
-
html, body { margin: 0; height: 100%; font-family: 'Consolas', 'Monaco', monospace; font-size: 12px; background: #1e1e1e; color: #d4d4d4; overflow: hidden; }
|
|
10
|
-
#toolbar { padding: 6px 8px; background: #252526; border-bottom: 1px solid #3c3c3c; display: flex; align-items: center; gap: 8px; }
|
|
11
|
-
#toolbar button { padding: 4px 10px; font-size: 11px; cursor: pointer; background: #0e639c; color: white; border: none; border-radius: 2px; }
|
|
12
|
-
#toolbar button:hover { background: #1177bb; }
|
|
13
|
-
#toolbar button.secondary { background: #3c3c3c; }
|
|
14
|
-
#toolbar button.secondary:hover { background: #505050; }
|
|
15
|
-
#log { padding: 8px; height: calc(100% - 36px); overflow: auto; white-space: pre-wrap; word-wrap: break-word; }
|
|
16
|
-
.error { color: #f48771; }
|
|
17
|
-
.warn { color: #dcdcaa; }
|
|
18
|
-
.info { color: #9cdcfe; }
|
|
19
|
-
</style>
|
|
20
|
-
</head>
|
|
21
|
-
<body>
|
|
22
|
-
<div id="toolbar">
|
|
23
|
-
<button id="refresh">Refresh</button>
|
|
24
|
-
<button id="clear" class="secondary">Clear view</button>
|
|
25
|
-
<span id="status" style="font-size: 11px; color: #858585;"></span>
|
|
26
|
-
</div>
|
|
27
|
-
<pre id="log"></pre>
|
|
28
|
-
<script>
|
|
29
|
-
const logEl = document.getElementById('log');
|
|
30
|
-
const statusEl = document.getElementById('status');
|
|
31
|
-
function render(content) {
|
|
32
|
-
const lines = (content || '').split('\n');
|
|
33
|
-
logEl.innerHTML = lines.map(l => {
|
|
34
|
-
if (l.includes('[ERROR]')) return '<span class="error">' + escapeHtml(l) + '</span>';
|
|
35
|
-
if (l.includes('[WARN]')) return '<span class="warn">' + escapeHtml(l) + '</span>';
|
|
36
|
-
return '<span class="info">' + escapeHtml(l) + '</span>';
|
|
37
|
-
}).join('\n');
|
|
38
|
-
logEl.scrollTop = logEl.scrollHeight;
|
|
39
|
-
}
|
|
40
|
-
function escapeHtml(s) { return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
|
41
|
-
async function load() {
|
|
42
|
-
try {
|
|
43
|
-
const r = await (window.api && window.api.getDebugLogContent ? window.api.getDebugLogContent() : Promise.resolve({ ok: false, content: '' }));
|
|
44
|
-
render(r.ok ? r.content : '');
|
|
45
|
-
statusEl.textContent = r.ok ? 'Live' : 'No API';
|
|
46
|
-
} catch (e) {
|
|
47
|
-
statusEl.textContent = 'Error: ' + e.message;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
document.getElementById('refresh').onclick = load;
|
|
51
|
-
document.getElementById('clear').onclick = () => { logEl.innerHTML = ''; statusEl.textContent = 'Cleared'; };
|
|
52
|
-
if (window.api && window.api.onDebugLogAppend) {
|
|
53
|
-
window.api.onDebugLogAppend((line) => {
|
|
54
|
-
const span = document.createElement('span');
|
|
55
|
-
span.className = line.includes('[ERROR]') ? 'error' : line.includes('[WARN]') ? 'warn' : 'info';
|
|
56
|
-
span.textContent = line + '\n';
|
|
57
|
-
logEl.appendChild(span);
|
|
58
|
-
logEl.scrollTop = logEl.scrollHeight;
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
load();
|
|
62
|
-
setInterval(load, 2000);
|
|
63
|
-
</script>
|
|
64
|
-
</body>
|
|
65
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Barnaby Debug Output</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; }
|
|
9
|
+
html, body { margin: 0; height: 100%; font-family: 'Consolas', 'Monaco', monospace; font-size: 12px; background: #1e1e1e; color: #d4d4d4; overflow: hidden; }
|
|
10
|
+
#toolbar { padding: 6px 8px; background: #252526; border-bottom: 1px solid #3c3c3c; display: flex; align-items: center; gap: 8px; }
|
|
11
|
+
#toolbar button { padding: 4px 10px; font-size: 11px; cursor: pointer; background: #0e639c; color: white; border: none; border-radius: 2px; }
|
|
12
|
+
#toolbar button:hover { background: #1177bb; }
|
|
13
|
+
#toolbar button.secondary { background: #3c3c3c; }
|
|
14
|
+
#toolbar button.secondary:hover { background: #505050; }
|
|
15
|
+
#log { padding: 8px; height: calc(100% - 36px); overflow: auto; white-space: pre-wrap; word-wrap: break-word; }
|
|
16
|
+
.error { color: #f48771; }
|
|
17
|
+
.warn { color: #dcdcaa; }
|
|
18
|
+
.info { color: #9cdcfe; }
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<div id="toolbar">
|
|
23
|
+
<button id="refresh">Refresh</button>
|
|
24
|
+
<button id="clear" class="secondary">Clear view</button>
|
|
25
|
+
<span id="status" style="font-size: 11px; color: #858585;"></span>
|
|
26
|
+
</div>
|
|
27
|
+
<pre id="log"></pre>
|
|
28
|
+
<script>
|
|
29
|
+
const logEl = document.getElementById('log');
|
|
30
|
+
const statusEl = document.getElementById('status');
|
|
31
|
+
function render(content) {
|
|
32
|
+
const lines = (content || '').split('\n');
|
|
33
|
+
logEl.innerHTML = lines.map(l => {
|
|
34
|
+
if (l.includes('[ERROR]')) return '<span class="error">' + escapeHtml(l) + '</span>';
|
|
35
|
+
if (l.includes('[WARN]')) return '<span class="warn">' + escapeHtml(l) + '</span>';
|
|
36
|
+
return '<span class="info">' + escapeHtml(l) + '</span>';
|
|
37
|
+
}).join('\n');
|
|
38
|
+
logEl.scrollTop = logEl.scrollHeight;
|
|
39
|
+
}
|
|
40
|
+
function escapeHtml(s) { return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
|
41
|
+
async function load() {
|
|
42
|
+
try {
|
|
43
|
+
const r = await (window.api && window.api.getDebugLogContent ? window.api.getDebugLogContent() : Promise.resolve({ ok: false, content: '' }));
|
|
44
|
+
render(r.ok ? r.content : '');
|
|
45
|
+
statusEl.textContent = r.ok ? 'Live' : 'No API';
|
|
46
|
+
} catch (e) {
|
|
47
|
+
statusEl.textContent = 'Error: ' + e.message;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
document.getElementById('refresh').onclick = load;
|
|
51
|
+
document.getElementById('clear').onclick = () => { logEl.innerHTML = ''; statusEl.textContent = 'Cleared'; };
|
|
52
|
+
if (window.api && window.api.onDebugLogAppend) {
|
|
53
|
+
window.api.onDebugLogAppend((line) => {
|
|
54
|
+
const span = document.createElement('span');
|
|
55
|
+
span.className = line.includes('[ERROR]') ? 'error' : line.includes('[WARN]') ? 'warn' : 'info';
|
|
56
|
+
span.textContent = line + '\n';
|
|
57
|
+
logEl.appendChild(span);
|
|
58
|
+
logEl.scrollTop = logEl.scrollHeight;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
load();
|
|
62
|
+
setInterval(load, 2000);
|
|
63
|
+
</script>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
package/public/splash.html
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>Barnaby Splash</title>
|
|
7
|
-
<style>
|
|
8
|
-
html, body {
|
|
9
|
-
margin: 0;
|
|
10
|
-
width: 100%;
|
|
11
|
-
height: 100%;
|
|
12
|
-
background: #0b0b0b;
|
|
13
|
-
overflow: hidden;
|
|
14
|
-
}
|
|
15
|
-
.root {
|
|
16
|
-
width: 100%;
|
|
17
|
-
height: 100%;
|
|
18
|
-
display: flex;
|
|
19
|
-
align-items: center;
|
|
20
|
-
justify-content: center;
|
|
21
|
-
}
|
|
22
|
-
img {
|
|
23
|
-
max-width: 90%;
|
|
24
|
-
max-height: 90%;
|
|
25
|
-
object-fit: contain;
|
|
26
|
-
user-select: none;
|
|
27
|
-
-webkit-user-drag: none;
|
|
28
|
-
}
|
|
29
|
-
.version {
|
|
30
|
-
position: fixed;
|
|
31
|
-
bottom: 8px;
|
|
32
|
-
right: 12px;
|
|
33
|
-
font-size: 11px;
|
|
34
|
-
color: white;
|
|
35
|
-
font-family: system-ui, sans-serif;
|
|
36
|
-
opacity: 0.8;
|
|
37
|
-
}
|
|
38
|
-
</style>
|
|
39
|
-
</head>
|
|
40
|
-
<body>
|
|
41
|
-
<div class="root">
|
|
42
|
-
<img src="./splash.png" alt="Barnaby splash" />
|
|
43
|
-
</div>
|
|
44
|
-
<div id="version" class="version"></div>
|
|
45
|
-
</body>
|
|
46
|
-
</html>
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Barnaby Splash</title>
|
|
7
|
+
<style>
|
|
8
|
+
html, body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 100%;
|
|
12
|
+
background: #0b0b0b;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
}
|
|
15
|
+
.root {
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 100%;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
img {
|
|
23
|
+
max-width: 90%;
|
|
24
|
+
max-height: 90%;
|
|
25
|
+
object-fit: contain;
|
|
26
|
+
user-select: none;
|
|
27
|
+
-webkit-user-drag: none;
|
|
28
|
+
}
|
|
29
|
+
.version {
|
|
30
|
+
position: fixed;
|
|
31
|
+
bottom: 8px;
|
|
32
|
+
right: 12px;
|
|
33
|
+
font-size: 11px;
|
|
34
|
+
color: white;
|
|
35
|
+
font-family: system-ui, sans-serif;
|
|
36
|
+
opacity: 0.8;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<div class="root">
|
|
42
|
+
<img src="./splash.png" alt="Barnaby splash" />
|
|
43
|
+
</div>
|
|
44
|
+
<div id="version" class="version"></div>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
package/scripts/bump-version.mjs
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
|
|
4
|
-
const root = process.cwd()
|
|
5
|
-
const packageJsonPath = path.join(root, 'package.json')
|
|
6
|
-
const packageLockPath = path.join(root, 'package-lock.json')
|
|
7
|
-
|
|
8
|
-
function bumpPatch(version) {
|
|
9
|
-
const parts = String(version).trim().split('.')
|
|
10
|
-
if (parts.length !== 3) {
|
|
11
|
-
throw new Error(`Expected semantic version x.y.z, got "${version}"`)
|
|
12
|
-
}
|
|
13
|
-
const [major, minor, patch] = parts.map((p) => Number(p))
|
|
14
|
-
if ([major, minor, patch].some((n) => Number.isNaN(n))) {
|
|
15
|
-
throw new Error(`Expected numeric semantic version, got "${version}"`)
|
|
16
|
-
}
|
|
17
|
-
return `${major}.${minor}.${patch + 1}`
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
21
|
-
const currentVersion = packageJson.version
|
|
22
|
-
const nextVersion = bumpPatch(currentVersion)
|
|
23
|
-
packageJson.version = nextVersion
|
|
24
|
-
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8')
|
|
25
|
-
|
|
26
|
-
if (fs.existsSync(packageLockPath)) {
|
|
27
|
-
const packageLock = JSON.parse(fs.readFileSync(packageLockPath, 'utf8'))
|
|
28
|
-
if (typeof packageLock.version === 'string') packageLock.version = nextVersion
|
|
29
|
-
if (packageLock.packages && packageLock.packages[''] && typeof packageLock.packages[''].version === 'string') {
|
|
30
|
-
packageLock.packages[''].version = nextVersion
|
|
31
|
-
}
|
|
32
|
-
fs.writeFileSync(packageLockPath, `${JSON.stringify(packageLock, null, 2)}\n`, 'utf8')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
console.log(`Version bumped: ${currentVersion} -> ${nextVersion}`)
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
const root = process.cwd()
|
|
5
|
+
const packageJsonPath = path.join(root, 'package.json')
|
|
6
|
+
const packageLockPath = path.join(root, 'package-lock.json')
|
|
7
|
+
|
|
8
|
+
function bumpPatch(version) {
|
|
9
|
+
const parts = String(version).trim().split('.')
|
|
10
|
+
if (parts.length !== 3) {
|
|
11
|
+
throw new Error(`Expected semantic version x.y.z, got "${version}"`)
|
|
12
|
+
}
|
|
13
|
+
const [major, minor, patch] = parts.map((p) => Number(p))
|
|
14
|
+
if ([major, minor, patch].some((n) => Number.isNaN(n))) {
|
|
15
|
+
throw new Error(`Expected numeric semantic version, got "${version}"`)
|
|
16
|
+
}
|
|
17
|
+
return `${major}.${minor}.${patch + 1}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
21
|
+
const currentVersion = packageJson.version
|
|
22
|
+
const nextVersion = bumpPatch(currentVersion)
|
|
23
|
+
packageJson.version = nextVersion
|
|
24
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8')
|
|
25
|
+
|
|
26
|
+
if (fs.existsSync(packageLockPath)) {
|
|
27
|
+
const packageLock = JSON.parse(fs.readFileSync(packageLockPath, 'utf8'))
|
|
28
|
+
if (typeof packageLock.version === 'string') packageLock.version = nextVersion
|
|
29
|
+
if (packageLock.packages && packageLock.packages[''] && typeof packageLock.packages[''].version === 'string') {
|
|
30
|
+
packageLock.packages[''].version = nextVersion
|
|
31
|
+
}
|
|
32
|
+
fs.writeFileSync(packageLockPath, `${JSON.stringify(packageLock, null, 2)}\n`, 'utf8')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`Version bumped: ${currentVersion} -> ${nextVersion}`)
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
|
|
4
|
-
const root = process.cwd()
|
|
5
|
-
const packageJsonPath = path.join(root, 'package.json')
|
|
6
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
7
|
-
const version = String(packageJson.version || '').trim()
|
|
8
|
-
|
|
9
|
-
if (!version) {
|
|
10
|
-
throw new Error('package.json version is missing.')
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const notesFilename = `RELEASE_NOTES_${version}.md`
|
|
14
|
-
const notesPath = path.join(root, notesFilename)
|
|
15
|
-
|
|
16
|
-
if (fs.existsSync(notesPath)) {
|
|
17
|
-
console.log(`Release notes already exist: ${notesFilename}`)
|
|
18
|
-
process.exit(0)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const now = new Date()
|
|
22
|
-
const month = now.toLocaleString('en-US', { month: 'long' })
|
|
23
|
-
const year = now.getFullYear()
|
|
24
|
-
|
|
25
|
-
const template = `# Barnaby ${version} - Release Notes
|
|
26
|
-
|
|
27
|
-
**Released:** ${month} ${year}
|
|
28
|
-
|
|
29
|
-
## Added
|
|
30
|
-
|
|
31
|
-
- TODO
|
|
32
|
-
|
|
33
|
-
## Changed
|
|
34
|
-
|
|
35
|
-
- TODO
|
|
36
|
-
|
|
37
|
-
## Fixed
|
|
38
|
-
|
|
39
|
-
- TODO
|
|
40
|
-
|
|
41
|
-
## Notes
|
|
42
|
-
|
|
43
|
-
- Artifact: \`release/${version}/Barnaby_${version}_portable.exe\`
|
|
44
|
-
`
|
|
45
|
-
|
|
46
|
-
fs.writeFileSync(notesPath, template, 'utf8')
|
|
47
|
-
console.log(`Created ${notesFilename}`)
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
const root = process.cwd()
|
|
5
|
+
const packageJsonPath = path.join(root, 'package.json')
|
|
6
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
7
|
+
const version = String(packageJson.version || '').trim()
|
|
8
|
+
|
|
9
|
+
if (!version) {
|
|
10
|
+
throw new Error('package.json version is missing.')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const notesFilename = `RELEASE_NOTES_${version}.md`
|
|
14
|
+
const notesPath = path.join(root, notesFilename)
|
|
15
|
+
|
|
16
|
+
if (fs.existsSync(notesPath)) {
|
|
17
|
+
console.log(`Release notes already exist: ${notesFilename}`)
|
|
18
|
+
process.exit(0)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const now = new Date()
|
|
22
|
+
const month = now.toLocaleString('en-US', { month: 'long' })
|
|
23
|
+
const year = now.getFullYear()
|
|
24
|
+
|
|
25
|
+
const template = `# Barnaby ${version} - Release Notes
|
|
26
|
+
|
|
27
|
+
**Released:** ${month} ${year}
|
|
28
|
+
|
|
29
|
+
## Added
|
|
30
|
+
|
|
31
|
+
- TODO
|
|
32
|
+
|
|
33
|
+
## Changed
|
|
34
|
+
|
|
35
|
+
- TODO
|
|
36
|
+
|
|
37
|
+
## Fixed
|
|
38
|
+
|
|
39
|
+
- TODO
|
|
40
|
+
|
|
41
|
+
## Notes
|
|
42
|
+
|
|
43
|
+
- Artifact: \`release/${version}/Barnaby_${version}_portable.exe\`
|
|
44
|
+
`
|
|
45
|
+
|
|
46
|
+
fs.writeFileSync(notesPath, template, 'utf8')
|
|
47
|
+
console.log(`Created ${notesFilename}`)
|
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Generate a license key for a customer.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* node scripts/generate-license-key.mjs --email customer@example.com
|
|
7
|
-
* node scripts/generate-license-key.mjs --email customer@example.com --tier pro
|
|
8
|
-
* node scripts/generate-license-key.mjs --email customer@example.com --exp 2027-01-01
|
|
9
|
-
*
|
|
10
|
-
* Options:
|
|
11
|
-
* --email Customer email (required)
|
|
12
|
-
* --tier License tier: pro, enterprise (default: pro)
|
|
13
|
-
* --exp Expiry date in YYYY-MM-DD format (optional, omit for perpetual)
|
|
14
|
-
*
|
|
15
|
-
* Reads the private key from: ~/.barnaby/license-private-key.pem
|
|
16
|
-
* Run license-keypair-init.mjs first if you haven't generated a keypair.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import crypto from 'node:crypto'
|
|
20
|
-
import fs from 'node:fs'
|
|
21
|
-
import path from 'node:path'
|
|
22
|
-
import os from 'node:os'
|
|
23
|
-
|
|
24
|
-
const PRIVATE_KEY_PATH = path.join(os.homedir(), '.barnaby', 'license-private-key.pem')
|
|
25
|
-
|
|
26
|
-
// Parse args
|
|
27
|
-
const args = process.argv.slice(2)
|
|
28
|
-
function getArg(name) {
|
|
29
|
-
const idx = args.indexOf(`--${name}`)
|
|
30
|
-
if (idx === -1 || idx + 1 >= args.length) return undefined
|
|
31
|
-
return args[idx + 1]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const email = getArg('email')
|
|
35
|
-
const tier = getArg('tier') || 'pro'
|
|
36
|
-
const expStr = getArg('exp')
|
|
37
|
-
|
|
38
|
-
if (!email) {
|
|
39
|
-
console.error('❌ Missing --email argument')
|
|
40
|
-
console.error('Usage: node scripts/generate-license-key.mjs --email customer@example.com')
|
|
41
|
-
process.exit(1)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Read private key
|
|
45
|
-
if (!fs.existsSync(PRIVATE_KEY_PATH)) {
|
|
46
|
-
console.error('❌ Private key not found at:', PRIVATE_KEY_PATH)
|
|
47
|
-
console.error(' Run: node scripts/license-keypair-init.mjs')
|
|
48
|
-
process.exit(1)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const privateKeyPem = fs.readFileSync(PRIVATE_KEY_PATH, 'utf8')
|
|
52
|
-
const privateKey = crypto.createPrivateKey(privateKeyPem)
|
|
53
|
-
|
|
54
|
-
// Build payload
|
|
55
|
-
const payload = {
|
|
56
|
-
email: email.toLowerCase().trim(),
|
|
57
|
-
product: 'orchestrator',
|
|
58
|
-
tier,
|
|
59
|
-
iat: Date.now(),
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (expStr) {
|
|
63
|
-
const expDate = new Date(expStr)
|
|
64
|
-
if (isNaN(expDate.getTime())) {
|
|
65
|
-
console.error('❌ Invalid --exp date format. Use YYYY-MM-DD.')
|
|
66
|
-
process.exit(1)
|
|
67
|
-
}
|
|
68
|
-
payload.exp = expDate.getTime()
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Encode payload
|
|
72
|
-
const payloadBuf = Buffer.from(JSON.stringify(payload), 'utf8')
|
|
73
|
-
|
|
74
|
-
// Sign
|
|
75
|
-
const signature = crypto.sign(null, payloadBuf, privateKey)
|
|
76
|
-
|
|
77
|
-
// Base64url encode (no padding)
|
|
78
|
-
function base64urlEncode(buf) {
|
|
79
|
-
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const key = `ORCH-${base64urlEncode(payloadBuf)}.${base64urlEncode(signature)}`
|
|
83
|
-
|
|
84
|
-
console.log()
|
|
85
|
-
console.log('✅ License key generated for:', email)
|
|
86
|
-
console.log()
|
|
87
|
-
console.log('Details:')
|
|
88
|
-
console.log(' Email:', payload.email)
|
|
89
|
-
console.log(' Tier:', payload.tier)
|
|
90
|
-
console.log(' Issued:', new Date(payload.iat).toISOString())
|
|
91
|
-
console.log(' Expires:', payload.exp ? new Date(payload.exp).toISOString() : 'Never (perpetual)')
|
|
92
|
-
console.log()
|
|
93
|
-
console.log('License Key:')
|
|
94
|
-
console.log('─'.repeat(60))
|
|
95
|
-
console.log(key)
|
|
96
|
-
console.log('─'.repeat(60))
|
|
97
|
-
console.log()
|
|
98
|
-
console.log('Send this key to the customer. They paste it into')
|
|
99
|
-
console.log('Settings → Orchestrator → License Key.')
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate a license key for a customer.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node scripts/generate-license-key.mjs --email customer@example.com
|
|
7
|
+
* node scripts/generate-license-key.mjs --email customer@example.com --tier pro
|
|
8
|
+
* node scripts/generate-license-key.mjs --email customer@example.com --exp 2027-01-01
|
|
9
|
+
*
|
|
10
|
+
* Options:
|
|
11
|
+
* --email Customer email (required)
|
|
12
|
+
* --tier License tier: pro, enterprise (default: pro)
|
|
13
|
+
* --exp Expiry date in YYYY-MM-DD format (optional, omit for perpetual)
|
|
14
|
+
*
|
|
15
|
+
* Reads the private key from: ~/.barnaby/license-private-key.pem
|
|
16
|
+
* Run license-keypair-init.mjs first if you haven't generated a keypair.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import crypto from 'node:crypto'
|
|
20
|
+
import fs from 'node:fs'
|
|
21
|
+
import path from 'node:path'
|
|
22
|
+
import os from 'node:os'
|
|
23
|
+
|
|
24
|
+
const PRIVATE_KEY_PATH = path.join(os.homedir(), '.barnaby', 'license-private-key.pem')
|
|
25
|
+
|
|
26
|
+
// Parse args
|
|
27
|
+
const args = process.argv.slice(2)
|
|
28
|
+
function getArg(name) {
|
|
29
|
+
const idx = args.indexOf(`--${name}`)
|
|
30
|
+
if (idx === -1 || idx + 1 >= args.length) return undefined
|
|
31
|
+
return args[idx + 1]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const email = getArg('email')
|
|
35
|
+
const tier = getArg('tier') || 'pro'
|
|
36
|
+
const expStr = getArg('exp')
|
|
37
|
+
|
|
38
|
+
if (!email) {
|
|
39
|
+
console.error('❌ Missing --email argument')
|
|
40
|
+
console.error('Usage: node scripts/generate-license-key.mjs --email customer@example.com')
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Read private key
|
|
45
|
+
if (!fs.existsSync(PRIVATE_KEY_PATH)) {
|
|
46
|
+
console.error('❌ Private key not found at:', PRIVATE_KEY_PATH)
|
|
47
|
+
console.error(' Run: node scripts/license-keypair-init.mjs')
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const privateKeyPem = fs.readFileSync(PRIVATE_KEY_PATH, 'utf8')
|
|
52
|
+
const privateKey = crypto.createPrivateKey(privateKeyPem)
|
|
53
|
+
|
|
54
|
+
// Build payload
|
|
55
|
+
const payload = {
|
|
56
|
+
email: email.toLowerCase().trim(),
|
|
57
|
+
product: 'orchestrator',
|
|
58
|
+
tier,
|
|
59
|
+
iat: Date.now(),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (expStr) {
|
|
63
|
+
const expDate = new Date(expStr)
|
|
64
|
+
if (isNaN(expDate.getTime())) {
|
|
65
|
+
console.error('❌ Invalid --exp date format. Use YYYY-MM-DD.')
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
68
|
+
payload.exp = expDate.getTime()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Encode payload
|
|
72
|
+
const payloadBuf = Buffer.from(JSON.stringify(payload), 'utf8')
|
|
73
|
+
|
|
74
|
+
// Sign
|
|
75
|
+
const signature = crypto.sign(null, payloadBuf, privateKey)
|
|
76
|
+
|
|
77
|
+
// Base64url encode (no padding)
|
|
78
|
+
function base64urlEncode(buf) {
|
|
79
|
+
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const key = `ORCH-${base64urlEncode(payloadBuf)}.${base64urlEncode(signature)}`
|
|
83
|
+
|
|
84
|
+
console.log()
|
|
85
|
+
console.log('✅ License key generated for:', email)
|
|
86
|
+
console.log()
|
|
87
|
+
console.log('Details:')
|
|
88
|
+
console.log(' Email:', payload.email)
|
|
89
|
+
console.log(' Tier:', payload.tier)
|
|
90
|
+
console.log(' Issued:', new Date(payload.iat).toISOString())
|
|
91
|
+
console.log(' Expires:', payload.exp ? new Date(payload.exp).toISOString() : 'Never (perpetual)')
|
|
92
|
+
console.log()
|
|
93
|
+
console.log('License Key:')
|
|
94
|
+
console.log('─'.repeat(60))
|
|
95
|
+
console.log(key)
|
|
96
|
+
console.log('─'.repeat(60))
|
|
97
|
+
console.log()
|
|
98
|
+
console.log('Send this key to the customer. They paste it into')
|
|
99
|
+
console.log('Settings → Orchestrator → License Key.')
|