careervivid 1.6.0 → 1.7.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 CHANGED
@@ -39,6 +39,7 @@ export interface ApiError {
39
39
  }[];
40
40
  }
41
41
  export declare function publishPost(payload: PublishPayload, dryRun?: boolean): Promise<PublishResult | ApiError>;
42
+ export declare function updatePost(postId: string, payload: Partial<PublishPayload>): Promise<PublishResult | ApiError>;
42
43
  /**
43
44
  * Verify an API key against the /verifyAuth endpoint.
44
45
  * 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;CACnB;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACrB,OAAO,EAAE,IAAI,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAmED,wBAAsB,WAAW,CAC7B,OAAO,EAAE,cAAc,EACvB,MAAM,UAAQ,GACf,OAAO,CAAC,aAAa,GAAG,QAAQ,CAAC,CAgBnC;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAwC9E;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAMzE;AAED,wBAAsB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEnJ;AAED,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEnK;AAED,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEpK;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEvJ;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,QAAQ,CAEpD"}
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.6.0";
9
+ const CLI_VERSION = "1.7.0";
10
10
  function requireApiKey() {
11
11
  const key = getApiKey();
12
12
  if (!key) {
@@ -73,6 +73,9 @@ export async function publishPost(payload, dryRun = false) {
73
73
  }
74
74
  return apiRequest("POST", "publish", payload);
75
75
  }
76
+ export async function updatePost(postId, payload) {
77
+ return apiRequest("PATCH", "publish", { ...payload, postId });
78
+ }
76
79
  /**
77
80
  * Verify an API key against the /verifyAuth endpoint.
78
81
  * 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;AAkIpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8F7D"}
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;AA8KpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6J7D"}
@@ -53,12 +53,31 @@ 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
+ const frontmatterMatch = content.match(/^---\s*[\r\n]+([\s\S]*?)[\r\n]+---\s*[\r\n]+/);
61
+ if (frontmatterMatch) {
62
+ const fm = frontmatterMatch[1];
63
+ // Match postId with potential leading/trailing whitespace or quotes
64
+ const idMatch = fm.match(/postId:\s*["']?([a-zA-Z0-9_-]+)["']?/i);
65
+ postId = idMatch?.[1];
66
+ cleanContent = content.slice(frontmatterMatch[0].length);
67
+ }
68
+ // Fail if onlyUpdate is set but no postId is found
69
+ if (opts.onlyUpdate && !postId) {
70
+ if (!jsonMode) {
71
+ console.error(chalk.red(`\n ✖ Missing postId frontmatter in ${filePath}. Use 'cv publish' to create a new post.`));
72
+ }
73
+ return { success: false };
74
+ }
56
75
  const format = opts.format ||
57
76
  (filePath !== "stdin" ? inferFormat(filePath) : "markdown");
58
77
  const type = opts.type || inferType(format);
59
78
  let title = opts.title || "";
60
79
  if (!title && format === "markdown") {
61
- const firstHeading = content.match(/^#\s+(.+)$/m);
80
+ const firstHeading = cleanContent.match(/^#\s+(.+)$/m);
62
81
  if (firstHeading) {
63
82
  title = firstHeading[1].trim();
64
83
  }
@@ -86,21 +105,43 @@ async function publishSingleFile(filePath, content, opts, jsonMode) {
86
105
  type,
87
106
  dataFormat: format,
88
107
  title,
89
- content,
108
+ content: cleanContent,
90
109
  tags,
91
110
  ...(opts.cover ? { coverImage: opts.cover } : {}),
92
111
  ...(opts.official ? { isOfficialPost: true } : {}),
93
112
  };
94
- const result = await publishPost(payload, dryRun);
113
+ let result;
114
+ if (postId && !dryRun) {
115
+ const { updatePost } = await import("../api.js");
116
+ result = await updatePost(postId, payload);
117
+ }
118
+ else {
119
+ result = await publishPost(payload, dryRun);
120
+ }
95
121
  if (isApiError(result)) {
96
122
  if (jsonMode) {
97
123
  handleApiError(result, true);
98
124
  }
99
125
  else {
100
- console.error(chalk.red(`\n ✖ Failed to publish ${filePath}: ${result.message}`));
126
+ console.error(chalk.red(`\n ✖ Failed to ${postId ? "update" : "publish"} ${filePath}: ${result.message}`));
101
127
  }
102
128
  return { success: false };
103
129
  }
130
+ // ── Write back postId if it's new ────────────────────────────────
131
+ if (!postId && !dryRun && filePath !== "stdin" && result.postId) {
132
+ const newFm = `---\npostId: ${result.postId}\n---\n\n`;
133
+ const updatedFileContent = newFm + content;
134
+ try {
135
+ const { writeFileSync } = await import("fs");
136
+ writeFileSync(filePath, updatedFileContent, "utf-8");
137
+ }
138
+ catch (err) {
139
+ // Non-fatal error
140
+ if (!jsonMode) {
141
+ console.warn(chalk.yellow(` ⚠ Could not write postId to ${filePath}`));
142
+ }
143
+ }
144
+ }
104
145
  return {
105
146
  success: true,
106
147
  url: result.url,
@@ -198,4 +239,62 @@ export function registerPublishCommand(program) {
198
239
  console.log(`\n ${chalk.yellow("Dry run complete.")} Validated ${chalk.bold(fileList.length)} files.\n`);
199
240
  }
200
241
  });
242
+ program
243
+ .command("update [files...]")
244
+ .description("Update existing content on CareerVivid (requires postId in frontmatter)")
245
+ .option("-t, --title <title>", "Override post title")
246
+ .option("--tags <tags>", "Comma-separated tags")
247
+ .option("--dry-run", "Validate without updating")
248
+ .option("--json", "Machine-readable output")
249
+ .option("-r, --recursive", "Recursively scan directories")
250
+ .action(async (files, opts) => {
251
+ const extendedOpts = { ...opts, onlyUpdate: true };
252
+ const jsonMode = !!opts.json;
253
+ const dryRun = !!opts.dryRun;
254
+ let fileList = [];
255
+ if (files.length === 0) {
256
+ printError("No files specified to update.", undefined, jsonMode);
257
+ process.exit(1);
258
+ }
259
+ for (const arg of files) {
260
+ try {
261
+ const stat = lstatSync(arg);
262
+ if (stat.isDirectory()) {
263
+ fileList = fileList.concat(getFiles(arg, !!opts.recursive));
264
+ }
265
+ else {
266
+ fileList.push(arg);
267
+ }
268
+ }
269
+ catch (err) {
270
+ printError(`Cannot find path: ${arg}`, undefined, jsonMode);
271
+ process.exit(1);
272
+ }
273
+ }
274
+ if (!jsonMode && !dryRun) {
275
+ console.log(`\n ${chalk.bold("Preparing to update")} ${chalk.cyan(fileList.length)} ${fileList.length === 1 ? "file" : "files"}...`);
276
+ }
277
+ const results = [];
278
+ let successCount = 0;
279
+ for (const filePath of fileList) {
280
+ const content = readFileSync(filePath, "utf-8");
281
+ const spinner = (!jsonMode && !dryRun) ? ora(`Updating ${chalk.cyan(filePath)}...`).start() : null;
282
+ const res = await publishSingleFile(filePath, content, extendedOpts, jsonMode);
283
+ spinner?.stop();
284
+ if (res.success) {
285
+ successCount++;
286
+ results.push({ file: filePath, ...res });
287
+ if (!jsonMode && !dryRun) {
288
+ console.log(` ${chalk.green("✔")} Updated: ${chalk.bold(res.title)}`);
289
+ console.log(` ${chalk.dim(res.url)}`);
290
+ }
291
+ }
292
+ }
293
+ if (jsonMode) {
294
+ console.log(JSON.stringify(results, null, 2));
295
+ }
296
+ else if (!dryRun) {
297
+ console.log(`\n ${chalk.green("Done!")} Successfully updated ${chalk.bold(successCount)} of ${chalk.bold(fileList.length)} files.\n`);
298
+ }
299
+ });
201
300
  }
@@ -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("update")
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 update Upgrade the CLI to the latest version
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 {};
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG"}
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 update Upgrade the CLI to the latest version
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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "careervivid",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Official CLI for CareerVivid — publish articles, diagrams, and portfolio updates from your terminal or AI agent",
5
5
  "type": "module",
6
6
  "bin": {