careervivid 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +3 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +7 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +131 -4
- package/dist/commands/update.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface PublishResult {
|
|
|
20
20
|
postId: string;
|
|
21
21
|
url: string;
|
|
22
22
|
message: string;
|
|
23
|
+
title?: string;
|
|
23
24
|
}
|
|
24
25
|
export interface VerifyResult {
|
|
25
26
|
userId: string;
|
|
@@ -39,6 +40,8 @@ export interface ApiError {
|
|
|
39
40
|
}[];
|
|
40
41
|
}
|
|
41
42
|
export declare function publishPost(payload: PublishPayload, dryRun?: boolean): Promise<PublishResult | ApiError>;
|
|
43
|
+
export declare function getPost(postId: string): Promise<PublishResult | ApiError>;
|
|
44
|
+
export declare function updatePost(postId: string, payload: Partial<PublishPayload>): Promise<PublishResult | ApiError>;
|
|
42
45
|
/**
|
|
43
46
|
* Verify an API key against the /verifyAuth endpoint.
|
|
44
47
|
* Optionally accepts a specific key (for set-key validation).
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAChD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { getApiKey, getApiUrl } from "./config.js";
|
|
8
8
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
9
|
-
const CLI_VERSION = "1.
|
|
9
|
+
const CLI_VERSION = "1.8.0";
|
|
10
10
|
function requireApiKey() {
|
|
11
11
|
const key = getApiKey();
|
|
12
12
|
if (!key) {
|
|
@@ -73,6 +73,12 @@ export async function publishPost(payload, dryRun = false) {
|
|
|
73
73
|
}
|
|
74
74
|
return apiRequest("POST", "publish", payload);
|
|
75
75
|
}
|
|
76
|
+
export async function getPost(postId) {
|
|
77
|
+
return apiRequest("GET", `publish/${postId}`);
|
|
78
|
+
}
|
|
79
|
+
export async function updatePost(postId, payload) {
|
|
80
|
+
return apiRequest("PATCH", "publish", { ...payload, postId });
|
|
81
|
+
}
|
|
76
82
|
/**
|
|
77
83
|
* Verify an API key against the /verifyAuth endpoint.
|
|
78
84
|
* Optionally accepts a specific key (for set-key validation).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4MpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6J7D"}
|
package/dist/commands/publish.js
CHANGED
|
@@ -53,12 +53,34 @@ function getFiles(dir, recursive) {
|
|
|
53
53
|
}
|
|
54
54
|
async function publishSingleFile(filePath, content, opts, jsonMode) {
|
|
55
55
|
const dryRun = !!opts.dryRun;
|
|
56
|
+
// ── Parse Frontmatter ───────────────────────────────────────────
|
|
57
|
+
let postId = undefined;
|
|
58
|
+
let cleanContent = content;
|
|
59
|
+
// Robust frontmatter extraction (multiline, flexible spacing)
|
|
60
|
+
// Matches --- start, any non-greedy content, and --- end with optional whitespace/newlines
|
|
61
|
+
const frontmatterMatch = content.match(/^---\s*[\r\n]+([\s\S]*?)[\r\n]+---/);
|
|
62
|
+
if (frontmatterMatch) {
|
|
63
|
+
const fm = frontmatterMatch[1];
|
|
64
|
+
// Match postId with potential leading/trailing whitespace or quotes
|
|
65
|
+
const idMatch = fm.match(/postId:\s*["']?([a-zA-Z0-9_-]+)["']?/i);
|
|
66
|
+
postId = idMatch?.[1];
|
|
67
|
+
// Remove the frontmatter block AND any trailing whitespace/newlines
|
|
68
|
+
const afterFm = content.slice(frontmatterMatch[0].length);
|
|
69
|
+
cleanContent = afterFm.replace(/^\s+/, "");
|
|
70
|
+
}
|
|
71
|
+
// Fail if onlyUpdate is set but no postId is found
|
|
72
|
+
if (opts.onlyUpdate && !postId) {
|
|
73
|
+
if (!jsonMode) {
|
|
74
|
+
console.error(chalk.red(`\n ✖ Missing postId frontmatter in ${filePath}. Use 'cv publish' to create a new post.`));
|
|
75
|
+
}
|
|
76
|
+
return { success: false };
|
|
77
|
+
}
|
|
56
78
|
const format = opts.format ||
|
|
57
79
|
(filePath !== "stdin" ? inferFormat(filePath) : "markdown");
|
|
58
80
|
const type = opts.type || inferType(format);
|
|
59
81
|
let title = opts.title || "";
|
|
60
82
|
if (!title && format === "markdown") {
|
|
61
|
-
const firstHeading =
|
|
83
|
+
const firstHeading = cleanContent.match(/^[#]{1,2}\s+(.+)$/m);
|
|
62
84
|
if (firstHeading) {
|
|
63
85
|
title = firstHeading[1].trim();
|
|
64
86
|
}
|
|
@@ -86,21 +108,68 @@ async function publishSingleFile(filePath, content, opts, jsonMode) {
|
|
|
86
108
|
type,
|
|
87
109
|
dataFormat: format,
|
|
88
110
|
title,
|
|
89
|
-
content,
|
|
111
|
+
content: cleanContent,
|
|
90
112
|
tags,
|
|
91
113
|
...(opts.cover ? { coverImage: opts.cover } : {}),
|
|
92
114
|
...(opts.official ? { isOfficialPost: true } : {}),
|
|
93
115
|
};
|
|
94
|
-
|
|
116
|
+
let result;
|
|
117
|
+
if (postId && !dryRun) {
|
|
118
|
+
const { updatePost, getPost } = await import("../api.js");
|
|
119
|
+
// Best Practice: Fetch remote title to verify identity before overwrite
|
|
120
|
+
const remotePost = await getPost(postId);
|
|
121
|
+
if (!isApiError(remotePost)) {
|
|
122
|
+
const remoteTitle = remotePost.title;
|
|
123
|
+
// Case-insensitive title comparison to catch potential mixups
|
|
124
|
+
if (title && remoteTitle && title.toLowerCase() !== remoteTitle.toLowerCase()) {
|
|
125
|
+
if (jsonMode) {
|
|
126
|
+
console.error(chalk.red(`\n ✖ Title mismatch for ${postId}. Local: "${title}", Remote: "${remoteTitle}". Aborting.`));
|
|
127
|
+
return { success: false };
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const enquirer = (await import("enquirer"));
|
|
131
|
+
const prompt = enquirer.default?.prompt || enquirer.prompt;
|
|
132
|
+
const answers = await prompt({
|
|
133
|
+
type: "confirm",
|
|
134
|
+
name: "confirm",
|
|
135
|
+
message: `Title Mismatch! Local: "${chalk.cyan(title)}", Remote: "${chalk.yellow(remoteTitle)}". Continue?`,
|
|
136
|
+
initial: false
|
|
137
|
+
});
|
|
138
|
+
if (!answers.confirm) {
|
|
139
|
+
return { success: false };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
result = await updatePost(postId, payload);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
result = await publishPost(payload, dryRun);
|
|
148
|
+
}
|
|
95
149
|
if (isApiError(result)) {
|
|
96
150
|
if (jsonMode) {
|
|
97
151
|
handleApiError(result, true);
|
|
98
152
|
}
|
|
99
153
|
else {
|
|
100
|
-
console.error(chalk.red(`\n ✖ Failed to publish ${filePath}: ${result.message}`));
|
|
154
|
+
console.error(chalk.red(`\n ✖ Failed to ${postId ? "update" : "publish"} ${filePath}: ${result.message}`));
|
|
101
155
|
}
|
|
102
156
|
return { success: false };
|
|
103
157
|
}
|
|
158
|
+
// ── Write back postId if it's new ────────────────────────────────
|
|
159
|
+
if (!postId && !dryRun && filePath !== "stdin" && result.postId) {
|
|
160
|
+
const newFm = `---\npostId: ${result.postId}\n---\n\n`;
|
|
161
|
+
const updatedFileContent = newFm + content;
|
|
162
|
+
try {
|
|
163
|
+
const { writeFileSync } = await import("fs");
|
|
164
|
+
writeFileSync(filePath, updatedFileContent, "utf-8");
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
// Non-fatal error
|
|
168
|
+
if (!jsonMode) {
|
|
169
|
+
console.warn(chalk.yellow(` ⚠ Could not write postId to ${filePath}`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
104
173
|
return {
|
|
105
174
|
success: true,
|
|
106
175
|
url: result.url,
|
|
@@ -198,4 +267,62 @@ export function registerPublishCommand(program) {
|
|
|
198
267
|
console.log(`\n ${chalk.yellow("Dry run complete.")} Validated ${chalk.bold(fileList.length)} files.\n`);
|
|
199
268
|
}
|
|
200
269
|
});
|
|
270
|
+
program
|
|
271
|
+
.command("update [files...]")
|
|
272
|
+
.description("Update existing content on CareerVivid (requires postId in frontmatter)")
|
|
273
|
+
.option("-t, --title <title>", "Override post title")
|
|
274
|
+
.option("--tags <tags>", "Comma-separated tags")
|
|
275
|
+
.option("--dry-run", "Validate without updating")
|
|
276
|
+
.option("--json", "Machine-readable output")
|
|
277
|
+
.option("-r, --recursive", "Recursively scan directories")
|
|
278
|
+
.action(async (files, opts) => {
|
|
279
|
+
const extendedOpts = { ...opts, onlyUpdate: true };
|
|
280
|
+
const jsonMode = !!opts.json;
|
|
281
|
+
const dryRun = !!opts.dryRun;
|
|
282
|
+
let fileList = [];
|
|
283
|
+
if (files.length === 0) {
|
|
284
|
+
printError("No files specified to update.", undefined, jsonMode);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
for (const arg of files) {
|
|
288
|
+
try {
|
|
289
|
+
const stat = lstatSync(arg);
|
|
290
|
+
if (stat.isDirectory()) {
|
|
291
|
+
fileList = fileList.concat(getFiles(arg, !!opts.recursive));
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
fileList.push(arg);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
printError(`Cannot find path: ${arg}`, undefined, jsonMode);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (!jsonMode && !dryRun) {
|
|
303
|
+
console.log(`\n ${chalk.bold("Preparing to update")} ${chalk.cyan(fileList.length)} ${fileList.length === 1 ? "file" : "files"}...`);
|
|
304
|
+
}
|
|
305
|
+
const results = [];
|
|
306
|
+
let successCount = 0;
|
|
307
|
+
for (const filePath of fileList) {
|
|
308
|
+
const content = readFileSync(filePath, "utf-8");
|
|
309
|
+
const spinner = (!jsonMode && !dryRun) ? ora(`Updating ${chalk.cyan(filePath)}...`).start() : null;
|
|
310
|
+
const res = await publishSingleFile(filePath, content, extendedOpts, jsonMode);
|
|
311
|
+
spinner?.stop();
|
|
312
|
+
if (res.success) {
|
|
313
|
+
successCount++;
|
|
314
|
+
results.push({ file: filePath, ...res });
|
|
315
|
+
if (!jsonMode && !dryRun) {
|
|
316
|
+
console.log(` ${chalk.green("✔")} Updated: ${chalk.bold(res.title)}`);
|
|
317
|
+
console.log(` ${chalk.dim(res.url)}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (jsonMode) {
|
|
322
|
+
console.log(JSON.stringify(results, null, 2));
|
|
323
|
+
}
|
|
324
|
+
else if (!dryRun) {
|
|
325
|
+
console.log(`\n ${chalk.green("Done!")} Successfully updated ${chalk.bold(successCount)} of ${chalk.bold(fileList.length)} files.\n`);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
201
328
|
}
|
package/dist/commands/update.js
CHANGED
|
@@ -10,7 +10,7 @@ import { spawn } from "child_process";
|
|
|
10
10
|
import { getHelpHeader } from "../branding.js";
|
|
11
11
|
export function registerUpdateCommand(program) {
|
|
12
12
|
program
|
|
13
|
-
.command("
|
|
13
|
+
.command("upgrade")
|
|
14
14
|
.description("Upgrade the CareerVivid CLI to the latest version")
|
|
15
15
|
.action(async () => {
|
|
16
16
|
console.log(getHelpHeader());
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
* cv config show Print current configuration
|
|
16
16
|
* cv config set <key> <value> Update a config value
|
|
17
17
|
* cv config get <key> Print a config value
|
|
18
|
-
* cv
|
|
18
|
+
* cv upgrade Upgrade the CLI to the latest version
|
|
19
|
+
* cv update [files...] Update existing content on CareerVivid
|
|
19
20
|
* cv --help / cv --version
|
|
20
21
|
*/
|
|
21
22
|
export {};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;GAmBG"}
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
* cv config show Print current configuration
|
|
16
16
|
* cv config set <key> <value> Update a config value
|
|
17
17
|
* cv config get <key> Print a config value
|
|
18
|
-
* cv
|
|
18
|
+
* cv upgrade Upgrade the CLI to the latest version
|
|
19
|
+
* cv update [files...] Update existing content on CareerVivid
|
|
19
20
|
* cv --help / cv --version
|
|
20
21
|
*/
|
|
21
22
|
import { Command } from "commander";
|