careermate 0.1.1 → 0.1.2

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.
Files changed (71) hide show
  1. package/README.md +12 -10
  2. package/THIRD_PARTY_NOTICES.md +15 -0
  3. package/apps/mcp/src/index.ts +4 -5
  4. package/apps/web/demo/demo-shim.js +536 -0
  5. package/apps/web/demo/demo.css +75 -0
  6. package/apps/web/demo/seed.js +269 -0
  7. package/apps/web/public/app.js +34 -2
  8. package/apps/web/public/index.html +10 -2
  9. package/apps/web/public/lib.js +134 -22
  10. package/apps/web/public/pages/applications.js +2 -9
  11. package/apps/web/public/pages/documents.js +66 -50
  12. package/apps/web/public/pages/home.js +12 -11
  13. package/apps/web/public/pages/interview.js +9 -12
  14. package/apps/web/public/pages/jobs.js +39 -36
  15. package/apps/web/public/pages/profile.js +66 -86
  16. package/apps/web/public/pages/settings.js +15 -10
  17. package/apps/web/public/styles.css +56 -2
  18. package/apps/web/src/http.ts +31 -4
  19. package/apps/web/src/info.ts +3 -3
  20. package/apps/web/src/routes.ts +2 -1
  21. package/apps/web/src/server.ts +1 -4
  22. package/dist/doctor.mjs +72607 -611
  23. package/dist/install-page/.omc/state/agent-replay-55a58c0d-bb6e-43ea-8a05-4b6c93b80b34.jsonl +12 -0
  24. package/dist/install-page/.omc/state/idle-notif-cooldown.json +3 -0
  25. package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/mission-state.json +147 -0
  26. package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/pre-tool-advisory-throttle.json +22 -0
  27. package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/subagent-tracking-state.json +44 -0
  28. package/dist/install-page/index.html +31 -70
  29. package/dist/install-page/landing.js +54 -0
  30. package/dist/install-page/start.html +13 -36
  31. package/dist/install-page/style.css +34 -1
  32. package/dist/mcp.mjs +73936 -1976
  33. package/dist/migrate.mjs +17 -10
  34. package/dist/pdf.worker.min.mjs +28 -0
  35. package/dist/public/app.js +34 -2
  36. package/dist/public/index.html +10 -2
  37. package/dist/public/lib.js +134 -22
  38. package/dist/public/pages/applications.js +2 -9
  39. package/dist/public/pages/documents.js +66 -50
  40. package/dist/public/pages/home.js +12 -11
  41. package/dist/public/pages/interview.js +9 -12
  42. package/dist/public/pages/jobs.js +39 -36
  43. package/dist/public/pages/profile.js +66 -86
  44. package/dist/public/pages/settings.js +15 -10
  45. package/dist/public/styles.css +56 -2
  46. package/dist/web.mjs +39631 -244
  47. package/docs/ARCHITECTURE.md +5 -5
  48. package/docs/DECISIONS.md +1 -1
  49. package/docs/DEMO.md +3 -3
  50. package/docs/MCP_TOOLS.md +49 -9
  51. package/docs/ROADMAP.md +8 -7
  52. package/docs/START_WORKFLOW.md +13 -7
  53. package/docs/TODO.md +1 -1
  54. package/docs/WORKFLOWS.md +7 -5
  55. package/docs/superpowers/specs/2026-06-16-demo-dashboard-deploy-design.md +64 -0
  56. package/package.json +6 -2
  57. package/packages/db/src/paths.ts +11 -0
  58. package/packages/mcp-tools/src/bridge.ts +55 -32
  59. package/packages/mcp-tools/src/index.ts +9 -0
  60. package/packages/mcp-tools/src/tools.ts +193 -70
  61. package/packages/mcp-tools/src/update.ts +330 -0
  62. package/packages/parsers/src/documents.ts +521 -0
  63. package/packages/parsers/src/index.ts +8 -5
  64. package/packages/prompts/src/onboarding.ts +2 -2
  65. package/packages/shared/src/build-info.ts +22 -0
  66. package/packages/shared/src/index.ts +1 -0
  67. package/packages/shared/src/schemas.ts +24 -3
  68. package/packages/workflows/src/definitions.ts +4 -2
  69. package/scripts/build-demo.mjs +130 -0
  70. package/scripts/build-dist.mjs +20 -1
  71. package/scripts/doctor.ts +16 -3
