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/doctor.mjs
CHANGED
|
@@ -6477,9 +6477,9 @@ var require_timers = __commonJS({
|
|
|
6477
6477
|
clearTimeout(this.handle);
|
|
6478
6478
|
};
|
|
6479
6479
|
var afterValue = function(value) {
|
|
6480
|
-
return
|
|
6480
|
+
return delay2(+this).thenReturn(value);
|
|
6481
6481
|
};
|
|
6482
|
-
var
|
|
6482
|
+
var delay2 = Promise2.delay = function(ms, value) {
|
|
6483
6483
|
var ret2;
|
|
6484
6484
|
var handle;
|
|
6485
6485
|
if (value !== void 0) {
|
|
@@ -6501,7 +6501,7 @@ var require_timers = __commonJS({
|
|
|
6501
6501
|
return ret2;
|
|
6502
6502
|
};
|
|
6503
6503
|
Promise2.prototype.delay = function(ms) {
|
|
6504
|
-
return
|
|
6504
|
+
return delay2(ms, this);
|
|
6505
6505
|
};
|
|
6506
6506
|
var afterTimeout = function(promise, message, parent) {
|
|
6507
6507
|
var err2;
|
|
@@ -28297,7 +28297,7 @@ var require_path_is_absolute = __commonJS({
|
|
|
28297
28297
|
var require_files = __commonJS({
|
|
28298
28298
|
"node_modules/mammoth/lib/docx/files.js"(exports2) {
|
|
28299
28299
|
"use strict";
|
|
28300
|
-
var
|
|
28300
|
+
var fs9 = __require("fs");
|
|
28301
28301
|
var url = __require("url");
|
|
28302
28302
|
var os3 = __require("os");
|
|
28303
28303
|
var dirname = __require("path").dirname;
|
|
@@ -28338,7 +28338,7 @@ var require_files = __commonJS({
|
|
|
28338
28338
|
read
|
|
28339
28339
|
};
|
|
28340
28340
|
}
|
|
28341
|
-
var readFile = promises.promisify(
|
|
28341
|
+
var readFile = promises.promisify(fs9.readFile.bind(fs9));
|
|
28342
28342
|
function uriToPath(uriString, platform) {
|
|
28343
28343
|
if (!platform) {
|
|
28344
28344
|
platform = os3.platform();
|
|
@@ -31080,11 +31080,11 @@ var require_options_reader = __commonJS({
|
|
|
31080
31080
|
var require_unzip = __commonJS({
|
|
31081
31081
|
"node_modules/mammoth/lib/unzip.js"(exports2) {
|
|
31082
31082
|
"use strict";
|
|
31083
|
-
var
|
|
31083
|
+
var fs9 = __require("fs");
|
|
31084
31084
|
var promises = require_promises();
|
|
31085
31085
|
var zipfile = require_zipfile();
|
|
31086
31086
|
exports2.openZip = openZip;
|
|
31087
|
-
var readFile = promises.promisify(
|
|
31087
|
+
var readFile = promises.promisify(fs9.readFile);
|
|
31088
31088
|
function openZip(options) {
|
|
31089
31089
|
if (options.path) {
|
|
31090
31090
|
return readFile(options.path).then(zipfile.openArrayBuffer);
|
|
@@ -31809,9 +31809,9 @@ var require_cjs = __commonJS({
|
|
|
31809
31809
|
}
|
|
31810
31810
|
return o;
|
|
31811
31811
|
}
|
|
31812
|
-
var
|
|
31812
|
+
var fs9;
|
|
31813
31813
|
function get_fs() {
|
|
31814
|
-
return
|
|
31814
|
+
return fs9 || (fs9 = require$$0__default["default"]);
|
|
31815
31815
|
}
|
|
31816
31816
|
function parse2(file, options) {
|
|
31817
31817
|
if (file[0] == 80 && file[1] == 75) return parse_zip(file, options);
|
|
@@ -32096,7 +32096,7 @@ var require_cjs = __commonJS({
|
|
|
32096
32096
|
}
|
|
32097
32097
|
function read_file(filename2, options) {
|
|
32098
32098
|
get_fs();
|
|
32099
|
-
return parse2(
|
|
32099
|
+
return parse2(fs9.readFileSync(filename2), options);
|
|
32100
32100
|
}
|
|
32101
32101
|
function read(blob, options) {
|
|
32102
32102
|
switch (options && options.type || "base64") {
|
|
@@ -32411,7 +32411,7 @@ var require_cjs = __commonJS({
|
|
|
32411
32411
|
function write_file(cfb2, filename2, options) {
|
|
32412
32412
|
get_fs();
|
|
32413
32413
|
var o = _write(cfb2, options);
|
|
32414
|
-
|
|
32414
|
+
fs9.writeFileSync(filename2, o);
|
|
32415
32415
|
}
|
|
32416
32416
|
function a2s(o) {
|
|
32417
32417
|
var out = new Array(o.length);
|
|
@@ -32423,7 +32423,7 @@ var require_cjs = __commonJS({
|
|
|
32423
32423
|
switch (options && options.type || "buffer") {
|
|
32424
32424
|
case "file":
|
|
32425
32425
|
get_fs();
|
|
32426
|
-
|
|
32426
|
+
fs9.writeFileSync(options.filename, o);
|
|
32427
32427
|
return o;
|
|
32428
32428
|
case "binary":
|
|
32429
32429
|
return typeof o == "string" ? o : a2s(o);
|
|
@@ -40116,8 +40116,8 @@ function blend(fg, bg, alpha) {
|
|
|
40116
40116
|
return Math.round(alpha * fg + (1 - alpha) * bg);
|
|
40117
40117
|
}
|
|
40118
40118
|
async function node_utils_fetchData(url) {
|
|
40119
|
-
const
|
|
40120
|
-
const data = await
|
|
40119
|
+
const fs9 = process.getBuiltinModule("fs/promises");
|
|
40120
|
+
const data = await fs9.readFile(url);
|
|
40121
40121
|
return new Uint8Array(data);
|
|
40122
40122
|
}
|
|
40123
40123
|
function initGPU() {
|
|
@@ -40746,11 +40746,11 @@ function network_getArrayBuffer(val) {
|
|
|
40746
40746
|
return typeof val !== "string" ? val : stringToBytes(val).buffer;
|
|
40747
40747
|
}
|
|
40748
40748
|
function getReadableStream(url, opts = null) {
|
|
40749
|
-
const
|
|
40749
|
+
const fs9 = process.getBuiltinModule("fs");
|
|
40750
40750
|
const {
|
|
40751
40751
|
Readable
|
|
40752
40752
|
} = process.getBuiltinModule("stream");
|
|
40753
|
-
const readStream =
|
|
40753
|
+
const readStream = fs9.createReadStream(url, opts);
|
|
40754
40754
|
return Readable.toWeb(readStream);
|
|
40755
40755
|
}
|
|
40756
40756
|
function getNetworkStream(url) {
|
|
@@ -57747,8 +57747,8 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
57747
57747
|
url
|
|
57748
57748
|
} = stream._source;
|
|
57749
57749
|
this._isStreamingSupported = !disableStream;
|
|
57750
|
-
const
|
|
57751
|
-
|
|
57750
|
+
const fs9 = process.getBuiltinModule("fs/promises");
|
|
57751
|
+
fs9.lstat(url).then((stat) => {
|
|
57752
57752
|
const readableStream = getReadableStream(url);
|
|
57753
57753
|
this._reader = readableStream.getReader();
|
|
57754
57754
|
const {
|
|
@@ -59394,9 +59394,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
59394
59394
|
return;
|
|
59395
59395
|
}
|
|
59396
59396
|
if (reason instanceof RenderingCancelledException) {
|
|
59397
|
-
let
|
|
59397
|
+
let delay2 = RENDERING_CANCELLED_TIMEOUT;
|
|
59398
59398
|
if (reason.extraDelay > 0 && reason.extraDelay < 1e3) {
|
|
59399
|
-
|
|
59399
|
+
delay2 += reason.extraDelay;
|
|
59400
59400
|
}
|
|
59401
59401
|
intentState.streamReaderCancelTimeout = setTimeout(() => {
|
|
59402
59402
|
intentState.streamReaderCancelTimeout = null;
|
|
@@ -59405,7 +59405,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
59405
59405
|
reason,
|
|
59406
59406
|
force: true
|
|
59407
59407
|
});
|
|
59408
|
-
},
|
|
59408
|
+
}, delay2);
|
|
59409
59409
|
return;
|
|
59410
59410
|
}
|
|
59411
59411
|
}
|
|
@@ -71040,7 +71040,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
71040
71040
|
});
|
|
71041
71041
|
|
|
71042
71042
|
// scripts/doctor.ts
|
|
71043
|
-
import
|
|
71043
|
+
import fs8 from "node:fs";
|
|
71044
71044
|
|
|
71045
71045
|
// packages/db/src/paths.ts
|
|
71046
71046
|
import os from "node:os";
|
|
@@ -75932,7 +75932,7 @@ var ApplicationContextSchema = external_exports.object({
|
|
|
75932
75932
|
});
|
|
75933
75933
|
|
|
75934
75934
|
// packages/shared/src/build-info.ts
|
|
75935
|
-
var APP_VERSION = true ? "0.0.
|
|
75935
|
+
var APP_VERSION = true ? "0.0.5" : "0.0.0-dev";
|
|
75936
75936
|
var BUNDLED = true;
|
|
75937
75937
|
|
|
75938
75938
|
// packages/shared/src/index.ts
|
|
@@ -76444,6 +76444,7 @@ var coverLetterRepo = {
|
|
|
76444
76444
|
remove(id2) {
|
|
76445
76445
|
const db = getDb();
|
|
76446
76446
|
return tx(() => {
|
|
76447
|
+
db.prepare(`UPDATE applications SET cover_letter_id=NULL, updated_at=? WHERE cover_letter_id=?`).run(now(), id2);
|
|
76447
76448
|
db.prepare(`DELETE FROM cover_letter_versions WHERE cover_letter_id=?`).run(id2);
|
|
76448
76449
|
return db.prepare(`DELETE FROM cover_letters WHERE id=?`).run(id2).changes > 0;
|
|
76449
76450
|
});
|
|
@@ -76532,6 +76533,7 @@ var jobRepo = {
|
|
|
76532
76533
|
remove(id2) {
|
|
76533
76534
|
const db = getDb();
|
|
76534
76535
|
return tx(() => {
|
|
76536
|
+
db.prepare(`UPDATE cover_letters SET job_id=NULL, updated_at=? WHERE job_id=?`).run(now(), id2);
|
|
76535
76537
|
db.prepare(`DELETE FROM fit_analyses WHERE job_id=?`).run(id2);
|
|
76536
76538
|
db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id2);
|
|
76537
76539
|
db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id2);
|
|
@@ -76730,7 +76732,7 @@ var activityRepo = {
|
|
|
76730
76732
|
};
|
|
76731
76733
|
|
|
76732
76734
|
// packages/mcp-tools/src/tools.ts
|
|
76733
|
-
import
|
|
76735
|
+
import fs7 from "node:fs";
|
|
76734
76736
|
import path7 from "node:path";
|
|
76735
76737
|
|
|
76736
76738
|
// packages/core/src/onboarding.ts
|
|
@@ -77210,7 +77212,7 @@ function saveJobPosting(input, id2) {
|
|
|
77210
77212
|
}
|
|
77211
77213
|
function saveFitAnalysis(input) {
|
|
77212
77214
|
const job = jobRepo.get(input.job_id);
|
|
77213
|
-
if (!job) throw notFound(
|
|
77215
|
+
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.");
|
|
77214
77216
|
applicationRepo.ensure(job.id, "draft");
|
|
77215
77217
|
const fit = fitRepo.save(input);
|
|
77216
77218
|
const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
|
|
@@ -77252,7 +77254,7 @@ function saveCoverLetterVersion(input) {
|
|
|
77252
77254
|
}
|
|
77253
77255
|
function updateApplicationStatus(jobId, status, note) {
|
|
77254
77256
|
const job = jobRepo.get(jobId);
|
|
77255
|
-
if (!job) throw notFound(
|
|
77257
|
+
if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
77256
77258
|
const application = applicationRepo.setStatus(jobId, status, note);
|
|
77257
77259
|
activityRepo.log(
|
|
77258
77260
|
"application_status_changed",
|
|
@@ -77262,17 +77264,17 @@ function updateApplicationStatus(jobId, status, note) {
|
|
|
77262
77264
|
);
|
|
77263
77265
|
const interview_unlocked = INTERVIEW_UNLOCK_STATUSES.includes(status);
|
|
77264
77266
|
const hasPrep = !!interviewRepo.getByJob(jobId);
|
|
77265
|
-
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?
|
|
77267
|
+
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;
|
|
77266
77268
|
return { application, job, interview_unlocked, hint };
|
|
77267
77269
|
}
|
|
77268
77270
|
function saveInterviewPrep(input) {
|
|
77269
77271
|
const job = jobRepo.get(input.job_id);
|
|
77270
|
-
if (!job) throw notFound(
|
|
77272
|
+
if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
77271
77273
|
const app = applicationRepo.getByJob(job.id);
|
|
77272
77274
|
if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
|
|
77273
77275
|
const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
|
|
77274
77276
|
throw conflict(
|
|
77275
|
-
`\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
|
|
77277
|
+
`\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.`
|
|
77276
77278
|
);
|
|
77277
77279
|
}
|
|
77278
77280
|
const prep = interviewRepo.save(input);
|
|
@@ -77370,8 +77372,8 @@ var WORKFLOWS = [
|
|
|
77370
77372
|
"\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.",
|
|
77371
77373
|
"\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).",
|
|
77372
77374
|
"\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.",
|
|
77373
|
-
'"\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?"\uB77C\uACE0 \uC790\uAE30\uC18C\uAC1C\uC11C \
|
|
77374
|
-
'\uC0AC\uC6A9\uC790\uAC00 \
|
|
77375
|
+
'\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".',
|
|
77376
|
+
'\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.',
|
|
77375
77377
|
"\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).",
|
|
77376
77378
|
"`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."
|
|
77377
77379
|
]
|
|
@@ -78639,14 +78641,94 @@ function fail(text) {
|
|
|
78639
78641
|
}
|
|
78640
78642
|
|
|
78641
78643
|
// packages/mcp-tools/src/bridge.ts
|
|
78642
|
-
import { spawn } from "node:child_process";
|
|
78644
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
78645
|
+
import fs5 from "node:fs";
|
|
78643
78646
|
import path5 from "node:path";
|
|
78644
78647
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
78645
78648
|
var __dirname = path5.dirname(fileURLToPath2(import.meta.url));
|
|
78646
78649
|
var REPO_ROOT = path5.resolve(__dirname, "..", "..", "..");
|
|
78647
|
-
|
|
78650
|
+
var HEALTH_TIMEOUT_MS = 800;
|
|
78651
|
+
var pendingStart = null;
|
|
78652
|
+
function dashboardLogPath() {
|
|
78653
|
+
return path5.join(getDataDir(), "server.log");
|
|
78654
|
+
}
|
|
78655
|
+
function launchdPlistPath() {
|
|
78656
|
+
return path5.join(getDataDir(), "dashboard.launchd.plist");
|
|
78657
|
+
}
|
|
78658
|
+
function launchdLabel() {
|
|
78659
|
+
let hash = 5381;
|
|
78660
|
+
for (const ch of getDataDir()) hash = (hash * 33 ^ ch.charCodeAt(0)) >>> 0;
|
|
78661
|
+
return `life.careermate.dashboard.${hash.toString(16)}`;
|
|
78662
|
+
}
|
|
78663
|
+
function xmlEscape(value) {
|
|
78664
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
78665
|
+
}
|
|
78666
|
+
function plistString(key, value) {
|
|
78667
|
+
return ` <key>${xmlEscape(key)}</key>
|
|
78668
|
+
<string>${xmlEscape(value)}</string>`;
|
|
78669
|
+
}
|
|
78670
|
+
function renderLaunchdPlist(bin, args, cwd, log) {
|
|
78671
|
+
const env2 = {
|
|
78672
|
+
CAREERMATE_DATA_DIR: getDataDir(),
|
|
78673
|
+
CAREERMATE_NO_OPEN: "1"
|
|
78674
|
+
};
|
|
78675
|
+
if (process.env.CAREERMATE_PORT) env2.CAREERMATE_PORT = process.env.CAREERMATE_PORT;
|
|
78676
|
+
if (process.env.PATH) env2.PATH = process.env.PATH;
|
|
78677
|
+
const programArgs = [bin, ...args].map((arg) => ` <string>${xmlEscape(arg)}</string>`).join("\n");
|
|
78678
|
+
const envEntries = Object.entries(env2).map(([key, value]) => `${plistString(key, value)}`).join("\n");
|
|
78679
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
78680
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
78681
|
+
<plist version="1.0">
|
|
78682
|
+
<dict>
|
|
78683
|
+
<key>Label</key>
|
|
78684
|
+
<string>${xmlEscape(launchdLabel())}</string>
|
|
78685
|
+
<key>ProgramArguments</key>
|
|
78686
|
+
<array>
|
|
78687
|
+
${programArgs}
|
|
78688
|
+
</array>
|
|
78689
|
+
<key>WorkingDirectory</key>
|
|
78690
|
+
<string>${xmlEscape(cwd)}</string>
|
|
78691
|
+
<key>EnvironmentVariables</key>
|
|
78692
|
+
<dict>
|
|
78693
|
+
${envEntries}
|
|
78694
|
+
</dict>
|
|
78695
|
+
<key>RunAtLoad</key>
|
|
78696
|
+
<true/>
|
|
78697
|
+
<key>KeepAlive</key>
|
|
78698
|
+
<false/>
|
|
78699
|
+
<key>StandardOutPath</key>
|
|
78700
|
+
<string>${xmlEscape(log)}</string>
|
|
78701
|
+
<key>StandardErrorPath</key>
|
|
78702
|
+
<string>${xmlEscape(log)}</string>
|
|
78703
|
+
</dict>
|
|
78704
|
+
</plist>
|
|
78705
|
+
`;
|
|
78706
|
+
}
|
|
78707
|
+
function delay(ms) {
|
|
78708
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
78709
|
+
}
|
|
78710
|
+
async function pingDashboard(url, timeoutMs = HEALTH_TIMEOUT_MS) {
|
|
78711
|
+
const controller = new AbortController();
|
|
78712
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
78713
|
+
timer.unref?.();
|
|
78714
|
+
try {
|
|
78715
|
+
const healthUrl = new URL("/api/health", url);
|
|
78716
|
+
const res = await fetch(healthUrl, {
|
|
78717
|
+
signal: controller.signal,
|
|
78718
|
+
headers: { accept: "application/json" }
|
|
78719
|
+
});
|
|
78720
|
+
if (!res.ok) return false;
|
|
78721
|
+
const body = await res.json().catch(() => null);
|
|
78722
|
+
return body?.ok === true;
|
|
78723
|
+
} catch {
|
|
78724
|
+
return false;
|
|
78725
|
+
} finally {
|
|
78726
|
+
clearTimeout(timer);
|
|
78727
|
+
}
|
|
78728
|
+
}
|
|
78729
|
+
async function liveServer() {
|
|
78648
78730
|
const info3 = readRuntimeInfo();
|
|
78649
|
-
if (info3 && isProcessAlive(info3.pid)) return info3;
|
|
78731
|
+
if (info3 && isProcessAlive(info3.pid) && await pingDashboard(info3.url)) return info3;
|
|
78650
78732
|
return null;
|
|
78651
78733
|
}
|
|
78652
78734
|
function spawnDetached(cmd, args, opts = {}) {
|
|
@@ -78655,19 +78737,101 @@ function spawnDetached(cmd, args, opts = {}) {
|
|
|
78655
78737
|
child.on("error", () => {
|
|
78656
78738
|
});
|
|
78657
78739
|
child.unref();
|
|
78740
|
+
return child.pid ?? null;
|
|
78658
78741
|
} catch {
|
|
78742
|
+
return null;
|
|
78659
78743
|
}
|
|
78660
78744
|
}
|
|
78745
|
+
function spawnViaLaunchd(bin, args, cwd, log) {
|
|
78746
|
+
if (process.platform !== "darwin" || process.env.CAREERMATE_LAUNCHER === "detached") return false;
|
|
78747
|
+
if (typeof process.getuid !== "function") return false;
|
|
78748
|
+
const domain = `gui/${process.getuid()}`;
|
|
78749
|
+
const label = launchdLabel();
|
|
78750
|
+
const plist = launchdPlistPath();
|
|
78751
|
+
try {
|
|
78752
|
+
fs5.writeFileSync(plist, renderLaunchdPlist(bin, args, cwd, log), "utf8");
|
|
78753
|
+
spawnSync("launchctl", ["bootout", `${domain}/${label}`], { stdio: "ignore" });
|
|
78754
|
+
const res = spawnSync("launchctl", ["bootstrap", domain, plist], {
|
|
78755
|
+
encoding: "utf8",
|
|
78756
|
+
timeout: 5e3
|
|
78757
|
+
});
|
|
78758
|
+
if (res.status === 0) return true;
|
|
78759
|
+
fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl bootstrap failed: ${res.stderr || res.stdout || res.status}
|
|
78760
|
+
`);
|
|
78761
|
+
} catch (e) {
|
|
78762
|
+
try {
|
|
78763
|
+
fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl launch failed: ${e instanceof Error ? e.message : String(e)}
|
|
78764
|
+
`);
|
|
78765
|
+
} catch {
|
|
78766
|
+
}
|
|
78767
|
+
}
|
|
78768
|
+
return false;
|
|
78769
|
+
}
|
|
78770
|
+
function spawnDashboardDetached(bin, args, cwd, logFd) {
|
|
78771
|
+
spawnDetached(bin, args, {
|
|
78772
|
+
cwd,
|
|
78773
|
+
env: { ...process.env, CAREERMATE_NO_OPEN: "1" },
|
|
78774
|
+
stdio: logFd === null ? "ignore" : ["ignore", logFd, logFd]
|
|
78775
|
+
});
|
|
78776
|
+
}
|
|
78661
78777
|
async function ensureServer(timeoutMs = 12e3) {
|
|
78662
|
-
const existing = liveServer();
|
|
78778
|
+
const existing = await liveServer();
|
|
78663
78779
|
if (existing) return { url: existing.url };
|
|
78664
|
-
|
|
78665
|
-
|
|
78780
|
+
if (pendingStart) return pendingStart;
|
|
78781
|
+
pendingStart = startServerBackground(timeoutMs);
|
|
78782
|
+
try {
|
|
78783
|
+
return await pendingStart;
|
|
78784
|
+
} finally {
|
|
78785
|
+
pendingStart = null;
|
|
78786
|
+
}
|
|
78787
|
+
}
|
|
78788
|
+
async function startServerBackground(timeoutMs) {
|
|
78789
|
+
const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path5.join(__dirname, "web.mjs")], __dirname] : [
|
|
78790
|
+
process.execPath,
|
|
78791
|
+
["--no-warnings", "--experimental-sqlite", "--import", "tsx", path5.join(REPO_ROOT, "apps/web/src/index.ts")],
|
|
78792
|
+
REPO_ROOT
|
|
78793
|
+
];
|
|
78794
|
+
const log = dashboardLogPath();
|
|
78795
|
+
let logFd = null;
|
|
78796
|
+
try {
|
|
78797
|
+
logFd = fs5.openSync(log, "a");
|
|
78798
|
+
fs5.writeSync(logFd, `
|
|
78799
|
+
[${(/* @__PURE__ */ new Date()).toISOString()}] CareerMate dashboard background launch
|
|
78800
|
+
`);
|
|
78801
|
+
} catch {
|
|
78802
|
+
logFd = null;
|
|
78803
|
+
}
|
|
78804
|
+
let launchedViaLaunchd = false;
|
|
78805
|
+
try {
|
|
78806
|
+
launchedViaLaunchd = spawnViaLaunchd(bin, args, cwd, log);
|
|
78807
|
+
if (!launchedViaLaunchd) spawnDashboardDetached(bin, args, cwd, logFd);
|
|
78808
|
+
} finally {
|
|
78809
|
+
if (logFd !== null) {
|
|
78810
|
+
try {
|
|
78811
|
+
fs5.closeSync(logFd);
|
|
78812
|
+
} catch {
|
|
78813
|
+
}
|
|
78814
|
+
}
|
|
78815
|
+
}
|
|
78666
78816
|
const start = Date.now();
|
|
78667
78817
|
while (Date.now() - start < timeoutMs) {
|
|
78668
|
-
const info3 = liveServer();
|
|
78818
|
+
const info3 = await liveServer();
|
|
78669
78819
|
if (info3) return { url: info3.url };
|
|
78670
|
-
await
|
|
78820
|
+
await delay(300);
|
|
78821
|
+
}
|
|
78822
|
+
if (launchedViaLaunchd) {
|
|
78823
|
+
try {
|
|
78824
|
+
fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchd did not produce a healthy server; falling back to detached spawn
|
|
78825
|
+
`);
|
|
78826
|
+
} catch {
|
|
78827
|
+
}
|
|
78828
|
+
spawnDashboardDetached(bin, args, cwd, null);
|
|
78829
|
+
const fallbackStart = Date.now();
|
|
78830
|
+
while (Date.now() - fallbackStart < Math.min(timeoutMs, 8e3)) {
|
|
78831
|
+
const info3 = await liveServer();
|
|
78832
|
+
if (info3) return { url: info3.url };
|
|
78833
|
+
await delay(300);
|
|
78834
|
+
}
|
|
78671
78835
|
}
|
|
78672
78836
|
return null;
|
|
78673
78837
|
}
|
|
@@ -78691,7 +78855,7 @@ function openInFileManager(dir) {
|
|
|
78691
78855
|
else openOsTarget(dir);
|
|
78692
78856
|
}
|
|
78693
78857
|
async function resolveDashboardUrl(hash = "") {
|
|
78694
|
-
const live = liveServer();
|
|
78858
|
+
const live = await liveServer();
|
|
78695
78859
|
if (live) return { url: live.url + hash, running: true };
|
|
78696
78860
|
const started = await ensureServer();
|
|
78697
78861
|
if (started) return { url: started.url + hash, running: true };
|
|
@@ -78699,7 +78863,7 @@ async function resolveDashboardUrl(hash = "") {
|
|
|
78699
78863
|
}
|
|
78700
78864
|
|
|
78701
78865
|
// packages/mcp-tools/src/update.ts
|
|
78702
|
-
import
|
|
78866
|
+
import fs6 from "node:fs";
|
|
78703
78867
|
import path6 from "node:path";
|
|
78704
78868
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
78705
78869
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -78756,14 +78920,14 @@ function comparePrerelease(a, b) {
|
|
|
78756
78920
|
}
|
|
78757
78921
|
function readCache() {
|
|
78758
78922
|
try {
|
|
78759
|
-
return JSON.parse(
|
|
78923
|
+
return JSON.parse(fs6.readFileSync(cacheFile(), "utf8"));
|
|
78760
78924
|
} catch {
|
|
78761
78925
|
return null;
|
|
78762
78926
|
}
|
|
78763
78927
|
}
|
|
78764
78928
|
function writeCache(c) {
|
|
78765
78929
|
try {
|
|
78766
|
-
|
|
78930
|
+
fs6.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
|
|
78767
78931
|
} catch {
|
|
78768
78932
|
}
|
|
78769
78933
|
}
|
|
@@ -78920,16 +79084,18 @@ async function runSelfUpdate() {
|
|
|
78920
79084
|
ok: true,
|
|
78921
79085
|
from,
|
|
78922
79086
|
to: latest,
|
|
78923
|
-
message: `
|
|
79087
|
+
message: `CareerMate\uB97C ${latest ? `v${latest}` : "\uCD5C\uC2E0 \uBC84\uC804"}(\uC73C)\uB85C \uC5C5\uB370\uC774\uD2B8\uD588\uC2B5\uB2C8\uB2E4. \uC801\uC6A9\uD558\uB824\uBA74 AI \uC571(\uB610\uB294 MCP \uC5F0\uACB0)\uC744 \uC7AC\uC2DC\uC791\uD558\uC138\uC694. \uC7AC\uC2DC\uC791 \uC2DC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uB294 \uC790\uB3D9\uC73C\uB85C \uB9C8\uC774\uADF8\uB808\uC774\uC158\uB418\uBA70 \uAE30\uC874 \uB370\uC774\uD130\uB294 \uBCF4\uC874\uB429\uB2C8\uB2E4.`
|
|
78924
79088
|
};
|
|
78925
79089
|
}
|
|
78926
79090
|
|
|
78927
79091
|
// packages/mcp-tools/src/tools.ts
|
|
78928
79092
|
var STATUS_LIST = APPLICATION_STATUSES.map((s) => `${s}(${APPLICATION_STATUS_LABELS[s]})`).join(", ");
|
|
79093
|
+
var DELETE_CONFIRMATION = "DELETE";
|
|
79094
|
+
var DASHBOARD_URL = "http://127.0.0.1:4319";
|
|
78929
79095
|
function listInboxFiles(dir) {
|
|
78930
79096
|
let names;
|
|
78931
79097
|
try {
|
|
78932
|
-
names =
|
|
79098
|
+
names = fs7.readdirSync(dir);
|
|
78933
79099
|
} catch {
|
|
78934
79100
|
return [];
|
|
78935
79101
|
}
|
|
@@ -78939,7 +79105,7 @@ function listInboxFiles(dir) {
|
|
|
78939
79105
|
if (SKIP.test(name)) continue;
|
|
78940
79106
|
const p = path7.join(dir, name);
|
|
78941
79107
|
try {
|
|
78942
|
-
if (
|
|
79108
|
+
if (fs7.statSync(p).isFile()) out.push({ name, path: p });
|
|
78943
79109
|
} catch {
|
|
78944
79110
|
}
|
|
78945
79111
|
}
|
|
@@ -79102,9 +79268,17 @@ ${lines}`;
|
|
|
79102
79268
|
if (upd.update_available) {
|
|
79103
79269
|
text += `
|
|
79104
79270
|
|
|
79105
|
-
\
|
|
79106
|
-
}
|
|
79107
|
-
return ok(text, {
|
|
79271
|
+
\uC0C8 \uBC84\uC804 v${upd.latest}\uC774 \uB098\uC654\uC2B5\uB2C8\uB2E4(\uD604\uC7AC v${upd.current}). \uC0AC\uC6A9\uC790\uAC00 \uC6D0\uD558\uBA74 \uBC14\uB85C \uC5C5\uB370\uC774\uD2B8\uB97C \uC9C4\uD589\uD558\uC138\uC694. \uC5C5\uB370\uC774\uD2B8 \uD6C4\uC5D0\uB294 AI \uC571 \uB610\uB294 MCP \uC5F0\uACB0\uC744 \uC7AC\uC2DC\uC791\uD574\uC57C \uC801\uC6A9\uB429\uB2C8\uB2E4.`;
|
|
79272
|
+
}
|
|
79273
|
+
return ok(text, {
|
|
79274
|
+
...s,
|
|
79275
|
+
verify_strict: strict,
|
|
79276
|
+
update: upd,
|
|
79277
|
+
suggested_next_action: upd.update_available ? {
|
|
79278
|
+
prompt: `\uC0C8 \uBC84\uC804 v${upd.latest}\uC774 \uB098\uC654\uC2B5\uB2C8\uB2E4. \uC9C0\uAE08 \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694?`,
|
|
79279
|
+
next_tool: "update_careermate"
|
|
79280
|
+
} : null
|
|
79281
|
+
});
|
|
79108
79282
|
}
|
|
79109
79283
|
},
|
|
79110
79284
|
/* --------------------------------------------------------------- profile */
|
|
@@ -79257,6 +79431,31 @@ ${lines}`;
|
|
|
79257
79431
|
}
|
|
79258
79432
|
}
|
|
79259
79433
|
},
|
|
79434
|
+
{
|
|
79435
|
+
name: "delete_cover_letter",
|
|
79436
|
+
title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC0AD\uC81C",
|
|
79437
|
+
description: "\uC0AC\uC6A9\uC790\uAC00 \uC800\uC7A5\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C \uC0AD\uC81C\uB97C \uBA85\uD655\uD788 \uC694\uCCAD\uD588\uC744 \uB54C\uB9CC \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uBA3C\uC800 get_cover_letters\uB85C \uC804\uCCB4 \uBAA9\uB85D \uB610\uB294 \uACF5\uACE0\uBCC4 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0, \uC0AD\uC81C\uD560 \uD56D\uBAA9\uC744 \uC0AC\uC6A9\uC790\uAC00 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD558\uC138\uC694. \uC0AD\uC81C\uD558\uBA74 \uD574\uB2F9 \uC790\uAE30\uC18C\uAC1C\uC11C\uC758 \uBAA8\uB4E0 \uBC84\uC804\uC774 \uD568\uAED8 \uC0AD\uC81C\uB418\uBA70 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
|
|
79438
|
+
inputSchema: {
|
|
79439
|
+
cover_letter_id: external_exports.string(),
|
|
79440
|
+
confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
|
|
79441
|
+
},
|
|
79442
|
+
handler: (args) => {
|
|
79443
|
+
if (args?.confirm !== DELETE_CONFIRMATION) {
|
|
79444
|
+
return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
|
|
79445
|
+
}
|
|
79446
|
+
const cl = coverLetterRepo.get(args.cover_letter_id, false);
|
|
79447
|
+
if (!cl) return fail("\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
79448
|
+
const removed = coverLetterRepo.remove(cl.id);
|
|
79449
|
+
if (!removed) return fail("\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
79450
|
+
const user_message = `'${cl.title}' \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC0AD\uC81C\uD588\uC5B4\uC694. \uC5F0\uACB0\uB41C \uC9C0\uC6D0 \uAE30\uB85D\uC5D0\uC11C\uB294 \uC790\uAE30\uC18C\uAC1C\uC11C \uC5F0\uACB0\uB9CC \uBE44\uC6CC\uB480\uC2B5\uB2C8\uB2E4.`;
|
|
79451
|
+
return ok(user_message, {
|
|
79452
|
+
deleted: true,
|
|
79453
|
+
cover_letter_id: cl.id,
|
|
79454
|
+
title: cl.title,
|
|
79455
|
+
user_message
|
|
79456
|
+
});
|
|
79457
|
+
}
|
|
79458
|
+
},
|
|
79260
79459
|
/* ------------------------------------------------------------------ jobs */
|
|
79261
79460
|
{
|
|
79262
79461
|
name: "save_job_posting",
|
|
@@ -79265,7 +79464,15 @@ ${lines}`;
|
|
|
79265
79464
|
inputSchema: JobInputSchema.shape,
|
|
79266
79465
|
handler: (args) => {
|
|
79267
79466
|
const { job } = saveJobPosting(args);
|
|
79268
|
-
|
|
79467
|
+
const user_message = `'${job.company} \xB7 ${job.position}' \uACF5\uACE0\uB97C \uC800\uC7A5\uD588\uC5B4\uC694.`;
|
|
79468
|
+
return ok(user_message, {
|
|
79469
|
+
...job,
|
|
79470
|
+
user_message,
|
|
79471
|
+
suggested_next_action: {
|
|
79472
|
+
prompt: "\uC774 \uACF5\uACE0\uC640 \uB0B4 \uD504\uB85C\uD544\uC744 \uBE44\uAD50\uD574 \uC801\uD569\uB3C4\uB97C \uBD84\uC11D\uD560\uAE4C\uC694?",
|
|
79473
|
+
next_workflow: "analyze_job"
|
|
79474
|
+
}
|
|
79475
|
+
});
|
|
79269
79476
|
}
|
|
79270
79477
|
},
|
|
79271
79478
|
{
|
|
@@ -79276,7 +79483,7 @@ ${lines}`;
|
|
|
79276
79483
|
readOnly: true,
|
|
79277
79484
|
handler: (args) => {
|
|
79278
79485
|
const job = jobRepo.get(args.job_id);
|
|
79279
|
-
if (!job) return fail(
|
|
79486
|
+
if (!job) return fail("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
79280
79487
|
return ok(`${job.company} \xB7 ${job.position}`, {
|
|
79281
79488
|
job: jobWithMeta(job),
|
|
79282
79489
|
fit: fitRepo.getByJob(job.id),
|
|
@@ -79297,6 +79504,35 @@ ${lines}`;
|
|
|
79297
79504
|
return ok(`\uACF5\uACE0 ${jobs.length}\uAC74`, jobs);
|
|
79298
79505
|
}
|
|
79299
79506
|
},
|
|
79507
|
+
{
|
|
79508
|
+
name: "delete_job_posting",
|
|
79509
|
+
title: "\uCC44\uC6A9\uACF5\uACE0 \uC0AD\uC81C",
|
|
79510
|
+
description: "\uC0AC\uC6A9\uC790\uAC00 \uC800\uC7A5\uB41C \uCC44\uC6A9\uACF5\uACE0 \uC0AD\uC81C\uB97C \uBA85\uD655\uD788 \uC694\uCCAD\uD588\uC744 \uB54C\uB9CC \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uBA3C\uC800 list_jobs\uB85C \uC804\uCCB4 \uACF5\uACE0 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0, \uC0AD\uC81C\uD560 \uACF5\uACE0\uB97C \uC0AC\uC6A9\uC790\uAC00 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD558\uC138\uC694. \uC0AD\uC81C\uD558\uBA74 \uC5F0\uACB0\uB41C \uC9C0\uC6D0 \uC0C1\uD0DC\xB7\uC801\uD569\uB3C4 \uBD84\uC11D\xB7\uBA74\uC811 \uC900\uBE44\uAC00 \uD568\uAED8 \uC0AD\uC81C\uB429\uB2C8\uB2E4. \uC5F0\uACB0\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C\uB294 \uC0AD\uC81C\uD558\uC9C0 \uC54A\uACE0 \uACF5\uACE0 \uC5F0\uACB0\uB9CC \uD574\uC81C\uD569\uB2C8\uB2E4. confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
|
|
79511
|
+
inputSchema: {
|
|
79512
|
+
job_id: external_exports.string(),
|
|
79513
|
+
confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
|
|
79514
|
+
},
|
|
79515
|
+
handler: (args) => {
|
|
79516
|
+
if (args?.confirm !== DELETE_CONFIRMATION) {
|
|
79517
|
+
return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
|
|
79518
|
+
}
|
|
79519
|
+
const job = jobRepo.get(args.job_id);
|
|
79520
|
+
if (!job) return fail("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
79521
|
+
const detachedCoverLetters = coverLetterRepo.listByJob(job.id).length;
|
|
79522
|
+
const removed = jobRepo.remove(job.id);
|
|
79523
|
+
if (!removed) return fail("\uACF5\uACE0\uB97C \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
79524
|
+
const detachedNote = detachedCoverLetters ? ` \uC5F0\uACB0\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C ${detachedCoverLetters}\uAC74\uC740 \uC0AD\uC81C\uD558\uC9C0 \uC54A\uACE0 \uACF5\uACE0 \uC5F0\uACB0\uB9CC \uD574\uC81C\uD588\uC2B5\uB2C8\uB2E4.` : "";
|
|
79525
|
+
const user_message = `'${job.company} \xB7 ${job.position}' \uACF5\uACE0\uB97C \uC0AD\uC81C\uD588\uC5B4\uC694. \uC5F0\uACB0\uB41C \uC9C0\uC6D0 \uC0C1\uD0DC, \uC801\uD569\uB3C4 \uBD84\uC11D, \uBA74\uC811 \uC900\uBE44\uB3C4 \uD568\uAED8 \uC815\uB9AC\uD588\uC2B5\uB2C8\uB2E4.${detachedNote}`;
|
|
79526
|
+
return ok(user_message, {
|
|
79527
|
+
deleted: true,
|
|
79528
|
+
job_id: job.id,
|
|
79529
|
+
company: job.company,
|
|
79530
|
+
position: job.position,
|
|
79531
|
+
detached_cover_letters: detachedCoverLetters,
|
|
79532
|
+
user_message
|
|
79533
|
+
});
|
|
79534
|
+
}
|
|
79535
|
+
},
|
|
79300
79536
|
/* ----------------------------------------------------- application context */
|
|
79301
79537
|
{
|
|
79302
79538
|
name: "get_application_context",
|
|
@@ -79319,7 +79555,7 @@ ${lines}`;
|
|
|
79319
79555
|
`\uC774\uB825\uC11C ${ctx.resumes.length} \xB7 \uACBD\uB825 ${ctx.experiences.length} \xB7 \uD504\uB85C\uC81D\uD2B8 ${ctx.projects.length} \xB7 \uC2A4\uD0AC ${ctx.skills.length} \xB7 \uC790\uC18C\uC11C ${ctx.cover_letters.length}`,
|
|
79320
79556
|
ctx.job ? `\uB300\uC0C1 \uACF5\uACE0: ${ctx.job.company} \xB7 ${ctx.job.position}` : "\uB300\uC0C1 \uACF5\uACE0 \uC5C6\uC74C",
|
|
79321
79557
|
ctx.writing_preferences.preferred_tone ? `\uC120\uD638 \uBB38\uCCB4: ${ctx.writing_preferences.preferred_tone}` : "",
|
|
79322
|
-
recommended_route ? `\
|
|
79558
|
+
recommended_route ? `\uB0B4\uBD80 \uC548\uB0B4: \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uB3C4\uAD6C\uBA85\uC774\uB098 ID\uB97C \uB9D0\uD558\uC9C0 \uB9D0\uACE0, \uBA3C\uC800 get_workflow_guide({ workflow_id: "${recommended_route}" })\uB85C \uC2E4\uD589 \uC808\uCC28\uC640 \uAC80\uC99D \uC21C\uC11C\uB97C \uBC1B\uC740 \uB4A4 \uC26C\uC6B4 \uB9D0\uB85C \uC548\uB0B4\uD558\uC138\uC694.` : "\uB0B4\uBD80 \uC548\uB0B4: \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uB3C4\uAD6C\uBA85\uC744 \uB9D0\uD558\uC9C0 \uB9D0\uACE0, \uBA3C\uC800 get_workflow_guide\uB85C \uC2E4\uD589 \uC808\uCC28\uC640 \uAC80\uC99D \uC21C\uC11C\uB97C \uBC1B\uC740 \uB4A4 \uC26C\uC6B4 \uB9D0\uB85C \uC548\uB0B4\uD558\uC138\uC694."
|
|
79323
79559
|
].filter(Boolean).join("\n");
|
|
79324
79560
|
return ok(summary, enriched);
|
|
79325
79561
|
}
|
|
@@ -79332,7 +79568,34 @@ ${lines}`;
|
|
|
79332
79568
|
handler: (args) => {
|
|
79333
79569
|
try {
|
|
79334
79570
|
const fit = saveFitAnalysis(args);
|
|
79335
|
-
|
|
79571
|
+
const suggested_next_action = {
|
|
79572
|
+
prompt: "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?",
|
|
79573
|
+
options: [
|
|
79574
|
+
{
|
|
79575
|
+
label: "\uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918",
|
|
79576
|
+
next_workflow: "write_cover_letter"
|
|
79577
|
+
},
|
|
79578
|
+
{
|
|
79579
|
+
label: "\uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918",
|
|
79580
|
+
next_workflow: null
|
|
79581
|
+
}
|
|
79582
|
+
]
|
|
79583
|
+
};
|
|
79584
|
+
const user_message = `\uC801\uD569\uB3C4 \uBD84\uC11D\uC744 \uC800\uC7A5\uD588\uC5B4\uC694${fit.score != null ? ` (${fit.score}\uC810)` : ""}. \uC790\uC138\uD55C \uACB0\uACFC\uB294 \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C\uB3C4 \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4: ${DASHBOARD_URL}`;
|
|
79585
|
+
return ok(
|
|
79586
|
+
[
|
|
79587
|
+
user_message,
|
|
79588
|
+
"",
|
|
79589
|
+
"\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB2E4\uC74C \uC120\uD0DD\uC9C0\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694:",
|
|
79590
|
+
"1. \uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918",
|
|
79591
|
+
"2. \uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918"
|
|
79592
|
+
].join("\n"),
|
|
79593
|
+
{
|
|
79594
|
+
...fit,
|
|
79595
|
+
user_message,
|
|
79596
|
+
suggested_next_action
|
|
79597
|
+
}
|
|
79598
|
+
);
|
|
79336
79599
|
} catch (e) {
|
|
79337
79600
|
return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
|
|
79338
79601
|
}
|
|
@@ -79351,11 +79614,19 @@ ${lines}`;
|
|
|
79351
79614
|
handler: (args) => {
|
|
79352
79615
|
try {
|
|
79353
79616
|
const res = updateApplicationStatus(args.job_id, args.status, args.note);
|
|
79354
|
-
|
|
79355
|
-
`\uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4.${res.hint ? `
|
|
79617
|
+
const user_message = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.` + (res.hint ? `
|
|
79356
79618
|
|
|
79357
|
-
|
|
79358
|
-
|
|
79619
|
+
${res.hint}` : "");
|
|
79620
|
+
return ok(
|
|
79621
|
+
user_message,
|
|
79622
|
+
{
|
|
79623
|
+
...res,
|
|
79624
|
+
user_message,
|
|
79625
|
+
suggested_next_action: res.hint ? {
|
|
79626
|
+
prompt: res.hint,
|
|
79627
|
+
next_workflow: "prepare_interview"
|
|
79628
|
+
} : null
|
|
79629
|
+
}
|
|
79359
79630
|
);
|
|
79360
79631
|
} catch (e) {
|
|
79361
79632
|
return fail(e instanceof Error ? e.message : "\uBCC0\uACBD \uC2E4\uD328");
|
|
@@ -79371,7 +79642,8 @@ ${lines}`;
|
|
|
79371
79642
|
handler: (args) => {
|
|
79372
79643
|
try {
|
|
79373
79644
|
const prep = saveInterviewPrep(args);
|
|
79374
|
-
|
|
79645
|
+
const user_message = `\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD588\uC5B4\uC694. \uC608\uC0C1 \uC9C8\uBB38 ${prep.questions.length}\uAC1C\uAC00 \uC900\uBE44\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`;
|
|
79646
|
+
return ok(user_message, { ...prep, user_message });
|
|
79375
79647
|
} catch (e) {
|
|
79376
79648
|
return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
|
|
79377
79649
|
}
|
|
@@ -79396,7 +79668,7 @@ ${lines}`;
|
|
|
79396
79668
|
const dir = getExportsDir();
|
|
79397
79669
|
const filePath = path7.join(dir, result2.filename);
|
|
79398
79670
|
try {
|
|
79399
|
-
|
|
79671
|
+
fs7.writeFileSync(filePath, result2.content, "utf8");
|
|
79400
79672
|
} catch (e) {
|
|
79401
79673
|
return fail(`\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}`);
|
|
79402
79674
|
}
|
|
@@ -79558,29 +79830,37 @@ ${report.disclaimer}`, report);
|
|
|
79558
79830
|
{
|
|
79559
79831
|
name: "check_for_update",
|
|
79560
79832
|
title: "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778",
|
|
79561
|
-
description: 'CareerMate\uC5D0 \uC0C8 \uBC84\uC804\uC774 \uC788\uB294\uC9C0 npm \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0\uC11C \uC9C0\uAE08 \uD655\uC778\uD569\uB2C8\uB2E4(\uACF5\uAC1C \uBC84\uC804 \uC815\uBCF4\uB9CC GET\uD558\uBA70 \uC0AC\uC6A9\uC790 \uB370\uC774\uD130\uB294 \uC804\uC1A1\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4). \uD604\uC7AC \uBC84\uC804\xB7\uCD5C\uC2E0 \uBC84\uC804\xB7\uC5C5\uB370\uC774\uD2B8 \uAC00\uB2A5 \uC5EC\uBD80\uB97C \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uCD5C\uC2E0\uC774\uC57C?/\uC5C5\uB370\uC774\uD2B8 \uC788\uC5B4?"\uB77C\uACE0 \uBB3B\uAC70\uB098, get_onboarding_status\uAC00 \uC0C8 \uBC84\uC804\uC744 \uC54C\uB9B4 \uB54C \uD638\uCD9C\uD558\uC138\uC694. \
|
|
79833
|
+
description: 'CareerMate\uC5D0 \uC0C8 \uBC84\uC804\uC774 \uC788\uB294\uC9C0 npm \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0\uC11C \uC9C0\uAE08 \uD655\uC778\uD569\uB2C8\uB2E4(\uACF5\uAC1C \uBC84\uC804 \uC815\uBCF4\uB9CC GET\uD558\uBA70 \uC0AC\uC6A9\uC790 \uB370\uC774\uD130\uB294 \uC804\uC1A1\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4). \uD604\uC7AC \uBC84\uC804\xB7\uCD5C\uC2E0 \uBC84\uC804\xB7\uC5C5\uB370\uC774\uD2B8 \uAC00\uB2A5 \uC5EC\uBD80\uB97C \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uCD5C\uC2E0\uC774\uC57C?/\uC5C5\uB370\uC774\uD2B8 \uC788\uC5B4?"\uB77C\uACE0 \uBB3B\uAC70\uB098, get_onboarding_status\uAC00 \uC0C8 \uBC84\uC804\uC744 \uC54C\uB9B4 \uB54C \uD638\uCD9C\uD558\uC138\uC694. \uD655\uC778\uB9CC \uD558\uACE0 \uC124\uCE58\uB294 \uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uC5C5\uB370\uC774\uD2B8\uD574\uC918/\uCD5C\uC2E0\uC73C\uB85C \uC62C\uB824\uC918"\uCC98\uB7FC \uBA85\uC2DC\uC801\uC73C\uB85C \uC694\uCCAD\uD558\uBA74 update_careermate\uB97C \uD638\uCD9C\uD558\uC138\uC694.',
|
|
79562
79834
|
inputSchema: {},
|
|
79563
79835
|
readOnly: true,
|
|
79564
79836
|
handler: async () => {
|
|
79565
79837
|
const u = await getUpdateStatusAsync();
|
|
79566
79838
|
if (u.latest === null) return ok("\uC5C5\uB370\uC774\uD2B8 \uD655\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4(\uC624\uD504\uB77C\uC778\uC774\uAC70\uB098 \uC77C\uC2DC\uC801 \uC624\uB958). \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.", u);
|
|
79839
|
+
const user_message = u.update_available ? `\uC0C8 \uBC84\uC804 v${u.latest}\uC774 \uB098\uC654\uC5B4\uC694. \uD604\uC7AC \uBC84\uC804\uC740 v${u.current}\uC785\uB2C8\uB2E4. \uC6D0\uD558\uC2DC\uBA74 \uC9C0\uAE08 \uC5C5\uB370\uC774\uD2B8\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.` : `\uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4 (v${u.current}).`;
|
|
79567
79840
|
return ok(
|
|
79568
|
-
|
|
79569
|
-
|
|
79841
|
+
user_message,
|
|
79842
|
+
{
|
|
79843
|
+
...u,
|
|
79844
|
+
user_message,
|
|
79845
|
+
suggested_next_action: u.update_available ? {
|
|
79846
|
+
prompt: "\uC9C0\uAE08 CareerMate\uB97C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694?",
|
|
79847
|
+
next_tool: "update_careermate"
|
|
79848
|
+
} : null
|
|
79849
|
+
}
|
|
79570
79850
|
);
|
|
79571
79851
|
}
|
|
79572
79852
|
},
|
|
79573
79853
|
{
|
|
79574
79854
|
name: "update_careermate",
|
|
79575
79855
|
title: "CareerMate \uC5C5\uB370\uC774\uD2B8",
|
|
79576
|
-
description: 'CareerMate\uB97C \uCD5C\uC2E0 \uBC84\uC804\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD569\uB2C8\uB2E4(\uC124\uCE58 \uD3F4\uB354\uC5D0 npm\uC73C\uB85C careermate@latest \uC124\uCE58). \uC0AC\uC6A9\uC790\uAC00 \uBA85\uC2DC\uC801\uC73C\uB85C "\uC5C5\uB370\uC774\uD2B8\uD574\uC918"\uB77C\uACE0 \uD560 \uB54C
|
|
79856
|
+
description: 'CareerMate\uB97C \uCD5C\uC2E0 \uBC84\uC804\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD569\uB2C8\uB2E4(\uC124\uCE58 \uD3F4\uB354\uC5D0 npm\uC73C\uB85C careermate@latest \uC124\uCE58). \uC0AC\uC6A9\uC790\uAC00 \uBA85\uC2DC\uC801\uC73C\uB85C "\uC5C5\uB370\uC774\uD2B8\uD574\uC918", "CareerMate \uC5C5\uB370\uC774\uD2B8\uD574\uC918", "\uCD5C\uC2E0\uC73C\uB85C \uC62C\uB824\uC918"\uB77C\uACE0 \uD560 \uB54C \uD638\uCD9C\uD558\uC138\uC694. \uC774 \uB3C4\uAD6C\uAC00 \uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778\uB3C4 \uD568\uAED8 \uD558\uBBC0\uB85C \uBCC4\uB3C4 \uD655\uC778 \uC9C8\uBB38 \uC5C6\uC774 \uC2E4\uD589\uD574\uB3C4 \uB429\uB2C8\uB2E4. \uC131\uACF5\uD558\uBA74 \uC801\uC6A9\uC744 \uC704\uD574 AI \uC571(\uB610\uB294 MCP \uC5F0\uACB0) \uC7AC\uC2DC\uC791\uC774 \uD544\uC694\uD558\uBA70, \uC7AC\uC2DC\uC791 \uC2DC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uB294 \uC790\uB3D9 \uB9C8\uC774\uADF8\uB808\uC774\uC158\uB418\uC5B4 \uAE30\uC874 \uB370\uC774\uD130\uB294 \uBCF4\uC874\uB429\uB2C8\uB2E4. \uC790\uB3D9 \uC124\uCE58\uAC00 \uB9C9\uD788\uBA74(\uAC1C\uBC1C \uBAA8\uB4DC\xB7\uBE44\uD45C\uC900 \uC124\uCE58) \uC9C1\uC811 \uCE60 \uBA85\uB839\uC744 \uC548\uB0B4\uD569\uB2C8\uB2E4.',
|
|
79577
79857
|
inputSchema: {},
|
|
79578
79858
|
handler: async () => {
|
|
79579
79859
|
const r = await runSelfUpdate();
|
|
79580
79860
|
if (!r.ok) return fail(`${r.message}${r.manual ? `
|
|
79581
79861
|
|
|
79582
79862
|
\uC9C1\uC811 \uC2E4\uD589: ${r.manual}` : ""}`);
|
|
79583
|
-
return ok(r.message, r);
|
|
79863
|
+
return ok(r.message, { ...r, user_message: r.message });
|
|
79584
79864
|
}
|
|
79585
79865
|
}
|
|
79586
79866
|
];
|
|
@@ -79601,7 +79881,7 @@ async function main() {
|
|
|
79601
79881
|
const nodeOk = checkNode();
|
|
79602
79882
|
const dir = getDataDir();
|
|
79603
79883
|
try {
|
|
79604
|
-
|
|
79884
|
+
fs8.accessSync(dir, fs8.constants.W_OK);
|
|
79605
79885
|
ok2(`\uB370\uC774\uD130 \uD3F4\uB354 \uC4F0\uAE30 \uAC00\uB2A5: ${dir}`);
|
|
79606
79886
|
} catch {
|
|
79607
79887
|
warn2(`\uB370\uC774\uD130 \uD3F4\uB354\uC5D0 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dir}`);
|