@gobing-ai/knowledge-kit 0.0.21 → 0.0.22
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/.claude-plugin/marketplace.json +1 -1
- package/dist/index.js +161 -142
- package/package.json +1 -1
- package/plugins/generations/content-gen/dist/index.js +20 -3
- package/plugins/generations/core-facts-gen/dist/index.js +20 -3
- package/plugins/generations/daily-article-gen/dist/index.js +20 -3
- package/plugins/generations/dailynews-gen/dist/index.js +20 -3
- package/plugins/generations/episode-plan-gen/dist/index.js +20 -3
- package/plugins/generations/image-gen/dist/index.js +147 -69
- package/plugins/generations/image-gen/src/presets.ts +2 -2
- package/plugins/generations/image-gen/src/providers/agnes.ts +6 -2
- package/plugins/generations/image-gen/src/providers/azure.ts +6 -2
- package/plugins/generations/image-gen/src/providers/dashscope.ts +10 -3
- package/plugins/generations/image-gen/src/providers/google.ts +70 -29
- package/plugins/generations/image-gen/src/providers/huggingface.ts +3 -0
- package/plugins/generations/image-gen/src/providers/jimeng.ts +9 -2
- package/plugins/generations/image-gen/src/providers/minimax.ts +10 -2
- package/plugins/generations/image-gen/src/providers/openai.ts +9 -3
- package/plugins/generations/image-gen/src/providers/openrouter.ts +16 -4
- package/plugins/generations/image-gen/src/providers/replicate.ts +13 -3
- package/plugins/generations/image-gen/src/providers/seedream.ts +4 -1
- package/plugins/generations/image-gen/src/providers/timeouts.ts +14 -0
- package/plugins/generations/image-gen/src/providers/types.ts +10 -0
- package/plugins/generations/image-gen/src/providers/zai.ts +4 -1
- package/plugins/generations/news-report-gen/dist/index.js +20 -3
- package/plugins/generations/voice-gen/dist/index.js +36 -10
- package/plugins/generations/voice-gen/src/voicebox-client.ts +18 -4
- package/plugins/generations/voice-gen/src/voicebox-server.ts +2 -1
- package/plugins/ingestions/aihot-ingest/dist/index.js +25 -5
- package/plugins/ingestions/aihot-ingest/src/client.ts +5 -0
- package/plugins/ingestions/horizon-ingest/dist/index.js +20 -3
- package/plugins/ingestions/last30days-ingest/dist/index.js +20 -3
- package/plugins/ingestions/web-search/dist/index.js +22 -3
- package/plugins/ingestions/web-search/src/index.ts +4 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/kk-workflow-stages.ts +35 -7
- package/plugins/kk/scripts/render-md.ts +7 -1
- package/plugins/publishings/emdash-pub/dist/index.js +76 -19
- package/plugins/publishings/emdash-pub/src/index.ts +4 -0
- package/plugins/publishings/infoq-pub/dist/index.js +74 -21
- package/plugins/publishings/juejin-pub/dist/index.js +74 -21
- package/plugins/publishings/medium-pub/dist/index.js +77 -19
- package/plugins/publishings/medium-pub/src/index.ts +5 -0
- package/plugins/publishings/podcast-pub/dist/index.js +130 -34
- package/plugins/publishings/podcast-pub/src/index.ts +20 -5
- package/plugins/publishings/podcast-pub/src/map.ts +11 -0
- package/plugins/publishings/podcast-pub/src/show-notes.ts +66 -13
- package/plugins/publishings/qiita-pub/dist/index.js +75 -18
- package/plugins/publishings/qiita-pub/src/index.ts +4 -0
- package/plugins/publishings/substack-pub/dist/index.js +74 -21
- package/plugins/publishings/surfdash-pub/dist/index.js +99 -62
- package/plugins/publishings/surfdash-pub/src/index.ts +60 -19
- package/plugins/publishings/wechatmp-pub/dist/index.js +79 -22
- package/plugins/publishings/wechatmp-pub/src/index.ts +12 -3
- package/plugins/publishings/x-pub/dist/index.js +56 -37
- package/plugins/publishings/x-pub/src/index.ts +4 -0
- package/plugins/publishings/xhs-pub/dist/index.js +74 -21
- package/plugins/publishings/zenn-pub/dist/index.js +103 -25
- package/plugins/publishings/zenn-pub/src/index.ts +43 -9
|
@@ -21781,6 +21781,22 @@ var ResultSchema = exports_external.object({
|
|
|
21781
21781
|
error: exports_external.string().optional(),
|
|
21782
21782
|
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
21783
21783
|
});
|
|
21784
|
+
var TargetOutcomeSchema = exports_external.object({
|
|
21785
|
+
target: exports_external.string(),
|
|
21786
|
+
ok: exports_external.boolean(),
|
|
21787
|
+
status: exports_external.enum(["published", "failed", "skipped"]),
|
|
21788
|
+
url: exports_external.string().optional(),
|
|
21789
|
+
id: exports_external.string().optional(),
|
|
21790
|
+
error: exports_external.string().optional(),
|
|
21791
|
+
durationMs: exports_external.number().optional(),
|
|
21792
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
21793
|
+
});
|
|
21794
|
+
var AggregateResultSchema = exports_external.object({
|
|
21795
|
+
ok: exports_external.boolean(),
|
|
21796
|
+
summary: exports_external.object({ total: exports_external.number(), succeeded: exports_external.number(), failed: exports_external.number(), skipped: exports_external.number() }),
|
|
21797
|
+
targets: exports_external.array(TargetOutcomeSchema),
|
|
21798
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
21799
|
+
});
|
|
21784
21800
|
var DocListSchema = exports_external.array(DocSchema);
|
|
21785
21801
|
var omniVoiceGenContentSchema = ContentSchema.extend({
|
|
21786
21802
|
format: exports_external.literal("audio"),
|
|
@@ -21809,14 +21825,15 @@ var PluginKindSchema = exports_external.enum(PLUGIN_KINDS);
|
|
|
21809
21825
|
// ../../packages/kk-core/src/manifest.ts
|
|
21810
21826
|
init_zod();
|
|
21811
21827
|
var OperationIOSchema = exports_external.enum(["doc-list", "markdown"]);
|
|
21812
|
-
var
|
|
21828
|
+
var PluginOperationSchema = exports_external.object({ input: OperationIOSchema, output: OperationIOSchema });
|
|
21829
|
+
var PluginManifestSchema = exports_external.looseObject({
|
|
21813
21830
|
name: exports_external.string().min(1),
|
|
21814
21831
|
kind: PluginKindSchema,
|
|
21815
21832
|
entry: exports_external.string().min(1),
|
|
21816
21833
|
version: exports_external.string().optional(),
|
|
21817
21834
|
description: exports_external.string().optional(),
|
|
21818
|
-
operations: exports_external.record(exports_external.string(),
|
|
21819
|
-
})
|
|
21835
|
+
operations: exports_external.record(exports_external.string(), PluginOperationSchema).optional()
|
|
21836
|
+
});
|
|
21820
21837
|
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.39/node_modules/@gobing-ai/ts-runtime/dist/index.js
|
|
21821
21838
|
init_file_system_node();
|
|
21822
21839
|
init_config();
|
|
@@ -21843,42 +21860,78 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
|
|
|
21843
21860
|
if (false) {}
|
|
21844
21861
|
// ../../packages/publish-harness/src/index.ts
|
|
21845
21862
|
import { homedir } from "os";
|
|
21846
|
-
import { join } from "path";
|
|
21863
|
+
import { join, resolve } from "path";
|
|
21864
|
+
|
|
21865
|
+
class FakeTransportCore {
|
|
21866
|
+
callLogs = [];
|
|
21867
|
+
nextResult;
|
|
21868
|
+
setNextResult(result) {
|
|
21869
|
+
this.nextResult = result;
|
|
21870
|
+
}
|
|
21871
|
+
async publish(payload, defaultResult) {
|
|
21872
|
+
this.callLogs.push({
|
|
21873
|
+
timestamp: new Date().toISOString(),
|
|
21874
|
+
payload
|
|
21875
|
+
});
|
|
21876
|
+
return this.nextResult ?? defaultResult;
|
|
21877
|
+
}
|
|
21878
|
+
getCallLogs() {
|
|
21879
|
+
return this.callLogs;
|
|
21880
|
+
}
|
|
21881
|
+
reset() {
|
|
21882
|
+
this.callLogs = [];
|
|
21883
|
+
this.nextResult = undefined;
|
|
21884
|
+
}
|
|
21885
|
+
}
|
|
21886
|
+
|
|
21887
|
+
class FakeFileCliTransport {
|
|
21888
|
+
kind = "file-cli";
|
|
21889
|
+
core = new FakeTransportCore;
|
|
21890
|
+
setNextResult(result) {
|
|
21891
|
+
this.core.setNextResult(result);
|
|
21892
|
+
}
|
|
21893
|
+
async publish(payload) {
|
|
21894
|
+
return this.core.publish(payload, {
|
|
21895
|
+
ok: true,
|
|
21896
|
+
target: "fake-file-cli",
|
|
21897
|
+
url: "https://example.com/published-post",
|
|
21898
|
+
id: "fake-id-123",
|
|
21899
|
+
message: "Published offline via FakeFileCliTransport"
|
|
21900
|
+
});
|
|
21901
|
+
}
|
|
21902
|
+
getCallLogs() {
|
|
21903
|
+
return this.core.getCallLogs();
|
|
21904
|
+
}
|
|
21905
|
+
reset() {
|
|
21906
|
+
this.core.reset();
|
|
21907
|
+
}
|
|
21908
|
+
}
|
|
21909
|
+
|
|
21847
21910
|
class FakeBrowserTransport {
|
|
21848
21911
|
kind = "browser";
|
|
21849
21912
|
driver;
|
|
21850
|
-
|
|
21851
|
-
nextResult;
|
|
21913
|
+
core = new FakeTransportCore;
|
|
21852
21914
|
constructor(driver = "agent") {
|
|
21853
21915
|
this.driver = driver;
|
|
21854
|
-
this.callLogs = [];
|
|
21855
21916
|
}
|
|
21856
21917
|
setNextResult(result) {
|
|
21857
|
-
this.
|
|
21918
|
+
this.core.setNextResult(result);
|
|
21858
21919
|
}
|
|
21859
21920
|
async publish(payload) {
|
|
21860
|
-
this.
|
|
21861
|
-
timestamp: new Date().toISOString(),
|
|
21862
|
-
payload
|
|
21863
|
-
});
|
|
21864
|
-
if (this.nextResult) {
|
|
21865
|
-
return this.nextResult;
|
|
21866
|
-
}
|
|
21867
|
-
return {
|
|
21921
|
+
return this.core.publish(payload, {
|
|
21868
21922
|
ok: true,
|
|
21869
21923
|
target: "fake-browser",
|
|
21870
21924
|
url: "https://example.com/browser-draft",
|
|
21871
21925
|
id: "fake-browser-id-123",
|
|
21872
21926
|
message: "Published offline via FakeBrowserTransport",
|
|
21873
21927
|
metadata: { driver: this.driver }
|
|
21874
|
-
};
|
|
21928
|
+
});
|
|
21875
21929
|
}
|
|
21876
21930
|
getCallLogs() {
|
|
21877
|
-
return this.
|
|
21931
|
+
return this.core.getCallLogs();
|
|
21878
21932
|
}
|
|
21879
21933
|
reset() {
|
|
21880
|
-
this.
|
|
21881
|
-
this.nextResult = undefined;
|
|
21934
|
+
this.core.reset();
|
|
21882
21935
|
}
|
|
21883
21936
|
}
|
|
21884
21937
|
function resolveBrowserProfileDir(channel, override) {
|
|
@@ -21887,7 +21940,7 @@ function resolveBrowserProfileDir(channel, override) {
|
|
|
21887
21940
|
if (trimmed.startsWith("~/")) {
|
|
21888
21941
|
return join(homedir(), trimmed.slice(2));
|
|
21889
21942
|
}
|
|
21890
|
-
return trimmed;
|
|
21943
|
+
return resolve(trimmed);
|
|
21891
21944
|
}
|
|
21892
21945
|
return join(homedir(), ".config", "kk", "browser-profiles", channel);
|
|
21893
21946
|
}
|
|
@@ -21776,78 +21776,78 @@ __export(exports_src, {
|
|
|
21776
21776
|
FakeBrowserTransport: () => FakeBrowserTransport
|
|
21777
21777
|
});
|
|
21778
21778
|
import { homedir } from "os";
|
|
21779
|
-
import { join } from "path";
|
|
21779
|
+
import { join, resolve } from "path";
|
|
21780
21780
|
|
|
21781
|
-
class
|
|
21782
|
-
|
|
21783
|
-
callLogs;
|
|
21781
|
+
class FakeTransportCore {
|
|
21782
|
+
callLogs = [];
|
|
21784
21783
|
nextResult;
|
|
21785
|
-
constructor() {
|
|
21786
|
-
this.callLogs = [];
|
|
21787
|
-
}
|
|
21788
21784
|
setNextResult(result) {
|
|
21789
21785
|
this.nextResult = result;
|
|
21790
21786
|
}
|
|
21791
|
-
async publish(payload) {
|
|
21787
|
+
async publish(payload, defaultResult) {
|
|
21792
21788
|
this.callLogs.push({
|
|
21793
21789
|
timestamp: new Date().toISOString(),
|
|
21794
21790
|
payload
|
|
21795
21791
|
});
|
|
21796
|
-
|
|
21797
|
-
|
|
21798
|
-
|
|
21799
|
-
return
|
|
21792
|
+
return this.nextResult ?? defaultResult;
|
|
21793
|
+
}
|
|
21794
|
+
getCallLogs() {
|
|
21795
|
+
return this.callLogs;
|
|
21796
|
+
}
|
|
21797
|
+
reset() {
|
|
21798
|
+
this.callLogs = [];
|
|
21799
|
+
this.nextResult = undefined;
|
|
21800
|
+
}
|
|
21801
|
+
}
|
|
21802
|
+
|
|
21803
|
+
class FakeFileCliTransport {
|
|
21804
|
+
kind = "file-cli";
|
|
21805
|
+
core = new FakeTransportCore;
|
|
21806
|
+
setNextResult(result) {
|
|
21807
|
+
this.core.setNextResult(result);
|
|
21808
|
+
}
|
|
21809
|
+
async publish(payload) {
|
|
21810
|
+
return this.core.publish(payload, {
|
|
21800
21811
|
ok: true,
|
|
21801
21812
|
target: "fake-file-cli",
|
|
21802
21813
|
url: "https://example.com/published-post",
|
|
21803
21814
|
id: "fake-id-123",
|
|
21804
21815
|
message: "Published offline via FakeFileCliTransport"
|
|
21805
|
-
};
|
|
21816
|
+
});
|
|
21806
21817
|
}
|
|
21807
21818
|
getCallLogs() {
|
|
21808
|
-
return this.
|
|
21819
|
+
return this.core.getCallLogs();
|
|
21809
21820
|
}
|
|
21810
21821
|
reset() {
|
|
21811
|
-
this.
|
|
21812
|
-
this.nextResult = undefined;
|
|
21822
|
+
this.core.reset();
|
|
21813
21823
|
}
|
|
21814
21824
|
}
|
|
21815
21825
|
|
|
21816
21826
|
class FakeBrowserTransport {
|
|
21817
21827
|
kind = "browser";
|
|
21818
21828
|
driver;
|
|
21819
|
-
|
|
21820
|
-
nextResult;
|
|
21829
|
+
core = new FakeTransportCore;
|
|
21821
21830
|
constructor(driver = "agent") {
|
|
21822
21831
|
this.driver = driver;
|
|
21823
|
-
this.callLogs = [];
|
|
21824
21832
|
}
|
|
21825
21833
|
setNextResult(result) {
|
|
21826
|
-
this.
|
|
21834
|
+
this.core.setNextResult(result);
|
|
21827
21835
|
}
|
|
21828
21836
|
async publish(payload) {
|
|
21829
|
-
this.
|
|
21830
|
-
timestamp: new Date().toISOString(),
|
|
21831
|
-
payload
|
|
21832
|
-
});
|
|
21833
|
-
if (this.nextResult) {
|
|
21834
|
-
return this.nextResult;
|
|
21835
|
-
}
|
|
21836
|
-
return {
|
|
21837
|
+
return this.core.publish(payload, {
|
|
21837
21838
|
ok: true,
|
|
21838
21839
|
target: "fake-browser",
|
|
21839
21840
|
url: "https://example.com/browser-draft",
|
|
21840
21841
|
id: "fake-browser-id-123",
|
|
21841
21842
|
message: "Published offline via FakeBrowserTransport",
|
|
21842
21843
|
metadata: { driver: this.driver }
|
|
21843
|
-
};
|
|
21844
|
+
});
|
|
21844
21845
|
}
|
|
21845
21846
|
getCallLogs() {
|
|
21846
|
-
return this.
|
|
21847
|
+
return this.core.getCallLogs();
|
|
21847
21848
|
}
|
|
21848
21849
|
reset() {
|
|
21849
|
-
this.
|
|
21850
|
-
this.nextResult = undefined;
|
|
21850
|
+
this.core.reset();
|
|
21851
21851
|
}
|
|
21852
21852
|
}
|
|
21853
21853
|
function resolveBrowserProfileDir(channel, override) {
|
|
@@ -21856,12 +21856,12 @@ function resolveBrowserProfileDir(channel, override) {
|
|
|
21856
21856
|
if (trimmed.startsWith("~/")) {
|
|
21857
21857
|
return join(homedir(), trimmed.slice(2));
|
|
21858
21858
|
}
|
|
21859
|
-
return trimmed;
|
|
21859
|
+
return resolve(trimmed);
|
|
21860
21860
|
}
|
|
21861
21861
|
return join(homedir(), ".config", "kk", "browser-profiles", channel);
|
|
21862
21862
|
}
|
|
21863
21863
|
async function trySelectors(page, selectors, options = {}) {
|
|
21864
|
-
const timeout = options.timeout ??
|
|
21864
|
+
const timeout = options.timeout ?? 2000;
|
|
21865
21865
|
const visible = options.visible ?? true;
|
|
21866
21866
|
for (const selector of selectors) {
|
|
21867
21867
|
try {
|
|
@@ -21871,7 +21871,7 @@ async function trySelectors(page, selectors, options = {}) {
|
|
|
21871
21871
|
continue;
|
|
21872
21872
|
}
|
|
21873
21873
|
if (visible) {
|
|
21874
|
-
const isVisible = await locator.isVisible({ timeout
|
|
21874
|
+
const isVisible = await locator.isVisible({ timeout }).catch(() => false);
|
|
21875
21875
|
if (!isVisible) {
|
|
21876
21876
|
continue;
|
|
21877
21877
|
}
|
|
@@ -21902,7 +21902,7 @@ var init_src = () => {};
|
|
|
21902
21902
|
// ../../plugins/publishings/surfdash-pub/src/index.ts
|
|
21903
21903
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
21904
21904
|
import { copyFile, cp, writeFile } from "fs/promises";
|
|
21905
|
-
import { dirname as dirname2, isAbsolute, join as join2, resolve, sep } from "path";
|
|
21905
|
+
import { dirname as dirname2, extname, isAbsolute, join as join2, resolve as resolve2, sep } from "path";
|
|
21906
21906
|
import { parseArgs } from "util";
|
|
21907
21907
|
|
|
21908
21908
|
// ../../packages/kk-core/src/kinds.ts
|
|
@@ -21939,6 +21939,22 @@ var ResultSchema = exports_external.object({
|
|
|
21939
21939
|
error: exports_external.string().optional(),
|
|
21940
21940
|
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
21941
21941
|
});
|
|
21942
|
+
var TargetOutcomeSchema = exports_external.object({
|
|
21943
|
+
target: exports_external.string(),
|
|
21944
|
+
ok: exports_external.boolean(),
|
|
21945
|
+
status: exports_external.enum(["published", "failed", "skipped"]),
|
|
21946
|
+
url: exports_external.string().optional(),
|
|
21947
|
+
id: exports_external.string().optional(),
|
|
21948
|
+
error: exports_external.string().optional(),
|
|
21949
|
+
durationMs: exports_external.number().optional(),
|
|
21950
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
21951
|
+
});
|
|
21952
|
+
var AggregateResultSchema = exports_external.object({
|
|
21953
|
+
ok: exports_external.boolean(),
|
|
21954
|
+
summary: exports_external.object({ total: exports_external.number(), succeeded: exports_external.number(), failed: exports_external.number(), skipped: exports_external.number() }),
|
|
21955
|
+
targets: exports_external.array(TargetOutcomeSchema),
|
|
21956
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
21957
|
+
});
|
|
21942
21958
|
var DocListSchema = exports_external.array(DocSchema);
|
|
21943
21959
|
var omniVoiceGenContentSchema = ContentSchema.extend({
|
|
21944
21960
|
format: exports_external.literal("audio"),
|
|
@@ -21967,14 +21983,15 @@ var PluginKindSchema = exports_external.enum(PLUGIN_KINDS);
|
|
|
21967
21983
|
// ../../packages/kk-core/src/manifest.ts
|
|
21968
21984
|
init_zod();
|
|
21969
21985
|
var OperationIOSchema = exports_external.enum(["doc-list", "markdown"]);
|
|
21970
|
-
var
|
|
21986
|
+
var PluginOperationSchema = exports_external.object({ input: OperationIOSchema, output: OperationIOSchema });
|
|
21987
|
+
var PluginManifestSchema = exports_external.looseObject({
|
|
21971
21988
|
name: exports_external.string().min(1),
|
|
21972
21989
|
kind: PluginKindSchema,
|
|
21973
21990
|
entry: exports_external.string().min(1),
|
|
21974
21991
|
version: exports_external.string().optional(),
|
|
21975
21992
|
description: exports_external.string().optional(),
|
|
21976
|
-
operations: exports_external.record(exports_external.string(),
|
|
21977
|
-
})
|
|
21993
|
+
operations: exports_external.record(exports_external.string(), PluginOperationSchema).optional()
|
|
21994
|
+
});
|
|
21978
21995
|
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.39/node_modules/@gobing-ai/ts-runtime/dist/index.js
|
|
21979
21996
|
init_file_system_node();
|
|
21980
21997
|
init_config();
|
|
@@ -22003,6 +22020,7 @@ if (false) {}
|
|
|
22003
22020
|
var TARGET = "surfdash";
|
|
22004
22021
|
var OPERATIONS = ["publish", "scaffold-locale", "sync"];
|
|
22005
22022
|
var SCAFFOLD_LOCALES = ["en", "ja"];
|
|
22023
|
+
var COVER_EXTS = new Set([".png", ".jpg", ".jpeg", ".webp", ".gif"]);
|
|
22006
22024
|
function slugify2(str) {
|
|
22007
22025
|
return str.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/[\s_]+/g, "-");
|
|
22008
22026
|
}
|
|
@@ -22046,6 +22064,13 @@ ${content.body.trim()}
|
|
|
22046
22064
|
function fail(error51) {
|
|
22047
22065
|
return ResultSchema.parse({ ok: false, target: TARGET, error: error51 });
|
|
22048
22066
|
}
|
|
22067
|
+
async function writePublishResult(outputPath, result) {
|
|
22068
|
+
const outDir = dirname2(outputPath);
|
|
22069
|
+
if (outDir && outDir !== ".") {
|
|
22070
|
+
await createNodeFileSystem().ensureDir(outDir);
|
|
22071
|
+
}
|
|
22072
|
+
await createNodeFileSystem().writeFile(outputPath, JSON.stringify(result, null, 2));
|
|
22073
|
+
}
|
|
22049
22074
|
function resolveOperation(options) {
|
|
22050
22075
|
const raw = options.operation ?? "publish";
|
|
22051
22076
|
if (typeof raw !== "string" || !OPERATIONS.includes(raw)) {
|
|
@@ -22065,8 +22090,8 @@ function resolvePostPath(options) {
|
|
|
22065
22090
|
return { postPath };
|
|
22066
22091
|
}
|
|
22067
22092
|
function postPathOutsideRoot(postPath, surfdashRoot) {
|
|
22068
|
-
const root =
|
|
22069
|
-
if (!
|
|
22093
|
+
const root = resolve2(surfdashRoot);
|
|
22094
|
+
if (!resolve2(postPath).startsWith(root + sep)) {
|
|
22070
22095
|
return `options.postPath must resolve inside the surfdash checkout (${root}): ${postPath}`;
|
|
22071
22096
|
}
|
|
22072
22097
|
return;
|
|
@@ -22090,7 +22115,7 @@ function deriveLocaleDraftPath(postPath, locale) {
|
|
|
22090
22115
|
}
|
|
22091
22116
|
function resolveSurfdashRoot() {
|
|
22092
22117
|
const postsurfingBin = process.env.POSTSURFING_BIN ?? "postsurfing";
|
|
22093
|
-
return process.env.SURFDASH_ROOT ?? (postsurfingBin.includes("/") ?
|
|
22118
|
+
return process.env.SURFDASH_ROOT ?? (postsurfingBin.includes("/") ? resolve2(dirname2(dirname2(postsurfingBin))) : undefined);
|
|
22094
22119
|
}
|
|
22095
22120
|
async function spawnCli(cmd, cwd) {
|
|
22096
22121
|
const proc = Bun.spawn(cmd, {
|
|
@@ -22206,16 +22231,13 @@ async function scaffoldLocale(options) {
|
|
|
22206
22231
|
function normalizeLocaleFrontmatter(markdown) {
|
|
22207
22232
|
const lines = markdown.split(`
|
|
22208
22233
|
`);
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
|
|
22214
|
-
|
|
22215
|
-
|
|
22216
|
-
lines[i] = "draft: false";
|
|
22217
|
-
}
|
|
22218
|
-
return lines.filter((line) => line !== "").join(`
|
|
22234
|
+
if (lines[0]?.trim() !== "---")
|
|
22235
|
+
return markdown;
|
|
22236
|
+
const close = lines.findIndex((l, i) => i > 0 && l.trim() === "---");
|
|
22237
|
+
if (close < 0)
|
|
22238
|
+
return markdown;
|
|
22239
|
+
const head = lines.slice(1, close).filter((l) => !/^canonical\s*:/.test(l)).map((l) => /^draft\s*:/.test(l) ? "draft: false" : l);
|
|
22240
|
+
return [lines[0], ...head, ...lines.slice(close)].join(`
|
|
22219
22241
|
`);
|
|
22220
22242
|
}
|
|
22221
22243
|
async function syncPost(options) {
|
|
@@ -22266,14 +22288,25 @@ async function publishPost(payload) {
|
|
|
22266
22288
|
const dateStamp = runDate || today;
|
|
22267
22289
|
const titleSlug = slugify2(payload.content.title ?? "");
|
|
22268
22290
|
const slug = titleSlug ? `${dateStamp}-${titleSlug}` : `${dateStamp}-post`;
|
|
22269
|
-
|
|
22291
|
+
if (runDate !== "" && !/^\d{8}$/.test(runDate)) {
|
|
22292
|
+
return fail(`metadata.runDate must be YYYYMMDD, got '${runDate}'`);
|
|
22293
|
+
}
|
|
22294
|
+
const articlesRoot = resolve2(surfdashRoot, "content", "posts", "articles");
|
|
22295
|
+
const localeRoot = resolve2(articlesRoot, locale);
|
|
22296
|
+
const postDir = resolve2(localeRoot, slug);
|
|
22297
|
+
if (dirname2(localeRoot) !== articlesRoot || dirname2(postDir) !== localeRoot) {
|
|
22298
|
+
return fail(`post path escapes ${articlesRoot}`);
|
|
22299
|
+
}
|
|
22300
|
+
const coverPath = payload.content.metadata?.coverImage;
|
|
22301
|
+
if (typeof coverPath === "string" && existsSync2(coverPath) && !COVER_EXTS.has(extname(coverPath).toLowerCase())) {
|
|
22302
|
+
return fail(`metadata.coverImage must be one of ${[...COVER_EXTS].join(", ")}`);
|
|
22303
|
+
}
|
|
22270
22304
|
await createNodeFileSystem().ensureDir(postDir);
|
|
22271
22305
|
let imageFrontmatter;
|
|
22272
|
-
const coverPath = payload.content.metadata?.coverImage;
|
|
22273
22306
|
if (typeof coverPath === "string" && existsSync2(coverPath)) {
|
|
22274
22307
|
const assetsDir = join2(postDir, "assets");
|
|
22275
22308
|
await createNodeFileSystem().ensureDir(assetsDir);
|
|
22276
|
-
const coverName = `cover${
|
|
22309
|
+
const coverName = `cover${extname(coverPath).toLowerCase()}`;
|
|
22277
22310
|
await copyFile(coverPath, join2(assetsDir, coverName));
|
|
22278
22311
|
imageFrontmatter = `./assets/${coverName}`;
|
|
22279
22312
|
}
|
|
@@ -22342,16 +22375,20 @@ function getPublishTransport() {
|
|
|
22342
22375
|
async function processPublishIO(inputPath, outputPath, transport = getPublishTransport()) {
|
|
22343
22376
|
await createNodeFileSystem().deleteFile(outputPath);
|
|
22344
22377
|
const rawInput = await createNodeFileSystem().readFile(inputPath);
|
|
22345
|
-
|
|
22346
|
-
|
|
22378
|
+
let raw;
|
|
22379
|
+
let content;
|
|
22380
|
+
try {
|
|
22381
|
+
raw = JSON.parse(rawInput);
|
|
22382
|
+
content = ContentSchema.parse(raw);
|
|
22383
|
+
} catch (error51) {
|
|
22384
|
+
const message = `Invalid Content input: ${error51 instanceof Error ? error51.message : String(error51)}`;
|
|
22385
|
+
await writePublishResult(outputPath, fail(message));
|
|
22386
|
+
throw new Error(message);
|
|
22387
|
+
}
|
|
22347
22388
|
const options = raw.options !== undefined && typeof raw.options === "object" && raw.options !== null ? raw.options : undefined;
|
|
22348
22389
|
const result = await transport.publish({ content, options });
|
|
22349
22390
|
const validatedResult = ResultSchema.parse(result);
|
|
22350
|
-
|
|
22351
|
-
if (outDir && outDir !== ".") {
|
|
22352
|
-
await createNodeFileSystem().ensureDir(outDir);
|
|
22353
|
-
}
|
|
22354
|
-
await createNodeFileSystem().writeFile(outputPath, JSON.stringify(validatedResult, null, 2));
|
|
22391
|
+
await writePublishResult(outputPath, validatedResult);
|
|
22355
22392
|
if (!validatedResult.ok) {
|
|
22356
22393
|
throw new Error(validatedResult.error ?? "Publish failed");
|
|
22357
22394
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { copyFile, cp, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { dirname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
3
|
+
import { dirname, extname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
4
4
|
import { parseArgs } from 'node:util';
|
|
5
5
|
import { type Content, ContentSchema, type Result, ResultSchema } from '@gobing-ai/kk-core';
|
|
6
6
|
import type { PublishTransport, PublishTransportPayload } from '@gobing-ai/publish-harness';
|
|
@@ -18,6 +18,9 @@ type Operation = (typeof OPERATIONS)[number];
|
|
|
18
18
|
const SCAFFOLD_LOCALES = ['en', 'ja'] as const;
|
|
19
19
|
type ScaffoldLocale = (typeof SCAFFOLD_LOCALES)[number];
|
|
20
20
|
|
|
21
|
+
/** Cover-image extensions accepted from `metadata.coverImage` (R4): anything else is rejected, not copied. */
|
|
22
|
+
const COVER_EXTS = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);
|
|
23
|
+
|
|
21
24
|
/** Target-specific options carried on the invoke payload's `options` channel. */
|
|
22
25
|
interface SurfdashPubOptions {
|
|
23
26
|
operation?: unknown;
|
|
@@ -113,6 +116,19 @@ function fail(error: string): Result {
|
|
|
113
116
|
return ResultSchema.parse({ ok: false, target: TARGET, error });
|
|
114
117
|
}
|
|
115
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Writes a Result JSON to `outputPath`, creating the directory as needed. Shared by the success
|
|
121
|
+
* path and the invalid-input path so a malformed payload still produces the spec §2.5 `ok:false`
|
|
122
|
+
* Result artifact (R7).
|
|
123
|
+
*/
|
|
124
|
+
async function writePublishResult(outputPath: string, result: Result): Promise<void> {
|
|
125
|
+
const outDir = dirname(outputPath);
|
|
126
|
+
if (outDir && outDir !== '.') {
|
|
127
|
+
await createNodeFileSystem().ensureDir(outDir);
|
|
128
|
+
}
|
|
129
|
+
await createNodeFileSystem().writeFile(outputPath, JSON.stringify(result, null, 2));
|
|
130
|
+
}
|
|
131
|
+
|
|
116
132
|
/** Parses and routes `options.operation`; unknown or misplaced values fail loud. */
|
|
117
133
|
function resolveOperation(options: SurfdashPubOptions): { operation: Operation } | { error: string } {
|
|
118
134
|
const raw = options.operation ?? 'publish';
|
|
@@ -359,14 +375,19 @@ async function scaffoldLocale(options: SurfdashPubOptions): Promise<Result> {
|
|
|
359
375
|
* for the agent prompt; safe because sync always runs after the agent's edit.
|
|
360
376
|
*/
|
|
361
377
|
export function normalizeLocaleFrontmatter(markdown: string): string {
|
|
378
|
+
// Frontmatter-only rewrite (R3). The previous implementation filtered empty lines across the
|
|
379
|
+
// WHOLE document, deleting every blank line in the body — that is the sync corruption. Only
|
|
380
|
+
// the head between the opening and closing `---` is rewritten here; `split`/`join` on '\n'
|
|
381
|
+
// round-trips the rest of the document byte-for-byte.
|
|
362
382
|
const lines = markdown.split('\n');
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
383
|
+
if (lines[0]?.trim() !== '---') return markdown;
|
|
384
|
+
const close = lines.findIndex((l, i) => i > 0 && l.trim() === '---');
|
|
385
|
+
if (close < 0) return markdown;
|
|
386
|
+
const head = lines
|
|
387
|
+
.slice(1, close)
|
|
388
|
+
.filter((l) => !/^canonical\s*:/.test(l))
|
|
389
|
+
.map((l) => (/^draft\s*:/.test(l) ? 'draft: false' : l));
|
|
390
|
+
return [lines[0], ...head, ...lines.slice(close)].join('\n');
|
|
370
391
|
}
|
|
371
392
|
|
|
372
393
|
async function syncPost(options: SurfdashPubOptions): Promise<Result> {
|
|
@@ -435,17 +456,32 @@ async function publishPost(payload: PublishTransportPayload): Promise<Result> {
|
|
|
435
456
|
const dateStamp = runDate || today;
|
|
436
457
|
const titleSlug = slugify(payload.content.title ?? '');
|
|
437
458
|
const slug = titleSlug ? `${dateStamp}-${titleSlug}` : `${dateStamp}-post`;
|
|
438
|
-
|
|
459
|
+
// Validate before touching disk (R4): a `metadata.runDate` or slug that escapes the articles
|
|
460
|
+
// tree would write a post outside it, and a non-image `coverImage` would be copied into a post
|
|
461
|
+
// that pushPostGit then publishes to a public repo.
|
|
462
|
+
if (runDate !== '' && !/^\d{8}$/.test(runDate)) {
|
|
463
|
+
return fail(`metadata.runDate must be YYYYMMDD, got '${runDate}'`);
|
|
464
|
+
}
|
|
465
|
+
const articlesRoot = resolve(surfdashRoot, 'content', 'posts', 'articles');
|
|
466
|
+
const localeRoot = resolve(articlesRoot, locale);
|
|
467
|
+
const postDir = resolve(localeRoot, slug);
|
|
468
|
+
if (dirname(localeRoot) !== articlesRoot || dirname(postDir) !== localeRoot) {
|
|
469
|
+
return fail(`post path escapes ${articlesRoot}`);
|
|
470
|
+
}
|
|
471
|
+
const coverPath = payload.content.metadata?.coverImage;
|
|
472
|
+
if (typeof coverPath === 'string' && existsSync(coverPath) && !COVER_EXTS.has(extname(coverPath).toLowerCase())) {
|
|
473
|
+
return fail(`metadata.coverImage must be one of ${[...COVER_EXTS].join(', ')}`);
|
|
474
|
+
}
|
|
475
|
+
|
|
439
476
|
await createNodeFileSystem().ensureDir(postDir);
|
|
440
477
|
|
|
441
478
|
// Cover image: metadata.coverImage (absolute local path) is copied into the post's
|
|
442
479
|
// assets dir and referenced relatively — the surfdash PostSchema accepts ./assets/<file>.
|
|
443
480
|
let imageFrontmatter: string | undefined;
|
|
444
|
-
const coverPath = payload.content.metadata?.coverImage;
|
|
445
481
|
if (typeof coverPath === 'string' && existsSync(coverPath)) {
|
|
446
482
|
const assetsDir = join(postDir, 'assets');
|
|
447
483
|
await createNodeFileSystem().ensureDir(assetsDir);
|
|
448
|
-
const coverName = `cover${
|
|
484
|
+
const coverName = `cover${extname(coverPath).toLowerCase()}`;
|
|
449
485
|
await copyFile(coverPath, join(assetsDir, coverName));
|
|
450
486
|
imageFrontmatter = `./assets/${coverName}`;
|
|
451
487
|
}
|
|
@@ -552,8 +588,18 @@ export async function processPublishIO(
|
|
|
552
588
|
): Promise<void> {
|
|
553
589
|
await createNodeFileSystem().deleteFile(outputPath);
|
|
554
590
|
const rawInput = await createNodeFileSystem().readFile(inputPath);
|
|
555
|
-
|
|
556
|
-
|
|
591
|
+
// A malformed payload must yield the spec §2.5 `ok:false` Result and a non-zero exit, not an
|
|
592
|
+
// uncaught SyntaxError (R7 — the other publishers were built from a template with this catch).
|
|
593
|
+
let raw: Record<string, unknown>;
|
|
594
|
+
let content: Content;
|
|
595
|
+
try {
|
|
596
|
+
raw = JSON.parse(rawInput) as Record<string, unknown>;
|
|
597
|
+
content = ContentSchema.parse(raw);
|
|
598
|
+
} catch (error: unknown) {
|
|
599
|
+
const message = `Invalid Content input: ${error instanceof Error ? error.message : String(error)}`;
|
|
600
|
+
await writePublishResult(outputPath, fail(message));
|
|
601
|
+
throw new Error(message);
|
|
602
|
+
}
|
|
557
603
|
const options =
|
|
558
604
|
raw.options !== undefined && typeof raw.options === 'object' && raw.options !== null
|
|
559
605
|
? (raw.options as Record<string, unknown>)
|
|
@@ -562,12 +608,7 @@ export async function processPublishIO(
|
|
|
562
608
|
const result = await transport.publish({ content, options });
|
|
563
609
|
const validatedResult = ResultSchema.parse(result);
|
|
564
610
|
|
|
565
|
-
|
|
566
|
-
if (outDir && outDir !== '.') {
|
|
567
|
-
await createNodeFileSystem().ensureDir(outDir);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
await createNodeFileSystem().writeFile(outputPath, JSON.stringify(validatedResult, null, 2));
|
|
611
|
+
await writePublishResult(outputPath, validatedResult);
|
|
571
612
|
|
|
572
613
|
if (!validatedResult.ok) {
|
|
573
614
|
throw new Error(validatedResult.error ?? 'Publish failed');
|