@@ -0,0 +1,130 @@
1
+ // =============================================================================
2
+ // build-demo.mjs — generate the no-server demo dashboard served at
3
+ // careermate.life/demo.
4
+ //
5
+ // What it does:
6
+ // 1. Copy apps/web/public/* -> install-page/demo/
7
+ // 2. Rewrite root-absolute asset paths (/lib.js, /pages/, /styles.css, ...)
8
+ // to /demo/... so they resolve under the subpath. (/api/* is left alone —
9
+ // the shim intercepts those at runtime.)
10
+ // 3. Copy the demo overlay (demo-shim.js, seed.js, demo.css) into install-page/demo/.
11
+ // 4. Inject the shim + demo.css into the copied index.html, and a hint that
12
+ // this is a demo build.
13
+ //
14
+ // The generated install-page/demo/ folder is committed so Vercel (which serves
15
+ // install-page/ statically) publishes it with no build step or config change.
16
+ //
17
+ // Run: node scripts/build-demo.mjs
18
+ //
19
+ // Note: avoids fs.rmSync(recursive) — it hard-crashes on Node 25 / Windows
20
+ // (see project memory). Uses a manual recursive remove instead.
21
+ // =============================================================================
22
+ import fs from 'node:fs';
23
+ import path from 'node:path';
24
+ import { fileURLToPath } from 'node:url';
25
+
26
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
27
+ const ROOT = path.resolve(__dirname, '..');
28
+ const SRC = path.join(ROOT, 'apps', 'web', 'public');
29
+ const OVERLAY = path.join(ROOT, 'apps', 'web', 'demo');
30
+ const OUT = path.join(ROOT, 'install-page', 'demo');
31
+
32
+ const TEXT_EXT = new Set(['.html', '.js', '.css', '.svg', '.json', '.txt', '.md']);
33
+
34
+ /** Manual recursive delete (Node-25-safe; fs.rmSync recursive crashes there). */
35
+ function rimraf(target) {
36
+ let st;
37
+ try { st = fs.lstatSync(target); } catch { return; }
38
+ if (st.isDirectory()) {
39
+ for (const entry of fs.readdirSync(target)) rimraf(path.join(target, entry));
40
+ fs.rmdirSync(target);
41
+ } else {
42
+ fs.unlinkSync(target);
43
+ }
44
+ }
45
+
46
+ /** Rewrite root-absolute references to assets so they work under /demo/. */
47
+ function rewritePaths(code) {
48
+ return code
49
+ // import/href/src strings: '/lib.js', "/app.js", '/styles.css', '/icon-180.png'
50
+ .replace(/(['"`])\/(lib\.js|app\.js|styles\.css|icon-180\.png)/g, '$1/demo/$2')
51
+ // directories referenced as '/pages/...' or '/fonts/...' (JS/HTML)
52
+ .replace(/(['"`])\/(pages|fonts)\//g, '$1/demo/$2/')
53
+ // CSS url(/fonts/...) — with or without quotes
54
+ .replace(/url\(\s*(['"]?)\/fonts\//g, 'url($1/demo/fonts/');
55
+ }
56
+
57
+ function copyTree(src, out) {
58
+ fs.mkdirSync(out, { recursive: true });
59
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
60
+ const from = path.join(src, entry.name);
61
+ const to = path.join(out, entry.name);
62
+ if (entry.isDirectory()) { copyTree(from, to); continue; }
63
+ const ext = path.extname(entry.name).toLowerCase();
64
+ if (TEXT_EXT.has(ext)) {
65
+ fs.writeFileSync(to, rewritePaths(fs.readFileSync(from, 'utf8')));
66
+ } else {
67
+ fs.copyFileSync(from, to);
68
+ }
69
+ }
70
+ }
71
+
72
+ function injectDemo(htmlPath) {
73
+ let html = fs.readFileSync(htmlPath, 'utf8');
74
+
75
+ // Pre-paint theme resolver — same key/default as the marketing site so the
76
+ // theme is shared and there's no flash (FOUC) before app.js runs.
77
+ const themeScript =
78
+ '\n <!-- Resolve the saved theme before first paint (shared with the marketing site; default dark). -->\n' +
79
+ ' <script>\n' +
80
+ ' (function () {\n' +
81
+ " try {\n" +
82
+ " var t = window.localStorage.getItem('careermate-theme') || window.localStorage.getItem('cf-theme');\n" +
83
+ " document.documentElement.setAttribute('data-theme', t === 'light' || t === 'dark' ? t : 'dark');\n" +
84
+ " } catch (e) { document.documentElement.setAttribute('data-theme', 'dark'); }\n" +
85
+ ' })();\n' +
86
+ ' </script>';
87
+ html = html.replace(
88
+ '<meta name="viewport" content="width=device-width, initial-scale=1" />',
89
+ '<meta name="viewport" content="width=device-width, initial-scale=1" />' + themeScript,
90
+ );
91
+
92
+ // demo.css after the dashboard stylesheet
93
+ html = html.replace(
94
+ '<link rel="stylesheet" href="/demo/styles.css" />',
95
+ '<link rel="stylesheet" href="/demo/styles.css" />\n <link rel="stylesheet" href="/demo/demo.css" />',
96
+ );
97
+
98
+ // Shim must run before app.js. Both are ES modules; module scripts execute in
99
+ // document order, so placing the shim immediately before app.js guarantees the
100
+ // fetch patch is installed before any page code calls it.
101
+ html = html.replace(
102
+ '<script type="module" src="/demo/app.js"></script>',
103
+ '<script type="module" src="/demo/demo-shim.js"></script>\n <script type="module" src="/demo/app.js"></script>',
104
+ );
105
+
106
+ // Title hint so the browser tab reads as a demo.
107
+ html = html.replace(
108
+ '<title>CareerMate — 내 커리어 흐름 관리</title>',
109
+ '<title>CareerMate 데모 — 대시보드 미리보기</title>',
110
+ );
111
+
112
+ fs.writeFileSync(htmlPath, html);
113
+ }
114
+
115
+ /* ------------------------------------------------------------------- run */
116
+ console.log('· clearing', path.relative(ROOT, OUT));
117
+ rimraf(OUT);
118
+
119
+ console.log('· copying dashboard', path.relative(ROOT, SRC), '->', path.relative(ROOT, OUT));
120
+ copyTree(SRC, OUT);
121
+
122
+ console.log('· copying demo overlay');
123
+ for (const f of ['demo-shim.js', 'seed.js', 'demo.css']) {
124
+ fs.copyFileSync(path.join(OVERLAY, f), path.join(OUT, f));
125
+ }
126
+
127
+ console.log('· injecting shim + styles into index.html');
128
+ injectDemo(path.join(OUT, 'index.html'));
129
+
130
+ console.log('✓ demo built at', path.relative(ROOT, OUT), '— serve install-page/ and open /demo');
@@ -67,11 +67,30 @@ async function main() {
67
67
  // install-page/careermate.mcpb 는 공개 사이트(careermate.life)의 다운로드 산출물일 뿐,
68
68
  // 번들 대시보드의 /install/ 라우트에는 필요 없다. 이걸 dist로 들이면 .mcpb 빌드가 직전
69
69
  // 번들(자기 자신, ~6MB)을 다시 접어 넣어 매 빌드마다 용량이 불어난다(자기참조). 제외한다.
70
+ // install-page/demo 는 공개 사이트(careermate.life/demo) 전용 미리보기다. 자산 경로가
71
+ // /demo/ 로 재작성돼 있어 번들 대시보드의 /install/ 라우트에서는 동작하지 않고, 폰트까지
72
+ // 포함해 ~2.5MB를 헛되이 키운다. dist에서 제외한다.
73
+ const demoDir = path.join(ROOT, 'install-page', 'demo');
70
74
  fs.cpSync(path.join(ROOT, 'install-page'), path.join(DIST, 'install-page'), {
71
75
  recursive: true,
72
- filter: (src) => path.basename(src) !== 'careermate.mcpb',
76
+ filter: (src) => path.basename(src) !== 'careermate.mcpb' && src !== demoDir,
73
77
  });
74
78
 
79
+ // pdfjs 워커 사이드카: pdf.mjs는 번들에 인라인되지만, 워커는 런타임에 별도 파일로
80
+ // 동적 로드된다(메인스레드 fake worker). 번들 옆에 복사해 두고 documents.ts가
81
+ // workerSrc로 가리킨다. 이게 없으면 PDF 추출 시 "fake worker" 로드가 실패한다.
82
+ // (node_modules가 없는 .mcpb 배포에서도 PDF가 동작하도록 dist에 자기완결로 동봉.)
83
+ const pdfWorker = path.join(ROOT, 'node_modules', 'pdfjs-dist', 'legacy', 'build', 'pdf.worker.min.mjs');
84
+ if (!fs.existsSync(pdfWorker)) {
85
+ // 경고로 넘기면 워커 없는 번들이 조용히 게시되고 런타임에 모든 PDF 추출이 깨진다(빌드는 초록).
86
+ // 빌드를 실패시켜 누락을 게시 전에 드러낸다. pdfjs 업그레이드로 워커 경로가 바뀌면 여기서 잡힌다.
87
+ throw new Error(
88
+ `pdfjs 워커를 찾지 못했습니다: ${pdfWorker}\n` +
89
+ `pdfjs-dist가 설치됐는지, 워커 파일명/경로가 바뀌지 않았는지 확인하세요(PDF 추출의 필수 자산).`,
90
+ );
91
+ }
92
+ fs.copyFileSync(pdfWorker, path.join(DIST, 'pdf.worker.min.mjs'));
93
+
75
94
  console.log('✅ dist 빌드 완료: dist/mcp.mjs, dist/web.mjs, dist/doctor.mjs, dist/migrate.mjs, dist/public, dist/install-page');
76
95
  }
77
96
 
package/scripts/doctor.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileRepo, experienceRepo, projectRepo, skillRepo, documentRepo,
11
11
  coverLetterRepo, jobRepo, applicationRepo, interviewRepo,
12
12
  } from '@careermate/db';
13
- import { TOOLS } from '@careermate/mcp-tools';
13
+ import { TOOLS, getUpdateStatusAsync } from '@careermate/mcp-tools';
14
14
 
15
15
  const ok = (s: string) => console.log(` ✅ ${s}`);
16
16
  const warn = (s: string) => console.log(` ⚠️ ${s}`);
@@ -24,7 +24,7 @@ function checkNode(): boolean {
24
24
  return good;
25
25
  }
26
26
 
27
- function main(): void {
27
+ async function main(): Promise<void> {
28
28
  console.log('\nCareerMate 상태 점검\n' + '─'.repeat(40));
29
29
 
30
30
  const nodeOk = checkNode();
@@ -68,6 +68,16 @@ function main(): void {
68
68
  // MCP tools
69
69
  ok(`MCP 도구 ${TOOLS.length}개 등록됨`);
70
70
 
71
+ // Update available?
72
+ try {
73
+ const u = await getUpdateStatusAsync();
74
+ if (u.latest === null) info(`버전 v${u.current} (업데이트 확인 생략 — 오프라인이거나 일시적 오류)`);
75
+ else if (u.update_available) warn(`새 버전 v${u.latest} 있음 (현재 v${u.current}). 업데이트: ${u.update_command}`);
76
+ else ok(`최신 버전 v${u.current}`);
77
+ } catch {
78
+ /* 업데이트 확인 실패는 점검을 막지 않는다. */
79
+ }
80
+
71
81
  // Dashboard running?
72
82
  const rt = readRuntimeInfo();
73
83
  if (rt && isProcessAlive(rt.pid)) ok(`대시보드 실행 중: ${rt.url}`);
@@ -78,4 +88,7 @@ function main(): void {
78
88
  process.exit(nodeOk && dbOk ? 0 : 1);
79
89
  }
80
90
 
81
- main();
91
+ main().catch((e) => {
92
+ console.error(e instanceof Error ? e.message : e);
93
+ process.exit(1);
94
+ });