@must-b/must-b 1.72.7 → 1.72.8
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/dist/BUILD.json +3 -3
- package/dist/embedded-skills.json +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/public/assets/{index-CbW1nI1o.js → index-CRphQOGC.js} +1 -1
- package/dist/public/index.html +1 -1
- package/package.json +3 -2
- package/scripts/build-prod.mjs +307 -0
- package/scripts/bundle-skills.mjs +261 -0
- package/scripts/create-must-b-shim.mjs +45 -0
- package/scripts/install.ps1 +113 -0
- package/scripts/install.sh +112 -0
- package/scripts/paparazzi.mjs +92 -0
- package/scripts/pipeline-check.mjs +75 -0
- package/scripts/replace-assets.cjs +184 -0
- package/scripts/take-screenshots.mjs +91 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Skill Singularity Compiler — Phase 42
|
|
4
|
+
*
|
|
5
|
+
* Reads all functional skill files from must-b-skills/ (commands, agents,
|
|
6
|
+
* hooks, SKILL.md) and compiles them into a single embedded JSON bundle at
|
|
7
|
+
* src/core/embedded-skills.json.
|
|
8
|
+
*
|
|
9
|
+
* Explicitly excluded:
|
|
10
|
+
* - demo.gif, LICENSE.md, README.md, CHANGELOG.md, SECURITY.md
|
|
11
|
+
* - .github/, .vscode/, .devcontainer/, examples/, scripts/, Script/
|
|
12
|
+
* - Any binary / non-UTF8 file
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
|
|
19
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
21
|
+
const SKILLS_BASE = path.join(ROOT, 'must-b-skills');
|
|
22
|
+
const OUT_FILE = path.join(ROOT, 'src', 'core', 'embedded-skills.json');
|
|
23
|
+
|
|
24
|
+
// ── Blocklists ──────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
const SKIP_DIRS = new Set([
|
|
27
|
+
'.github', '.vscode', '.devcontainer',
|
|
28
|
+
'examples', 'scripts', 'Script', 'node_modules',
|
|
29
|
+
'references', 'hooks-handlers', // docs-only
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const SKIP_FILES = new Set([
|
|
33
|
+
'README.md', 'CHANGELOG.md', 'SECURITY.md', 'LICENSE.md',
|
|
34
|
+
'LICENSE', 'LICENSE.txt', 'demo.gif',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const ALLOWED_EXTENSIONS = new Set(['.md', '.json', '.yaml', '.yml', '.txt']);
|
|
38
|
+
|
|
39
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
function slugify(name) {
|
|
42
|
+
return name
|
|
43
|
+
.replace(/\.md$/i, '')
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
46
|
+
.replace(/^-|-$/g, '');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const PLUGIN_EMOJIS = {
|
|
50
|
+
'agent-sdk-dev': '🤖',
|
|
51
|
+
'claude-opus-4-5-migration': '⬆️',
|
|
52
|
+
'code-review': '🔍',
|
|
53
|
+
'commit-commands': '📦',
|
|
54
|
+
'explanatory-output-style': '📝',
|
|
55
|
+
'feature-dev': '🚀',
|
|
56
|
+
'frontend-design': '🎨',
|
|
57
|
+
'hookify': '🪝',
|
|
58
|
+
'learning-output-style': '📚',
|
|
59
|
+
'finance-core': '📈',
|
|
60
|
+
'os-tools': '🖥️',
|
|
61
|
+
'plugin-dev': '🔌',
|
|
62
|
+
'pr-review-toolkit': '🔎',
|
|
63
|
+
'ralph-wiggum': '🎭',
|
|
64
|
+
'security-guidance': '🔒',
|
|
65
|
+
'global': '🌐',
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function parseFrontmatter(content) {
|
|
69
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
70
|
+
if (!match) return { name: '', description: '', argumentHint: '', allowedTools: [], body: content };
|
|
71
|
+
|
|
72
|
+
const fm = match[1];
|
|
73
|
+
const body = match[2] ?? '';
|
|
74
|
+
|
|
75
|
+
let name = '';
|
|
76
|
+
const np = fm.match(/^name:\s*(.+)$/m);
|
|
77
|
+
if (np) name = np[1].trim().replace(/^["']|["']$/g, '');
|
|
78
|
+
|
|
79
|
+
let description = '';
|
|
80
|
+
const dq = fm.match(/^description:\s*"((?:[^"\\]|\\.)*)"\s*$/m);
|
|
81
|
+
if (dq) {
|
|
82
|
+
description = dq[1].replace(/\\"/g, '"');
|
|
83
|
+
} else {
|
|
84
|
+
const dp = fm.match(/^description:\s*(.+)$/m);
|
|
85
|
+
if (dp) description = dp[1].trim().replace(/^["']|["']$/g, '');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let argumentHint = '';
|
|
89
|
+
const ah = fm.match(/^argument-hint:\s*(.+)$/m);
|
|
90
|
+
if (ah) argumentHint = ah[1].trim().replace(/^["'\[]|["'\]]$/g, '');
|
|
91
|
+
|
|
92
|
+
let allowedTools = [];
|
|
93
|
+
const atLine = fm.match(/^allowed-tools:\s*([\s\S]*?)(?=\n\S|$)/m);
|
|
94
|
+
if (atLine) {
|
|
95
|
+
const raw = atLine[1].trim();
|
|
96
|
+
if (raw.startsWith('[')) {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(raw);
|
|
99
|
+
if (Array.isArray(parsed)) allowedTools = parsed.map(String);
|
|
100
|
+
} catch {
|
|
101
|
+
allowedTools = raw.replace(/^\[|\]$/g, '').split(',')
|
|
102
|
+
.map(s => s.trim().replace(/^"|"$/g, '')).filter(Boolean);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
allowedTools = raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { name, description, argumentHint, allowedTools, body };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildEntry(filePath, fileName, pluginName, idPrefix, extraTags) {
|
|
113
|
+
let content;
|
|
114
|
+
try { content = fs.readFileSync(filePath, 'utf8'); } catch { return null; }
|
|
115
|
+
|
|
116
|
+
const { name: fmName, description, argumentHint, allowedTools, body } = parseFrontmatter(content);
|
|
117
|
+
const fileSlug = slugify(fileName);
|
|
118
|
+
const id = `${idPrefix}-${fileSlug}`;
|
|
119
|
+
const emoji = PLUGIN_EMOJIS[pluginName] ?? '🔧';
|
|
120
|
+
const displayName = fmName || fileSlug.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
id,
|
|
124
|
+
name: displayName,
|
|
125
|
+
description: description || displayName,
|
|
126
|
+
emoji,
|
|
127
|
+
tags: ['imported', 'claude-code', pluginName, ...extraTags],
|
|
128
|
+
pluginName,
|
|
129
|
+
commandFile: fileName,
|
|
130
|
+
allowedTools,
|
|
131
|
+
argumentHint,
|
|
132
|
+
promptBody: body,
|
|
133
|
+
hasScripts: false,
|
|
134
|
+
requires: {},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── Loaders ──────────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
function loadDir(dir, pluginName, idPrefix, extraTags) {
|
|
141
|
+
if (!fs.existsSync(dir)) return [];
|
|
142
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
143
|
+
const skills = [];
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
if (!entry.isFile()) continue;
|
|
146
|
+
if (SKIP_FILES.has(entry.name)) continue;
|
|
147
|
+
if (!ALLOWED_EXTENSIONS.has(path.extname(entry.name))) continue;
|
|
148
|
+
if (!entry.name.endsWith('.md')) continue;
|
|
149
|
+
const skill = buildEntry(path.join(dir, entry.name), entry.name, pluginName, idPrefix, extraTags);
|
|
150
|
+
if (skill) skills.push(skill);
|
|
151
|
+
}
|
|
152
|
+
return skills;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function loadSkillsRecursive(skillsDir, pluginName) {
|
|
156
|
+
if (!fs.existsSync(skillsDir)) return [];
|
|
157
|
+
const results = [];
|
|
158
|
+
|
|
159
|
+
function walk(dir) {
|
|
160
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
if (entry.isDirectory()) {
|
|
163
|
+
if (!SKIP_DIRS.has(entry.name)) walk(path.join(dir, entry.name));
|
|
164
|
+
} else if (entry.isFile() && entry.name === 'SKILL.md') {
|
|
165
|
+
const rel = path.relative(skillsDir, path.join(dir, entry.name));
|
|
166
|
+
const relSlug = rel.replace(/[\\/]/g, '-').replace(/\.md$/i, '').toLowerCase().replace(/[^a-z0-9-]+/g, '-');
|
|
167
|
+
const skill = buildEntry(
|
|
168
|
+
path.join(dir, entry.name),
|
|
169
|
+
entry.name,
|
|
170
|
+
pluginName,
|
|
171
|
+
`cc-${slugify(pluginName)}-skill-${relSlug}`,
|
|
172
|
+
['skill'],
|
|
173
|
+
);
|
|
174
|
+
if (skill) {
|
|
175
|
+
skill.id = `cc-${slugify(pluginName)}-skill-${relSlug}`;
|
|
176
|
+
results.push(skill);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
walk(skillsDir);
|
|
183
|
+
return results;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function loadHooks(hooksDir, pluginName) {
|
|
187
|
+
if (!fs.existsSync(hooksDir)) return [];
|
|
188
|
+
const entries = fs.readdirSync(hooksDir, { withFileTypes: true });
|
|
189
|
+
const skills = [];
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
if (!entry.isFile()) continue;
|
|
192
|
+
if (SKIP_FILES.has(entry.name)) continue;
|
|
193
|
+
if (!ALLOWED_EXTENSIONS.has(path.extname(entry.name))) continue;
|
|
194
|
+
const skill = buildEntry(
|
|
195
|
+
path.join(hooksDir, entry.name),
|
|
196
|
+
entry.name,
|
|
197
|
+
pluginName,
|
|
198
|
+
`cc-${slugify(pluginName)}-hook`,
|
|
199
|
+
['hook'],
|
|
200
|
+
);
|
|
201
|
+
if (skill) skills.push(skill);
|
|
202
|
+
}
|
|
203
|
+
return skills;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── Main scan ────────────────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
function scan() {
|
|
209
|
+
if (!fs.existsSync(SKILLS_BASE)) {
|
|
210
|
+
console.error(`[bundle-skills] ERROR: must-b-skills/ not found at ${SKILLS_BASE}`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const all = [];
|
|
215
|
+
|
|
216
|
+
// Global .claude/commands/
|
|
217
|
+
const globalCmds = path.join(SKILLS_BASE, '.claude', 'commands');
|
|
218
|
+
all.push(...loadDir(globalCmds, 'global', 'cc-global', ['command', 'global']));
|
|
219
|
+
|
|
220
|
+
// Per-plugin
|
|
221
|
+
const pluginsRoot = path.join(SKILLS_BASE, 'plugins');
|
|
222
|
+
if (fs.existsSync(pluginsRoot)) {
|
|
223
|
+
for (const entry of fs.readdirSync(pluginsRoot, { withFileTypes: true })) {
|
|
224
|
+
if (!entry.isDirectory()) continue;
|
|
225
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
226
|
+
const pluginDir = path.join(pluginsRoot, entry.name);
|
|
227
|
+
const pluginName = entry.name;
|
|
228
|
+
const pSlug = slugify(pluginName);
|
|
229
|
+
|
|
230
|
+
all.push(...loadDir(path.join(pluginDir, 'commands'), pluginName, `cc-${pSlug}`, ['command']));
|
|
231
|
+
all.push(...loadDir(path.join(pluginDir, 'agents'), pluginName, `cc-${pSlug}-agent`, ['agent']));
|
|
232
|
+
all.push(...loadSkillsRecursive(path.join(pluginDir, 'skills'), pluginName));
|
|
233
|
+
all.push(...loadHooks(path.join(pluginDir, 'hooks'), pluginName));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Deduplicate by id
|
|
238
|
+
const seen = new Set();
|
|
239
|
+
const deduped = all.filter(s => {
|
|
240
|
+
if (seen.has(s.id)) return false;
|
|
241
|
+
seen.add(s.id);
|
|
242
|
+
return true;
|
|
243
|
+
});
|
|
244
|
+
deduped.sort((a, b) => a.id.localeCompare(b.id));
|
|
245
|
+
return deduped;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ── Write output ─────────────────────────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
const skills = scan();
|
|
251
|
+
const bundle = {
|
|
252
|
+
version: '1.71.4',
|
|
253
|
+
generatedAt: new Date().toISOString(),
|
|
254
|
+
count: skills.length,
|
|
255
|
+
skills,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true });
|
|
259
|
+
fs.writeFileSync(OUT_FILE, JSON.stringify(bundle, null, 2), 'utf8');
|
|
260
|
+
|
|
261
|
+
console.log(`[bundle-skills] ✓ Compiled ${skills.length} skills → ${path.relative(ROOT, OUT_FILE)}`);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recreates node_modules/must-b/ after every npm install.
|
|
3
|
+
* The must-b package shim maps ./plugin-sdk/* to the internal TypeScript
|
|
4
|
+
* source tree so that channel extensions can import from "must-b/plugin-sdk/*"
|
|
5
|
+
* without a separate published npm package.
|
|
6
|
+
*
|
|
7
|
+
* Global installs (npm install -g) run postinstall with elevated file-system
|
|
8
|
+
* restrictions — writing to node_modules may be denied with EACCES or EPERM.
|
|
9
|
+
* The shim is best-effort: a permission failure is logged as a warning and the
|
|
10
|
+
* install continues cleanly. Channel extensions will not load without the shim,
|
|
11
|
+
* but all core Must-b features remain fully operational.
|
|
12
|
+
*/
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
18
|
+
const shimDir = path.join(root, 'node_modules', 'must-b');
|
|
19
|
+
|
|
20
|
+
const shimPkg = JSON.stringify({
|
|
21
|
+
name: 'must-b',
|
|
22
|
+
version: '1.0.0',
|
|
23
|
+
exports: {
|
|
24
|
+
'./plugin-sdk/*': '../../src/core/source/plugin-sdk/*.ts',
|
|
25
|
+
},
|
|
26
|
+
}, null, 2);
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
fs.mkdirSync(shimDir, { recursive: true });
|
|
30
|
+
fs.writeFileSync(path.join(shimDir, 'package.json'), shimPkg);
|
|
31
|
+
console.log('[postinstall] must-b plugin-sdk shim written →', shimDir);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const code = err && err.code;
|
|
34
|
+
if (code === 'EACCES' || code === 'EPERM' || code === 'EROFS') {
|
|
35
|
+
console.warn(
|
|
36
|
+
'[postinstall] WARNING: Could not write must-b shim to node_modules ' +
|
|
37
|
+
`(${code} — likely a global npm install with strict permissions).\n` +
|
|
38
|
+
' Channel extensions may not load. All core Must-b features are unaffected.\n' +
|
|
39
|
+
' To enable extensions, run: npm install --prefix <your-project-dir>'
|
|
40
|
+
);
|
|
41
|
+
} else {
|
|
42
|
+
// Unexpected error — surface it but still don't crash the install
|
|
43
|
+
console.warn('[postinstall] WARNING: must-b shim creation failed:', err && err.message || err);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
Must-b Installer for Windows (PowerShell)
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Downloads and installs Must-b — the Autonomous AI Platform.
|
|
7
|
+
Usage: iex (irm https://must-b.com/install.ps1)
|
|
8
|
+
— or —
|
|
9
|
+
pwsh scripts/install.ps1
|
|
10
|
+
#>
|
|
11
|
+
|
|
12
|
+
$ErrorActionPreference = "Stop"
|
|
13
|
+
|
|
14
|
+
$REPO = "https://github.com/aytac43-0/Must-b"
|
|
15
|
+
$INSTALL_DIR = Join-Path $env:USERPROFILE ".must-b"
|
|
16
|
+
$NODE_MIN = 20
|
|
17
|
+
|
|
18
|
+
# ── UI helpers ───────────────────────────────────────────────────────────
|
|
19
|
+
function Header($msg) { Write-Host "`n $msg" -ForegroundColor DarkYellow }
|
|
20
|
+
function Ok($msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
|
|
21
|
+
function Fail($msg) { Write-Host " [ERROR] $msg" -ForegroundColor Red; exit 1 }
|
|
22
|
+
|
|
23
|
+
Write-Host @"
|
|
24
|
+
|
|
25
|
+
__ __ _ _
|
|
26
|
+
| \/ |_ _ ___| |_ | |__
|
|
27
|
+
| |\/| | | | / __| __| __ | '_ \
|
|
28
|
+
| | | | |_| \__ \ |_ / \ | |_) |
|
|
29
|
+
|_| |_|\__,_|___/\__| \__/ |_.__/
|
|
30
|
+
Autonomous AI Platform
|
|
31
|
+
|
|
32
|
+
"@ -ForegroundColor DarkYellow
|
|
33
|
+
|
|
34
|
+
# ── Prerequisites ─────────────────────────────────────────────────────────
|
|
35
|
+
Header "Checking prerequisites..."
|
|
36
|
+
|
|
37
|
+
# Node.js
|
|
38
|
+
$nodePath = Get-Command node -ErrorAction SilentlyContinue
|
|
39
|
+
if (-not $nodePath) {
|
|
40
|
+
Fail "Node.js not found. Install Node.js $NODE_MIN+ from https://nodejs.org and re-run."
|
|
41
|
+
}
|
|
42
|
+
$nodeVersion = (node -e "process.stdout.write(process.version.slice(1))").Trim()
|
|
43
|
+
$nodeMajor = [int]($nodeVersion.Split(".")[0])
|
|
44
|
+
if ($nodeMajor -lt $NODE_MIN) {
|
|
45
|
+
Fail "Node.js $NODE_MIN+ required (found v$nodeVersion). Please upgrade."
|
|
46
|
+
}
|
|
47
|
+
Ok "Node.js v$nodeVersion"
|
|
48
|
+
|
|
49
|
+
# Git
|
|
50
|
+
$gitPath = Get-Command git -ErrorAction SilentlyContinue
|
|
51
|
+
if (-not $gitPath) {
|
|
52
|
+
Fail "git not found. Install Git from https://git-scm.com and re-run."
|
|
53
|
+
}
|
|
54
|
+
Ok "git $(git --version)"
|
|
55
|
+
|
|
56
|
+
# npm
|
|
57
|
+
$npmPath = Get-Command npm -ErrorAction SilentlyContinue
|
|
58
|
+
if (-not $npmPath) { Fail "npm not found." }
|
|
59
|
+
Ok "npm $(npm --version)"
|
|
60
|
+
|
|
61
|
+
# ── Clone / update ────────────────────────────────────────────────────────
|
|
62
|
+
Header "Installing Must-b to $INSTALL_DIR..."
|
|
63
|
+
|
|
64
|
+
if (Test-Path (Join-Path $INSTALL_DIR ".git")) {
|
|
65
|
+
Write-Host " Existing installation found — pulling latest..."
|
|
66
|
+
git -C $INSTALL_DIR pull --ff-only
|
|
67
|
+
} else {
|
|
68
|
+
git clone --depth 1 $REPO $INSTALL_DIR
|
|
69
|
+
}
|
|
70
|
+
Ok "Repository ready"
|
|
71
|
+
|
|
72
|
+
# ── Install dependencies ──────────────────────────────────────────────────
|
|
73
|
+
Header "Installing backend dependencies..."
|
|
74
|
+
Set-Location $INSTALL_DIR
|
|
75
|
+
npm install --prefer-offline --omit=dev
|
|
76
|
+
Ok "Backend dependencies installed"
|
|
77
|
+
|
|
78
|
+
Header "Installing frontend dependencies..."
|
|
79
|
+
Set-Location (Join-Path $INSTALL_DIR "public\must-b-ui")
|
|
80
|
+
npm install --prefer-offline --omit=dev
|
|
81
|
+
Ok "Frontend dependencies installed"
|
|
82
|
+
|
|
83
|
+
# ── Create launcher (must-b.cmd) ──────────────────────────────────────────
|
|
84
|
+
Header "Creating launcher..."
|
|
85
|
+
$binDir = Join-Path $env:USERPROFILE "AppData\Local\Programs\must-b\bin"
|
|
86
|
+
$launcher = Join-Path $binDir "must-b.cmd"
|
|
87
|
+
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
|
|
88
|
+
|
|
89
|
+
@"
|
|
90
|
+
@echo off
|
|
91
|
+
cd /d "$INSTALL_DIR"
|
|
92
|
+
node --experimental-specifier-resolution=node dist/index.js %*
|
|
93
|
+
"@ | Set-Content $launcher -Encoding ASCII
|
|
94
|
+
|
|
95
|
+
# Add to user PATH if not already present
|
|
96
|
+
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
|
|
97
|
+
if ($userPath -notlike "*$binDir*") {
|
|
98
|
+
[Environment]::SetEnvironmentVariable("PATH", "$userPath;$binDir", "User")
|
|
99
|
+
Write-Host " Added $binDir to user PATH"
|
|
100
|
+
}
|
|
101
|
+
Ok "Launcher created at $launcher"
|
|
102
|
+
|
|
103
|
+
# ── Done ─────────────────────────────────────────────────────────────────
|
|
104
|
+
Set-Location $INSTALL_DIR
|
|
105
|
+
Write-Host ""
|
|
106
|
+
Write-Host " Must-b installed successfully!" -ForegroundColor Green
|
|
107
|
+
Write-Host ""
|
|
108
|
+
Write-Host " Start the gateway: must-b"
|
|
109
|
+
Write-Host " Open in browser: http://localhost:4309"
|
|
110
|
+
Write-Host " Run setup wizard: must-b onboard"
|
|
111
|
+
Write-Host ""
|
|
112
|
+
Write-Host " Tip: restart your terminal for the PATH change to take effect."
|
|
113
|
+
Write-Host ""
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
# Must-b Installer — Linux / macOS
|
|
4
|
+
# Usage: curl -fsSL https://must-b.com/install.sh | bash
|
|
5
|
+
# — or —
|
|
6
|
+
# bash scripts/install.sh
|
|
7
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
REPO="https://github.com/aytac43-0/Must-b"
|
|
11
|
+
INSTALL_DIR="$HOME/.must-b"
|
|
12
|
+
NODE_MIN_MAJOR=20
|
|
13
|
+
BOLD="\033[1m"
|
|
14
|
+
ORANGE="\033[38;5;208m"
|
|
15
|
+
GREEN="\033[0;32m"
|
|
16
|
+
RED="\033[0;31m"
|
|
17
|
+
RESET="\033[0m"
|
|
18
|
+
|
|
19
|
+
header() { echo -e "\n${ORANGE}${BOLD}▸ $*${RESET}"; }
|
|
20
|
+
ok() { echo -e " ${GREEN}✓${RESET} $*"; }
|
|
21
|
+
fail() { echo -e " ${RED}✗ $*${RESET}"; exit 1; }
|
|
22
|
+
|
|
23
|
+
echo -e "\n${ORANGE}${BOLD}"
|
|
24
|
+
cat <<'BANNER'
|
|
25
|
+
__ __ _ _
|
|
26
|
+
| \/ |_ _ ___| |_ | |__
|
|
27
|
+
| |\/| | | | / __| __| __ | '_ \
|
|
28
|
+
| | | | |_| \__ \ |_ / \ | |_) |
|
|
29
|
+
|_| |_|\__,_|___/\__| \__/ |_.__/
|
|
30
|
+
Autonomous AI Platform
|
|
31
|
+
BANNER
|
|
32
|
+
echo -e "${RESET}"
|
|
33
|
+
|
|
34
|
+
# ── Prerequisites ─────────────────────────────────────────────────────────
|
|
35
|
+
header "Checking prerequisites…"
|
|
36
|
+
|
|
37
|
+
# Node.js
|
|
38
|
+
if ! command -v node &>/dev/null; then
|
|
39
|
+
fail "Node.js not found. Install Node.js ${NODE_MIN_MAJOR}+ from https://nodejs.org and re-run."
|
|
40
|
+
fi
|
|
41
|
+
NODE_MAJOR=$(node -e "process.stdout.write(process.version.slice(1).split('.')[0])")
|
|
42
|
+
if [ "$NODE_MAJOR" -lt "$NODE_MIN_MAJOR" ]; then
|
|
43
|
+
fail "Node.js ${NODE_MIN_MAJOR}+ required (found v${NODE_MAJOR}). Please upgrade."
|
|
44
|
+
fi
|
|
45
|
+
ok "Node.js $(node --version)"
|
|
46
|
+
|
|
47
|
+
# Git
|
|
48
|
+
if ! command -v git &>/dev/null; then
|
|
49
|
+
fail "git not found. Please install git and re-run."
|
|
50
|
+
fi
|
|
51
|
+
ok "git $(git --version | awk '{print $3}')"
|
|
52
|
+
|
|
53
|
+
# npm
|
|
54
|
+
if ! command -v npm &>/dev/null; then
|
|
55
|
+
fail "npm not found."
|
|
56
|
+
fi
|
|
57
|
+
ok "npm $(npm --version)"
|
|
58
|
+
|
|
59
|
+
# ── Clone / update ────────────────────────────────────────────────────────
|
|
60
|
+
header "Installing Must-b to ${INSTALL_DIR}…"
|
|
61
|
+
|
|
62
|
+
if [ -d "$INSTALL_DIR/.git" ]; then
|
|
63
|
+
echo " Existing installation found — pulling latest…"
|
|
64
|
+
git -C "$INSTALL_DIR" pull --ff-only
|
|
65
|
+
else
|
|
66
|
+
git clone --depth 1 "$REPO" "$INSTALL_DIR"
|
|
67
|
+
fi
|
|
68
|
+
ok "Repository ready"
|
|
69
|
+
|
|
70
|
+
# ── Install dependencies ──────────────────────────────────────────────────
|
|
71
|
+
header "Installing backend dependencies…"
|
|
72
|
+
cd "$INSTALL_DIR"
|
|
73
|
+
npm install --prefer-offline --omit=dev
|
|
74
|
+
ok "Backend dependencies installed"
|
|
75
|
+
|
|
76
|
+
header "Installing frontend dependencies…"
|
|
77
|
+
cd "$INSTALL_DIR/public/must-b-ui"
|
|
78
|
+
npm install --prefer-offline --omit=dev
|
|
79
|
+
ok "Frontend dependencies installed"
|
|
80
|
+
|
|
81
|
+
# ── Create launcher ───────────────────────────────────────────────────────
|
|
82
|
+
header "Creating launcher…"
|
|
83
|
+
LAUNCHER="$HOME/.local/bin/must-b"
|
|
84
|
+
mkdir -p "$(dirname "$LAUNCHER")"
|
|
85
|
+
|
|
86
|
+
cat > "$LAUNCHER" <<EOF
|
|
87
|
+
#!/usr/bin/env bash
|
|
88
|
+
cd "$INSTALL_DIR"
|
|
89
|
+
exec node --experimental-specifier-resolution=node dist/index.js "\$@"
|
|
90
|
+
EOF
|
|
91
|
+
chmod +x "$LAUNCHER"
|
|
92
|
+
|
|
93
|
+
# Add ~/.local/bin to PATH hint
|
|
94
|
+
SHELL_RC=""
|
|
95
|
+
case "$SHELL" in
|
|
96
|
+
*/zsh) SHELL_RC="$HOME/.zshrc" ;;
|
|
97
|
+
*/bash) SHELL_RC="$HOME/.bashrc" ;;
|
|
98
|
+
esac
|
|
99
|
+
if [ -n "$SHELL_RC" ] && ! grep -q '.local/bin' "$SHELL_RC" 2>/dev/null; then
|
|
100
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$SHELL_RC"
|
|
101
|
+
echo " Added ~/.local/bin to PATH in ${SHELL_RC}"
|
|
102
|
+
fi
|
|
103
|
+
ok "Launcher created at ${LAUNCHER}"
|
|
104
|
+
|
|
105
|
+
# ── Done ─────────────────────────────────────────────────────────────────
|
|
106
|
+
echo -e "\n${GREEN}${BOLD}Must-b installed successfully!${RESET}\n"
|
|
107
|
+
echo " Start the gateway: must-b"
|
|
108
|
+
echo " Open in browser: http://localhost:4309"
|
|
109
|
+
echo " Run setup wizard: must-b onboard"
|
|
110
|
+
echo ""
|
|
111
|
+
echo " Tip: restart your terminal (or run: source ${SHELL_RC:-~/.bashrc})"
|
|
112
|
+
echo ""
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paparazzi.mjs — Must-b UI Screenshot Utility
|
|
3
|
+
*
|
|
4
|
+
* Usage: node scripts/paparazzi.mjs
|
|
5
|
+
*
|
|
6
|
+
* Requires Must-b to be running on localhost:4309.
|
|
7
|
+
* Saves screenshots to screenshots/<route_name>.png (git + npm ignored).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
17
|
+
const OUT_DIR = path.join(ROOT, 'screenshots');
|
|
18
|
+
const BASE_URL = 'http://localhost:4309';
|
|
19
|
+
|
|
20
|
+
const ROUTES = [
|
|
21
|
+
{ name: 'browser', path: '/app/browser' },
|
|
22
|
+
{ name: 'automations', path: '/app/automations' },
|
|
23
|
+
{ name: 'skills', path: '/app/skills' },
|
|
24
|
+
{ name: 'plugins', path: '/app/plugins' },
|
|
25
|
+
{ name: 'files', path: '/app/files' },
|
|
26
|
+
{ name: 'memory', path: '/app/memory' },
|
|
27
|
+
{ name: 'settings', path: '/app/settings' },
|
|
28
|
+
{ name: 'keys', path: '/app/settings' }, // settings hosts API keys
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
// ── Wait for server ──────────────────────────────────────────────────────────
|
|
32
|
+
async function waitForServer(timeoutMs = 20_000) {
|
|
33
|
+
const start = Date.now();
|
|
34
|
+
process.stdout.write(` Waiting for ${BASE_URL} `);
|
|
35
|
+
while (Date.now() - start < timeoutMs) {
|
|
36
|
+
try {
|
|
37
|
+
const r = await fetch(`${BASE_URL}/api/setup/status`, { signal: AbortSignal.timeout(1200) });
|
|
38
|
+
if (r.status < 500) { process.stdout.write(' ready!\n'); return; }
|
|
39
|
+
} catch { /* not up yet */ }
|
|
40
|
+
await new Promise(r => setTimeout(r, 400));
|
|
41
|
+
process.stdout.write('.');
|
|
42
|
+
}
|
|
43
|
+
process.stdout.write('\n');
|
|
44
|
+
throw new Error(`Server not responding after ${timeoutMs}ms — start Must-b first.`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
48
|
+
async function main() {
|
|
49
|
+
console.log('\n Must-b Paparazzi\n ─────────────────────────');
|
|
50
|
+
|
|
51
|
+
await waitForServer();
|
|
52
|
+
fs.mkdirSync(OUT_DIR, { recursive: true });
|
|
53
|
+
|
|
54
|
+
const browser = await chromium.launch({ headless: true });
|
|
55
|
+
const context = await browser.newContext({
|
|
56
|
+
viewport: { width: 1440, height: 900 },
|
|
57
|
+
colorScheme: 'dark',
|
|
58
|
+
deviceScaleFactor: 1,
|
|
59
|
+
});
|
|
60
|
+
const page = await context.newPage();
|
|
61
|
+
|
|
62
|
+
// Dismiss any JS alerts automatically
|
|
63
|
+
page.on('dialog', d => d.dismiss().catch(() => {}));
|
|
64
|
+
|
|
65
|
+
let ok = 0, fail = 0;
|
|
66
|
+
|
|
67
|
+
for (const route of ROUTES) {
|
|
68
|
+
const url = BASE_URL + route.path;
|
|
69
|
+
const outFile = path.join(OUT_DIR, `${route.name}.png`);
|
|
70
|
+
process.stdout.write(` → ${(route.path).padEnd(24)}`);
|
|
71
|
+
try {
|
|
72
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: 25_000 });
|
|
73
|
+
// Extra settle for animations and deferred fetches
|
|
74
|
+
await page.waitForTimeout(1800);
|
|
75
|
+
await page.screenshot({ path: outFile, fullPage: false });
|
|
76
|
+
process.stdout.write(` ✓ screenshots/${route.name}.png\n`);
|
|
77
|
+
ok++;
|
|
78
|
+
} catch (e) {
|
|
79
|
+
process.stdout.write(` ✗ ${String(e.message).split('\n')[0]}\n`);
|
|
80
|
+
fail++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
console.log(`\n ─────────────────────────`);
|
|
87
|
+
console.log(` Done — ${ok} captured, ${fail} failed.`);
|
|
88
|
+
if (ok > 0) console.log(` Folder: ${OUT_DIR}`);
|
|
89
|
+
console.log();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main().catch(err => { console.error('\n Error:', err.message); process.exit(1); });
|