@zzclub/pipeline 0.2.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 -432
- 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,509 @@
|
|
|
1
|
+
import { copyFile, mkdir, readFile } from "fs/promises";
|
|
2
|
+
import { extname, join, resolve } from "path";
|
|
3
|
+
|
|
4
|
+
import { parseArgs, optionalArg, requireArg } from "../args";
|
|
5
|
+
import { loadConfig, resolveWorkspacePaths, resolveWorkspaceRoot } from "../config";
|
|
6
|
+
import { printResult } from "../output";
|
|
7
|
+
import { resolveFullRoute } from "../routes";
|
|
8
|
+
import {
|
|
9
|
+
defaultArtifacts,
|
|
10
|
+
defaultContentReview,
|
|
11
|
+
defaultImages,
|
|
12
|
+
defaultMetadata,
|
|
13
|
+
defaultPhase,
|
|
14
|
+
defaultPublish,
|
|
15
|
+
defaultState,
|
|
16
|
+
generateRunId,
|
|
17
|
+
getRunStatePath,
|
|
18
|
+
readState,
|
|
19
|
+
writeState,
|
|
20
|
+
type ContentForm,
|
|
21
|
+
type HandoffAuthoringPolicy,
|
|
22
|
+
type HandoffResearchPolicy,
|
|
23
|
+
type HandoffReviewPolicy,
|
|
24
|
+
type Target,
|
|
25
|
+
type WorkflowState,
|
|
26
|
+
} from "../state";
|
|
27
|
+
import { findTask, getTaskByStatePath } from "../task-manager";
|
|
28
|
+
|
|
29
|
+
interface PublishHandoffInput {
|
|
30
|
+
content_form: ContentForm;
|
|
31
|
+
body_path: string;
|
|
32
|
+
target_account: string;
|
|
33
|
+
title: string;
|
|
34
|
+
user_intent_text: string;
|
|
35
|
+
explicit_constraints?: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type WorkflowHandoffMode = "new" | "resume";
|
|
39
|
+
|
|
40
|
+
interface WorkflowHandoffInput {
|
|
41
|
+
mode?: WorkflowHandoffMode;
|
|
42
|
+
state_path?: string;
|
|
43
|
+
run_id?: string;
|
|
44
|
+
content_form?: ContentForm;
|
|
45
|
+
body_path?: string;
|
|
46
|
+
materials_path?: string;
|
|
47
|
+
target_account?: string;
|
|
48
|
+
title?: string;
|
|
49
|
+
user_intent_text?: string;
|
|
50
|
+
explicit_constraints?: string[];
|
|
51
|
+
research_policy?: Exclude<HandoffResearchPolicy, "default">;
|
|
52
|
+
authoring_policy?: Exclude<HandoffAuthoringPolicy, "default">;
|
|
53
|
+
review_policy?: Exclude<HandoffReviewPolicy, "default">;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface ResolvedWorkflowHandoff {
|
|
57
|
+
source: "publish_handoff" | "workflow_handoff";
|
|
58
|
+
mode: WorkflowHandoffMode;
|
|
59
|
+
state_path?: string;
|
|
60
|
+
run_id?: string;
|
|
61
|
+
content_form?: ContentForm;
|
|
62
|
+
body_path?: string;
|
|
63
|
+
materials_path?: string;
|
|
64
|
+
target_account?: string;
|
|
65
|
+
title?: string;
|
|
66
|
+
user_intent_text?: string;
|
|
67
|
+
explicit_constraints: string[];
|
|
68
|
+
research_policy: HandoffResearchPolicy;
|
|
69
|
+
authoring_policy: HandoffAuthoringPolicy;
|
|
70
|
+
review_policy: HandoffReviewPolicy;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function cleanString(value: unknown, field: string): string | undefined {
|
|
74
|
+
if (value === undefined || value === null) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
78
|
+
throw new Error(`Invalid ${field}: expected a non-empty string`);
|
|
79
|
+
}
|
|
80
|
+
return value.trim();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseExplicitConstraints(value: unknown): string[] {
|
|
84
|
+
return Array.isArray(value)
|
|
85
|
+
? value.filter((item): item is string => typeof item === "string" && item.trim().length > 0)
|
|
86
|
+
: [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function parseContentForm(value: unknown, field: string): ContentForm | undefined {
|
|
90
|
+
if (value === undefined || value === null) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
if (value === "article" || value === "newspic" || value === "unknown") {
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
throw new Error(`Invalid ${field}: expected article | newspic | unknown`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function parseWorkflowMode(value: unknown): WorkflowHandoffMode | undefined {
|
|
100
|
+
if (value === undefined || value === null) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
if (value === "new" || value === "resume") {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
throw new Error("Invalid workflow_handoff.mode: expected new | resume");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseResearchPolicy(value: unknown): HandoffResearchPolicy {
|
|
110
|
+
if (value === undefined || value === null) {
|
|
111
|
+
return "default";
|
|
112
|
+
}
|
|
113
|
+
if (value === "need" || value === "skip") {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
throw new Error("Invalid workflow_handoff.research_policy: expected need | skip");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parseAuthoringPolicy(value: unknown): HandoffAuthoringPolicy {
|
|
120
|
+
if (value === undefined || value === null) {
|
|
121
|
+
return "default";
|
|
122
|
+
}
|
|
123
|
+
if (
|
|
124
|
+
value === "write" ||
|
|
125
|
+
value === "write_from_materials" ||
|
|
126
|
+
value === "revise" ||
|
|
127
|
+
value === "format_only"
|
|
128
|
+
) {
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
throw new Error(
|
|
132
|
+
"Invalid workflow_handoff.authoring_policy: expected write | write_from_materials | revise | format_only",
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function parseReviewPolicy(value: unknown): HandoffReviewPolicy {
|
|
137
|
+
if (value === undefined || value === null) {
|
|
138
|
+
return "default";
|
|
139
|
+
}
|
|
140
|
+
if (value === "required" || value === "trust_user") {
|
|
141
|
+
return value;
|
|
142
|
+
}
|
|
143
|
+
throw new Error("Invalid workflow_handoff.review_policy: expected required | trust_user");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function parsePublishHandoff(raw: unknown): ResolvedWorkflowHandoff {
|
|
147
|
+
const candidate =
|
|
148
|
+
raw && typeof raw === "object" && "publish_handoff" in (raw as Record<string, unknown>)
|
|
149
|
+
? (raw as Record<string, unknown>).publish_handoff
|
|
150
|
+
: raw;
|
|
151
|
+
|
|
152
|
+
if (!candidate || typeof candidate !== "object") {
|
|
153
|
+
throw new Error("Invalid publish_handoff file: expected an object or { publish_handoff: { ... } }");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const input = candidate as PublishHandoffInput;
|
|
157
|
+
const contentForm = parseContentForm(input.content_form, "publish_handoff.content_form");
|
|
158
|
+
const bodyPath = cleanString(input.body_path, "publish_handoff.body_path");
|
|
159
|
+
const targetAccount = cleanString(input.target_account, "publish_handoff.target_account");
|
|
160
|
+
const title = cleanString(input.title, "publish_handoff.title");
|
|
161
|
+
const userIntentText = cleanString(input.user_intent_text, "publish_handoff.user_intent_text");
|
|
162
|
+
|
|
163
|
+
if (!contentForm || !bodyPath || !targetAccount || !title || !userIntentText) {
|
|
164
|
+
throw new Error("Invalid publish_handoff: missing required fields");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
source: "publish_handoff",
|
|
169
|
+
mode: "new",
|
|
170
|
+
content_form: contentForm,
|
|
171
|
+
body_path: bodyPath,
|
|
172
|
+
target_account: targetAccount,
|
|
173
|
+
title,
|
|
174
|
+
user_intent_text: userIntentText,
|
|
175
|
+
explicit_constraints: parseExplicitConstraints(input.explicit_constraints),
|
|
176
|
+
research_policy: "skip",
|
|
177
|
+
authoring_policy: "format_only",
|
|
178
|
+
review_policy: "required",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function parseWorkflowHandoff(raw: unknown): ResolvedWorkflowHandoff {
|
|
183
|
+
const candidate =
|
|
184
|
+
raw && typeof raw === "object" && "workflow_handoff" in (raw as Record<string, unknown>)
|
|
185
|
+
? (raw as Record<string, unknown>).workflow_handoff
|
|
186
|
+
: raw;
|
|
187
|
+
|
|
188
|
+
if (!candidate || typeof candidate !== "object") {
|
|
189
|
+
throw new Error("Invalid workflow_handoff file: expected an object or { workflow_handoff: { ... } }");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const input = candidate as WorkflowHandoffInput;
|
|
193
|
+
const mode =
|
|
194
|
+
parseWorkflowMode(input.mode) ??
|
|
195
|
+
(cleanString(input.state_path, "workflow_handoff.state_path") || cleanString(input.run_id, "workflow_handoff.run_id")
|
|
196
|
+
? "resume"
|
|
197
|
+
: "new");
|
|
198
|
+
const handoff: ResolvedWorkflowHandoff = {
|
|
199
|
+
source: "workflow_handoff",
|
|
200
|
+
mode,
|
|
201
|
+
state_path: cleanString(input.state_path, "workflow_handoff.state_path"),
|
|
202
|
+
run_id: cleanString(input.run_id, "workflow_handoff.run_id"),
|
|
203
|
+
content_form: parseContentForm(input.content_form, "workflow_handoff.content_form"),
|
|
204
|
+
body_path: cleanString(input.body_path, "workflow_handoff.body_path"),
|
|
205
|
+
materials_path: cleanString(input.materials_path, "workflow_handoff.materials_path"),
|
|
206
|
+
target_account: cleanString(input.target_account, "workflow_handoff.target_account"),
|
|
207
|
+
title: cleanString(input.title, "workflow_handoff.title"),
|
|
208
|
+
user_intent_text: cleanString(input.user_intent_text, "workflow_handoff.user_intent_text"),
|
|
209
|
+
explicit_constraints: parseExplicitConstraints(input.explicit_constraints),
|
|
210
|
+
research_policy: parseResearchPolicy(input.research_policy),
|
|
211
|
+
authoring_policy: parseAuthoringPolicy(input.authoring_policy),
|
|
212
|
+
review_policy: parseReviewPolicy(input.review_policy),
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
if (handoff.mode === "new") {
|
|
216
|
+
if (!handoff.content_form) {
|
|
217
|
+
throw new Error("workflow_handoff new mode requires content_form");
|
|
218
|
+
}
|
|
219
|
+
if (!handoff.target_account) {
|
|
220
|
+
throw new Error("workflow_handoff new mode requires target_account");
|
|
221
|
+
}
|
|
222
|
+
if (!handoff.title) {
|
|
223
|
+
throw new Error("workflow_handoff new mode requires title");
|
|
224
|
+
}
|
|
225
|
+
if (!handoff.user_intent_text) {
|
|
226
|
+
throw new Error("workflow_handoff new mode requires user_intent_text");
|
|
227
|
+
}
|
|
228
|
+
if (!handoff.body_path && !handoff.materials_path) {
|
|
229
|
+
throw new Error("workflow_handoff new mode requires body_path or materials_path");
|
|
230
|
+
}
|
|
231
|
+
} else if (!handoff.state_path && !handoff.run_id) {
|
|
232
|
+
throw new Error("workflow_handoff resume mode requires state_path or run_id");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (handoff.authoring_policy === "format_only" && !handoff.body_path && handoff.mode === "new") {
|
|
236
|
+
throw new Error("workflow_handoff authoring_policy=format_only requires body_path in new mode");
|
|
237
|
+
}
|
|
238
|
+
if (handoff.authoring_policy === "write_from_materials" && !handoff.materials_path && handoff.mode === "new") {
|
|
239
|
+
throw new Error("workflow_handoff authoring_policy=write_from_materials requires materials_path in new mode");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return handoff;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function shouldParseAsWorkflow(raw: unknown): boolean {
|
|
246
|
+
if (!raw || typeof raw !== "object") {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const input = raw as Record<string, unknown>;
|
|
251
|
+
return [
|
|
252
|
+
"workflow_handoff",
|
|
253
|
+
"mode",
|
|
254
|
+
"state_path",
|
|
255
|
+
"run_id",
|
|
256
|
+
"materials_path",
|
|
257
|
+
"research_policy",
|
|
258
|
+
"authoring_policy",
|
|
259
|
+
"review_policy",
|
|
260
|
+
].some((key) => key in input);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function parseHandoff(raw: unknown): ResolvedWorkflowHandoff {
|
|
264
|
+
if (shouldParseAsWorkflow(raw)) {
|
|
265
|
+
return parseWorkflowHandoff(raw);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (raw && typeof raw === "object" && "workflow_handoff" in (raw as Record<string, unknown>)) {
|
|
269
|
+
return parseWorkflowHandoff(raw);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return parsePublishHandoff(raw);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function stageManagedInputFile(
|
|
276
|
+
workspaceRoot: string,
|
|
277
|
+
runId: string,
|
|
278
|
+
sourcePath: string,
|
|
279
|
+
stem: string,
|
|
280
|
+
): Promise<string> {
|
|
281
|
+
const tempRoot = resolveWorkspacePaths(workspaceRoot).tempRoot;
|
|
282
|
+
const extension = extname(sourcePath) || ".md";
|
|
283
|
+
const managedDir = join(tempRoot, runId);
|
|
284
|
+
const managedPath = join(managedDir, `${stem}${extension}`);
|
|
285
|
+
await mkdir(managedDir, { recursive: true });
|
|
286
|
+
if (resolve(sourcePath) !== resolve(managedPath)) {
|
|
287
|
+
await copyFile(sourcePath, managedPath);
|
|
288
|
+
}
|
|
289
|
+
return managedPath;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function arraysEqual(a: string[], b: string[]): boolean {
|
|
293
|
+
return a.length === b.length && a.every((item, index) => item === b[index]);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function routesEqual(a: WorkflowState["route"], b: WorkflowState["route"]): boolean {
|
|
297
|
+
return (
|
|
298
|
+
a.primary === b.primary &&
|
|
299
|
+
a.account === b.account &&
|
|
300
|
+
a.content_profile === b.content_profile &&
|
|
301
|
+
arraysEqual(a.extras, b.extras)
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function resetDerivedState(state: WorkflowState): void {
|
|
306
|
+
state.mode = "active";
|
|
307
|
+
state.phase = defaultPhase();
|
|
308
|
+
state.asset_path = "";
|
|
309
|
+
state.formatted_body_path = null;
|
|
310
|
+
state.metadata = {
|
|
311
|
+
...defaultMetadata(),
|
|
312
|
+
title: state.metadata.title,
|
|
313
|
+
};
|
|
314
|
+
state.artifacts = defaultArtifacts();
|
|
315
|
+
state.images = defaultImages();
|
|
316
|
+
state.publish = defaultPublish();
|
|
317
|
+
state.content_review = defaultContentReview();
|
|
318
|
+
state.redo_hint = null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function resolveExistingStatePath(
|
|
322
|
+
workspaceRoot: string,
|
|
323
|
+
handoff: ResolvedWorkflowHandoff,
|
|
324
|
+
): Promise<string> {
|
|
325
|
+
if (handoff.state_path) {
|
|
326
|
+
return handoff.state_path;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const task = await findTask(workspaceRoot, { run_id: handoff.run_id });
|
|
330
|
+
if (!task) {
|
|
331
|
+
throw new Error(`Task not found for run_id: ${handoff.run_id}`);
|
|
332
|
+
}
|
|
333
|
+
return task.summary.state_path;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function applyHandoffToState(
|
|
337
|
+
state: WorkflowState,
|
|
338
|
+
handoff: ResolvedWorkflowHandoff,
|
|
339
|
+
): Promise<void> {
|
|
340
|
+
const targets: Target[] = state.intent.targets.length > 0 ? state.intent.targets : ["wechat"];
|
|
341
|
+
let restartFromPrepare = false;
|
|
342
|
+
|
|
343
|
+
const nextContentForm = handoff.content_form ?? state.intent.content_form;
|
|
344
|
+
if (handoff.content_form && handoff.content_form !== state.intent.content_form) {
|
|
345
|
+
restartFromPrepare = true;
|
|
346
|
+
}
|
|
347
|
+
state.intent.content_form = nextContentForm;
|
|
348
|
+
state.intent.task_kind = "publish";
|
|
349
|
+
state.intent.targets = targets;
|
|
350
|
+
state.intent.content_origin = handoff.body_path ? "user" : handoff.materials_path ? "external" : state.intent.content_origin;
|
|
351
|
+
state.intent.requires.render = true;
|
|
352
|
+
state.intent.requires.publish = true;
|
|
353
|
+
if (handoff.research_policy === "need") {
|
|
354
|
+
state.intent.requires.research = true;
|
|
355
|
+
} else if (handoff.research_policy === "skip") {
|
|
356
|
+
state.intent.requires.research = false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (handoff.user_intent_text && handoff.user_intent_text !== state.intent.intent_text) {
|
|
360
|
+
state.intent.intent_text = handoff.user_intent_text;
|
|
361
|
+
restartFromPrepare = true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (
|
|
365
|
+
handoff.explicit_constraints.length > 0 &&
|
|
366
|
+
!arraysEqual(handoff.explicit_constraints, state.intent.explicit_constraints)
|
|
367
|
+
) {
|
|
368
|
+
state.intent.explicit_constraints = handoff.explicit_constraints;
|
|
369
|
+
restartFromPrepare = true;
|
|
370
|
+
} else if (handoff.mode === "new") {
|
|
371
|
+
state.intent.explicit_constraints = handoff.explicit_constraints;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (handoff.title && handoff.title !== state.metadata.title) {
|
|
375
|
+
state.metadata.title = handoff.title;
|
|
376
|
+
restartFromPrepare = true;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (handoff.research_policy !== "default") {
|
|
380
|
+
state.handoff.research_policy = handoff.research_policy;
|
|
381
|
+
}
|
|
382
|
+
if (handoff.authoring_policy !== "default") {
|
|
383
|
+
state.handoff.authoring_policy = handoff.authoring_policy;
|
|
384
|
+
}
|
|
385
|
+
if (handoff.review_policy !== "default") {
|
|
386
|
+
state.handoff.review_policy = handoff.review_policy;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const nextRoute = resolveFullRoute(state.intent.intent_text ?? "", {
|
|
390
|
+
account: handoff.target_account ?? state.route.account,
|
|
391
|
+
contentForm: state.intent.content_form,
|
|
392
|
+
targets,
|
|
393
|
+
});
|
|
394
|
+
if (!routesEqual(nextRoute, state.route)) {
|
|
395
|
+
restartFromPrepare = true;
|
|
396
|
+
state.route = nextRoute;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (handoff.body_path) {
|
|
400
|
+
state.source_body_path = await stageManagedInputFile(
|
|
401
|
+
state.workspace_root,
|
|
402
|
+
state.run_id,
|
|
403
|
+
handoff.body_path,
|
|
404
|
+
"source-body",
|
|
405
|
+
);
|
|
406
|
+
state.handoff.source_materials_path = null;
|
|
407
|
+
restartFromPrepare = true;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (handoff.materials_path) {
|
|
411
|
+
state.handoff.source_materials_path = await stageManagedInputFile(
|
|
412
|
+
state.workspace_root,
|
|
413
|
+
state.run_id,
|
|
414
|
+
handoff.materials_path,
|
|
415
|
+
"source-materials",
|
|
416
|
+
);
|
|
417
|
+
if (!handoff.body_path) {
|
|
418
|
+
state.source_body_path = null;
|
|
419
|
+
}
|
|
420
|
+
restartFromPrepare = true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (
|
|
424
|
+
state.handoff.authoring_policy === "write_from_materials" &&
|
|
425
|
+
!state.handoff.source_materials_path
|
|
426
|
+
) {
|
|
427
|
+
throw new Error("workflow_handoff authoring_policy=write_from_materials requires materials_path");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (restartFromPrepare) {
|
|
431
|
+
resetDerivedState(state);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (state.handoff.review_policy === "trust_user" && state.source_body_path) {
|
|
435
|
+
state.content_review = { status: "passed", feedback: null };
|
|
436
|
+
} else if (state.handoff.review_policy === "required" && restartFromPrepare) {
|
|
437
|
+
state.content_review = defaultContentReview();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
state.mode = "active";
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function buildNewState(workspaceRoot: string, statePath: string, runId: string): WorkflowState {
|
|
444
|
+
const state = defaultState();
|
|
445
|
+
state.run_id = runId;
|
|
446
|
+
state.workspace_root = workspaceRoot;
|
|
447
|
+
state.state_path = statePath;
|
|
448
|
+
state.mode = "active";
|
|
449
|
+
return state;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export async function ingestHandoff(args: string[]): Promise<void> {
|
|
453
|
+
const parsed = parseArgs(args);
|
|
454
|
+
|
|
455
|
+
if (parsed.help) {
|
|
456
|
+
console.log(`
|
|
457
|
+
Usage: zzhub-pipeline ingest-handoff [options]
|
|
458
|
+
|
|
459
|
+
Options:
|
|
460
|
+
--file Path to a publish_handoff/workflow_handoff JSON file (required)
|
|
461
|
+
--workspace Workspace root directory (optional; defaults to config/env)
|
|
462
|
+
`.trim());
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const filePath = requireArg(parsed, "file", "publish_handoff/workflow_handoff JSON file");
|
|
467
|
+
const config = loadConfig();
|
|
468
|
+
const workspace = resolveWorkspaceRoot(optionalArg(parsed, "workspace"), config);
|
|
469
|
+
const raw = JSON.parse(await readFile(filePath, "utf-8")) as unknown;
|
|
470
|
+
const handoff = parseHandoff(raw);
|
|
471
|
+
|
|
472
|
+
const statePath =
|
|
473
|
+
handoff.mode === "resume"
|
|
474
|
+
? await resolveExistingStatePath(workspace, handoff)
|
|
475
|
+
: getRunStatePath(workspace, generateRunId());
|
|
476
|
+
const state =
|
|
477
|
+
handoff.mode === "resume"
|
|
478
|
+
? await readState(statePath)
|
|
479
|
+
: buildNewState(workspace, statePath, statePath.split("/").pop()?.replace(/\.json$/, "") ?? generateRunId());
|
|
480
|
+
|
|
481
|
+
if (handoff.mode === "new" && !state.run_id) {
|
|
482
|
+
state.run_id = generateRunId();
|
|
483
|
+
state.state_path = getRunStatePath(workspace, state.run_id);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
await applyHandoffToState(state, handoff);
|
|
487
|
+
await writeState(state.state_path, state);
|
|
488
|
+
|
|
489
|
+
const task = await getTaskByStatePath(state.state_path);
|
|
490
|
+
printResult({
|
|
491
|
+
accepted_handoff: {
|
|
492
|
+
source: handoff.source,
|
|
493
|
+
mode: handoff.mode,
|
|
494
|
+
run_id: task.summary.run_id,
|
|
495
|
+
state_path: task.summary.state_path,
|
|
496
|
+
content_form: state.intent.content_form,
|
|
497
|
+
target_account: state.route.account,
|
|
498
|
+
title: state.metadata.title,
|
|
499
|
+
user_intent_text: state.intent.intent_text,
|
|
500
|
+
explicit_constraints: state.intent.explicit_constraints,
|
|
501
|
+
research_policy: state.handoff.research_policy,
|
|
502
|
+
authoring_policy: state.handoff.authoring_policy,
|
|
503
|
+
review_policy: state.handoff.review_policy,
|
|
504
|
+
body_attached: Boolean(state.source_body_path),
|
|
505
|
+
materials_attached: Boolean(state.handoff.source_materials_path),
|
|
506
|
+
},
|
|
507
|
+
...task,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* init — Create initial run state from intent classification.
|
|
3
|
+
*
|
|
4
|
+
* This is called by the orchestrator after intent classification.
|
|
5
|
+
* It creates the temporary run state JSON.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* zzhub-pipeline init \
|
|
9
|
+
* --workspace /abs/workspace \
|
|
10
|
+
* --task-kind publish \
|
|
11
|
+
* --content-form article \
|
|
12
|
+
* --targets wechat \
|
|
13
|
+
* --content-origin user \
|
|
14
|
+
* [--newspic-render-spec-file /abs/spec.json] \
|
|
15
|
+
* [--style-hint fact_report] \
|
|
16
|
+
* [--requires-research] \
|
|
17
|
+
* [--requires-style] \
|
|
18
|
+
* [--requires-render] \
|
|
19
|
+
* [--requires-publish]
|
|
20
|
+
*
|
|
21
|
+
* Output: JSON state written to {workspace}/.zzhub-media/runs/{run_id}.json
|
|
22
|
+
* Prints the state path to stdout.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { readFile } from "fs/promises";
|
|
26
|
+
import { parseArgs, requireArg, optionalArg, flagArg } from "../args";
|
|
27
|
+
import { printResult, renderInit } from "../output";
|
|
28
|
+
import { loadConfig, resolveWorkspaceRoot } from "../config";
|
|
29
|
+
import { resolveFullRoute } from "../routes";
|
|
30
|
+
import {
|
|
31
|
+
defaultState,
|
|
32
|
+
generateRunId,
|
|
33
|
+
getRunStatePath,
|
|
34
|
+
normalizeNewspicRenderSpec,
|
|
35
|
+
writeState,
|
|
36
|
+
type ContentForm,
|
|
37
|
+
type ContentOrigin,
|
|
38
|
+
type Target,
|
|
39
|
+
type TaskKind,
|
|
40
|
+
} from "../state";
|
|
41
|
+
|
|
42
|
+
export async function init(args: string[]): Promise<void> {
|
|
43
|
+
const parsed = parseArgs(args);
|
|
44
|
+
|
|
45
|
+
if (parsed.help) {
|
|
46
|
+
console.log(`
|
|
47
|
+
Usage: zzhub-pipeline init [options]
|
|
48
|
+
|
|
49
|
+
Options:
|
|
50
|
+
--workspace Workspace root directory (optional; defaults to config/env)
|
|
51
|
+
--task-kind draft | polish | organize | publish | mixed (required)
|
|
52
|
+
--content-form article | newspic | unknown (required)
|
|
53
|
+
--targets Comma-separated: wechat,blog (required)
|
|
54
|
+
--content-origin user | external | unknown (required)
|
|
55
|
+
--intent-text Original user request for route/account resolution (optional)
|
|
56
|
+
--account Explicit account override (optional)
|
|
57
|
+
--newspic-render-spec-file JSON file for newspic pagination / page-image intent (optional)
|
|
58
|
+
--style-hint e.g. fact_report (optional)
|
|
59
|
+
--requires-research Flag
|
|
60
|
+
--requires-style Flag
|
|
61
|
+
--requires-render Flag
|
|
62
|
+
--requires-publish Flag
|
|
63
|
+
`.trim());
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const taskKind = requireArg(parsed, "task-kind", "task kind") as TaskKind;
|
|
68
|
+
const contentForm = requireArg(parsed, "content-form", "content form") as ContentForm;
|
|
69
|
+
const targetsRaw = requireArg(parsed, "targets", "publish targets");
|
|
70
|
+
const contentOrigin = requireArg(parsed, "content-origin", "content origin") as ContentOrigin;
|
|
71
|
+
const intentText = optionalArg(parsed, "intent-text") ?? "";
|
|
72
|
+
const accountOverride = optionalArg(parsed, "account");
|
|
73
|
+
const styleHint = optionalArg(parsed, "style-hint") ?? null;
|
|
74
|
+
const newspicRenderSpecFile = optionalArg(parsed, "newspic-render-spec-file");
|
|
75
|
+
const config = loadConfig();
|
|
76
|
+
const workspace = resolveWorkspaceRoot(optionalArg(parsed, "workspace"), config);
|
|
77
|
+
|
|
78
|
+
const targets = targetsRaw.split(",").map((t) => t.trim()) as Target[];
|
|
79
|
+
|
|
80
|
+
const runId = generateRunId();
|
|
81
|
+
const statePath = getRunStatePath(workspace, runId);
|
|
82
|
+
let newspicRender = null;
|
|
83
|
+
|
|
84
|
+
if (newspicRenderSpecFile) {
|
|
85
|
+
const specRaw = JSON.parse(await readFile(newspicRenderSpecFile, "utf-8")) as unknown;
|
|
86
|
+
newspicRender = normalizeNewspicRenderSpec(specRaw);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const state = defaultState();
|
|
90
|
+
state.run_id = runId;
|
|
91
|
+
state.workspace_root = workspace;
|
|
92
|
+
state.state_path = statePath;
|
|
93
|
+
state.mode = "active";
|
|
94
|
+
state.route = resolveFullRoute(intentText, {
|
|
95
|
+
account: accountOverride,
|
|
96
|
+
contentForm,
|
|
97
|
+
targets,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
state.intent = {
|
|
101
|
+
task_kind: taskKind,
|
|
102
|
+
content_form: contentForm,
|
|
103
|
+
targets,
|
|
104
|
+
content_origin: contentOrigin,
|
|
105
|
+
intent_text: intentText || null,
|
|
106
|
+
explicit_constraints: [],
|
|
107
|
+
style_hint: styleHint,
|
|
108
|
+
newspic_render: newspicRender,
|
|
109
|
+
requires: {
|
|
110
|
+
research: flagArg(parsed, "requires-research"),
|
|
111
|
+
style: flagArg(parsed, "requires-style"),
|
|
112
|
+
render: flagArg(parsed, "requires-render"),
|
|
113
|
+
publish: flagArg(parsed, "requires-publish"),
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
await writeState(statePath, state);
|
|
118
|
+
|
|
119
|
+
const output = {
|
|
120
|
+
run_id: runId,
|
|
121
|
+
state_path: statePath,
|
|
122
|
+
mode: state.mode,
|
|
123
|
+
phase: state.phase.current,
|
|
124
|
+
};
|
|
125
|
+
printResult(output, renderInit);
|
|
126
|
+
}
|