careermate 0.0.2 → 0.0.4
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/INSTALL.md +3 -3
- package/README.md +10 -8
- package/bin/careermate.mjs +2 -2
- package/dist/doctor.mjs +185 -39
- package/dist/mcp.mjs +188 -42
- package/dist/migrate.mjs +23 -1
- package/dist/public/lib.js +5 -1
- package/dist/public/pages/applications.js +7 -0
- package/dist/public/pages/jobs.js +22 -15
- package/dist/public/styles.css +9 -0
- package/dist/site/index.html +3 -43
- package/dist/site/llms-install.txt +4 -6
- package/dist/site/start.html +122 -102
- package/dist/web.mjs +138 -13
- package/docs/MCP_TOOLS.md +22 -7
- package/docs/START_WORKFLOW.md +2 -1
- package/docs/WORKFLOWS.md +1 -1
- package/package.json +2 -2
- package/packages/core/src/services.ts +67 -5
- package/packages/db/src/repositories.ts +0 -0
- package/packages/mcp-tools/src/tools.ts +21 -18
- package/packages/prompts/src/onboarding.ts +1 -1
- package/packages/shared/src/enums.ts +7 -2
- package/packages/shared/src/errors.ts +33 -0
- package/packages/shared/src/index.ts +1 -0
- package/packages/shared/src/schemas.ts +56 -2
- package/packages/workflows/src/definitions.ts +1 -1
- package/scripts/init.ts +11 -5
package/dist/mcp.mjs
CHANGED
|
@@ -75028,7 +75028,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
75028
75028
|
const ij = i2 * width + j;
|
|
75029
75029
|
const center = buf[ij];
|
|
75030
75030
|
let sum = 0;
|
|
75031
|
-
let
|
|
75031
|
+
let norm2 = 0;
|
|
75032
75032
|
for (let k = 0; k < kernelSize; k++) {
|
|
75033
75033
|
const y = i2 + k - halfSize;
|
|
75034
75034
|
if (y < 0 || y >= height) {
|
|
@@ -75042,10 +75042,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
75042
75042
|
const neighbour = buf[y * width + x2];
|
|
75043
75043
|
const w = kernel[k * kernelSize + l2] * rangeValues[Math.abs(neighbour - center)];
|
|
75044
75044
|
sum += neighbour * w;
|
|
75045
|
-
|
|
75045
|
+
norm2 += w;
|
|
75046
75046
|
}
|
|
75047
75047
|
}
|
|
75048
|
-
const pix = out[ij] = Math.round(sum /
|
|
75048
|
+
const pix = out[ij] = Math.round(sum / norm2);
|
|
75049
75049
|
histogram[pix]++;
|
|
75050
75050
|
}
|
|
75051
75051
|
}
|
|
@@ -84952,13 +84952,13 @@ function _array(Class2, element, params) {
|
|
|
84952
84952
|
});
|
|
84953
84953
|
}
|
|
84954
84954
|
function _custom(Class2, fn, _params) {
|
|
84955
|
-
const
|
|
84956
|
-
|
|
84955
|
+
const norm2 = normalizeParams(_params);
|
|
84956
|
+
norm2.abort ?? (norm2.abort = true);
|
|
84957
84957
|
const schema = new Class2({
|
|
84958
84958
|
type: "custom",
|
|
84959
84959
|
check: "custom",
|
|
84960
84960
|
fn,
|
|
84961
|
-
...
|
|
84961
|
+
...norm2
|
|
84962
84962
|
});
|
|
84963
84963
|
return schema;
|
|
84964
84964
|
}
|
|
@@ -92516,6 +92516,24 @@ function setVerifyStrict(on) {
|
|
|
92516
92516
|
return on;
|
|
92517
92517
|
}
|
|
92518
92518
|
|
|
92519
|
+
// packages/shared/src/errors.ts
|
|
92520
|
+
var CareerMateError = class extends Error {
|
|
92521
|
+
constructor(message, httpStatus = 400, code) {
|
|
92522
|
+
super(message);
|
|
92523
|
+
this.httpStatus = httpStatus;
|
|
92524
|
+
this.code = code;
|
|
92525
|
+
this.name = "CareerMateError";
|
|
92526
|
+
}
|
|
92527
|
+
httpStatus;
|
|
92528
|
+
code;
|
|
92529
|
+
};
|
|
92530
|
+
function notFound(message) {
|
|
92531
|
+
return new CareerMateError(message, 404, "not_found");
|
|
92532
|
+
}
|
|
92533
|
+
function conflict(message) {
|
|
92534
|
+
return new CareerMateError(message, 409, "conflict");
|
|
92535
|
+
}
|
|
92536
|
+
|
|
92519
92537
|
// packages/shared/src/enums.ts
|
|
92520
92538
|
var APPLICATION_STATUSES = [
|
|
92521
92539
|
"draft",
|
|
@@ -92566,8 +92584,9 @@ function canTransitionStatus(from, to) {
|
|
|
92566
92584
|
}
|
|
92567
92585
|
function assertStatusTransition(from, to) {
|
|
92568
92586
|
if (!canTransitionStatus(from, to)) {
|
|
92569
|
-
|
|
92570
|
-
|
|
92587
|
+
const suffix = to === "draft" ? ` ('${APPLICATION_STATUS_LABELS[from]}' \uB2E8\uACC4\uC758 \uC9C0\uC6D0\uC740 \uC791\uC131 \uC911(\uBBF8\uCD94\uC801)\uC73C\uB85C \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uCD08\uAE30 \uB2E8\uACC4\uB85C \uC62E\uAE30\uB824\uBA74 '\uC9C0\uC6D0 \uC608\uC815'\uC744 \uC120\uD0DD\uD558\uC138\uC694.)` : "";
|
|
92588
|
+
throw conflict(
|
|
92589
|
+
`'${APPLICATION_STATUS_LABELS[from]}'\uC5D0\uC11C '${APPLICATION_STATUS_LABELS[to]}'(\uC73C)\uB85C\uB294 \uC0C1\uD0DC\uB97C \uBC14\uAFC0 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.${suffix}`
|
|
92571
92590
|
);
|
|
92572
92591
|
}
|
|
92573
92592
|
}
|
|
@@ -92619,7 +92638,20 @@ var optLine = external_exports.string().max(MAX_LINE).optional();
|
|
|
92619
92638
|
var optNote = external_exports.string().max(MAX_NOTE).optional();
|
|
92620
92639
|
var optBody = external_exports.string().max(MAX_BODY).optional();
|
|
92621
92640
|
var reqBody = external_exports.string().max(MAX_BODY).refine((s) => s.trim().length > 0, { message: EMPTY_MSG });
|
|
92622
|
-
|
|
92641
|
+
function toStrList(v) {
|
|
92642
|
+
if (typeof v !== "string") return v;
|
|
92643
|
+
const s = v.trim();
|
|
92644
|
+
if (!s) return [];
|
|
92645
|
+
if (s.startsWith("[")) {
|
|
92646
|
+
try {
|
|
92647
|
+
const parsed = JSON.parse(s);
|
|
92648
|
+
if (Array.isArray(parsed)) return parsed;
|
|
92649
|
+
} catch {
|
|
92650
|
+
}
|
|
92651
|
+
}
|
|
92652
|
+
return s.split(/[\n,]/).map((x2) => x2.trim()).filter(Boolean);
|
|
92653
|
+
}
|
|
92654
|
+
var strList = external_exports.preprocess(toStrList, external_exports.array(external_exports.string().max(MAX_ITEM)).max(MAX_ITEMS));
|
|
92623
92655
|
var baseRecord = {
|
|
92624
92656
|
id,
|
|
92625
92657
|
created_at: isoDateTime,
|
|
@@ -92681,6 +92713,9 @@ var ExperienceRecordSchema = external_exports.object({
|
|
|
92681
92713
|
tech: external_exports.array(external_exports.string()),
|
|
92682
92714
|
order_index: external_exports.number()
|
|
92683
92715
|
});
|
|
92716
|
+
var ExperienceBatchInputSchema = external_exports.object({
|
|
92717
|
+
experiences: external_exports.array(ExperienceInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uACBD\uB825 \uBAA9\uB85D(\uD55C \uAC74\uC774\uC5B4\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uC785\uC0AC\uC77C\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
|
|
92718
|
+
});
|
|
92684
92719
|
var ProjectInputSchema = external_exports.object({
|
|
92685
92720
|
name: reqLine,
|
|
92686
92721
|
role: optLine,
|
|
@@ -92704,6 +92739,9 @@ var ProjectRecordSchema = external_exports.object({
|
|
|
92704
92739
|
end_date: external_exports.string().nullable(),
|
|
92705
92740
|
order_index: external_exports.number()
|
|
92706
92741
|
});
|
|
92742
|
+
var ProjectBatchInputSchema = external_exports.object({
|
|
92743
|
+
projects: external_exports.array(ProjectInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uC774\uB984\uC758 \uD504\uB85C\uC81D\uD2B8\uB294 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
|
|
92744
|
+
});
|
|
92707
92745
|
var SkillInputSchema = external_exports.object({
|
|
92708
92746
|
name: reqLine,
|
|
92709
92747
|
category: external_exports.string().max(MAX_LINE).optional().describe("\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1"),
|
|
@@ -92719,6 +92757,9 @@ var SkillRecordSchema = external_exports.object({
|
|
|
92719
92757
|
years: external_exports.number().nullable(),
|
|
92720
92758
|
order_index: external_exports.number()
|
|
92721
92759
|
});
|
|
92760
|
+
var SkillBatchInputSchema = external_exports.object({
|
|
92761
|
+
skills: external_exports.array(SkillInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uAE30\uC220\uC2A4\uD0DD \uBAA9\uB85D(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uC774\uB984\uC758 \uAE30\uC220\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
|
|
92762
|
+
});
|
|
92722
92763
|
var DocumentInputSchema = external_exports.object({
|
|
92723
92764
|
kind: external_exports.enum(DOCUMENT_KINDS),
|
|
92724
92765
|
title: reqLine,
|
|
@@ -92915,7 +92956,7 @@ var ApplicationContextSchema = external_exports.object({
|
|
|
92915
92956
|
});
|
|
92916
92957
|
|
|
92917
92958
|
// packages/shared/src/build-info.ts
|
|
92918
|
-
var APP_VERSION = true ? "0.0.
|
|
92959
|
+
var APP_VERSION = true ? "0.0.4" : "0.0.0-dev";
|
|
92919
92960
|
var BUNDLED = true;
|
|
92920
92961
|
|
|
92921
92962
|
// packages/shared/src/index.ts
|
|
@@ -92930,6 +92971,7 @@ function now() {
|
|
|
92930
92971
|
|
|
92931
92972
|
// packages/db/src/repositories.ts
|
|
92932
92973
|
var PROFILE_ID = "profile-singleton";
|
|
92974
|
+
var norm = (s) => (s ?? "").trim().toLowerCase();
|
|
92933
92975
|
function mapProfile(r) {
|
|
92934
92976
|
return {
|
|
92935
92977
|
id: r.id,
|
|
@@ -93007,6 +93049,9 @@ function mapExperience(r) {
|
|
|
93007
93049
|
updated_at: r.updated_at
|
|
93008
93050
|
};
|
|
93009
93051
|
}
|
|
93052
|
+
function expKey(company, role, start) {
|
|
93053
|
+
return [norm(company), norm(role), norm(start)].join("\0");
|
|
93054
|
+
}
|
|
93010
93055
|
var experienceRepo = {
|
|
93011
93056
|
list() {
|
|
93012
93057
|
return getDb().prepare(`SELECT * FROM experiences ORDER BY order_index ASC, start_date DESC`).all().map(mapExperience);
|
|
@@ -93015,6 +93060,32 @@ var experienceRepo = {
|
|
|
93015
93060
|
const r = getDb().prepare(`SELECT * FROM experiences WHERE id = ?`).get(id2);
|
|
93016
93061
|
return r ? mapExperience(r) : null;
|
|
93017
93062
|
},
|
|
93063
|
+
/** Existing experience with the same company+role+start_date, or null (for idempotent batch upserts). */
|
|
93064
|
+
findMatch(input) {
|
|
93065
|
+
const key = expKey(input.company, input.role ?? null, input.start_date ?? null);
|
|
93066
|
+
return this.list().find((e) => expKey(e.company, e.role, e.start_date) === key) ?? null;
|
|
93067
|
+
},
|
|
93068
|
+
/** Insert, or merge into the matching experience (omitted fields keep their stored value). */
|
|
93069
|
+
upsert(input) {
|
|
93070
|
+
const existing = this.findMatch(input);
|
|
93071
|
+
if (existing) return { record: this.update(existing.id, input), created: false };
|
|
93072
|
+
return { record: this.add(input), created: true };
|
|
93073
|
+
},
|
|
93074
|
+
/** Batch insert/merge in one transaction; within-batch duplicates collapse onto one row. */
|
|
93075
|
+
addMany(inputs) {
|
|
93076
|
+
return tx(() => {
|
|
93077
|
+
const records = [];
|
|
93078
|
+
let created = 0;
|
|
93079
|
+
let updated = 0;
|
|
93080
|
+
for (const input of inputs) {
|
|
93081
|
+
const { record: record2, created: isNew } = this.upsert(input);
|
|
93082
|
+
records.push(record2);
|
|
93083
|
+
if (isNew) created += 1;
|
|
93084
|
+
else updated += 1;
|
|
93085
|
+
}
|
|
93086
|
+
return { records, created, updated };
|
|
93087
|
+
});
|
|
93088
|
+
},
|
|
93018
93089
|
add(input) {
|
|
93019
93090
|
const ts = now();
|
|
93020
93091
|
const id2 = newId("exp_");
|
|
@@ -93088,6 +93159,32 @@ var projectRepo = {
|
|
|
93088
93159
|
const r = getDb().prepare(`SELECT * FROM projects WHERE id = ?`).get(id2);
|
|
93089
93160
|
return r ? mapProject(r) : null;
|
|
93090
93161
|
},
|
|
93162
|
+
/** Existing project with the same name (case-insensitive), or null (for idempotent batch upserts). */
|
|
93163
|
+
findByName(name) {
|
|
93164
|
+
const key = norm(name);
|
|
93165
|
+
return this.list().find((p) => norm(p.name) === key) ?? null;
|
|
93166
|
+
},
|
|
93167
|
+
/** Insert, or merge into the same-named project (omitted fields keep their stored value). */
|
|
93168
|
+
upsert(input) {
|
|
93169
|
+
const existing = this.findByName(input.name);
|
|
93170
|
+
if (existing) return { record: this.update(existing.id, input), created: false };
|
|
93171
|
+
return { record: this.add(input), created: true };
|
|
93172
|
+
},
|
|
93173
|
+
/** Batch insert/merge in one transaction; within-batch duplicates collapse onto one row. */
|
|
93174
|
+
addMany(inputs) {
|
|
93175
|
+
return tx(() => {
|
|
93176
|
+
const records = [];
|
|
93177
|
+
let created = 0;
|
|
93178
|
+
let updated = 0;
|
|
93179
|
+
for (const input of inputs) {
|
|
93180
|
+
const { record: record2, created: isNew } = this.upsert(input);
|
|
93181
|
+
records.push(record2);
|
|
93182
|
+
if (isNew) created += 1;
|
|
93183
|
+
else updated += 1;
|
|
93184
|
+
}
|
|
93185
|
+
return { records, created, updated };
|
|
93186
|
+
});
|
|
93187
|
+
},
|
|
93091
93188
|
add(input) {
|
|
93092
93189
|
const ts = now();
|
|
93093
93190
|
const id2 = newId("prj_");
|
|
@@ -93155,6 +93252,32 @@ var skillRepo = {
|
|
|
93155
93252
|
const r = getDb().prepare(`SELECT * FROM skills WHERE id = ?`).get(id2);
|
|
93156
93253
|
return r ? mapSkill(r) : null;
|
|
93157
93254
|
},
|
|
93255
|
+
/** Existing skill with the same name (case-insensitive), or null (for idempotent batch upserts). */
|
|
93256
|
+
findByName(name) {
|
|
93257
|
+
const key = norm(name);
|
|
93258
|
+
return this.list().find((s) => norm(s.name) === key) ?? null;
|
|
93259
|
+
},
|
|
93260
|
+
/** Insert, or merge into the same-named skill (omitted fields keep their stored value). */
|
|
93261
|
+
upsert(input) {
|
|
93262
|
+
const existing = this.findByName(input.name);
|
|
93263
|
+
if (existing) return { record: this.update(existing.id, input), created: false };
|
|
93264
|
+
return { record: this.add(input), created: true };
|
|
93265
|
+
},
|
|
93266
|
+
/** Batch insert/merge in one transaction; within-batch duplicates collapse onto one row. */
|
|
93267
|
+
addMany(inputs) {
|
|
93268
|
+
return tx(() => {
|
|
93269
|
+
const records = [];
|
|
93270
|
+
let created = 0;
|
|
93271
|
+
let updated = 0;
|
|
93272
|
+
for (const input of inputs) {
|
|
93273
|
+
const { record: record2, created: isNew } = this.upsert(input);
|
|
93274
|
+
records.push(record2);
|
|
93275
|
+
if (isNew) created += 1;
|
|
93276
|
+
else updated += 1;
|
|
93277
|
+
}
|
|
93278
|
+
return { records, created, updated };
|
|
93279
|
+
});
|
|
93280
|
+
},
|
|
93158
93281
|
add(input) {
|
|
93159
93282
|
const ts = now();
|
|
93160
93283
|
const id2 = newId("skl_");
|
|
@@ -93984,7 +94107,7 @@ function styleSignals(text) {
|
|
|
93984
94107
|
}
|
|
93985
94108
|
var DISCLAIMER = "\uC774 \uC810\uAC80\uC740 LLM \uC5C6\uC774 \uC140 \uC218 \uC788\uB294 \uAC83\uB9CC \uBD05\uB2C8\uB2E4(\uC22B\uC790 \uCD9C\uCC98\xB7\uBB38\uCCB4 \uC2E0\uD638). \uD1B5\uACFC=\uD569\uACA9\uC774 \uC544\uB2C8\uBA70, \uC0AC\uC2E4\xB7\uB3D9\uBB38\uC11C\uB2F5\xB7\uC758\uBBF8 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uC9C1\uC811 \uD574\uC57C \uD569\uB2C8\uB2E4.";
|
|
93986
94109
|
function lintArtifact(kind, text, corpus, opts = {}) {
|
|
93987
|
-
const
|
|
94110
|
+
const norm2 = normalizeText(text);
|
|
93988
94111
|
const corpusAvailable = (corpus.documents + corpus.structured).trim().length > 0;
|
|
93989
94112
|
const provenance = analyzeProvenance(text, corpus);
|
|
93990
94113
|
const signals = styleSignals(text);
|
|
@@ -94008,7 +94131,7 @@ function lintArtifact(kind, text, corpus, opts = {}) {
|
|
|
94008
94131
|
}
|
|
94009
94132
|
return {
|
|
94010
94133
|
kind,
|
|
94011
|
-
charCount: [...
|
|
94134
|
+
charCount: [...norm2].length,
|
|
94012
94135
|
signals,
|
|
94013
94136
|
provenance,
|
|
94014
94137
|
blocking,
|
|
@@ -94068,20 +94191,40 @@ function addResume(input) {
|
|
|
94068
94191
|
activityRepo.log("resume_added", `${doc.title} \uBB38\uC11C\uB97C \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, "document", doc.id);
|
|
94069
94192
|
return doc;
|
|
94070
94193
|
}
|
|
94071
|
-
function
|
|
94072
|
-
const
|
|
94073
|
-
|
|
94074
|
-
return
|
|
94194
|
+
function batchNames(names) {
|
|
94195
|
+
const shown = names.filter(Boolean).slice(0, 8).join(", ");
|
|
94196
|
+
if (!shown) return "";
|
|
94197
|
+
return `: ${shown}${names.length > 8 ? " \uC678" : ""}`;
|
|
94075
94198
|
}
|
|
94076
|
-
function
|
|
94077
|
-
const
|
|
94078
|
-
activityRepo.log(
|
|
94079
|
-
|
|
94199
|
+
function addExperiences(inputs) {
|
|
94200
|
+
const res = experienceRepo.addMany(inputs);
|
|
94201
|
+
activityRepo.log(
|
|
94202
|
+
"profile_updated",
|
|
94203
|
+
`\uACBD\uB825 ${res.records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4(\uC2E0\uADDC ${res.created} \xB7 \uAC31\uC2E0 ${res.updated})${batchNames(res.records.map((e) => e.company))}.`,
|
|
94204
|
+
"experience",
|
|
94205
|
+
res.records[0]?.id
|
|
94206
|
+
);
|
|
94207
|
+
return res;
|
|
94208
|
+
}
|
|
94209
|
+
function addProjects(inputs) {
|
|
94210
|
+
const res = projectRepo.addMany(inputs);
|
|
94211
|
+
activityRepo.log(
|
|
94212
|
+
"profile_updated",
|
|
94213
|
+
`\uD504\uB85C\uC81D\uD2B8 ${res.records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4(\uC2E0\uADDC ${res.created} \xB7 \uAC31\uC2E0 ${res.updated})${batchNames(res.records.map((p) => p.name))}.`,
|
|
94214
|
+
"project",
|
|
94215
|
+
res.records[0]?.id
|
|
94216
|
+
);
|
|
94217
|
+
return res;
|
|
94080
94218
|
}
|
|
94081
|
-
function
|
|
94082
|
-
const
|
|
94083
|
-
activityRepo.log(
|
|
94084
|
-
|
|
94219
|
+
function addSkills(inputs) {
|
|
94220
|
+
const res = skillRepo.addMany(inputs);
|
|
94221
|
+
activityRepo.log(
|
|
94222
|
+
"profile_updated",
|
|
94223
|
+
`\uAE30\uC220 ${res.records.length}\uAC1C\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4(\uC2E0\uADDC ${res.created} \xB7 \uAC31\uC2E0 ${res.updated})${batchNames(res.records.map((s) => s.name))}.`,
|
|
94224
|
+
"skill",
|
|
94225
|
+
res.records[0]?.id
|
|
94226
|
+
);
|
|
94227
|
+
return res;
|
|
94085
94228
|
}
|
|
94086
94229
|
function saveJobPosting(input, id2) {
|
|
94087
94230
|
const job = jobRepo.upsert(input, id2);
|
|
@@ -94091,7 +94234,7 @@ function saveJobPosting(input, id2) {
|
|
|
94091
94234
|
}
|
|
94092
94235
|
function saveFitAnalysis(input) {
|
|
94093
94236
|
const job = jobRepo.get(input.job_id);
|
|
94094
|
-
if (!job) throw
|
|
94237
|
+
if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}. \uBA3C\uC800 save_job_posting\uC73C\uB85C \uACF5\uACE0\uB97C \uC800\uC7A5\uD558\uC138\uC694.`);
|
|
94095
94238
|
applicationRepo.ensure(job.id, "draft");
|
|
94096
94239
|
const fit = fitRepo.save(input);
|
|
94097
94240
|
const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
|
|
@@ -94106,7 +94249,7 @@ function saveCoverLetterVersion(input) {
|
|
|
94106
94249
|
const detail = verification.blocking.map((b) => `${b.label}: ${b.detail}`).join("; ");
|
|
94107
94250
|
const strictHit = verification.blocking.some((b) => b.id === "unverified_numbers_strict");
|
|
94108
94251
|
const tail = strictHit ? `\uC5C4\uACA9 \uBAA8\uB4DC\uAC00 \uCF1C\uC838 \uC788\uC5B4, \uC62C\uB9B0 \uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uC5C6\uB294 \uC218\uCE58\uB294 \uB9C9\uD799\uB2C8\uB2E4. \uC774\uB825\uC11C\uB97C \uC62C\uB9AC\uAC70\uB098 \uC5C4\uACA9 \uBAA8\uB4DC\uB97C \uB044\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774\uBA74 force: true\uB85C \uC800\uC7A5\uD558\uC138\uC694.` : `\uC774 \uC218\uCE58\uB294 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4. \uC6D0\uBCF8 \uB370\uC774\uD130\uC758 \uC2E4\uC81C \uC218\uCE58\uB85C \uACE0\uCE58\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774 \uB9DE\uB2E4\uBA74 \uBA3C\uC800 \uACBD\uB825/\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADF8 \uC218\uCE58\uB97C \uCD94\uAC00\uD55C \uB4A4 \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694(\uADF8\uB300\uB85C \uC800\uC7A5\uD558\uB824\uBA74 force: true).`;
|
|
94109
|
-
throw new
|
|
94252
|
+
throw new CareerMateError(`\uC800\uC7A5 \uC804 \uC790\uB3D9 \uC810\uAC80\uC5D0\uC11C \uB9C9\uD614\uC2B5\uB2C8\uB2E4 \u2014 ${detail}. ${tail}`, 400, "save_gate");
|
|
94110
94253
|
}
|
|
94111
94254
|
const { coverLetter, version: version3 } = coverLetterRepo.addVersion({
|
|
94112
94255
|
cover_letter_id: input.cover_letter_id,
|
|
@@ -94133,7 +94276,7 @@ function saveCoverLetterVersion(input) {
|
|
|
94133
94276
|
}
|
|
94134
94277
|
function updateApplicationStatus(jobId, status, note) {
|
|
94135
94278
|
const job = jobRepo.get(jobId);
|
|
94136
|
-
if (!job) throw
|
|
94279
|
+
if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${jobId}`);
|
|
94137
94280
|
const application = applicationRepo.setStatus(jobId, status, note);
|
|
94138
94281
|
activityRepo.log(
|
|
94139
94282
|
"application_status_changed",
|
|
@@ -94148,11 +94291,11 @@ function updateApplicationStatus(jobId, status, note) {
|
|
|
94148
94291
|
}
|
|
94149
94292
|
function saveInterviewPrep(input) {
|
|
94150
94293
|
const job = jobRepo.get(input.job_id);
|
|
94151
|
-
if (!job) throw
|
|
94294
|
+
if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}`);
|
|
94152
94295
|
const app = applicationRepo.getByJob(job.id);
|
|
94153
94296
|
if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
|
|
94154
94297
|
const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
|
|
94155
|
-
throw
|
|
94298
|
+
throw conflict(
|
|
94156
94299
|
`\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 update_application_status\uB85C \uC0C1\uD0DC\uB97C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
94157
94300
|
);
|
|
94158
94301
|
}
|
|
@@ -94233,7 +94376,7 @@ var WORKFLOWS = [
|
|
|
94233
94376
|
'\uD30C\uC77C \uC785\uB825 \uBD84\uAE30: \u2460 \uC0AC\uC6A9\uC790\uAC00 "\uD30C\uC77C\uC774 \uC788\uB2E4"\uACE0 \uD558\uBA74 `open_inbox`\uB85C \uC778\uC785 \uD3F4\uB354\uB97C \uC5F4\uC5B4 \uD30C\uC77C\uC744 \uB123\uAC8C \uD558\uACE0, "\uB2E4 \uB123\uC5C8\uB2E4"\uACE0 \uD558\uBA74 `read_inbox`\uB85C \uBCF8\uBB38\uC744 \uC77D\uB294\uB2E4(pdf\xB7\uC774\uBBF8\uC9C0\uB294 read_inbox\uAC00 \uACBD\uB85C\uB9CC \uC8FC\uBBC0\uB85C \uD074\uB77C\uC774\uC5B8\uD2B8\uC758 \uD30C\uC77C \uC77D\uAE30\uB85C \uC9C1\uC811 \uC77D\uB294\uB2E4). \u2461 \uC0AC\uC6A9\uC790\uAC00 \uD30C\uC77C \uACBD\uB85C\uB97C \uC9C1\uC811 \uC54C\uB824\uC8FC\uBA74 `read_document`\uB85C \uC77D\uB294\uB2E4. \u2462 "\uC5C6\uB2E4/\uC9C1\uC811 \uC785\uB825/\uD14D\uC2A4\uD2B8\uB85C \uBD99\uC5EC\uB123\uAE30"\uBA74 \uD3F4\uB354\uB97C \uC5F4\uC9C0 \uB9D0\uACE0 \uBC1B\uC740 \uD14D\uC2A4\uD2B8\uB97C \uADF8\uB300\uB85C \uC4F4\uB2E4. (\uBB3B\uC9C0\uB3C4 \uC54A\uACE0 open_inbox\uB85C \uD3F4\uB354\uB97C \uC790\uB3D9\uC73C\uB85C \uC5F4\uC9C0 \uC54A\uB294\uB2E4.)',
|
|
94234
94377
|
"\uC218\uC9D1\uD55C \uC815\uBCF4\uB97C \uAD6C\uC870\uD654\uD574 `save_profile`\uB85C \uC800\uC7A5\uD55C\uB2E4(\uC774\uB984\xB7\uC5F0\uB77D\uCC98\xB7headline\xB7summary\xB7desired_roles\xB7desired_conditions, \uADF8\uB9AC\uACE0 \uAE00\uC4F0\uAE30 \uC120\uD638\uC778 preferred_tone\uACFC emphasis_points \uD3EC\uD568). \uD30C\uC77C \uCD9C\uCC98\uB294 source=upload, \uC9C1\uC811 \uC785\uB825\uC740 manual.",
|
|
94235
94378
|
"\uC5C5\uB85C\uB4DC\uD55C \uC774\uB825\uC11C/\uACBD\uB825\uAE30\uC220\uC11C/\uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uBCF8\uBB38\uC744 `add_resume`\uB85C \uC800\uC7A5\uD55C\uB2E4(kind\xB7title \uC9C0\uC815, \uB300\uD45C \uBB38\uC11C\uB294 is_primary=true).",
|
|
94236
|
-
"\uACBD\uB825\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uC2A4\uD0AC\uC774 \uD30C\uC545\uB418\uBA74 `add_experience`\xB7`add_project`\xB7`add_skill`\uB85C \
|
|
94379
|
+
"\uACBD\uB825\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uC2A4\uD0AC\uC774 \uD30C\uC545\uB418\uBA74 `add_experience`\xB7`add_project`\xB7`add_skill`\uB85C \uC800\uC7A5\uD55C\uB2E4 \u2014 \uAC01 \uB3C4\uAD6C\uB294 \uBC30\uC5F4\uC744 \uBC1B\uC73C\uBBC0\uB85C \uD30C\uC545\uD55C \uD56D\uBAA9\uC744 \uD55C \uBC88\uC5D0 \uBAA8\uC544 \uB118\uAE34\uB2E4(\uD56D\uBAA9\uB9C8\uB2E4 \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uC54A\uB294\uB2E4). \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uB123\uC5B4\uB3C4 \uC911\uBCF5 \uC5C6\uC774 \uAC31\uC2E0\uB41C\uB2E4(\uC644\uC131\uB3C4\uC758 has_experience/has_skills\uC5D0 \uBC18\uC601).",
|
|
94237
94380
|
"\uAE30\uC874 \uC790\uAE30\uC18C\uAC1C\uC11C\uAC00 \uC788\uC73C\uBA74 `save_cover_letter_version`\uC73C\uB85C \uC800\uC7A5\uD55C\uB2E4(title+content\uBA74 v1\uB85C \uC800\uC7A5, \uC5C6\uC73C\uBA74 \uB098\uC911\uC5D0 \uC791\uC131 \uAC00\uB2A5\uD568\uC744 \uC548\uB0B4).",
|
|
94238
94381
|
"`open_dashboard`\uB97C \uD638\uCD9C\uD574 \uC0AC\uC6A9\uC790\uAC00 \uC790\uAE30 \uB370\uC774\uD130\uB97C \uD655\uC778\uD558\uAC8C \uD55C\uB2E4.",
|
|
94239
94382
|
"\uB2E4\uC2DC `get_onboarding_status`\uB85C \uC644\uC131\uB3C4\uB97C \uD655\uC778\uD558\uACE0, \uB2E4\uC74C \uB2E8\uACC4(\uACF5\uACE0 \uBD84\uC11D \uB4F1)\uB97C \uC81C\uC548\uD55C\uB2E4."
|
|
@@ -96054,11 +96197,12 @@ ${lines}`;
|
|
|
96054
96197
|
{
|
|
96055
96198
|
name: "add_experience",
|
|
96056
96199
|
title: "\uACBD\uB825 \uCD94\uAC00",
|
|
96057
|
-
description: "\uAD6C\uC870\uD654\uB41C \uACBD\uB825(\uC9C1\uC7A5 \uB2E8\uC704)\uC744 \
|
|
96058
|
-
inputSchema:
|
|
96200
|
+
description: "\uAD6C\uC870\uD654\uB41C \uACBD\uB825(\uC9C1\uC7A5 \uB2E8\uC704)\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC774\uB825\uC11C\uC5D0\uC11C \uD30C\uC545\uD55C \uACBD\uB825\uC744 experiences \uBC30\uC5F4\uC5D0 \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC74\uC774\uC5B4\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uD56D\uBAA9\uB9C8\uB2E4 \uB3C4\uAD6C\uB97C \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uC774\uB825\uC11C \uBCF8\uBB38(add_resume)\uACFC \uB2EC\uB9AC \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uAE30\uAC04\xB7\uC131\uACFC\uB97C \uD544\uB4DC\uB85C \uB098\uB220 \uC800\uC7A5\uD574 \uC801\uD569\uB3C4 \uBD84\uC11D/\uC790\uC18C\uC11C \uC791\uC131 \uC2DC \uC815\uBC00\uD558\uAC8C \uD65C\uC6A9\uB429\uB2C8\uB2E4. \uAC01 \uD56D\uBAA9\uC740 company\uAC00 \uD544\uC218\uC774\uBA70, achievements\uC5D0\uB294 \uC815\uB7C9 \uC9C0\uD45C\uAC00 \uB2F4\uAE34 \uC131\uACFC\uB97C, tech\uC5D0\uB294 \uC0AC\uC6A9 \uAE30\uC220\uC744 \uBC30\uC5F4\uB85C \uB123\uC73C\uC138\uC694. \uAC19\uC740 \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uC785\uC0AC\uC77C(start_date)\uC774 \uC77C\uCE58\uD558\uBA74 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4(get_onboarding_status)\uC758 has_experience\uB97C \uCC44\uC6B0\uB824\uBA74 1\uAC74 \uC774\uC0C1 \uCD94\uAC00\uD558\uC138\uC694.",
|
|
96201
|
+
inputSchema: ExperienceBatchInputSchema.shape,
|
|
96059
96202
|
handler: (args) => {
|
|
96060
|
-
const
|
|
96061
|
-
|
|
96203
|
+
const { records, created, updated } = addExperiences(args.experiences);
|
|
96204
|
+
const names = records.map((e) => `${e.company}${e.role ? ` \xB7 ${e.role}` : ""}`).join(", ");
|
|
96205
|
+
return ok(`\uACBD\uB825 ${records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC2E0\uADDC ${created} \xB7 \uAC31\uC2E0 ${updated}): ${names}`, records);
|
|
96062
96206
|
}
|
|
96063
96207
|
},
|
|
96064
96208
|
{
|
|
@@ -96076,11 +96220,12 @@ ${lines}`;
|
|
|
96076
96220
|
{
|
|
96077
96221
|
name: "add_project",
|
|
96078
96222
|
title: "\uD504\uB85C\uC81D\uD2B8 \uCD94\uAC00",
|
|
96079
|
-
description: "\uB300\uD45C \uD504\uB85C\uC81D\uD2B8\uB97C \
|
|
96080
|
-
inputSchema:
|
|
96223
|
+
description: "\uB300\uD45C \uD504\uB85C\uC81D\uD2B8\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. projects \uBC30\uC5F4\uC5D0 \uC5EC\uB7EC \uAC1C\uB97C \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uD56D\uBAA9\uB9C8\uB2E4 \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uAC01 \uD56D\uBAA9\uC740 name\uC774 \uD544\uC218\uC774\uBA70, highlights(\uD575\uC2EC \uC131\uACFC)\uC640 tech(\uC0AC\uC6A9 \uAE30\uC220)\uB97C \uBC30\uC5F4\uB85C \uB123\uC73C\uBA74 \uC790\uC18C\uC11C\xB7\uBA74\uC811 \uC900\uBE44\uC5D0\uC11C \uADFC\uAC70\uB85C \uD65C\uC6A9\uB429\uB2C8\uB2E4. url\uC5D0 \uACB0\uACFC\uBB3C \uB9C1\uD06C\uB97C \uB123\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uAC19\uC740 \uC774\uB984\uC758 \uD504\uB85C\uC81D\uD2B8\uB294 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4.",
|
|
96224
|
+
inputSchema: ProjectBatchInputSchema.shape,
|
|
96081
96225
|
handler: (args) => {
|
|
96082
|
-
const
|
|
96083
|
-
|
|
96226
|
+
const { records, created, updated } = addProjects(args.projects);
|
|
96227
|
+
const names = records.map((p) => p.name).join(", ");
|
|
96228
|
+
return ok(`\uD504\uB85C\uC81D\uD2B8 ${records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC2E0\uADDC ${created} \xB7 \uAC31\uC2E0 ${updated}): ${names}`, records);
|
|
96084
96229
|
}
|
|
96085
96230
|
},
|
|
96086
96231
|
{
|
|
@@ -96098,11 +96243,12 @@ ${lines}`;
|
|
|
96098
96243
|
{
|
|
96099
96244
|
name: "add_skill",
|
|
96100
96245
|
title: "\uAE30\uC220\uC2A4\uD0DD \uCD94\uAC00",
|
|
96101
|
-
description: "\uBCF4\uC720 \uAE30\uC220\uC2A4\uD0DD\uC744 \
|
|
96102
|
-
inputSchema:
|
|
96246
|
+
description: "\uBCF4\uC720 \uAE30\uC220\uC2A4\uD0DD\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC5EC\uB7EC \uAC1C\uB97C skills \uBC30\uC5F4\uC5D0 \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uAE30\uC220\uB9C8\uB2E4 \uB3C4\uAD6C\uB97C \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uAC01 \uD56D\uBAA9\uC740 name\uC774 \uD544\uC218\uC774\uBA70, category(\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1)\xB7level\xB7years\uB85C \uC219\uB828\uB3C4\uB97C \uD568\uAED8 \uAE30\uB85D\uD558\uBA74 \uACF5\uACE0 \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC774 \uC815\uD655\uD574\uC9D1\uB2C8\uB2E4. \uAC19\uC740 \uC774\uB984\uC758 \uAE30\uC220\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4\uC758 has_skills\uB97C \uCC44\uC6B0\uB824\uBA74 1\uAC1C \uC774\uC0C1 \uCD94\uAC00\uD558\uC138\uC694.",
|
|
96247
|
+
inputSchema: SkillBatchInputSchema.shape,
|
|
96103
96248
|
handler: (args) => {
|
|
96104
|
-
const
|
|
96105
|
-
|
|
96249
|
+
const { records, created, updated } = addSkills(args.skills);
|
|
96250
|
+
const names = records.map((s) => s.name).join(", ");
|
|
96251
|
+
return ok(`\uAE30\uC220 ${records.length}\uAC1C\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC2E0\uADDC ${created} \xB7 \uAC31\uC2E0 ${updated}): ${names}`, records);
|
|
96106
96252
|
}
|
|
96107
96253
|
},
|
|
96108
96254
|
{
|
package/dist/migrate.mjs
CHANGED
|
@@ -4436,7 +4436,20 @@ var optLine = external_exports.string().max(MAX_LINE).optional();
|
|
|
4436
4436
|
var optNote = external_exports.string().max(MAX_NOTE).optional();
|
|
4437
4437
|
var optBody = external_exports.string().max(MAX_BODY).optional();
|
|
4438
4438
|
var reqBody = external_exports.string().max(MAX_BODY).refine((s) => s.trim().length > 0, { message: EMPTY_MSG });
|
|
4439
|
-
|
|
4439
|
+
function toStrList(v) {
|
|
4440
|
+
if (typeof v !== "string") return v;
|
|
4441
|
+
const s = v.trim();
|
|
4442
|
+
if (!s) return [];
|
|
4443
|
+
if (s.startsWith("[")) {
|
|
4444
|
+
try {
|
|
4445
|
+
const parsed = JSON.parse(s);
|
|
4446
|
+
if (Array.isArray(parsed)) return parsed;
|
|
4447
|
+
} catch {
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
return s.split(/[\n,]/).map((x) => x.trim()).filter(Boolean);
|
|
4451
|
+
}
|
|
4452
|
+
var strList = external_exports.preprocess(toStrList, external_exports.array(external_exports.string().max(MAX_ITEM)).max(MAX_ITEMS));
|
|
4440
4453
|
var baseRecord = {
|
|
4441
4454
|
id,
|
|
4442
4455
|
created_at: isoDateTime,
|
|
@@ -4498,6 +4511,9 @@ var ExperienceRecordSchema = external_exports.object({
|
|
|
4498
4511
|
tech: external_exports.array(external_exports.string()),
|
|
4499
4512
|
order_index: external_exports.number()
|
|
4500
4513
|
});
|
|
4514
|
+
var ExperienceBatchInputSchema = external_exports.object({
|
|
4515
|
+
experiences: external_exports.array(ExperienceInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uACBD\uB825 \uBAA9\uB85D(\uD55C \uAC74\uC774\uC5B4\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uC785\uC0AC\uC77C\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
|
|
4516
|
+
});
|
|
4501
4517
|
var ProjectInputSchema = external_exports.object({
|
|
4502
4518
|
name: reqLine,
|
|
4503
4519
|
role: optLine,
|
|
@@ -4521,6 +4537,9 @@ var ProjectRecordSchema = external_exports.object({
|
|
|
4521
4537
|
end_date: external_exports.string().nullable(),
|
|
4522
4538
|
order_index: external_exports.number()
|
|
4523
4539
|
});
|
|
4540
|
+
var ProjectBatchInputSchema = external_exports.object({
|
|
4541
|
+
projects: external_exports.array(ProjectInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uC774\uB984\uC758 \uD504\uB85C\uC81D\uD2B8\uB294 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
|
|
4542
|
+
});
|
|
4524
4543
|
var SkillInputSchema = external_exports.object({
|
|
4525
4544
|
name: reqLine,
|
|
4526
4545
|
category: external_exports.string().max(MAX_LINE).optional().describe("\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1"),
|
|
@@ -4536,6 +4555,9 @@ var SkillRecordSchema = external_exports.object({
|
|
|
4536
4555
|
years: external_exports.number().nullable(),
|
|
4537
4556
|
order_index: external_exports.number()
|
|
4538
4557
|
});
|
|
4558
|
+
var SkillBatchInputSchema = external_exports.object({
|
|
4559
|
+
skills: external_exports.array(SkillInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uAE30\uC220\uC2A4\uD0DD \uBAA9\uB85D(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uC774\uB984\uC758 \uAE30\uC220\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
|
|
4560
|
+
});
|
|
4539
4561
|
var DocumentInputSchema = external_exports.object({
|
|
4540
4562
|
kind: external_exports.enum(DOCUMENT_KINDS),
|
|
4541
4563
|
title: reqLine,
|
package/dist/public/lib.js
CHANGED
|
@@ -135,7 +135,11 @@ export async function api(method, path, body) {
|
|
|
135
135
|
const text = await res.text();
|
|
136
136
|
let data = {};
|
|
137
137
|
if (text) { try { data = JSON.parse(text); } catch { /* non-JSON error page */ } }
|
|
138
|
-
if (!res.ok)
|
|
138
|
+
if (!res.ok) {
|
|
139
|
+
const e = new Error(data.error || friendlyStatus(res.status));
|
|
140
|
+
e.status = res.status; // lets callers self-heal (e.g. drop a stale row on 404)
|
|
141
|
+
throw e;
|
|
142
|
+
}
|
|
139
143
|
return data;
|
|
140
144
|
}
|
|
141
145
|
// Friendly fallback when the server didn't supply a message — never show a raw status code.
|
|
@@ -100,6 +100,13 @@ export async function render(ctx) {
|
|
|
100
100
|
await render(ctx); // refetch + re-render so the item moves to its new group
|
|
101
101
|
} catch (err) {
|
|
102
102
|
toastError(err);
|
|
103
|
+
if (err && err.status === 404) {
|
|
104
|
+
// The job/application no longer exists (e.g. a stale tab after a reset).
|
|
105
|
+
// Refresh so the phantom row disappears instead of erroring on every retry.
|
|
106
|
+
await ctx.refreshNav();
|
|
107
|
+
await render(ctx);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
103
110
|
selectEl.value = app.status; // revert on failure
|
|
104
111
|
selectEl.disabled = false;
|
|
105
112
|
}
|
|
@@ -262,7 +262,13 @@ async function renderDetail(ctx, jobId) {
|
|
|
262
262
|
if (res && res.hint) toastOk(res.hint);
|
|
263
263
|
await ctx.refreshNav();
|
|
264
264
|
await remount();
|
|
265
|
-
} catch (err) {
|
|
265
|
+
} catch (err) {
|
|
266
|
+
toastError(err);
|
|
267
|
+
// Stale tab: the job was removed since this page loaded. Re-render so the
|
|
268
|
+
// detail reflects reality (shows "not found") instead of looping the error.
|
|
269
|
+
if (err && err.status === 404) { await ctx.refreshNav(); await remount(); return; }
|
|
270
|
+
e.target.value = job.status;
|
|
271
|
+
}
|
|
266
272
|
},
|
|
267
273
|
},
|
|
268
274
|
);
|
|
@@ -309,21 +315,19 @@ async function renderDetail(ctx, jobId) {
|
|
|
309
315
|
el('div', { class: 'page-head__actions' }, headActions),
|
|
310
316
|
);
|
|
311
317
|
|
|
312
|
-
// --- Layout
|
|
313
|
-
|
|
318
|
+
// --- Layout ---------------------------------------------------------------
|
|
319
|
+
// 자기소개서·면접을 맨 위 2열로(둘 다 짧음), 그 아래 공고 정보(원문은 접힘)와
|
|
320
|
+
// 적합도를 전체폭으로 쌓는다. 한쪽 칼럼만 길어 빈 공간이 도드라지던 50/50
|
|
321
|
+
// 그리드를 대체한다. 관련 기록은 있을 때만 맨 아래.
|
|
322
|
+
const wrap = el('div', { class: 'stack-4' },
|
|
323
|
+
head,
|
|
324
|
+
el('div', { class: 'grid grid--2' },
|
|
325
|
+
CoverLettersCard(job.cover_letters),
|
|
326
|
+
InterviewCard(job)),
|
|
314
327
|
PostingCard(job),
|
|
315
328
|
FitCard(job.fit),
|
|
316
|
-
);
|
|
317
|
-
const right = el('div', { class: 'stack-4' },
|
|
318
|
-
CoverLettersCard(job.cover_letters),
|
|
319
|
-
InterviewCard(job),
|
|
320
329
|
RelatedCard(job.related),
|
|
321
330
|
);
|
|
322
|
-
|
|
323
|
-
const wrap = el('div', { class: 'stack-4' },
|
|
324
|
-
head,
|
|
325
|
-
el('div', { class: 'grid grid--2' }, left, right),
|
|
326
|
-
);
|
|
327
331
|
mount(ctx.view, wrap);
|
|
328
332
|
}
|
|
329
333
|
|
|
@@ -353,9 +357,12 @@ function PostingCard(job) {
|
|
|
353
357
|
}
|
|
354
358
|
|
|
355
359
|
if (job.description) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
el('
|
|
360
|
+
// 원문은 가장 길어 칼럼을 늘리는 주범 — 기본 접힘(아코디언)으로 둔다.
|
|
361
|
+
body.push(el('details', { class: 'disclosure mt-3' },
|
|
362
|
+
el('summary', { class: 'disclosure__summary' },
|
|
363
|
+
icon('chevronRight', 'disclosure__chevron'),
|
|
364
|
+
el('span', {}, '공고 원문')),
|
|
365
|
+
el('div', { class: 'doc-preview mt-2' }, job.description)));
|
|
359
366
|
}
|
|
360
367
|
|
|
361
368
|
return Card({ title: '공고 정보', body });
|
package/dist/public/styles.css
CHANGED
|
@@ -403,6 +403,15 @@ h1,h2,h3,h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
|
|
|
403
403
|
/* ----------------------------------------------------------------- doc preview */
|
|
404
404
|
.doc-preview { white-space: pre-wrap; word-break: break-word; font-size: 15px; line-height: 1.75; color: var(--text);
|
|
405
405
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 22px 26px; max-height: 60vh; overflow-y: auto; }
|
|
406
|
+
|
|
407
|
+
/* disclosure — native <details> for collapsing long content (예: 공고 원문) */
|
|
408
|
+
.disclosure__summary { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
|
|
409
|
+
list-style: none; color: var(--text-secondary); font-size: 13px; font-weight: 600; user-select: none; }
|
|
410
|
+
.disclosure__summary::-webkit-details-marker { display: none; }
|
|
411
|
+
.disclosure__summary:hover { color: var(--text); }
|
|
412
|
+
.disclosure__summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
|
|
413
|
+
.disclosure__chevron { width: 16px; height: 16px; flex: none; transition: transform .15s ease; }
|
|
414
|
+
.disclosure[open] .disclosure__chevron { transform: rotate(90deg); }
|
|
406
415
|
.kv { display: grid; grid-template-columns: 132px 1fr; gap: 8px 16px; }
|
|
407
416
|
.kv dt { color: var(--text-tertiary); font-size: 13px; }
|
|
408
417
|
/* min-width:0 overrides a grid item's default min-width:auto so the 1fr column
|