resuml 1.20.1 → 2.0.0
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/DOCS.md +314 -0
- package/README.md +7 -2
- package/dist/{chunk-KRJMZ2RQ.js → chunk-GRIYYG45.js} +242 -2
- package/dist/chunk-GRIYYG45.js.map +1 -0
- package/dist/index.d.ts +422 -3
- package/dist/index.js +119 -54
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +4 -8
- package/dist/mcp/server.js.map +1 -1
- package/package.json +26 -52
- package/dist/api.d.ts +0 -9
- package/dist/api.js +0 -20
- package/dist/api.js.map +0 -1
- package/dist/chunk-4ZOTZUAW.js +0 -6666
- package/dist/chunk-4ZOTZUAW.js.map +0 -1
- package/dist/chunk-JP7UCR3P.js +0 -182
- package/dist/chunk-JP7UCR3P.js.map +0 -1
- package/dist/chunk-KRJMZ2RQ.js.map +0 -1
- package/dist/chunk-ZLA7NFYP.js +0 -90
- package/dist/chunk-ZLA7NFYP.js.map +0 -1
- package/dist/index-yHdKpxms.d.ts +0 -422
- package/dist/themeLoader-ZGWEGYXG.js +0 -7
- package/dist/themeLoader-ZGWEGYXG.js.map +0 -1
- package/scripts/build-builder.js +0 -25
- package/scripts/build-skills-db.js +0 -314
- package/scripts/bundle-themes.js +0 -1104
- package/scripts/dev-server.js +0 -392
- package/scripts/enrich-themes-manifest.mjs +0 -156
- package/scripts/generate-types.cjs +0 -55
- package/scripts/mcp-call.mjs +0 -99
- package/scripts/quick-bundle.cjs +0 -129
- package/scripts/render-theme-thumbs.mjs +0 -117
- package/scripts/test-mcp.mjs +0 -583
package/scripts/mcp-call.mjs
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Simple MCP client, sends one tool/resource/prompt call and prints the result.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* node scripts/mcp-call.mjs tool <toolName> '<json args>'
|
|
7
|
-
* node scripts/mcp-call.mjs resource <uri>
|
|
8
|
-
* node scripts/mcp-call.mjs prompt <name> '<json args>'
|
|
9
|
-
* node scripts/mcp-call.mjs list-tools
|
|
10
|
-
* node scripts/mcp-call.mjs list-resources
|
|
11
|
-
* node scripts/mcp-call.mjs list-prompts
|
|
12
|
-
*/
|
|
13
|
-
import { spawn } from 'child_process';
|
|
14
|
-
import { resolve, dirname } from 'path';
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
16
|
-
import { existsSync } from 'fs';
|
|
17
|
-
|
|
18
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const ROOT = resolve(__dirname, '..');
|
|
20
|
-
const entry = existsSync(resolve(ROOT, 'dist/index.js'))
|
|
21
|
-
? './dist/index.js' : './dist/index.cjs';
|
|
22
|
-
|
|
23
|
-
const [,, cmd, arg1, arg2] = process.argv;
|
|
24
|
-
|
|
25
|
-
async function run() {
|
|
26
|
-
const proc = spawn('node', [entry, 'mcp'], { cwd: ROOT, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
27
|
-
let buf = '';
|
|
28
|
-
const pending = new Map();
|
|
29
|
-
let msgId = 0;
|
|
30
|
-
|
|
31
|
-
proc.stdout.on('data', (chunk) => {
|
|
32
|
-
buf += chunk.toString();
|
|
33
|
-
let idx;
|
|
34
|
-
while ((idx = buf.indexOf('\n')) !== -1) {
|
|
35
|
-
const line = buf.slice(0, idx).trim();
|
|
36
|
-
buf = buf.slice(idx + 1);
|
|
37
|
-
if (!line) continue;
|
|
38
|
-
try {
|
|
39
|
-
const msg = JSON.parse(line);
|
|
40
|
-
if (msg.id != null && pending.has(msg.id)) {
|
|
41
|
-
const { resolve } = pending.get(msg.id);
|
|
42
|
-
pending.delete(msg.id);
|
|
43
|
-
resolve(msg);
|
|
44
|
-
}
|
|
45
|
-
} catch {}
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
function send(method, params) {
|
|
50
|
-
return new Promise((resolve) => {
|
|
51
|
-
const id = ++msgId;
|
|
52
|
-
pending.set(id, { resolve });
|
|
53
|
-
proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Initialize
|
|
58
|
-
await send('initialize', {
|
|
59
|
-
protocolVersion: '2025-03-26',
|
|
60
|
-
capabilities: {},
|
|
61
|
-
clientInfo: { name: 'mcp-call', version: '1.0.0' },
|
|
62
|
-
});
|
|
63
|
-
proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized', params: {} }) + '\n');
|
|
64
|
-
|
|
65
|
-
let result;
|
|
66
|
-
switch (cmd) {
|
|
67
|
-
case 'tool': {
|
|
68
|
-
const args = arg2 ? JSON.parse(arg2) : {};
|
|
69
|
-
result = await send('tools/call', { name: arg1, arguments: args });
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
case 'resource':
|
|
73
|
-
result = await send('resources/read', { uri: arg1 });
|
|
74
|
-
break;
|
|
75
|
-
case 'prompt': {
|
|
76
|
-
const args = arg2 ? JSON.parse(arg2) : {};
|
|
77
|
-
result = await send('prompts/get', { name: arg1, arguments: args });
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
case 'list-tools':
|
|
81
|
-
result = await send('tools/list', {});
|
|
82
|
-
break;
|
|
83
|
-
case 'list-resources':
|
|
84
|
-
result = await send('resources/list', {});
|
|
85
|
-
break;
|
|
86
|
-
case 'list-prompts':
|
|
87
|
-
result = await send('prompts/list', {});
|
|
88
|
-
break;
|
|
89
|
-
default:
|
|
90
|
-
console.error('Usage: mcp-call.mjs tool|resource|prompt|list-tools|list-resources|list-prompts');
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
console.log(JSON.stringify(result.result ?? result.error, null, 2));
|
|
95
|
-
proc.stdin.end();
|
|
96
|
-
proc.kill();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
run().catch(e => { console.error(e); process.exit(1); });
|
package/scripts/quick-bundle.cjs
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Quick bundler: takes a theme already in node_modules and bundles it
|
|
3
|
-
* for the browser as an ESM module.
|
|
4
|
-
*
|
|
5
|
-
* Shims Node.js builtins (fs, path, url, crypto) so themes that
|
|
6
|
-
* reference them at import time still bundle, the shims are no-ops
|
|
7
|
-
* or minimal polyfills that cover what Handlebars-based themes need.
|
|
8
|
-
*/
|
|
9
|
-
const esbuild = require('esbuild');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
|
|
13
|
-
const THEMES_DIR = path.resolve(__dirname, '../docs/themes');
|
|
14
|
-
const SHIMS_DIR = path.resolve(__dirname, '_shims');
|
|
15
|
-
fs.mkdirSync(THEMES_DIR, { recursive: true });
|
|
16
|
-
fs.mkdirSync(SHIMS_DIR, { recursive: true });
|
|
17
|
-
|
|
18
|
-
// Write browser shims for Node built-ins
|
|
19
|
-
fs.writeFileSync(path.join(SHIMS_DIR, 'fs.js'), `
|
|
20
|
-
export function readFileSync() { return ''; }
|
|
21
|
-
export function writeFileSync() {}
|
|
22
|
-
export function existsSync() { return false; }
|
|
23
|
-
export function mkdirSync() {}
|
|
24
|
-
export default { readFileSync, writeFileSync, existsSync, mkdirSync };
|
|
25
|
-
`);
|
|
26
|
-
fs.writeFileSync(path.join(SHIMS_DIR, 'path.js'), `
|
|
27
|
-
export function join(...p) { return p.join('/'); }
|
|
28
|
-
export function resolve(...p) { return p.join('/'); }
|
|
29
|
-
export function dirname(p) { return p.replace(/\\/[^/]*$/, ''); }
|
|
30
|
-
export function basename(p) { return p.replace(/.*\\//, ''); }
|
|
31
|
-
export function extname(p) { const m = p.match(/\\.[^.]+$/); return m ? m[0] : ''; }
|
|
32
|
-
export const sep = '/';
|
|
33
|
-
export default { join, resolve, dirname, basename, extname, sep };
|
|
34
|
-
`);
|
|
35
|
-
fs.writeFileSync(path.join(SHIMS_DIR, 'url.js'), `
|
|
36
|
-
export function fileURLToPath(u) { return u.replace('file://', ''); }
|
|
37
|
-
export function pathToFileURL(p) { return 'file://' + p; }
|
|
38
|
-
export class URL { constructor(u) { this.href = u; } }
|
|
39
|
-
export default { fileURLToPath, pathToFileURL, URL };
|
|
40
|
-
`);
|
|
41
|
-
fs.writeFileSync(path.join(SHIMS_DIR, 'crypto.js'), `
|
|
42
|
-
export function randomUUID() {
|
|
43
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
44
|
-
var r = Math.random() * 16 | 0;
|
|
45
|
-
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
export function createHash() {
|
|
49
|
-
return { update() { return this; }, digest() { return 'stub'; } };
|
|
50
|
-
}
|
|
51
|
-
export default { randomUUID, createHash };
|
|
52
|
-
`);
|
|
53
|
-
|
|
54
|
-
// Themes to bundle (must already be in node_modules)
|
|
55
|
-
const themes = [
|
|
56
|
-
'stackoverflow',
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
async function bundleTheme(name) {
|
|
60
|
-
const pkg = `jsonresume-theme-${name}`;
|
|
61
|
-
const entryCode = `
|
|
62
|
-
var theme = require("${pkg}");
|
|
63
|
-
export var render = theme.render;
|
|
64
|
-
export var pdfRenderOptions = theme.pdfRenderOptions;
|
|
65
|
-
`;
|
|
66
|
-
|
|
67
|
-
const entryFile = path.join(THEMES_DIR, `_entry_${name}.js`);
|
|
68
|
-
fs.writeFileSync(entryFile, entryCode);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
await esbuild.build({
|
|
72
|
-
entryPoints: [entryFile],
|
|
73
|
-
bundle: true,
|
|
74
|
-
minify: true,
|
|
75
|
-
format: 'esm',
|
|
76
|
-
target: 'es2022',
|
|
77
|
-
platform: 'browser',
|
|
78
|
-
outfile: path.join(THEMES_DIR, `${name}.js`),
|
|
79
|
-
define: {
|
|
80
|
-
'process.env.NODE_ENV': '"production"',
|
|
81
|
-
'global': 'globalThis',
|
|
82
|
-
},
|
|
83
|
-
alias: {
|
|
84
|
-
'fs': path.join(SHIMS_DIR, 'fs.js'),
|
|
85
|
-
'path': path.join(SHIMS_DIR, 'path.js'),
|
|
86
|
-
'url': path.join(SHIMS_DIR, 'url.js'),
|
|
87
|
-
'node:crypto': path.join(SHIMS_DIR, 'crypto.js'),
|
|
88
|
-
'crypto': path.join(SHIMS_DIR, 'crypto.js'),
|
|
89
|
-
'node:fs': path.join(SHIMS_DIR, 'fs.js'),
|
|
90
|
-
'node:path': path.join(SHIMS_DIR, 'path.js'),
|
|
91
|
-
'node:url': path.join(SHIMS_DIR, 'url.js'),
|
|
92
|
-
},
|
|
93
|
-
logLevel: 'warning',
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
fs.unlinkSync(entryFile);
|
|
97
|
-
const size = fs.statSync(path.join(THEMES_DIR, `${name}.js`)).size;
|
|
98
|
-
console.log(`OK ${name}: ${(size / 1024).toFixed(0)}KB`);
|
|
99
|
-
return { name, ok: true, size };
|
|
100
|
-
} catch (err) {
|
|
101
|
-
fs.unlinkSync(entryFile);
|
|
102
|
-
console.error(`FAIL ${name}: ${err.message}`);
|
|
103
|
-
return { name, ok: false };
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async function main() {
|
|
108
|
-
const results = [];
|
|
109
|
-
for (const t of themes) {
|
|
110
|
-
results.push(await bundleTheme(t));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Write manifest
|
|
114
|
-
const manifest = results.map(r => ({
|
|
115
|
-
name: r.name,
|
|
116
|
-
displayName: r.name.charAt(0).toUpperCase() + r.name.slice(1).replace(/-/g, ' '),
|
|
117
|
-
description: '',
|
|
118
|
-
browserCompatible: r.ok,
|
|
119
|
-
fileSize: r.size || 0,
|
|
120
|
-
}));
|
|
121
|
-
fs.writeFileSync(path.join(THEMES_DIR, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
122
|
-
|
|
123
|
-
// Cleanup shims
|
|
124
|
-
fs.rmSync(SHIMS_DIR, { recursive: true, force: true });
|
|
125
|
-
|
|
126
|
-
console.log(`\nManifest written with ${results.filter(r => r.ok).length}/${results.length} themes`);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
main();
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Render a small PNG thumbnail of every theme's `.snapshot.html` so the
|
|
4
|
-
* theme picker can show visual previews instead of "Stackoverfl…" ×5.
|
|
5
|
-
*
|
|
6
|
-
* For each theme in docs/themes/manifest.json that has hasSnapshot=true,
|
|
7
|
-
* open the snapshot in Playwright at 1200×1600 (full page), then
|
|
8
|
-
* screenshot-scale to ~320×420. Output to docs/themes/<name>.thumb.jpg.
|
|
9
|
-
*
|
|
10
|
-
* Requires the dev server to be running (defaults to :3010) so relative
|
|
11
|
-
* URLs inside the snapshot (fonts, CSS) resolve correctly.
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* node scripts/render-theme-thumbs.mjs
|
|
15
|
-
* node scripts/render-theme-thumbs.mjs --only stackoverflow,even
|
|
16
|
-
* node scripts/render-theme-thumbs.mjs --force # re-render existing
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { readFileSync, existsSync, statSync } from 'node:fs';
|
|
20
|
-
import { resolve, dirname } from 'node:path';
|
|
21
|
-
import { fileURLToPath } from 'node:url';
|
|
22
|
-
import { chromium } from 'playwright';
|
|
23
|
-
|
|
24
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
|
-
const ROOT = resolve(__dirname, '..');
|
|
26
|
-
const THEMES_DIR = resolve(ROOT, 'docs/themes');
|
|
27
|
-
const MANIFEST = resolve(THEMES_DIR, 'manifest.json');
|
|
28
|
-
const DEV_URL = 'http://localhost:3010';
|
|
29
|
-
|
|
30
|
-
const args = process.argv.slice(2);
|
|
31
|
-
const onlyArg = args.find((a) => a.startsWith('--only='));
|
|
32
|
-
const only = onlyArg ? new Set(onlyArg.split('=')[1].split(',')) : null;
|
|
33
|
-
const force = args.includes('--force');
|
|
34
|
-
|
|
35
|
-
function pickTargets() {
|
|
36
|
-
const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8'));
|
|
37
|
-
const out = [];
|
|
38
|
-
for (const t of manifest) {
|
|
39
|
-
if (only && !only.has(t.name)) continue;
|
|
40
|
-
if (!t.hasSnapshot) continue;
|
|
41
|
-
const thumbPath = resolve(THEMES_DIR, `${t.name}.thumb.jpg`);
|
|
42
|
-
if (!force && existsSync(thumbPath)) {
|
|
43
|
-
// Skip if thumb is newer than the snapshot
|
|
44
|
-
const snapMtime = statSync(resolve(THEMES_DIR, `${t.name}.snapshot.html`)).mtimeMs;
|
|
45
|
-
const thumbMtime = statSync(thumbPath).mtimeMs;
|
|
46
|
-
if (thumbMtime >= snapMtime) continue;
|
|
47
|
-
}
|
|
48
|
-
out.push(t.name);
|
|
49
|
-
}
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function probe() {
|
|
54
|
-
try {
|
|
55
|
-
const r = await fetch(DEV_URL);
|
|
56
|
-
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
57
|
-
} catch {
|
|
58
|
-
console.error(`Dev server not responding on ${DEV_URL}. Start it with:`);
|
|
59
|
-
console.error(' node scripts/dev-server.js --port 3010');
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function main() {
|
|
65
|
-
await probe();
|
|
66
|
-
const targets = pickTargets();
|
|
67
|
-
if (targets.length === 0) {
|
|
68
|
-
console.log('Nothing to render (use --force to regenerate existing).');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
console.log(`Rendering ${targets.length} thumbnails…`);
|
|
72
|
-
|
|
73
|
-
const browser = await chromium.launch();
|
|
74
|
-
// Render at ~2x the display size (display ≈ 240×320 in the picker grid)
|
|
75
|
-
// so thumbnails still look crisp on retina. JPEG keeps repo size down
|
|
76
|
-
// compared to PNG, ~15–25 KB per image vs 150+ for PNG.
|
|
77
|
-
const ctx = await browser.newContext({
|
|
78
|
-
viewport: { width: 480, height: 640 },
|
|
79
|
-
deviceScaleFactor: 1,
|
|
80
|
-
});
|
|
81
|
-
const page = await ctx.newPage();
|
|
82
|
-
page.on('pageerror', () => {});
|
|
83
|
-
page.on('console', () => {});
|
|
84
|
-
|
|
85
|
-
let done = 0;
|
|
86
|
-
let failed = 0;
|
|
87
|
-
for (const name of targets) {
|
|
88
|
-
try {
|
|
89
|
-
await page.goto(`${DEV_URL}/themes/${name}.snapshot.html`, {
|
|
90
|
-
waitUntil: 'networkidle',
|
|
91
|
-
timeout: 15_000,
|
|
92
|
-
});
|
|
93
|
-
// Small wait so webfonts/CSS transitions settle.
|
|
94
|
-
await page.waitForTimeout(400);
|
|
95
|
-
// Clamp to the hero portion, long resumes lose legibility when
|
|
96
|
-
// the whole page is scaled down to thumb size.
|
|
97
|
-
await page.screenshot({
|
|
98
|
-
path: resolve(THEMES_DIR, `${name}.thumb.jpg`),
|
|
99
|
-
clip: { x: 0, y: 0, width: 480, height: 640 },
|
|
100
|
-
type: 'jpeg',
|
|
101
|
-
quality: 72,
|
|
102
|
-
});
|
|
103
|
-
done++;
|
|
104
|
-
if (done % 20 === 0) process.stdout.write(` ${done}/${targets.length}\r`);
|
|
105
|
-
} catch (e) {
|
|
106
|
-
failed++;
|
|
107
|
-
console.log(` ⚠ ${name}: ${e.message?.slice(0, 80) ?? 'failed'}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
await browser.close();
|
|
111
|
-
console.log(`\n✅ Rendered ${done}/${targets.length} thumbnails (${failed} failed)`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
main().catch((e) => {
|
|
115
|
-
console.error(e);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
});
|