@zzclub/pipeline 0.3.0 → 0.4.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/README.md +366 -170
- package/package.json +4 -7
- package/src/adapter-loader.ts +138 -0
- package/src/adapter-types.ts +172 -0
- package/src/adapters/builtin-image-renderer.ts +353 -0
- package/src/adapters/builtin-markdown-renderer.ts +63 -0
- package/src/adapters/index.ts +2 -0
- package/src/args.test.ts +118 -0
- package/src/args.ts +90 -0
- package/src/cli.ts +51 -0
- package/src/commands/abandon.test.ts +104 -0
- package/src/commands/abandon.ts +202 -0
- package/src/commands/attach-body-images.ts +88 -0
- package/src/commands/attach-body.ts +86 -0
- package/src/commands/attach-newspic-spec.ts +41 -0
- package/src/commands/checkpoint.test.ts +91 -0
- package/src/commands/checkpoint.ts +66 -0
- package/src/commands/config.ts +106 -0
- package/src/commands/cos-upload.ts +62 -0
- package/src/commands/doctor.ts +66 -0
- package/src/commands/find-run.ts +55 -0
- package/src/commands/hermes-metrics.ts +226 -0
- package/src/commands/imgx.ts +60 -0
- package/src/commands/ingest-handoff.ts +509 -0
- package/src/commands/init.ts +126 -0
- package/src/commands/prepare-finalize.ts +348 -0
- package/src/commands/prepare.ts +310 -0
- package/src/commands/publish.ts +147 -0
- package/src/commands/reconcile.ts +36 -0
- package/src/commands/render.ts +244 -0
- package/src/commands/reset.test.ts +151 -0
- package/src/commands/reset.ts +159 -0
- package/src/commands/review.ts +77 -0
- package/src/commands/status.ts +68 -0
- package/src/commands/sync-blog.ts +39 -0
- package/src/commands/tasks.ts +50 -0
- package/src/commands/wechat-export.ts +68 -0
- package/src/config.ts +0 -0
- package/src/imgx/ascii-portrait.ts +77 -0
- package/src/imgx/assets/fonts/AlimamaShuHeiTi-Bold.ttf +0 -0
- package/src/imgx/assets/fonts/LXGWNeoZhiSongPlus.ttf +0 -0
- package/src/imgx/assets/fonts/LXGWWenKai-Regular.ttf +0 -0
- package/src/imgx/assets/icons/logo.png +0 -0
- package/src/imgx/assets/icons/logo.svg +8 -0
- package/{dist/assets → src}/imgx/assets/templates/longform-3-4.html +1 -1
- package/{dist/assets → src}/imgx/assets/templates/poster-3-4.html +69 -30
- package/{dist/assets → src}/imgx/assets/templates/wechat-cover-split.html +58 -27
- package/src/imgx/cli.ts +57 -0
- package/src/imgx/geometry.ts +68 -0
- package/src/imgx/index.ts +5 -0
- package/src/imgx/longform-theme.ts +243 -0
- package/src/imgx/poster-recipe.ts +81 -0
- package/src/imgx/pretext-adapter.ts +14 -0
- package/src/imgx/pretext-runtime.ts +61 -0
- package/src/imgx/references/ascii-portrait-3-4.md +59 -0
- package/src/imgx/references/longform-3-4.md +173 -0
- package/src/imgx/references/poster-3-4.md +87 -0
- package/src/imgx/references/tips-3-4.md +69 -0
- package/src/imgx/references/wechat-cover-split.md +89 -0
- package/src/imgx/references/x-like-posts.md +139 -0
- package/src/imgx/render-article.test.ts +53 -0
- package/src/imgx/render-article.ts +1364 -0
- package/src/imgx/render-ascii-portrait.ts +53 -0
- package/src/imgx/render-card.ts +232 -0
- package/src/imgx/render-x-like-posts.ts +235 -0
- package/src/imgx/runtime.ts +403 -0
- package/src/imgx/typographic-scale.test.ts +56 -0
- package/src/imgx/typographic-scale.ts +44 -0
- package/src/output.ts +632 -0
- package/src/plugins.ts +101 -0
- package/src/profiles.ts +149 -0
- package/src/providers/blog.test.ts +58 -0
- package/src/providers/blog.ts +154 -0
- package/src/providers/cos.ts +183 -0
- package/src/providers/index.test.ts +26 -0
- package/src/providers/index.ts +185 -0
- package/src/providers/wechat.test.ts +215 -0
- package/src/providers/wechat.ts +548 -0
- package/src/routes.ts +287 -0
- package/src/runtime-paths.ts +179 -0
- package/src/schema/config.ts +133 -0
- package/src/schema/state.ts +376 -0
- package/src/spawn.ts +103 -0
- package/src/state.ts +265 -0
- package/src/task-manager.ts +657 -0
- package/src/task-views.ts +222 -0
- package/src/text.test.ts +689 -0
- package/src/text.ts +683 -0
- package/src/wechat-preview/assets/fonts/SweiCurveLegCJKsc-Regular.woff +0 -0
- package/src/wechat-preview/browser/editor-export.css +241 -0
- package/src/wechat-preview/browser/editor-export.ts +103 -0
- package/src/wechat-preview/frontmatter-handler.ts +73 -0
- package/src/wechat-preview/index.test.ts +154 -0
- package/src/wechat-preview/index.ts +294 -0
- package/src/wechat-preview/themes.ts +141 -0
- package/src/wechat-preview/wechat-formatter.test.ts +288 -0
- package/src/wechat-preview/wechat-formatter.ts +1506 -0
- package/src/wechat-preview.test.ts +73 -0
- package/src/workflow-materials.test.ts +225 -0
- package/src/workflow-materials.ts +217 -0
- package/src/workflow.test.ts +2087 -0
- package/dist/assets/imgx/assets/icons/avatar_jinx_cartoon.jpg +0 -0
- package/dist/assets/imgx/assets/icons/fishbone-logo-square.png +0 -0
- package/dist/assets/imgx/assets/icons/fishbone-logo.jpg +0 -0
- package/dist/assets/imgx/assets/icons/fishbone-logo.png +0 -0
- package/dist/assets/imgx/assets/icons/openclaw-logo.svg +0 -22
- package/dist/assets/imgx/assets/icons/zzclub-logo-black.jpg +0 -0
- package/dist/assets/imgx/assets/icons/zzclub-logo-gray.svg +0 -25
- package/dist/cli.js +0 -431
- package/dist/node_modules/@chenglou/pretext/dist/analysis.d.ts +0 -33
- package/dist/node_modules/@chenglou/pretext/dist/analysis.js +0 -1063
- package/dist/node_modules/@chenglou/pretext/dist/bidi.d.ts +0 -1
- package/dist/node_modules/@chenglou/pretext/dist/bidi.js +0 -175
- package/dist/node_modules/@chenglou/pretext/dist/generated/bidi-data.d.ts +0 -4
- package/dist/node_modules/@chenglou/pretext/dist/generated/bidi-data.js +0 -979
- package/dist/node_modules/@chenglou/pretext/dist/layout.d.ts +0 -70
- package/dist/node_modules/@chenglou/pretext/dist/layout.js +0 -496
- package/dist/node_modules/@chenglou/pretext/dist/line-break.d.ts +0 -36
- package/dist/node_modules/@chenglou/pretext/dist/line-break.js +0 -820
- package/dist/node_modules/@chenglou/pretext/dist/measurement.d.ts +0 -28
- package/dist/node_modules/@chenglou/pretext/dist/measurement.js +0 -219
- package/dist/node_modules/@chenglou/pretext/dist/rich-inline.d.ts +0 -51
- package/dist/node_modules/@chenglou/pretext/dist/rich-inline.js +0 -401
- /package/{dist/assets → src}/imgx/assets/browser/obstacle-flow.d.ts +0 -0
- /package/{dist/assets → src}/imgx/assets/browser/obstacle-flow.js +0 -0
- /package/{dist/assets → src}/imgx/assets/icons/clover.svg +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/ascii-portrait-3-4.html +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/ascii-portrait-tile.html +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/tips-3-4.html +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/x-like-posts.html +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/.vite/manifest.json +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/SweiCurveLegCJKsc-Regular-B3Ut5bwH.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/style-BYNXnUvc.css +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/editor-export.js +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/templates/export-shell.html +0 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for WorkflowState.
|
|
3
|
+
*
|
|
4
|
+
* Replaces hand-written interfaces + normalize functions + default functions.
|
|
5
|
+
* - Schema.parse({}) → valid default state
|
|
6
|
+
* - Schema.parse(unknownInput) → normalized state
|
|
7
|
+
* - z.infer<typeof Schema> → TS type
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
// ── Helpers ───────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/** Coerce string/number to int, with fallback */
|
|
15
|
+
const intFromUnknown = (fallback: number) =>
|
|
16
|
+
z.preprocess((val) => {
|
|
17
|
+
if (typeof val === "number" && Number.isFinite(val)) return Math.floor(val);
|
|
18
|
+
if (typeof val === "string") {
|
|
19
|
+
const n = Number.parseInt(val, 10);
|
|
20
|
+
return Number.isFinite(n) ? n : undefined;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}, z.number().int().default(fallback));
|
|
24
|
+
|
|
25
|
+
/** Trimmed non-empty string, or null if empty/whitespace */
|
|
26
|
+
const trimmedStringOrNull = z.preprocess(
|
|
27
|
+
(val) => {
|
|
28
|
+
if (typeof val === "string") {
|
|
29
|
+
const trimmed = val.trim();
|
|
30
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
31
|
+
}
|
|
32
|
+
return val;
|
|
33
|
+
},
|
|
34
|
+
z.string().nullable().default(null),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/** Trimmed string with fallback */
|
|
38
|
+
const trimmedString = (fallback: string) =>
|
|
39
|
+
z.preprocess(
|
|
40
|
+
(val) => (typeof val === "string" && val.trim().length > 0 ? val.trim() : undefined),
|
|
41
|
+
z.string().default(fallback),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
/** Coerce "true" string to boolean */
|
|
45
|
+
const booleanLike = z.preprocess(
|
|
46
|
+
(val) => val === true || val === "true",
|
|
47
|
+
z.boolean().default(false),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Make an object schema accept undefined/missing input, filling in the
|
|
52
|
+
* schema's own default (i.e. all fields with their defaults applied).
|
|
53
|
+
* Works in Zod 4 where preprocess doesn't run for missing fields.
|
|
54
|
+
*/
|
|
55
|
+
function withObjectDefault<T extends z.ZodObject<any>>(schema: T) {
|
|
56
|
+
// All fields in the schema have defaults, so parse({}) always succeeds.
|
|
57
|
+
return schema.optional().default(() => schema.parse({}) as any);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Enums ─────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
const ModeSchema = z.enum(["active", "handoff", "done", "failed", "abandoned"]).default("active");
|
|
63
|
+
|
|
64
|
+
const PhaseNameSchema = z.enum(["prepare", "render", "publish", "done", "failed"]).default("prepare");
|
|
65
|
+
|
|
66
|
+
const PhaseStatusSchema = z.enum(["pending", "done", "handoff", "failed"]).default("pending");
|
|
67
|
+
|
|
68
|
+
const TaskKindSchema = z.enum(["draft", "polish", "organize", "publish", "mixed"]).default("publish");
|
|
69
|
+
|
|
70
|
+
const ContentFormSchema = z.enum(["article", "newspic", "unknown"]).default("unknown");
|
|
71
|
+
|
|
72
|
+
const ContentOriginSchema = z.enum(["user", "external", "unknown"]).default("unknown");
|
|
73
|
+
|
|
74
|
+
const TargetSchema = z.enum(["wechat", "blog"]);
|
|
75
|
+
|
|
76
|
+
const RoutePrimarySchema = z.enum(["wechat-article", "wechat-newspic", "blog"]).default("wechat-article");
|
|
77
|
+
|
|
78
|
+
const StyleModeSchema = z.enum(["none", "polish", "deep_rewrite", "fact_report"]).default("none");
|
|
79
|
+
|
|
80
|
+
const ImagePlanStatusSchema = z.enum(["planned", "rendered", "skipped"]).default("skipped");
|
|
81
|
+
|
|
82
|
+
const BodyInputScopeSchema = z.enum(["article", "newspic-longform", "none"]).default("none");
|
|
83
|
+
|
|
84
|
+
const BodyInputStatusSchema = z.enum(["none", "pending", "ready"]).default("none");
|
|
85
|
+
|
|
86
|
+
const RenderAssetKindSchema = z.enum(["cover", "page"]);
|
|
87
|
+
|
|
88
|
+
const PublishResultStatusSchema = z.enum(["success", "handoff", "skipped", "failed"]);
|
|
89
|
+
|
|
90
|
+
const ContentReviewStatusSchema = z.enum(["unchecked", "passed", "needs_revision"]).default("unchecked");
|
|
91
|
+
|
|
92
|
+
const HandoffResearchPolicySchema = z.enum(["default", "need", "skip"]).default("default");
|
|
93
|
+
|
|
94
|
+
const HandoffAuthoringPolicySchema = z
|
|
95
|
+
.enum(["default", "write", "write_from_materials", "revise", "format_only"])
|
|
96
|
+
.default("default");
|
|
97
|
+
|
|
98
|
+
const HandoffReviewPolicySchema = z.enum(["default", "required", "trust_user"]).default("default");
|
|
99
|
+
|
|
100
|
+
const NewspicPaginationModeSchema = z.enum(["auto", "single", "multi"]).default("single");
|
|
101
|
+
|
|
102
|
+
// ── Sub-schemas ───────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
const PhaseEntrySchema = withObjectDefault(
|
|
105
|
+
z.object({
|
|
106
|
+
status: PhaseStatusSchema,
|
|
107
|
+
error: z.string().nullable().default(null),
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const PhaseSchema = withObjectDefault(
|
|
112
|
+
z.object({
|
|
113
|
+
current: PhaseNameSchema,
|
|
114
|
+
prepare: PhaseEntrySchema,
|
|
115
|
+
render: PhaseEntrySchema,
|
|
116
|
+
publish: PhaseEntrySchema,
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const IntentRequiresSchema = withObjectDefault(
|
|
121
|
+
z.object({
|
|
122
|
+
research: z.boolean().default(false),
|
|
123
|
+
style: z.boolean().default(false),
|
|
124
|
+
render: z.boolean().default(false),
|
|
125
|
+
publish: z.boolean().default(false),
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const NewspicPageSpecSchema = z.object({
|
|
130
|
+
page: intFromUnknown(1).pipe(z.number().min(1)),
|
|
131
|
+
image_markers: z
|
|
132
|
+
.array(z.string())
|
|
133
|
+
.default([])
|
|
134
|
+
.transform((arr) =>
|
|
135
|
+
arr
|
|
136
|
+
.map((s) => s.trim())
|
|
137
|
+
.filter(Boolean),
|
|
138
|
+
),
|
|
139
|
+
image_layout: trimmedStringOrNull,
|
|
140
|
+
target_fill_ratio: z.preprocess(
|
|
141
|
+
(val) => {
|
|
142
|
+
if (typeof val === "number" && Number.isFinite(val)) return Math.min(0.95, Math.max(0.35, val));
|
|
143
|
+
if (typeof val === "string") {
|
|
144
|
+
const n = Number.parseFloat(val);
|
|
145
|
+
return Number.isFinite(n) ? Math.min(0.95, Math.max(0.35, n)) : null;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
},
|
|
149
|
+
z.number().nullable().default(null),
|
|
150
|
+
),
|
|
151
|
+
note: trimmedStringOrNull,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
export const NewspicRenderSpecSchema = z.object({
|
|
155
|
+
pagination_mode: NewspicPaginationModeSchema,
|
|
156
|
+
min_pages: intFromUnknown(1).pipe(z.number().min(1)),
|
|
157
|
+
max_pages: intFromUnknown(0).pipe(z.number().min(0)),
|
|
158
|
+
require_image_every_page: booleanLike,
|
|
159
|
+
default_image_layout: trimmedString("staggered"),
|
|
160
|
+
target_fill_ratio: z.preprocess(
|
|
161
|
+
(val) => {
|
|
162
|
+
if (typeof val === "number" && Number.isFinite(val)) return Math.min(0.95, Math.max(0.35, val));
|
|
163
|
+
if (typeof val === "string") {
|
|
164
|
+
const n = Number.parseFloat(val);
|
|
165
|
+
return Number.isFinite(n) ? Math.min(0.95, Math.max(0.35, n)) : undefined;
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
},
|
|
169
|
+
z.number().default(0.8),
|
|
170
|
+
),
|
|
171
|
+
page_specs: z
|
|
172
|
+
.array(NewspicPageSpecSchema)
|
|
173
|
+
.default([])
|
|
174
|
+
.transform((arr) => [...arr].sort((a, b) => a.page - b.page)),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const AccountVisualParamsSchema = z.object({
|
|
178
|
+
footer: z.string().default(""),
|
|
179
|
+
bg: z.string().default(""),
|
|
180
|
+
highlight: z.string().default(""),
|
|
181
|
+
fallback_icon: z.string().default(""),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const RouteSchema = withObjectDefault(
|
|
185
|
+
z.object({
|
|
186
|
+
primary: RoutePrimarySchema,
|
|
187
|
+
extras: z.array(RoutePrimarySchema).default([]),
|
|
188
|
+
account: trimmedString("default"),
|
|
189
|
+
content_profile: trimmedString("user"),
|
|
190
|
+
account_visual_params: AccountVisualParamsSchema.nullable().default(null),
|
|
191
|
+
highlight_words: z.array(z.string()).default([]),
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const AuthoringSchema = withObjectDefault(
|
|
196
|
+
z.object({
|
|
197
|
+
rewrite_allowed: z.boolean().default(false),
|
|
198
|
+
style_mode: StyleModeSchema,
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const MetadataSchema = withObjectDefault(
|
|
203
|
+
z.object({
|
|
204
|
+
title: z.string().default(""),
|
|
205
|
+
slug: z.string().default(""),
|
|
206
|
+
date: z.string().default(""),
|
|
207
|
+
description: z.string().nullable().default(null),
|
|
208
|
+
tags: z.array(z.string()).default([]),
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const ArtifactsSchema = withObjectDefault(
|
|
213
|
+
z.object({
|
|
214
|
+
content_version: z.number().int().default(0),
|
|
215
|
+
render_version: z.number().int().default(0),
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const ImagePlanSchema = withObjectDefault(
|
|
220
|
+
z.object({
|
|
221
|
+
needed: z.boolean().default(false),
|
|
222
|
+
template: z.string().nullable().default(null),
|
|
223
|
+
cover_template: z.string().nullable().default(null),
|
|
224
|
+
cover_title: z.string().nullable().default(null),
|
|
225
|
+
output_dir: z.string().nullable().default(null),
|
|
226
|
+
preview_required: z.boolean().default(false),
|
|
227
|
+
status: ImagePlanStatusSchema,
|
|
228
|
+
}),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const BodyInputReceivedSchema = z.object({
|
|
232
|
+
marker: z.string().default(""),
|
|
233
|
+
path: z.string().default(""),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const BodyInputsSchema = withObjectDefault(
|
|
237
|
+
z.object({
|
|
238
|
+
scope: BodyInputScopeSchema,
|
|
239
|
+
expected: z.number().int().default(0),
|
|
240
|
+
received: z.array(BodyInputReceivedSchema).default([]),
|
|
241
|
+
status: BodyInputStatusSchema,
|
|
242
|
+
layout: trimmedString("staggered"),
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const RenderAssetSchema = z.object({
|
|
247
|
+
kind: RenderAssetKindSchema,
|
|
248
|
+
route: RoutePrimarySchema,
|
|
249
|
+
path: z.string().default(""),
|
|
250
|
+
index: z.number().int().optional(),
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const ImagesSchema = withObjectDefault(
|
|
254
|
+
z.object({
|
|
255
|
+
plan: ImagePlanSchema,
|
|
256
|
+
body_inputs: BodyInputsSchema,
|
|
257
|
+
render_assets: z.array(RenderAssetSchema).default([]),
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
const PublishResultSchema = z.object({
|
|
262
|
+
route: RoutePrimarySchema,
|
|
263
|
+
status: PublishResultStatusSchema,
|
|
264
|
+
detail: z.string().nullable().default(null),
|
|
265
|
+
published_at: z.string().nullable().default(null),
|
|
266
|
+
content_version: z.number().int().default(0),
|
|
267
|
+
render_version: z.number().int().default(0),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const PublishResultsSchema = withObjectDefault(
|
|
271
|
+
z.object({
|
|
272
|
+
results: z.array(PublishResultSchema).default([]),
|
|
273
|
+
}),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const ContentReviewSchema = withObjectDefault(
|
|
277
|
+
z.object({
|
|
278
|
+
status: ContentReviewStatusSchema,
|
|
279
|
+
feedback: z.string().nullable().default(null),
|
|
280
|
+
}),
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const HandoffSchema = withObjectDefault(
|
|
284
|
+
z.object({
|
|
285
|
+
source_materials_path: trimmedStringOrNull,
|
|
286
|
+
research_policy: HandoffResearchPolicySchema,
|
|
287
|
+
authoring_policy: HandoffAuthoringPolicySchema,
|
|
288
|
+
review_policy: HandoffReviewPolicySchema,
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
const IntentSchema = withObjectDefault(
|
|
293
|
+
z.object({
|
|
294
|
+
task_kind: TaskKindSchema,
|
|
295
|
+
content_form: ContentFormSchema,
|
|
296
|
+
targets: z.array(TargetSchema).default([]),
|
|
297
|
+
content_origin: ContentOriginSchema,
|
|
298
|
+
intent_text: trimmedStringOrNull,
|
|
299
|
+
explicit_constraints: z
|
|
300
|
+
.array(z.string())
|
|
301
|
+
.default([])
|
|
302
|
+
.transform((arr) => arr.filter((s) => s.trim().length > 0)),
|
|
303
|
+
style_hint: z.string().nullable().default(null),
|
|
304
|
+
newspic_render: NewspicRenderSpecSchema.nullable().default(null),
|
|
305
|
+
requires: IntentRequiresSchema,
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// ── Top-level schema ──────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
export const WorkflowStateSchema = z.object({
|
|
312
|
+
run_id: z.string().default(""),
|
|
313
|
+
created_at: z.string().default(""),
|
|
314
|
+
updated_at: z.string().default(""),
|
|
315
|
+
workspace_root: z.string().default(""),
|
|
316
|
+
asset_path: z.string().default(""),
|
|
317
|
+
state_path: z.string().default(""),
|
|
318
|
+
source_body_path: z.string().nullable().default(null),
|
|
319
|
+
formatted_body_path: trimmedStringOrNull,
|
|
320
|
+
handoff: HandoffSchema,
|
|
321
|
+
mode: ModeSchema,
|
|
322
|
+
phase: PhaseSchema,
|
|
323
|
+
intent: IntentSchema,
|
|
324
|
+
route: RouteSchema,
|
|
325
|
+
authoring: AuthoringSchema,
|
|
326
|
+
metadata: MetadataSchema,
|
|
327
|
+
artifacts: ArtifactsSchema,
|
|
328
|
+
images: ImagesSchema,
|
|
329
|
+
publish: PublishResultsSchema,
|
|
330
|
+
content_review: ContentReviewSchema,
|
|
331
|
+
redo_hint: z.string().nullable().default(null),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
export type WorkflowState = z.infer<typeof WorkflowStateSchema>;
|
|
335
|
+
|
|
336
|
+
// ── Sub-type exports ──────────────────────────────────────────────
|
|
337
|
+
|
|
338
|
+
export type Mode = z.infer<typeof ModeSchema>;
|
|
339
|
+
export type PhaseName = z.infer<typeof PhaseNameSchema>;
|
|
340
|
+
export type PhaseStatus = z.infer<typeof PhaseStatusSchema>;
|
|
341
|
+
export type PhaseEntry = z.infer<typeof PhaseEntrySchema>;
|
|
342
|
+
export type Phase = z.infer<typeof PhaseSchema>;
|
|
343
|
+
export type TaskKind = z.infer<typeof TaskKindSchema>;
|
|
344
|
+
export type ContentForm = z.infer<typeof ContentFormSchema>;
|
|
345
|
+
export type ContentOrigin = z.infer<typeof ContentOriginSchema>;
|
|
346
|
+
export type Target = z.infer<typeof TargetSchema>;
|
|
347
|
+
export type IntentRequires = z.infer<typeof IntentRequiresSchema>;
|
|
348
|
+
export type Intent = z.infer<typeof IntentSchema>;
|
|
349
|
+
export type NewspicPaginationMode = z.infer<typeof NewspicPaginationModeSchema>;
|
|
350
|
+
export type NewspicPageSpec = z.infer<typeof NewspicPageSpecSchema>;
|
|
351
|
+
export type NewspicRenderSpec = z.infer<typeof NewspicRenderSpecSchema>;
|
|
352
|
+
export type RoutePrimary = z.infer<typeof RoutePrimarySchema>;
|
|
353
|
+
export type AccountVisualParams = z.infer<typeof AccountVisualParamsSchema>;
|
|
354
|
+
export type Route = z.infer<typeof RouteSchema>;
|
|
355
|
+
export type StyleMode = z.infer<typeof StyleModeSchema>;
|
|
356
|
+
export type Authoring = z.infer<typeof AuthoringSchema>;
|
|
357
|
+
export type Metadata = z.infer<typeof MetadataSchema>;
|
|
358
|
+
export type Artifacts = z.infer<typeof ArtifactsSchema>;
|
|
359
|
+
export type ImagePlanStatus = z.infer<typeof ImagePlanStatusSchema>;
|
|
360
|
+
export type ImagePlan = z.infer<typeof ImagePlanSchema>;
|
|
361
|
+
export type BodyInputScope = z.infer<typeof BodyInputScopeSchema>;
|
|
362
|
+
export type BodyInputStatus = z.infer<typeof BodyInputStatusSchema>;
|
|
363
|
+
export type BodyInputReceived = z.infer<typeof BodyInputReceivedSchema>;
|
|
364
|
+
export type BodyInputs = z.infer<typeof BodyInputsSchema>;
|
|
365
|
+
export type RenderAssetKind = z.infer<typeof RenderAssetKindSchema>;
|
|
366
|
+
export type RenderAsset = z.infer<typeof RenderAssetSchema>;
|
|
367
|
+
export type Images = z.infer<typeof ImagesSchema>;
|
|
368
|
+
export type PublishResultStatus = z.infer<typeof PublishResultStatusSchema>;
|
|
369
|
+
export type PublishResult = z.infer<typeof PublishResultSchema>;
|
|
370
|
+
export type PublishResults = z.infer<typeof PublishResultsSchema>;
|
|
371
|
+
export type ContentReviewStatus = z.infer<typeof ContentReviewStatusSchema>;
|
|
372
|
+
export type ContentReview = z.infer<typeof ContentReviewSchema>;
|
|
373
|
+
export type HandoffResearchPolicy = z.infer<typeof HandoffResearchPolicySchema>;
|
|
374
|
+
export type HandoffAuthoringPolicy = z.infer<typeof HandoffAuthoringPolicySchema>;
|
|
375
|
+
export type HandoffReviewPolicy = z.infer<typeof HandoffReviewPolicySchema>;
|
|
376
|
+
export type Handoff = z.infer<typeof HandoffSchema>;
|
package/src/spawn.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spawn — Runtime-neutral child process wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Why: zzhub-pipeline may be invoked from Bun, Node, or an agent exec
|
|
5
|
+
* sandbox whose PATH differs from the user's interactive shell. We avoid
|
|
6
|
+
* Bun-specific process APIs here so nested commands still work when the
|
|
7
|
+
* outer runtime is not Bun.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import { homedir } from "os";
|
|
12
|
+
import { delimiter } from "path";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { spawnSync as nodeSpawnSync, type SpawnSyncReturns } from "child_process";
|
|
15
|
+
|
|
16
|
+
/** Directories to ensure are present on PATH */
|
|
17
|
+
const EXTRA_PATH_DIRS = [
|
|
18
|
+
join(homedir(), ".bun", "bin"),
|
|
19
|
+
"/opt/homebrew/bin",
|
|
20
|
+
"/usr/local/bin",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/** Build an env object with enriched PATH */
|
|
24
|
+
function enrichedEnv(): Record<string, string> {
|
|
25
|
+
const env = { ...process.env } as Record<string, string>;
|
|
26
|
+
const currentPath = env.PATH ?? "";
|
|
27
|
+
|
|
28
|
+
const missing = EXTRA_PATH_DIRS.filter(
|
|
29
|
+
(dir) => !currentPath.split(":").includes(dir),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (missing.length > 0) {
|
|
33
|
+
env.PATH = [...missing, currentPath].join(":");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return env;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SpawnOptions {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface SpawnResult {
|
|
44
|
+
exitCode: number | null;
|
|
45
|
+
error?: Error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function findOnPath(binary: string, envPath: string): string | null {
|
|
49
|
+
for (const dir of envPath.split(delimiter)) {
|
|
50
|
+
if (!dir) continue;
|
|
51
|
+
const candidate = join(dir, binary);
|
|
52
|
+
if (existsSync(candidate)) {
|
|
53
|
+
return candidate;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function resolveBunBinary(): string {
|
|
60
|
+
const env = enrichedEnv();
|
|
61
|
+
const candidates = [
|
|
62
|
+
process.env.BUN_BIN,
|
|
63
|
+
process.execPath?.endsWith("/bun") ? process.execPath : null,
|
|
64
|
+
join(homedir(), ".bun", "bin", "bun"),
|
|
65
|
+
"/opt/homebrew/bin/bun",
|
|
66
|
+
"/usr/local/bin/bun",
|
|
67
|
+
env.PATH ? findOnPath("bun", env.PATH) : null,
|
|
68
|
+
].filter((value): value is string => Boolean(value));
|
|
69
|
+
|
|
70
|
+
for (const candidate of candidates) {
|
|
71
|
+
if (existsSync(candidate)) {
|
|
72
|
+
return candidate;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new Error(
|
|
77
|
+
"Unable to locate bun binary. Set BUN_BIN or ensure bun is installed in a standard location.",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function normalizeResult(result: SpawnSyncReturns<Buffer>): SpawnResult {
|
|
82
|
+
return {
|
|
83
|
+
exitCode: result.status,
|
|
84
|
+
...(result.error ? { error: result.error } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Run a command synchronously with enriched PATH.
|
|
90
|
+
* stdout and stderr are inherited (printed to terminal).
|
|
91
|
+
*
|
|
92
|
+
* Returns a normalized spawn result.
|
|
93
|
+
*/
|
|
94
|
+
export function spawnSync(cmd: string[], opts?: SpawnOptions): SpawnResult {
|
|
95
|
+
const [binary, ...args] = cmd;
|
|
96
|
+
const result = nodeSpawnSync(binary, args, {
|
|
97
|
+
stdio: "inherit",
|
|
98
|
+
env: enrichedEnv(),
|
|
99
|
+
...(opts?.cwd ? { cwd: opts.cwd } : {}),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return normalizeResult(result);
|
|
103
|
+
}
|