careermate 0.0.4 → 0.0.5
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/AGENTS.md +3 -0
- package/INSTALL.md +2 -1
- package/README.md +8 -6
- package/dist/doctor.mjs +345 -65
- package/dist/mcp.mjs +344 -64
- package/dist/public/lib.js +1 -0
- package/dist/public/pages/settings.js +160 -14
- package/dist/public/styles.css +10 -2
- package/dist/site/index.html +88 -188
- package/dist/site/landing.js +109 -0
- package/dist/site/start.html +97 -94
- package/dist/site/style.css +169 -63
- package/dist/web.mjs +369 -32
- package/docs/FAQ.md +3 -2
- package/docs/MCP_TOOLS.md +10 -3
- package/docs/WORKFLOWS.md +5 -5
- package/package.json +2 -2
- package/packages/core/src/services.ts +5 -5
- package/packages/db/src/repositories.ts +0 -0
- package/packages/mcp-tools/src/bridge.ts +194 -13
- package/packages/mcp-tools/src/tools.ts +145 -17
- package/packages/mcp-tools/src/update.ts +1 -1
- package/packages/prompts/src/system.ts +10 -2
- package/packages/workflows/src/definitions.ts +2 -2
package/dist/web.mjs
CHANGED
|
@@ -6746,15 +6746,15 @@ var require_generators = __commonJS({
|
|
|
6746
6746
|
var stack = new Error().stack;
|
|
6747
6747
|
return function() {
|
|
6748
6748
|
var generator = generatorFunction.apply(this, arguments);
|
|
6749
|
-
var
|
|
6749
|
+
var spawn3 = new PromiseSpawn$(
|
|
6750
6750
|
void 0,
|
|
6751
6751
|
void 0,
|
|
6752
6752
|
yieldHandler,
|
|
6753
6753
|
stack
|
|
6754
6754
|
);
|
|
6755
|
-
var ret2 =
|
|
6756
|
-
|
|
6757
|
-
|
|
6755
|
+
var ret2 = spawn3.promise();
|
|
6756
|
+
spawn3._generator = generator;
|
|
6757
|
+
spawn3._promiseFulfilled(void 0);
|
|
6758
6758
|
return ret2;
|
|
6759
6759
|
};
|
|
6760
6760
|
};
|
|
@@ -6769,9 +6769,9 @@ var require_generators = __commonJS({
|
|
|
6769
6769
|
if (typeof generatorFunction !== "function") {
|
|
6770
6770
|
return apiRejection("generatorFunction must be a function\n\n See http://goo.gl/MqrFmX\n");
|
|
6771
6771
|
}
|
|
6772
|
-
var
|
|
6773
|
-
var ret2 =
|
|
6774
|
-
|
|
6772
|
+
var spawn3 = new PromiseSpawn(generatorFunction, this);
|
|
6773
|
+
var ret2 = spawn3.promise();
|
|
6774
|
+
spawn3._run(Promise2.spawn);
|
|
6775
6775
|
return ret2;
|
|
6776
6776
|
};
|
|
6777
6777
|
};
|
|
@@ -28299,7 +28299,7 @@ var require_files = __commonJS({
|
|
|
28299
28299
|
"use strict";
|
|
28300
28300
|
var fs5 = __require("fs");
|
|
28301
28301
|
var url = __require("url");
|
|
28302
|
-
var
|
|
28302
|
+
var os3 = __require("os");
|
|
28303
28303
|
var dirname = __require("path").dirname;
|
|
28304
28304
|
var resolvePath = __require("path").resolve;
|
|
28305
28305
|
var isAbsolutePath = require_path_is_absolute();
|
|
@@ -28341,7 +28341,7 @@ var require_files = __commonJS({
|
|
|
28341
28341
|
var readFile = promises.promisify(fs5.readFile.bind(fs5));
|
|
28342
28342
|
function uriToPath(uriString, platform) {
|
|
28343
28343
|
if (!platform) {
|
|
28344
|
-
platform =
|
|
28344
|
+
platform = os3.platform();
|
|
28345
28345
|
}
|
|
28346
28346
|
var uri = url.parse(uriString);
|
|
28347
28347
|
if (isLocalFileUri(uri) || isRelativeUri(uri)) {
|
|
@@ -39354,12 +39354,12 @@ var require_cjs = __commonJS({
|
|
|
39354
39354
|
});
|
|
39355
39355
|
|
|
39356
39356
|
// apps/web/src/index.ts
|
|
39357
|
-
import { spawn } from "node:child_process";
|
|
39357
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
39358
39358
|
|
|
39359
39359
|
// apps/web/src/server.ts
|
|
39360
39360
|
import http from "node:http";
|
|
39361
39361
|
import path5 from "node:path";
|
|
39362
|
-
import { fileURLToPath } from "node:url";
|
|
39362
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
39363
39363
|
|
|
39364
39364
|
// packages/db/src/paths.ts
|
|
39365
39365
|
import os from "node:os";
|
|
@@ -44263,7 +44263,7 @@ var ApplicationContextSchema = external_exports.object({
|
|
|
44263
44263
|
});
|
|
44264
44264
|
|
|
44265
44265
|
// packages/shared/src/build-info.ts
|
|
44266
|
-
var APP_VERSION = true ? "0.0.
|
|
44266
|
+
var APP_VERSION = true ? "0.0.5" : "0.0.0-dev";
|
|
44267
44267
|
var BUNDLED = true;
|
|
44268
44268
|
|
|
44269
44269
|
// packages/shared/src/index.ts
|
|
@@ -44775,6 +44775,7 @@ var coverLetterRepo = {
|
|
|
44775
44775
|
remove(id3) {
|
|
44776
44776
|
const db = getDb();
|
|
44777
44777
|
return tx(() => {
|
|
44778
|
+
db.prepare(`UPDATE applications SET cover_letter_id=NULL, updated_at=? WHERE cover_letter_id=?`).run(now(), id3);
|
|
44778
44779
|
db.prepare(`DELETE FROM cover_letter_versions WHERE cover_letter_id=?`).run(id3);
|
|
44779
44780
|
return db.prepare(`DELETE FROM cover_letters WHERE id=?`).run(id3).changes > 0;
|
|
44780
44781
|
});
|
|
@@ -44863,6 +44864,7 @@ var jobRepo = {
|
|
|
44863
44864
|
remove(id3) {
|
|
44864
44865
|
const db = getDb();
|
|
44865
44866
|
return tx(() => {
|
|
44867
|
+
db.prepare(`UPDATE cover_letters SET job_id=NULL, updated_at=? WHERE job_id=?`).run(now(), id3);
|
|
44866
44868
|
db.prepare(`DELETE FROM fit_analyses WHERE job_id=?`).run(id3);
|
|
44867
44869
|
db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id3);
|
|
44868
44870
|
db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id3);
|
|
@@ -45151,7 +45153,7 @@ function injectToken(html) {
|
|
|
45151
45153
|
// apps/web/src/http.ts
|
|
45152
45154
|
import fs3 from "node:fs";
|
|
45153
45155
|
import path3 from "node:path";
|
|
45154
|
-
var MAX_BODY2 =
|
|
45156
|
+
var MAX_BODY2 = 32 * 1024 * 1024;
|
|
45155
45157
|
var Router = class {
|
|
45156
45158
|
routes = [];
|
|
45157
45159
|
add(method, path6, handler) {
|
|
@@ -45746,7 +45748,7 @@ function saveJobPosting(input, id3) {
|
|
|
45746
45748
|
}
|
|
45747
45749
|
function saveFitAnalysis(input) {
|
|
45748
45750
|
const job = jobRepo.get(input.job_id);
|
|
45749
|
-
if (!job) throw notFound(
|
|
45751
|
+
if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uACF5\uACE0\uB97C \uC800\uC7A5\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.");
|
|
45750
45752
|
applicationRepo.ensure(job.id, "draft");
|
|
45751
45753
|
const fit = fitRepo.save(input);
|
|
45752
45754
|
const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
|
|
@@ -45788,7 +45790,7 @@ function saveCoverLetterVersion(input) {
|
|
|
45788
45790
|
}
|
|
45789
45791
|
function updateApplicationStatus(jobId, status, note) {
|
|
45790
45792
|
const job = jobRepo.get(jobId);
|
|
45791
|
-
if (!job) throw notFound(
|
|
45793
|
+
if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
45792
45794
|
const application = applicationRepo.setStatus(jobId, status, note);
|
|
45793
45795
|
activityRepo.log(
|
|
45794
45796
|
"application_status_changed",
|
|
@@ -45798,17 +45800,17 @@ function updateApplicationStatus(jobId, status, note) {
|
|
|
45798
45800
|
);
|
|
45799
45801
|
const interview_unlocked = INTERVIEW_UNLOCK_STATUSES.includes(status);
|
|
45800
45802
|
const hasPrep = !!interviewRepo.getByJob(jobId);
|
|
45801
|
-
const hint = interview_unlocked && !hasPrep ? "\uC11C\uB958 \uB2E8\uACC4\uB97C \uD1B5\uACFC\uD588\uC5B4\uC694. \uC774 \uACF5\uACE0 \uAE30\uC900\uC73C\uB85C \uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38\uACFC 1\uBD84 \uC790\uAE30\uC18C\uAC1C\uB97C \uC900\uBE44\uD560\uAE4C\uC694?
|
|
45803
|
+
const hint = interview_unlocked && !hasPrep ? "\uC11C\uB958 \uB2E8\uACC4\uB97C \uD1B5\uACFC\uD588\uC5B4\uC694. \uC774 \uACF5\uACE0 \uAE30\uC900\uC73C\uB85C \uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38\uACFC 1\uBD84 \uC790\uAE30\uC18C\uAC1C\uB97C \uC900\uBE44\uD560\uAE4C\uC694?" : null;
|
|
45802
45804
|
return { application, job, interview_unlocked, hint };
|
|
45803
45805
|
}
|
|
45804
45806
|
function saveInterviewPrep(input) {
|
|
45805
45807
|
const job = jobRepo.get(input.job_id);
|
|
45806
|
-
if (!job) throw notFound(
|
|
45808
|
+
if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
45807
45809
|
const app = applicationRepo.getByJob(job.id);
|
|
45808
45810
|
if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
|
|
45809
45811
|
const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
|
|
45810
45812
|
throw conflict(
|
|
45811
|
-
`\uBA74\uC811 \uC900\uBE44\uB294 '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1 \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC800\uC7A5\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uC0C1\uD0DC: '${current}'. \uBA3C\uC800
|
|
45813
|
+
`\uBA74\uC811 \uC900\uBE44\uB294 '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1 \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC800\uC7A5\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uC0C1\uD0DC: '${current}'. \uBA3C\uC800 \uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1\uC73C\uB85C \uBC14\uAFBC \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
45812
45814
|
);
|
|
45813
45815
|
}
|
|
45814
45816
|
const prep = interviewRepo.save(input);
|
|
@@ -45896,17 +45898,25 @@ CareerMate\uB294 \uC0AC\uC6A9\uC790\uC758 \uCEF4\uD4E8\uD130\uC5D0\uC11C \uB3D9\
|
|
|
45896
45898
|
|
|
45897
45899
|
6. \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB9BD\uB2C8\uB2E4.
|
|
45898
45900
|
- "MCP", "\uD234 \uD638\uCD9C", "job_id" \uAC19\uC740 \uAE30\uC220 \uC6A9\uC5B4\uB294 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC9C1\uC811 \uB178\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694. "\uACF5\uACE0\uB97C \uC800\uC7A5\uD588\uC5B4\uC694", "\uC801\uD569\uB3C4\uB97C \uBD84\uC11D\uD588\uC5B4\uC694"\uCC98\uB7FC \uC77C\uC0C1\uC801\uC778 \uD45C\uD604\uC73C\uB85C \uC9C4\uD589 \uC0C1\uD669\uC744 \uC54C\uB9AC\uC138\uC694.
|
|
45901
|
+
- \uB3C4\uAD6C \uC751\uB2F5\uC5D0 \`user_message\`\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uACE0\uD560 \uB54C \uADF8 \uBB38\uC7A5\uC744 \uC6B0\uC120 \uD65C\uC6A9\uD558\uC138\uC694. \`id\`, \`job_id\`, \`cover_letter_id\`, \`workflow_id\`, JSON \uC6D0\uBB38, \uB3C4\uAD6C \uC774\uB984\uC740 \uB0B4\uBD80 \uC791\uC5C5\uC6A9\uC73C\uB85C\uB9CC \uC4F0\uACE0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uADF8\uB300\uB85C \uBCF5\uC0AC\uD558\uC9C0 \uB9C8\uC138\uC694.
|
|
45902
|
+
- \uB3C4\uAD6C \uC751\uB2F5\uC5D0 \`suggested_next_action\`\uC774 \uC788\uC73C\uBA74 \uB2E4\uC74C \uD589\uB3D9\uC744 \uAC15\uC694\uD558\uC9C0 \uB9D0\uACE0 \uC120\uD0DD\uC9C0\uB85C \uC81C\uC548\uD558\uC138\uC694.
|
|
45899
45903
|
- \uBB34\uC5C7\uC744 \uD588\uACE0, \uBB34\uC5C7\uC744 \uC54C\uAC8C \uB418\uC5C8\uACE0, \uB2E4\uC74C\uC5D0 \uBB34\uC5C7\uC744 \uD560 \uC218 \uC788\uB294\uC9C0 \uC9E7\uACE0 \uBA85\uD655\uD558\uAC8C \uC804\uB2EC\uD558\uC138\uC694.
|
|
45900
45904
|
|
|
45901
45905
|
7. \uB2E4\uC74C \uB2E8\uACC4\uB97C \uBA3C\uC800 \uC81C\uC548\uD569\uB2C8\uB2E4.
|
|
45902
45906
|
- \uC791\uC5C5\uC774 \uB05D\uB098\uBA74 \uC790\uC5F0\uC2A4\uB7EC\uC6B4 \uB2E4\uC74C \uD589\uB3D9\uC744 \uC81C\uC548\uD558\uC138\uC694. \uC608: \uACF5\uACE0 \uBD84\uC11D \uD6C4 \u2192 "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?", \uC790\uAE30\uC18C\uAC1C\uC11C \uC800\uC7A5 \uD6C4 \u2192 "\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '\uC9C0\uC6D0 \uC644\uB8CC'\uB85C \uBC14\uAFC0\uAE4C\uC694?", \uC0C1\uD0DC\uAC00 '\uC11C\uB958 \uD569\uACA9'\uC774 \uB418\uBA74 \u2192 "\uBA74\uC811 \uC900\uBE44\uB97C \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?".
|
|
45907
|
+
- \uACF5\uACE0 \uBD84\uC11D \uD6C4\uC5D0\uB294 \uACE7\uC7A5 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC4F0\uC9C0 \uB9D0\uACE0, \uBC18\uB4DC\uC2DC \uC120\uD0DD\uC9C0\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694: "1. \uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918" / "2. \uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918". \uC0AC\uC6A9\uC790\uAC00 1\uBC88\uC744 \uACE0\uB978 \uACBD\uC6B0\uC5D0\uB9CC \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131 \uD750\uB984\uC73C\uB85C \uC774\uC5B4\uAC11\uB2C8\uB2E4.
|
|
45903
45908
|
- \`get_onboarding_status\`\uC758 next_steps\uB098 \`open_dashboard\`/\`open_application\`\uC744 \uD65C\uC6A9\uD574 \uC0AC\uC6A9\uC790\uB97C \uB2E4\uC74C \uD589\uB3D9\uC73C\uB85C \uC548\uB0B4\uD558\uC138\uC694.
|
|
45904
45909
|
|
|
45905
|
-
8. \
|
|
45910
|
+
8. \uC5C5\uB370\uC774\uD2B8 \uC694\uCCAD\uC740 \uBA85\uD655\uD558\uAC8C \uCC98\uB9AC\uD569\uB2C8\uB2E4.
|
|
45911
|
+
- \uC0AC\uC6A9\uC790\uAC00 "\uCD5C\uC2E0\uC774\uC57C?", "\uC5C5\uB370\uC774\uD2B8 \uC788\uC5B4?"\uCC98\uB7FC \uD655\uC778\uB9CC \uC694\uCCAD\uD558\uBA74 \`check_for_update\`\uB97C \uD638\uCD9C\uD558\uACE0 \uACB0\uACFC\uB97C \uC26C\uC6B4 \uB9D0\uB85C \uC54C\uB824\uC8FC\uC138\uC694.
|
|
45912
|
+
- \uC0AC\uC6A9\uC790\uAC00 "\uC5C5\uB370\uC774\uD2B8\uD574\uC918", "CareerMate \uC5C5\uB370\uC774\uD2B8\uD574\uC918", "\uCD5C\uC2E0\uC73C\uB85C \uC62C\uB824\uC918"\uCC98\uB7FC \uC801\uC6A9\uC744 \uBA85\uC2DC\uD558\uBA74 \`update_careermate\`\uB97C \uBC14\uB85C \uD638\uCD9C\uD558\uC138\uC694. \uC774 \uB3C4\uAD6C\uAC00 \uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778\uB3C4 \uD568\uAED8 \uD569\uB2C8\uB2E4.
|
|
45913
|
+
- \uC5C5\uB370\uC774\uD2B8 \uC131\uACF5 \uD6C4\uC5D0\uB294 \uC801\uC6A9\uD558\uB824\uBA74 AI \uC571 \uB610\uB294 MCP \uC5F0\uACB0\uC744 \uC7AC\uC2DC\uC791\uD574\uC57C \uD55C\uB2E4\uACE0 \uC548\uB0B4\uD558\uC138\uC694. \uC0AC\uC6A9\uC790 \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130\uB294 \uB85C\uCEEC\uC5D0 \uBCF4\uC874\uB418\uACE0, npm\uC5D0\uB294 \uACF5\uAC1C \uBC84\uC804 \uC815\uBCF4 \uD655\uC778/\uC124\uCE58 \uC694\uCCAD\uB9CC \uB098\uAC11\uB2C8\uB2E4.
|
|
45914
|
+
|
|
45915
|
+
9. \uAE00\uC740 AI \uD2F0 \uC548 \uB098\uAC8C \uC501\uB2C8\uB2E4.
|
|
45906
45916
|
- \uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uC790\uAE30 PR\xB7\uC9C0\uC6D0 \uBA54\uC77C \uB4F1 \uC0AC\uB78C\uC774 \uC4F4 \uB4EF\uD55C \uAE00\uC744 \uC791\uC131\uD558\uAE30 \uC9C1\uC804\uC5D0\uB294 \`get_writing_style_guide\`\uB97C \uD638\uCD9C\uD574 \uAE00\uC4F0\uAE30 \uADDC\uCE59\uC744 \uAC00\uC838\uC624\uACE0 \uADF8\uB300\uB85C \uC801\uC6A9\uD558\uC138\uC694.
|
|
45907
45917
|
- \uBC88\uC5ED\uD22C, "\uC5F4\uC815\uC744 \uAC00\uC9C0\uACE0/\uADC0\uC0AC\uC758 \uBC1C\uC804\uC5D0 \uAE30\uC5EC" \uAC19\uC740 \uD074\uB9AC\uC170, \uAE30\uACC4\uC801 \uBCD1\uB82C(\uCCAB\uC9F8\xB7\uB458\uC9F8\xB7\uC14B\uC9F8), \uC0C1\uD22C\uC801 \uC5F0\uACB0\uC5B4, \uADE0\uC77C\uD55C \uBB38\uC7A5 \uB9AC\uB4EC\uC744 \uD53C\uD558\uACE0 \uB2F4\uBC31\uD558\uACE0 \uAD6C\uCCB4\uC801\uC73C\uB85C \uC501\uB2C8\uB2E4. \uB2E8, \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uC808\uB300 \uBC14\uAFB8\uAC70\uB098 \uC9C0\uC5B4\uB0B4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.
|
|
45908
45918
|
|
|
45909
|
-
|
|
45919
|
+
10. \uC804\uBB38\uAC00 \uC808\uCC28\uB85C \uB9CC\uB4E4\uACE0, \uC800\uC7A5 \uC804 \uB2F9\uC2E0\uC774 \uC790\uAC00\uAC80\uC99D\uD569\uB2C8\uB2E4.
|
|
45910
45920
|
- \uACF5\uACE0 \uBD84\uC11D\xB7\uC790\uC18C\uC11C\xB7\uBA74\uC811 \uC790\uB8CC\uCC98\uB7FC \uC0AC\uC6A9\uC790 \uB300\uBA74 \uACB0\uACFC\uB97C \uB9CC\uB4E4\uAE30 \uC804\uC5D0\uB294, \uC989\uB2F5\uD558\uC9C0 \uB9D0\uACE0 \`get_workflow_guide\`(workflow_id \uC9C0\uC815)\uB97C \uBA3C\uC800 \uD638\uCD9C\uD574 \uADF8 \uC791\uC5C5\uC758 \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28(EOP)\uC640 \uC801\uC6A9\uD560 \uD50C\uB808\uC774\uBD81\xB7\uAC80\uC99D\uAE30 \uC21C\uC11C\uB97C \uBC1B\uC73C\uC138\uC694. \uC774\uC5B4 \`get_playbook\`\uC73C\uB85C \uB3C4\uBA54\uC778 \uC9C0\uC2DD\uC744 \uBC1B\uC544 \uC791\uC131\uD558\uACE0, save\uB85C \uC800\uC7A5\uD558\uAE30 \uC9C1\uC804 \`get_verifier\`\uB85C \uAC80\uC99D \uB8E8\uBE0C\uB9AD\uC744 \uBC1B\uC544 **\uB2F9\uC2E0\uC774 \uC9C1\uC811** \uC810\uAC80\xB7\uC218\uC815\uD55C \uB4A4 \uC800\uC7A5\uD569\uB2C8\uB2E4.
|
|
45911
45921
|
- CareerMate\uB294 \uC2AC\uB86D \uBC00\uB3C4\xB7\uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uCC98\uB7FC \uC140 \uC218 \uC788\uB294 \uD56D\uBAA9\uB9CC \uBCF4\uC870\uB85C \uB3D5\uACE0, \uC758\uBBF8 \uD310\uB2E8(\uC774 \uAE00\uC774 \uC9C8\uBB38\uC5D0 \uB2F5\uD558\uB294\uAC00\xB7\uD1A4\uC774 \uB9DE\uB294\uAC00)\uC740 \uB2F9\uC2E0\uC774 \uD569\uB2C8\uB2E4. \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uADF8\uB300\uB85C \uBCF4\uC874\uD569\uB2C8\uB2E4.
|
|
45912
45922
|
|
|
@@ -46097,8 +46107,8 @@ var WORKFLOWS = [
|
|
|
46097
46107
|
"\uC544\uC9C1 \uC800\uC7A5\uB418\uC9C0 \uC54A\uC740 \uACF5\uACE0\uB77C\uBA74 `save_job_posting`\uC73C\uB85C \uACF5\uACE0\uB97C \uC800\uC7A5\uD558\uACE0 job_id\uB97C \uD655\uBCF4\uD55C\uB2E4.",
|
|
46098
46108
|
"\uB3C4\uCD9C\uD55C \uBD84\uC11D \uACB0\uACFC\uB97C `save_fit_analysis`\uB85C \uC800\uC7A5\uD55C\uB2E4(job_id, score, summary, strengths, gaps, matched_keywords, missing_keywords, recommendations).",
|
|
46099
46109
|
"\uBD84\uC11D \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC124\uBA85\uD55C\uB2E4(\uC798 \uB9DE\uB294 \uC810, \uBCF4\uC644\uD560 \uC810, \uCD94\uCC9C \uC804\uB7B5). \uAE30\uC220 \uC6A9\uC5B4\uB294 \uBE7C\uACE0 \uC804\uB2EC\uD55C\uB2E4.",
|
|
46100
|
-
'"\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?"\uB77C\uACE0 \uC790\uAE30\uC18C\uAC1C\uC11C \
|
|
46101
|
-
'\uC0AC\uC6A9\uC790\uAC00 \
|
|
46110
|
+
'\uBD84\uC11D \uB4A4 \uACE7\uC7A5 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC4F0\uC9C0 \uB9D0\uACE0, "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?"\uB77C\uACE0 \uBB3B\uACE0 \uC120\uD0DD\uC9C0\uB97C \uC81C\uC2DC\uD55C\uB2E4: \u2460 "\uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918" \u2461 "\uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918".',
|
|
46111
|
+
'\uC0AC\uC6A9\uC790\uAC00 \u2460\uC744 \uC120\uD0DD\uD558\uBA74 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC791\uC131\uD558\uAE30 \uC9C1\uC804\uC5D0 `get_writing_style_guide`\uB97C \uD638\uCD9C\uD574 "AI \uD2F0 \uC548 \uB098\uB294 \uAE00\uC4F0\uAE30 \uADDC\uCE59"\uC744 \uAC00\uC838\uC628\uB2E4. \u2461\uB97C \uC120\uD0DD\uD558\uBA74 \uC790\uC18C\uC11C \uC791\uC131 \uC5C6\uC774 \uBA48\uCD94\uACE0 \uC800\uC7A5 \uACB0\uACFC\uC640 \uB300\uC2DC\uBCF4\uB4DC \uD655\uC778\uB9CC \uC548\uB0B4\uD55C\uB2E4.',
|
|
46102
46112
|
"\uC801\uD569\uB3C4 \uBD84\uC11D\xB7\uAE00\uC4F0\uAE30 \uC120\uD638\uC640 \uAE00\uC4F0\uAE30 \uADDC\uCE59\uC744 \uD568\uAED8 \uBC18\uC601\uD574 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC791\uC131\uD558\uACE0(\uBC88\uC5ED\uD22C\xB7\uD074\uB9AC\uC170\xB7\uAE30\uACC4\uC801 \uBCD1\uB82C \uC81C\uAC70, \uC0AC\uC2E4\xB7\uC218\uCE58\uB294 \uADF8\uB300\uB85C), `save_cover_letter_version`\uC73C\uB85C \uC800\uC7A5\uD55C\uB2E4(job_id \uC5F0\uACB0, note\uC5D0 \uC791\uC131 \uC758\uB3C4 \uC694\uC57D).",
|
|
46103
46113
|
"`open_application`\uC73C\uB85C \uD574\uB2F9 \uC9C0\uC6D0 \uAC74\uC744 \uC5F4\uC5B4 \uC0AC\uC6A9\uC790\uAC00 \uACB0\uACFC\uB97C \uD655\uC778\uD558\uAC8C \uD558\uACE0, \uC9C0\uC6D0 \uC0C1\uD0DC \uBCC0\uACBD(\uC608: \uC9C0\uC6D0 \uC608\uC815/\uC9C0\uC6D0 \uC644\uB8CC)\uC744 \uC81C\uC548\uD55C\uB2E4."
|
|
46104
46114
|
]
|
|
@@ -46874,20 +46884,286 @@ function getServerInfo() {
|
|
|
46874
46884
|
|
|
46875
46885
|
// apps/web/src/settings.ts
|
|
46876
46886
|
import fs4 from "node:fs";
|
|
46887
|
+
import os2 from "node:os";
|
|
46877
46888
|
import path4 from "node:path";
|
|
46889
|
+
import { spawn } from "node:child_process";
|
|
46890
|
+
import { fileURLToPath } from "node:url";
|
|
46891
|
+
var __dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
46878
46892
|
function listTables(db) {
|
|
46879
46893
|
return db.prepare(
|
|
46880
46894
|
`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name != '_meta' ORDER BY name`
|
|
46881
46895
|
).all().map((r) => r.name);
|
|
46882
46896
|
}
|
|
46897
|
+
var RESTORE_ORDER = [
|
|
46898
|
+
"profile",
|
|
46899
|
+
"experiences",
|
|
46900
|
+
"projects",
|
|
46901
|
+
"skills",
|
|
46902
|
+
"documents",
|
|
46903
|
+
"jobs",
|
|
46904
|
+
"cover_letters",
|
|
46905
|
+
"cover_letter_versions",
|
|
46906
|
+
"fit_analyses",
|
|
46907
|
+
"applications",
|
|
46908
|
+
"interview_preps",
|
|
46909
|
+
"activities"
|
|
46910
|
+
];
|
|
46911
|
+
function quoteIdent(name) {
|
|
46912
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
46913
|
+
}
|
|
46914
|
+
function shQuote(value) {
|
|
46915
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
46916
|
+
}
|
|
46917
|
+
function cmdQuote(value) {
|
|
46918
|
+
return value.replace(/"/g, '""');
|
|
46919
|
+
}
|
|
46920
|
+
function schemaVersion(db) {
|
|
46921
|
+
const ver = db.prepare(`SELECT value FROM _meta WHERE key='schema_version'`).get();
|
|
46922
|
+
return Number(ver?.value ?? 0);
|
|
46923
|
+
}
|
|
46924
|
+
function tableColumns(db, table) {
|
|
46925
|
+
return db.prepare(`PRAGMA table_info(${quoteIdent(table)})`).all().map((r) => r.name);
|
|
46926
|
+
}
|
|
46927
|
+
function isRecord(v) {
|
|
46928
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
46929
|
+
}
|
|
46930
|
+
function appRoot() {
|
|
46931
|
+
return BUNDLED ? __dirname : path4.resolve(__dirname, "..", "..", "..");
|
|
46932
|
+
}
|
|
46933
|
+
function shortcutFolder() {
|
|
46934
|
+
const override = process.env.CAREERMATE_SHORTCUT_DIR?.trim();
|
|
46935
|
+
if (override) return override;
|
|
46936
|
+
const desktop = path4.join(os2.homedir(), "Desktop");
|
|
46937
|
+
const parent = fs4.existsSync(desktop) ? desktop : getExportsDir();
|
|
46938
|
+
return path4.join(parent, "CareerMate \uB300\uC2DC\uBCF4\uB4DC");
|
|
46939
|
+
}
|
|
46940
|
+
function openFolder(dir) {
|
|
46941
|
+
try {
|
|
46942
|
+
if (process.platform === "win32") spawn("explorer", [dir], { stdio: "ignore", detached: true }).unref();
|
|
46943
|
+
else if (process.platform === "darwin") spawn("open", [dir], { stdio: "ignore", detached: true }).unref();
|
|
46944
|
+
else spawn("xdg-open", [dir], { stdio: "ignore", detached: true }).unref();
|
|
46945
|
+
} catch {
|
|
46946
|
+
}
|
|
46947
|
+
}
|
|
46948
|
+
function parseBackup(raw) {
|
|
46949
|
+
if (!isRecord(raw) || !isRecord(raw.tables)) {
|
|
46950
|
+
throw new Error("CareerMate \uBC31\uC5C5 JSON \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4.");
|
|
46951
|
+
}
|
|
46952
|
+
const rawTables = raw.tables;
|
|
46953
|
+
const db = getDb();
|
|
46954
|
+
const currentVersion = schemaVersion(db);
|
|
46955
|
+
const version = Number(raw.version);
|
|
46956
|
+
if (!Number.isInteger(version) || version < 0) {
|
|
46957
|
+
throw new Error("\uBC31\uC5C5 \uD30C\uC77C\uC758 \uC2A4\uD0A4\uB9C8 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
46958
|
+
}
|
|
46959
|
+
if (version > currentVersion) {
|
|
46960
|
+
throw new Error("\uC774 \uBC31\uC5C5\uC740 \uB354 \uCD5C\uC2E0 CareerMate\uC5D0\uC11C \uB9CC\uB4E4\uC5B4\uC84C\uC2B5\uB2C8\uB2E4. \uC571\uC744 \uC5C5\uB370\uC774\uD2B8\uD55C \uB4A4 \uB2E4\uC2DC \uAC00\uC838\uC640 \uC8FC\uC138\uC694.");
|
|
46961
|
+
}
|
|
46962
|
+
const allowedTables = new Set(listTables(db));
|
|
46963
|
+
const unknownTables = Object.keys(rawTables).filter((name) => !allowedTables.has(name));
|
|
46964
|
+
if (unknownTables.length > 0) {
|
|
46965
|
+
throw new Error(`\uD604\uC7AC \uC571\uC5D0\uC11C \uC54C \uC218 \uC5C6\uB294 \uB370\uC774\uD130\uAC00 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: ${unknownTables.join(", ")}`);
|
|
46966
|
+
}
|
|
46967
|
+
const columnsByTable = /* @__PURE__ */ new Map();
|
|
46968
|
+
for (const table of allowedTables) columnsByTable.set(table, new Set(tableColumns(db, table)));
|
|
46969
|
+
const tables = {};
|
|
46970
|
+
const counts = {};
|
|
46971
|
+
let totalRows = 0;
|
|
46972
|
+
for (const table of allowedTables) {
|
|
46973
|
+
const rows = rawTables[table] ?? [];
|
|
46974
|
+
if (!Array.isArray(rows)) throw new Error(`${table} \uB370\uC774\uD130\uAC00 \uBC30\uC5F4 \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4.`);
|
|
46975
|
+
const columns = columnsByTable.get(table);
|
|
46976
|
+
tables[table] = rows.map((row, rowIndex) => {
|
|
46977
|
+
if (!isRecord(row)) throw new Error(`${table} ${rowIndex + 1}\uBC88\uC9F8 \uD589\uC774 \uC62C\uBC14\uB978 \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4.`);
|
|
46978
|
+
const clean = {};
|
|
46979
|
+
for (const [key, value] of Object.entries(row)) {
|
|
46980
|
+
if (!columns.has(key)) throw new Error(`${table} \uB370\uC774\uD130\uC5D0 \uD604\uC7AC \uC571\uC5D0\uC11C \uC4F0\uC9C0 \uC54A\uB294 \uD56D\uBAA9(${key})\uC774 \uC788\uC2B5\uB2C8\uB2E4.`);
|
|
46981
|
+
if (value !== null && !["string", "number", "boolean"].includes(typeof value)) {
|
|
46982
|
+
throw new Error(`${table} \uB370\uC774\uD130\uC758 ${key} \uAC12 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`);
|
|
46983
|
+
}
|
|
46984
|
+
clean[key] = value;
|
|
46985
|
+
}
|
|
46986
|
+
if (Object.keys(clean).length === 0) throw new Error(`${table} ${rowIndex + 1}\uBC88\uC9F8 \uD589\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`);
|
|
46987
|
+
return clean;
|
|
46988
|
+
});
|
|
46989
|
+
counts[table] = tables[table].length;
|
|
46990
|
+
totalRows += tables[table].length;
|
|
46991
|
+
}
|
|
46992
|
+
const warnings = [...allowedTables].filter((table) => !(table in rawTables)).map((table) => `${table} \uB370\uC774\uD130\uAC00 \uBC31\uC5C5 \uD30C\uC77C\uC5D0 \uC5C6\uC5B4 \uBE48 \uAC12\uC73C\uB85C \uAC00\uC838\uC635\uB2C8\uB2E4.`);
|
|
46993
|
+
return {
|
|
46994
|
+
exported_at: typeof raw.exported_at === "string" ? raw.exported_at : null,
|
|
46995
|
+
version,
|
|
46996
|
+
current_version: currentVersion,
|
|
46997
|
+
tables,
|
|
46998
|
+
counts,
|
|
46999
|
+
total_rows: totalRows,
|
|
47000
|
+
warnings
|
|
47001
|
+
};
|
|
47002
|
+
}
|
|
47003
|
+
function macLauncherScript() {
|
|
47004
|
+
const root2 = appRoot();
|
|
47005
|
+
const dataDir = getDataDir();
|
|
47006
|
+
const nodePath = process.execPath;
|
|
47007
|
+
const binPath = path4.join(root2, "bin", "careermate.mjs");
|
|
47008
|
+
const bundledWeb = BUNDLED ? path4.join(root2, "web.mjs") : path4.join(root2, "dist", "web.mjs");
|
|
47009
|
+
return `#!/bin/zsh
|
|
47010
|
+
set -e
|
|
47011
|
+
|
|
47012
|
+
export CAREERMATE_DATA_DIR=${shQuote(dataDir)}
|
|
47013
|
+
NODE=${shQuote(nodePath)}
|
|
47014
|
+
ROOT=${shQuote(root2)}
|
|
47015
|
+
BIN=${shQuote(binPath)}
|
|
47016
|
+
BUNDLED_WEB=${shQuote(bundledWeb)}
|
|
47017
|
+
INFO="$CAREERMATE_DATA_DIR/server.json"
|
|
47018
|
+
|
|
47019
|
+
if [ ! -x "$NODE" ]; then
|
|
47020
|
+
NODE="$(command -v node || true)"
|
|
47021
|
+
fi
|
|
47022
|
+
if [ -z "$NODE" ]; then
|
|
47023
|
+
echo "Node.js\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. CareerMate\uB97C \uC124\uCE58\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694."
|
|
47024
|
+
exit 1
|
|
47025
|
+
fi
|
|
47026
|
+
|
|
47027
|
+
URL=$("$NODE" -e 'const fs=require("fs"); try { const info=JSON.parse(fs.readFileSync(process.argv[1], "utf8")); process.stdout.write(info.url || ""); } catch {}' "$INFO")
|
|
47028
|
+
if [ -n "$URL" ] && command -v curl >/dev/null 2>&1 && curl -fsS --max-time 1 "$URL/api/health" >/dev/null 2>&1; then
|
|
47029
|
+
open "$URL"
|
|
47030
|
+
exit 0
|
|
47031
|
+
fi
|
|
47032
|
+
|
|
47033
|
+
echo "CareerMate \uB300\uC2DC\uBCF4\uB4DC\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4."
|
|
47034
|
+
if [ -f "$BIN" ]; then
|
|
47035
|
+
cd "$ROOT"
|
|
47036
|
+
exec "$NODE" "$BIN" start
|
|
47037
|
+
fi
|
|
47038
|
+
|
|
47039
|
+
if [ -f "$BUNDLED_WEB" ]; then
|
|
47040
|
+
cd "$(dirname "$BUNDLED_WEB")"
|
|
47041
|
+
exec "$NODE" --no-warnings --experimental-sqlite "$BUNDLED_WEB"
|
|
47042
|
+
fi
|
|
47043
|
+
|
|
47044
|
+
if command -v careermate >/dev/null 2>&1; then
|
|
47045
|
+
exec careermate start
|
|
47046
|
+
fi
|
|
47047
|
+
|
|
47048
|
+
exec npx -y careermate start
|
|
47049
|
+
`;
|
|
47050
|
+
}
|
|
47051
|
+
function windowsLauncherScript() {
|
|
47052
|
+
const root2 = appRoot();
|
|
47053
|
+
const dataDir = getDataDir();
|
|
47054
|
+
const nodePath = process.execPath;
|
|
47055
|
+
const binPath = path4.join(root2, "bin", "careermate.mjs");
|
|
47056
|
+
const bundledWeb = BUNDLED ? path4.join(root2, "web.mjs") : path4.join(root2, "dist", "web.mjs");
|
|
47057
|
+
return `@echo off
|
|
47058
|
+
setlocal
|
|
47059
|
+
set "CAREERMATE_DATA_DIR=${cmdQuote(dataDir)}"
|
|
47060
|
+
set "NODE=${cmdQuote(nodePath)}"
|
|
47061
|
+
set "ROOT=${cmdQuote(root2)}"
|
|
47062
|
+
set "BIN=${cmdQuote(binPath)}"
|
|
47063
|
+
set "BUNDLED_WEB=${cmdQuote(bundledWeb)}"
|
|
47064
|
+
set "INFO=%CAREERMATE_DATA_DIR%\\server.json"
|
|
47065
|
+
set "URL="
|
|
47066
|
+
|
|
47067
|
+
if not exist "%NODE%" (
|
|
47068
|
+
for /f "usebackq delims=" %%N in (\`where node 2^>nul\`) do (
|
|
47069
|
+
set "NODE=%%N"
|
|
47070
|
+
goto :node_found
|
|
47071
|
+
)
|
|
47072
|
+
)
|
|
47073
|
+
:node_found
|
|
47074
|
+
if not exist "%NODE%" (
|
|
47075
|
+
echo Node.js\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. CareerMate\uB97C \uC124\uCE58\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.
|
|
47076
|
+
pause
|
|
47077
|
+
exit /b 1
|
|
47078
|
+
)
|
|
47079
|
+
|
|
47080
|
+
for /f "usebackq delims=" %%U in (\`"%NODE%" -e "const fs=require('fs'); try { const info=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(info.url || ''); } catch {}" "%INFO%"\`) do set "URL=%%U"
|
|
47081
|
+
if defined URL (
|
|
47082
|
+
powershell -NoProfile -ExecutionPolicy Bypass -Command "try { Invoke-WebRequest -UseBasicParsing -TimeoutSec 1 '%URL%/api/health' | Out-Null; Start-Process '%URL%'; exit 0 } catch { exit 1 }"
|
|
47083
|
+
if not errorlevel 1 exit /b 0
|
|
47084
|
+
)
|
|
47085
|
+
|
|
47086
|
+
echo CareerMate \uB300\uC2DC\uBCF4\uB4DC\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4.
|
|
47087
|
+
if exist "%BIN%" (
|
|
47088
|
+
cd /d "%ROOT%"
|
|
47089
|
+
"%NODE%" "%BIN%" start
|
|
47090
|
+
exit /b %ERRORLEVEL%
|
|
47091
|
+
)
|
|
47092
|
+
|
|
47093
|
+
if exist "%BUNDLED_WEB%" (
|
|
47094
|
+
cd /d "%ROOT%"
|
|
47095
|
+
"%NODE%" --no-warnings --experimental-sqlite "%BUNDLED_WEB%"
|
|
47096
|
+
exit /b %ERRORLEVEL%
|
|
47097
|
+
)
|
|
47098
|
+
|
|
47099
|
+
where careermate >nul 2>nul
|
|
47100
|
+
if not errorlevel 1 (
|
|
47101
|
+
careermate start
|
|
47102
|
+
exit /b %ERRORLEVEL%
|
|
47103
|
+
)
|
|
47104
|
+
|
|
47105
|
+
npx -y careermate start
|
|
47106
|
+
`;
|
|
47107
|
+
}
|
|
47108
|
+
function linuxLauncherScript() {
|
|
47109
|
+
return macLauncherScript().replace("#!/bin/zsh", "#!/usr/bin/env sh").replace('\n open "$URL"', '\n xdg-open "$URL"');
|
|
47110
|
+
}
|
|
46883
47111
|
function exportAll() {
|
|
46884
47112
|
const db = getDb();
|
|
46885
47113
|
const tables = {};
|
|
46886
47114
|
for (const t of listTables(db)) {
|
|
46887
|
-
tables[t] = db.prepare(`SELECT * FROM ${t}`).all();
|
|
47115
|
+
tables[t] = db.prepare(`SELECT * FROM ${quoteIdent(t)}`).all();
|
|
46888
47116
|
}
|
|
46889
|
-
|
|
46890
|
-
|
|
47117
|
+
return { exported_at: (/* @__PURE__ */ new Date()).toISOString(), version: schemaVersion(db), tables };
|
|
47118
|
+
}
|
|
47119
|
+
function previewBackupImport(raw) {
|
|
47120
|
+
const parsed = parseBackup(raw);
|
|
47121
|
+
return {
|
|
47122
|
+
exported_at: parsed.exported_at,
|
|
47123
|
+
version: parsed.version,
|
|
47124
|
+
current_version: parsed.current_version,
|
|
47125
|
+
counts: parsed.counts,
|
|
47126
|
+
total_rows: parsed.total_rows,
|
|
47127
|
+
warnings: parsed.warnings
|
|
47128
|
+
};
|
|
47129
|
+
}
|
|
47130
|
+
function restoreBackup(raw) {
|
|
47131
|
+
const parsed = parseBackup(raw);
|
|
47132
|
+
const safety = createBackup();
|
|
47133
|
+
const db = getDb();
|
|
47134
|
+
const tables = listTables(db);
|
|
47135
|
+
const restoreOrder = [
|
|
47136
|
+
...RESTORE_ORDER.filter((table) => tables.includes(table)),
|
|
47137
|
+
...tables.filter((table) => !RESTORE_ORDER.includes(table))
|
|
47138
|
+
];
|
|
47139
|
+
tx(() => {
|
|
47140
|
+
for (const table of [...restoreOrder].reverse()) {
|
|
47141
|
+
db.prepare(`DELETE FROM ${quoteIdent(table)}`).run();
|
|
47142
|
+
}
|
|
47143
|
+
for (const table of restoreOrder) {
|
|
47144
|
+
for (const row of parsed.tables[table] ?? []) {
|
|
47145
|
+
const columns = Object.keys(row);
|
|
47146
|
+
const placeholders = columns.map(() => "?").join(",");
|
|
47147
|
+
const values2 = columns.map((column) => {
|
|
47148
|
+
const value = row[column];
|
|
47149
|
+
return typeof value === "boolean" ? Number(value) : value;
|
|
47150
|
+
});
|
|
47151
|
+
db.prepare(
|
|
47152
|
+
`INSERT INTO ${quoteIdent(table)} (${columns.map(quoteIdent).join(",")}) VALUES (${placeholders})`
|
|
47153
|
+
).run(...values2);
|
|
47154
|
+
}
|
|
47155
|
+
}
|
|
47156
|
+
const fkProblems = db.prepare(`PRAGMA foreign_key_check`).all();
|
|
47157
|
+
if (fkProblems.length > 0) {
|
|
47158
|
+
throw new Error("\uBC31\uC5C5 \uD30C\uC77C\uC758 \uB370\uC774\uD130 \uAD00\uACC4\uAC00 \uB9DE\uC9C0 \uC54A\uC544 \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
47159
|
+
}
|
|
47160
|
+
});
|
|
47161
|
+
return {
|
|
47162
|
+
ok: true,
|
|
47163
|
+
safety_backup_path: safety.backup_path,
|
|
47164
|
+
safety_json_path: safety.json_path,
|
|
47165
|
+
restored: previewBackupImport(raw)
|
|
47166
|
+
};
|
|
46891
47167
|
}
|
|
46892
47168
|
function createBackup() {
|
|
46893
47169
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -46908,6 +47184,38 @@ function createBackup() {
|
|
|
46908
47184
|
fs4.writeFileSync(jsonBackup, JSON.stringify(exportAll(), null, 2), "utf8");
|
|
46909
47185
|
return { backup_path: dbBackup, json_path: jsonBackup };
|
|
46910
47186
|
}
|
|
47187
|
+
function createDashboardShortcut(open = false) {
|
|
47188
|
+
const dir = shortcutFolder();
|
|
47189
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
47190
|
+
const readme = `CareerMate \uB300\uC2DC\uBCF4\uB4DC \uBC14\uB85C\uAC00\uAE30
|
|
47191
|
+
|
|
47192
|
+
- \uB300\uC2DC\uBCF4\uB4DC\uAC00 \uC774\uBBF8 \uC2E4\uD589 \uC911\uC774\uBA74 \uBE0C\uB77C\uC6B0\uC800\uB9CC \uC5FD\uB2C8\uB2E4.
|
|
47193
|
+
- \uB300\uC2DC\uBCF4\uB4DC\uAC00 \uAEBC\uC838 \uC788\uC73C\uBA74 \uC11C\uBC84\uB97C \uC2DC\uC791\uD558\uACE0 \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5FD\uB2C8\uB2E4.
|
|
47194
|
+
- \uC774 \uD3F4\uB354\uB294 \uC774 \uCEF4\uD4E8\uD130\uC758 CareerMate \uC124\uCE58 \uACBD\uB85C\uC640 \uB370\uC774\uD130 \uD3F4\uB354\uB97C \uAE30\uC900\uC73C\uB85C \uB9CC\uB4E4\uC5B4\uC84C\uC2B5\uB2C8\uB2E4.
|
|
47195
|
+
`;
|
|
47196
|
+
const readmePath = path4.join(dir, "README.txt");
|
|
47197
|
+
fs4.writeFileSync(readmePath, readme, "utf8");
|
|
47198
|
+
let launcherPath;
|
|
47199
|
+
if (process.platform === "win32") {
|
|
47200
|
+
launcherPath = path4.join(dir, "CareerMate \uB300\uC2DC\uBCF4\uB4DC.cmd");
|
|
47201
|
+
fs4.writeFileSync(launcherPath, windowsLauncherScript(), "utf8");
|
|
47202
|
+
} else if (process.platform === "darwin") {
|
|
47203
|
+
launcherPath = path4.join(dir, "CareerMate \uB300\uC2DC\uBCF4\uB4DC.command");
|
|
47204
|
+
fs4.writeFileSync(launcherPath, macLauncherScript(), { encoding: "utf8", mode: 493 });
|
|
47205
|
+
fs4.chmodSync(launcherPath, 493);
|
|
47206
|
+
} else {
|
|
47207
|
+
launcherPath = path4.join(dir, "careermate-dashboard.sh");
|
|
47208
|
+
fs4.writeFileSync(launcherPath, linuxLauncherScript(), { encoding: "utf8", mode: 493 });
|
|
47209
|
+
fs4.chmodSync(launcherPath, 493);
|
|
47210
|
+
}
|
|
47211
|
+
if (open) openFolder(dir);
|
|
47212
|
+
return {
|
|
47213
|
+
shortcut_dir: dir,
|
|
47214
|
+
launcher_path: launcherPath,
|
|
47215
|
+
platform: process.platform,
|
|
47216
|
+
files: [launcherPath, readmePath]
|
|
47217
|
+
};
|
|
47218
|
+
}
|
|
46911
47219
|
function resetAll(confirm) {
|
|
46912
47220
|
if (confirm !== "DELETE") {
|
|
46913
47221
|
return { ok: false };
|
|
@@ -46916,7 +47224,7 @@ function resetAll(confirm) {
|
|
|
46916
47224
|
const db = getDb();
|
|
46917
47225
|
db.exec("BEGIN");
|
|
46918
47226
|
try {
|
|
46919
|
-
for (const t of listTables(db)) db.exec(`DELETE FROM ${t}`);
|
|
47227
|
+
for (const t of listTables(db)) db.exec(`DELETE FROM ${quoteIdent(t)}`);
|
|
46920
47228
|
db.exec("COMMIT");
|
|
46921
47229
|
} catch (e) {
|
|
46922
47230
|
db.exec("ROLLBACK");
|
|
@@ -46944,6 +47252,9 @@ function fmt(ctx) {
|
|
|
46944
47252
|
const f = (ctx.query.get("format") ?? "md").toLowerCase();
|
|
46945
47253
|
return ["md", "html", "txt"].includes(f) ? f : "md";
|
|
46946
47254
|
}
|
|
47255
|
+
function backupError(e) {
|
|
47256
|
+
return new HttpError(400, e instanceof Error ? e.message : "\uBC31\uC5C5 \uD30C\uC77C\uC744 \uCC98\uB9AC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.", "backup_import");
|
|
47257
|
+
}
|
|
46947
47258
|
function jobDetail(jobId) {
|
|
46948
47259
|
const job = jobRepo.get(jobId);
|
|
46949
47260
|
if (!job) throw new HttpError(404, "\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
@@ -47160,6 +47471,32 @@ function registerApiRoutes(router2) {
|
|
|
47160
47471
|
return { verify_strict: on };
|
|
47161
47472
|
});
|
|
47162
47473
|
router2.post("/api/settings/backup", () => createBackup());
|
|
47474
|
+
router2.post("/api/settings/dashboard-shortcut", async (ctx) => {
|
|
47475
|
+
const { open } = await readJsonBody(ctx.req, external_exports.object({ open: external_exports.boolean().optional() }));
|
|
47476
|
+
return { shortcut: createDashboardShortcut(open === true) };
|
|
47477
|
+
});
|
|
47478
|
+
router2.post("/api/settings/import-preview", async (ctx) => {
|
|
47479
|
+
const { backup } = await readJsonBody(ctx.req, external_exports.object({ backup: external_exports.unknown() }));
|
|
47480
|
+
try {
|
|
47481
|
+
return { preview: previewBackupImport(backup) };
|
|
47482
|
+
} catch (e) {
|
|
47483
|
+
throw backupError(e);
|
|
47484
|
+
}
|
|
47485
|
+
});
|
|
47486
|
+
router2.post("/api/settings/restore", async (ctx) => {
|
|
47487
|
+
const { backup, confirm } = await readJsonBody(
|
|
47488
|
+
ctx.req,
|
|
47489
|
+
external_exports.object({ backup: external_exports.unknown(), confirm: external_exports.string() })
|
|
47490
|
+
);
|
|
47491
|
+
if (confirm !== "RESTORE") {
|
|
47492
|
+
throw new HttpError(400, "\uBCF5\uC6D0\uC744 \uC9C4\uD589\uD558\uB824\uBA74 \uD655\uC778 \uAC12\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", "restore_confirm_required");
|
|
47493
|
+
}
|
|
47494
|
+
try {
|
|
47495
|
+
return restoreBackup(backup);
|
|
47496
|
+
} catch (e) {
|
|
47497
|
+
throw backupError(e);
|
|
47498
|
+
}
|
|
47499
|
+
});
|
|
47163
47500
|
router2.post("/api/settings/reset", async (ctx) => {
|
|
47164
47501
|
const { confirm } = await readJsonBody(ctx.req, external_exports.object({ confirm: external_exports.string() }));
|
|
47165
47502
|
const result2 = resetAll(confirm);
|
|
@@ -47187,9 +47524,9 @@ function isDownload(v) {
|
|
|
47187
47524
|
}
|
|
47188
47525
|
|
|
47189
47526
|
// apps/web/src/server.ts
|
|
47190
|
-
var
|
|
47191
|
-
var WEB_ROOT = BUNDLED ? path5.resolve(
|
|
47192
|
-
var INSTALL_ROOT = BUNDLED ? path5.resolve(
|
|
47527
|
+
var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
|
|
47528
|
+
var WEB_ROOT = BUNDLED ? path5.resolve(__dirname2, "public") : path5.resolve(__dirname2, "..", "public");
|
|
47529
|
+
var INSTALL_ROOT = BUNDLED ? path5.resolve(__dirname2, "site") : path5.resolve(__dirname2, "..", "..", "..", "site");
|
|
47193
47530
|
var router = new Router();
|
|
47194
47531
|
registerApiRoutes(router);
|
|
47195
47532
|
function createServer() {
|
|
@@ -47291,11 +47628,11 @@ function openBrowser(url) {
|
|
|
47291
47628
|
const platform = process.platform;
|
|
47292
47629
|
try {
|
|
47293
47630
|
if (platform === "win32") {
|
|
47294
|
-
|
|
47631
|
+
spawn2("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
|
|
47295
47632
|
} else if (platform === "darwin") {
|
|
47296
|
-
|
|
47633
|
+
spawn2("open", [url], { stdio: "ignore", detached: true }).unref();
|
|
47297
47634
|
} else {
|
|
47298
|
-
|
|
47635
|
+
spawn2("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
|
|
47299
47636
|
}
|
|
47300
47637
|
} catch {
|
|
47301
47638
|
}
|
package/docs/FAQ.md
CHANGED
|
@@ -32,10 +32,11 @@ CareerMate는 **Node.js 22.5.0 이상**이 필요합니다(`node:sqlite` 사용)
|
|
|
32
32
|
## 대시보드
|
|
33
33
|
|
|
34
34
|
### "대시보드가 안 열려요" (`http://127.0.0.1:4319`)
|
|
35
|
-
- 그 주소는 **CareerMate가 실행 중일 때만** 열립니다. AI에게
|
|
36
|
-
서버가
|
|
35
|
+
- 그 주소는 **CareerMate가 실행 중일 때만** 열립니다. AI에게 **"대시보드 열어줘"** 라고 하면
|
|
36
|
+
서버가 꺼져 있을 때도 백그라운드로 자동 시작한 뒤 브라우저를 엽니다.
|
|
37
37
|
- **포트가 이미 사용 중**이면 자동으로 다른 포트로 폴백합니다. 환경변수
|
|
38
38
|
`CAREERMATE_PORT`로 고정할 수 있습니다.
|
|
39
|
+
- 자동 시작이 실패하면 `~/.careermate/server.log`를 확인하세요.
|
|
39
40
|
- 대시보드는 보안상 **`127.0.0.1`(이 컴퓨터)** 에만 열립니다. 다른 기기에서는 접속되지
|
|
40
41
|
않습니다(의도된 동작).
|
|
41
42
|
|