@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,88 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
|
|
3
|
+
import { parseArgs, optionalArg, requireArg } from "../args";
|
|
4
|
+
import { printResult, renderTaskShape } from "../output";
|
|
5
|
+
import { readState, writeState, type BodyInputReceived } from "../state";
|
|
6
|
+
import { getTaskByStatePath } from "../task-manager";
|
|
7
|
+
import { reconcileStateArtifacts } from "../workflow-materials";
|
|
8
|
+
|
|
9
|
+
function parseReceivedImages(raw: unknown): BodyInputReceived[] {
|
|
10
|
+
if (Array.isArray(raw)) {
|
|
11
|
+
return raw.flatMap((item) => {
|
|
12
|
+
if (!item || typeof item !== "object") {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const input = item as Record<string, unknown>;
|
|
16
|
+
const marker = typeof input.marker === "string" ? input.marker.trim() : "";
|
|
17
|
+
const path = typeof input.path === "string" ? input.path.trim() : "";
|
|
18
|
+
return marker && path ? [{ marker, path }] : [];
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (raw && typeof raw === "object") {
|
|
23
|
+
const input = raw as Record<string, unknown>;
|
|
24
|
+
if (Array.isArray(input.images)) {
|
|
25
|
+
return parseReceivedImages(input.images);
|
|
26
|
+
}
|
|
27
|
+
return Object.entries(input).flatMap(([marker, path]) => {
|
|
28
|
+
const normalizedMarker = marker.trim();
|
|
29
|
+
const normalizedPath = typeof path === "string" ? path.trim() : "";
|
|
30
|
+
return normalizedMarker && normalizedPath
|
|
31
|
+
? [{ marker: normalizedMarker, path: normalizedPath }]
|
|
32
|
+
: [];
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function attachBodyImages(args: string[]): Promise<void> {
|
|
40
|
+
const parsed = parseArgs(args);
|
|
41
|
+
|
|
42
|
+
if (parsed.help) {
|
|
43
|
+
console.log(`
|
|
44
|
+
Usage: zzhub-pipeline attach-body-images [options]
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--state Path to state JSON (required)
|
|
48
|
+
--images-file JSON file mapping marker -> path or [{ marker, path }] (required)
|
|
49
|
+
--scope article | newspic-longform (optional)
|
|
50
|
+
--layout Layout hint such as staggered/editorial (optional)
|
|
51
|
+
`.trim());
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const statePath = requireArg(parsed, "state", "state JSON path");
|
|
56
|
+
const imagesFile = requireArg(parsed, "images-file", "JSON file with marker/path data");
|
|
57
|
+
const scope = optionalArg(parsed, "scope");
|
|
58
|
+
const layout = optionalArg(parsed, "layout");
|
|
59
|
+
|
|
60
|
+
const state = await readState(statePath);
|
|
61
|
+
const raw = JSON.parse(await readFile(imagesFile, "utf-8")) as unknown;
|
|
62
|
+
const incoming = parseReceivedImages(raw);
|
|
63
|
+
const merged = new Map(state.images.body_inputs.received.map((item) => [item.marker, item.path]));
|
|
64
|
+
for (const item of incoming) {
|
|
65
|
+
merged.set(item.marker, item.path);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
state.images.body_inputs.received = [...merged.entries()].map(([marker, path]) => ({ marker, path }));
|
|
69
|
+
if (scope === "article" || scope === "newspic-longform") {
|
|
70
|
+
state.images.body_inputs.scope = scope;
|
|
71
|
+
}
|
|
72
|
+
if (layout) {
|
|
73
|
+
state.images.body_inputs.layout = layout;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await reconcileStateArtifacts(state);
|
|
77
|
+
await writeState(statePath, state);
|
|
78
|
+
|
|
79
|
+
const task = await getTaskByStatePath(statePath);
|
|
80
|
+
printResult(
|
|
81
|
+
{
|
|
82
|
+
summary: task.summary,
|
|
83
|
+
blockers: task.blockers,
|
|
84
|
+
next_action: task.next_action,
|
|
85
|
+
},
|
|
86
|
+
renderTaskShape,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { copyFile, mkdir, writeFile } from "fs/promises";
|
|
2
|
+
import { extname, join, resolve } from "path";
|
|
3
|
+
|
|
4
|
+
import { parseArgs, optionalArg, requireArg } from "../args";
|
|
5
|
+
import { printResult, renderTaskShape } from "../output";
|
|
6
|
+
import { resolveWorkspacePaths } from "../config";
|
|
7
|
+
import { readState, writeState } from "../state";
|
|
8
|
+
import { getTaskByStatePath } from "../task-manager";
|
|
9
|
+
import { reconcileStateArtifacts } from "../workflow-materials";
|
|
10
|
+
|
|
11
|
+
async function stageManagedBodyFile(
|
|
12
|
+
workspaceRoot: string,
|
|
13
|
+
runId: string,
|
|
14
|
+
sourcePath: string | null,
|
|
15
|
+
bodyText: string | null,
|
|
16
|
+
): Promise<string> {
|
|
17
|
+
const tempRoot = resolveWorkspacePaths(workspaceRoot).tempRoot;
|
|
18
|
+
const extension = sourcePath ? extname(sourcePath) || ".md" : ".md";
|
|
19
|
+
const managedDir = join(tempRoot, runId);
|
|
20
|
+
const managedPath = join(managedDir, `source-body${extension}`);
|
|
21
|
+
await mkdir(managedDir, { recursive: true });
|
|
22
|
+
|
|
23
|
+
if (bodyText !== null) {
|
|
24
|
+
await writeFile(managedPath, bodyText, "utf-8");
|
|
25
|
+
} else if (sourcePath) {
|
|
26
|
+
if (resolve(sourcePath) !== resolve(managedPath)) {
|
|
27
|
+
await copyFile(sourcePath, managedPath);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error("Either sourcePath or bodyText must be provided");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return managedPath;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function attachBody(args: string[]): Promise<void> {
|
|
37
|
+
const parsed = parseArgs(args);
|
|
38
|
+
|
|
39
|
+
if (parsed.help) {
|
|
40
|
+
console.log(`
|
|
41
|
+
Usage: zzhub-pipeline attach-body [options]
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
--state Path to state JSON (required)
|
|
45
|
+
--body Path to markdown body file (optional)
|
|
46
|
+
--body-text Inline markdown body text (optional)
|
|
47
|
+
`.trim());
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const statePath = requireArg(parsed, "state", "state JSON path");
|
|
52
|
+
const bodyPath = optionalArg(parsed, "body");
|
|
53
|
+
const bodyText = optionalArg(parsed, "body-text");
|
|
54
|
+
if (!bodyPath && bodyText === undefined) {
|
|
55
|
+
throw new Error("Missing required argument: --body or --body-text");
|
|
56
|
+
}
|
|
57
|
+
if (bodyPath && bodyText !== undefined) {
|
|
58
|
+
throw new Error("Use either --body or --body-text, not both");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const state = await readState(statePath);
|
|
62
|
+
state.source_body_path = await stageManagedBodyFile(
|
|
63
|
+
state.workspace_root,
|
|
64
|
+
state.run_id,
|
|
65
|
+
bodyPath ?? null,
|
|
66
|
+
bodyText ?? null,
|
|
67
|
+
);
|
|
68
|
+
await reconcileStateArtifacts(state);
|
|
69
|
+
await writeState(statePath, state);
|
|
70
|
+
|
|
71
|
+
const task = await getTaskByStatePath(statePath);
|
|
72
|
+
printResult(
|
|
73
|
+
{
|
|
74
|
+
summary: {
|
|
75
|
+
run_id: task.summary.run_id,
|
|
76
|
+
mode: task.summary.mode,
|
|
77
|
+
phase: task.summary.phase,
|
|
78
|
+
route: task.summary.route,
|
|
79
|
+
metadata: task.summary.metadata,
|
|
80
|
+
},
|
|
81
|
+
blockers: task.blockers,
|
|
82
|
+
next_action: task.next_action,
|
|
83
|
+
},
|
|
84
|
+
renderTaskShape,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
|
|
3
|
+
import { parseArgs, requireArg } from "../args";
|
|
4
|
+
import { printResult, renderTaskShape } from "../output";
|
|
5
|
+
import { normalizeNewspicRenderSpec, readState, writeState } from "../state";
|
|
6
|
+
import { getTaskByStatePath } from "../task-manager";
|
|
7
|
+
import { reconcileStateArtifacts } from "../workflow-materials";
|
|
8
|
+
|
|
9
|
+
export async function attachNewspicSpec(args: string[]): Promise<void> {
|
|
10
|
+
const parsed = parseArgs(args);
|
|
11
|
+
|
|
12
|
+
if (parsed.help) {
|
|
13
|
+
console.log(`
|
|
14
|
+
Usage: zzhub-pipeline attach-newspic-spec [options]
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
--state Path to state JSON (required)
|
|
18
|
+
--file Path to newspic render spec JSON (required)
|
|
19
|
+
`.trim());
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const statePath = requireArg(parsed, "state", "state JSON path");
|
|
24
|
+
const specPath = requireArg(parsed, "file", "newspic render spec file");
|
|
25
|
+
|
|
26
|
+
const state = await readState(statePath);
|
|
27
|
+
const spec = JSON.parse(await readFile(specPath, "utf-8")) as unknown;
|
|
28
|
+
state.intent.newspic_render = normalizeNewspicRenderSpec(spec);
|
|
29
|
+
await reconcileStateArtifacts(state);
|
|
30
|
+
await writeState(statePath, state);
|
|
31
|
+
|
|
32
|
+
const task = await getTaskByStatePath(statePath);
|
|
33
|
+
printResult(
|
|
34
|
+
{
|
|
35
|
+
summary: task.summary,
|
|
36
|
+
blockers: task.blockers,
|
|
37
|
+
next_action: task.next_action,
|
|
38
|
+
},
|
|
39
|
+
renderTaskShape,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, writeFile } from "fs/promises";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
|
|
6
|
+
import { checkpoint } from "./checkpoint";
|
|
7
|
+
import { defaultState, writeState } from "../state";
|
|
8
|
+
|
|
9
|
+
async function makeTempDir(prefix: string): Promise<string> {
|
|
10
|
+
return mkdtemp(join(tmpdir(), prefix));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function captureJsonOutput<T>(fn: () => Promise<void>): Promise<T> {
|
|
14
|
+
const original = console.log;
|
|
15
|
+
const lines: string[] = [];
|
|
16
|
+
console.log = (...args: unknown[]) => {
|
|
17
|
+
lines.push(args.map((value) => String(value)).join(" "));
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
await fn();
|
|
21
|
+
} finally {
|
|
22
|
+
console.log = original;
|
|
23
|
+
}
|
|
24
|
+
return JSON.parse(lines.join("\n").trim()) as T;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function captureTextOutput(fn: () => Promise<void>): Promise<string> {
|
|
28
|
+
const original = console.log;
|
|
29
|
+
const lines: string[] = [];
|
|
30
|
+
console.log = (...args: unknown[]) => {
|
|
31
|
+
lines.push(args.map((value) => String(value)).join(" "));
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
await fn();
|
|
35
|
+
} finally {
|
|
36
|
+
console.log = original;
|
|
37
|
+
}
|
|
38
|
+
return lines.join("\n").trim();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("checkpoint", () => {
|
|
42
|
+
test("reports validation passing for a well-formed prepare state", async () => {
|
|
43
|
+
const configPath = join(tmpdir(), `zzhub-test-checkpoint-${process.pid}-${Date.now()}.json`);
|
|
44
|
+
await writeFile(configPath, "{}", "utf-8");
|
|
45
|
+
process.env.ZZHUB_PIPELINE_CONFIG = configPath;
|
|
46
|
+
|
|
47
|
+
const workspace = await makeTempDir("zzhub-checkpoint-");
|
|
48
|
+
const statePath = join(workspace, "state.json");
|
|
49
|
+
|
|
50
|
+
const state = defaultState();
|
|
51
|
+
state.workspace_root = workspace;
|
|
52
|
+
state.state_path = statePath;
|
|
53
|
+
state.run_id = "run-checkpoint-1";
|
|
54
|
+
state.phase.current = "prepare";
|
|
55
|
+
await writeState(statePath, state);
|
|
56
|
+
|
|
57
|
+
const output = await captureJsonOutput<{
|
|
58
|
+
validation: { phase_checked: string; valid: boolean; errors: unknown[] };
|
|
59
|
+
next_action: { action: string };
|
|
60
|
+
}>(() => checkpoint(["--state", statePath]));
|
|
61
|
+
|
|
62
|
+
expect(output.validation.phase_checked).toBe("prepare");
|
|
63
|
+
expect(output.validation.valid).toBe(true);
|
|
64
|
+
expect(output.validation.errors).toEqual([]);
|
|
65
|
+
expect(output.next_action).toBeDefined();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("agent view renders markdown with next action", async () => {
|
|
69
|
+
const configPath = join(tmpdir(), `zzhub-test-checkpoint-${process.pid}-${Date.now()}.json`);
|
|
70
|
+
await writeFile(configPath, "{}", "utf-8");
|
|
71
|
+
process.env.ZZHUB_PIPELINE_CONFIG = configPath;
|
|
72
|
+
|
|
73
|
+
const workspace = await makeTempDir("zzhub-checkpoint-view-");
|
|
74
|
+
const statePath = join(workspace, "state.json");
|
|
75
|
+
|
|
76
|
+
const state = defaultState();
|
|
77
|
+
state.workspace_root = workspace;
|
|
78
|
+
state.state_path = statePath;
|
|
79
|
+
state.run_id = "run-checkpoint-view";
|
|
80
|
+
state.route.primary = "wechat-article";
|
|
81
|
+
state.metadata.title = "Test Title";
|
|
82
|
+
await writeState(statePath, state);
|
|
83
|
+
|
|
84
|
+
const output = await captureTextOutput(() =>
|
|
85
|
+
checkpoint(["--state", statePath, "--view", "agent"]),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(output).toContain("# Current Task");
|
|
89
|
+
expect(output).toContain("## Next Action");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* checkpoint — Read and validate state, report phase status.
|
|
3
|
+
*
|
|
4
|
+
* Used by orchestrator to check state at phase boundaries.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* zzhub-pipeline checkpoint --state /path/to/state.json [--phase render]
|
|
8
|
+
*
|
|
9
|
+
* Output: JSON with state summary and validation errors.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs, requireArg, optionalArg } from "../args";
|
|
13
|
+
import { printResult, renderTaskShape } from "../output";
|
|
14
|
+
import { getTaskByStatePath } from "../task-manager";
|
|
15
|
+
import { parseTaskViewMode, renderTaskStatusMarkdown } from "../task-views";
|
|
16
|
+
import type { PhaseName } from "../state";
|
|
17
|
+
import { validateForPhase } from "../state";
|
|
18
|
+
|
|
19
|
+
export async function checkpoint(args: string[]): Promise<void> {
|
|
20
|
+
const parsed = parseArgs(args);
|
|
21
|
+
|
|
22
|
+
if (parsed.help) {
|
|
23
|
+
console.log(`
|
|
24
|
+
Usage: zzhub-pipeline checkpoint [options]
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--state Path to state JSON (required)
|
|
28
|
+
--phase Phase to validate for (optional; defaults to state.phase.current)
|
|
29
|
+
--view json | markdown | agent (default: json)
|
|
30
|
+
`.trim());
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const statePath = requireArg(parsed, "state", "state JSON path");
|
|
35
|
+
const phaseOverride = optionalArg(parsed, "phase") as PhaseName | undefined;
|
|
36
|
+
const view = parseTaskViewMode(optionalArg(parsed, "view"));
|
|
37
|
+
|
|
38
|
+
const task = await getTaskByStatePath(statePath);
|
|
39
|
+
const phase = phaseOverride ?? task.state.phase.current;
|
|
40
|
+
const errors = validateForPhase(task.state, phase);
|
|
41
|
+
|
|
42
|
+
const output = {
|
|
43
|
+
summary: task.summary,
|
|
44
|
+
validation: {
|
|
45
|
+
phase_checked: phase,
|
|
46
|
+
valid: errors.length === 0,
|
|
47
|
+
errors,
|
|
48
|
+
},
|
|
49
|
+
gaps: task.gaps,
|
|
50
|
+
blockers: task.blockers,
|
|
51
|
+
next_action: task.next_action,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (view !== "json") {
|
|
55
|
+
console.log(renderTaskStatusMarkdown({
|
|
56
|
+
...task,
|
|
57
|
+
validation: output.validation,
|
|
58
|
+
}, view));
|
|
59
|
+
} else {
|
|
60
|
+
printResult(output, renderTaskShape);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (errors.length > 0) {
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { parseArgs, optionalArg, flagArg } from "../args";
|
|
3
|
+
import { printResult, renderConfig } from "../output";
|
|
4
|
+
import {
|
|
5
|
+
configSummary,
|
|
6
|
+
getConfigValue,
|
|
7
|
+
loadConfig,
|
|
8
|
+
PipelineConfigSchema,
|
|
9
|
+
redactConfig,
|
|
10
|
+
redactConfigValue,
|
|
11
|
+
saveConfig,
|
|
12
|
+
setConfigValue,
|
|
13
|
+
} from "../config";
|
|
14
|
+
|
|
15
|
+
export async function configCommand(args: string[]): Promise<void> {
|
|
16
|
+
const parsed = parseArgs(args);
|
|
17
|
+
|
|
18
|
+
if (parsed.help) {
|
|
19
|
+
console.log(`
|
|
20
|
+
Usage: zzhub-pipeline config [options]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--key Dot-path key to read or update
|
|
24
|
+
--value New value for --key
|
|
25
|
+
--json Force JSON output for scalar reads
|
|
26
|
+
--export Print full config as JSON (secrets redacted; use --raw to show all)
|
|
27
|
+
--import Path to a JSON file to merge into current config
|
|
28
|
+
--raw Show secrets unredacted (use with --export)
|
|
29
|
+
`.trim());
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
|
|
35
|
+
// --export
|
|
36
|
+
if (flagArg(parsed, "export")) {
|
|
37
|
+
const raw = flagArg(parsed, "raw");
|
|
38
|
+
const output = raw ? config : redactConfig(config);
|
|
39
|
+
console.log(JSON.stringify(output, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// --import
|
|
44
|
+
const importPath = optionalArg(parsed, "import");
|
|
45
|
+
if (importPath) {
|
|
46
|
+
let imported: unknown;
|
|
47
|
+
try {
|
|
48
|
+
imported = JSON.parse(readFileSync(importPath, "utf-8"));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
throw new Error(`Failed to read import file: ${err instanceof Error ? err.message : String(err)}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Merge: existing values win, imported fills gaps
|
|
54
|
+
const importedObj = (typeof imported === "object" && imported !== null ? imported : {}) as Record<string, unknown>;
|
|
55
|
+
const mergedWx = {
|
|
56
|
+
...config.wx,
|
|
57
|
+
...(importedObj.wx as Record<string, unknown> ?? {}),
|
|
58
|
+
accounts: {
|
|
59
|
+
...config.wx.accounts,
|
|
60
|
+
...((importedObj.wx as Record<string, unknown>)?.accounts as Record<string, unknown> ?? {}),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const raw = {
|
|
64
|
+
paths: { ...config.paths, ...(importedObj.paths ?? {}) },
|
|
65
|
+
services: { ...config.services, ...(importedObj.services ?? {}) },
|
|
66
|
+
commands: { ...config.commands, ...(importedObj.commands ?? {}) },
|
|
67
|
+
wx: mergedWx,
|
|
68
|
+
cos: { ...config.cos, ...(importedObj.cos ?? {}) },
|
|
69
|
+
plugins: { ...config.plugins, ...(importedObj.plugins ?? {}) },
|
|
70
|
+
};
|
|
71
|
+
// Validate through Zod schema — strips unknown fields, applies defaults
|
|
72
|
+
const merged = PipelineConfigSchema.parse(raw);
|
|
73
|
+
|
|
74
|
+
saveConfig(merged);
|
|
75
|
+
printResult(configSummary(merged), renderConfig);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// --key / --value
|
|
80
|
+
const key = optionalArg(parsed, "key");
|
|
81
|
+
const value = optionalArg(parsed, "value");
|
|
82
|
+
const forceJson = flagArg(parsed, "json");
|
|
83
|
+
|
|
84
|
+
if (!key) {
|
|
85
|
+
printResult(configSummary(config), renderConfig);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (value === undefined) {
|
|
90
|
+
const currentValue = getConfigValue(config, key);
|
|
91
|
+
const redactedValue = redactConfigValue(key, currentValue);
|
|
92
|
+
if (forceJson || typeof currentValue !== "string") {
|
|
93
|
+
printResult({ key, value: redactedValue }, renderConfig);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
printResult(String(redactedValue), renderConfig);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const nextConfig = setConfigValue(config, key, value);
|
|
101
|
+
saveConfig(nextConfig);
|
|
102
|
+
printResult(
|
|
103
|
+
{ key, value: redactConfigValue(key, getConfigValue(nextConfig, key)) },
|
|
104
|
+
renderConfig,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { optionalArg, parseArgs, requireArg } from "../args";
|
|
2
|
+
import { loadConfig } from "../config";
|
|
3
|
+
import { printHelp, printResult } from "../output";
|
|
4
|
+
import { uploadFileToCos, type CosUploadResult } from "../providers/cos";
|
|
5
|
+
|
|
6
|
+
type CosUploader = (input: {
|
|
7
|
+
localPath: string;
|
|
8
|
+
folder: string;
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
cosPat: string;
|
|
11
|
+
publicBaseUrl: string;
|
|
12
|
+
}) => Promise<CosUploadResult>;
|
|
13
|
+
|
|
14
|
+
export function createCosUploadCommand(uploader: CosUploader = uploadFileToCos) {
|
|
15
|
+
return async function cosUploadCommand(args: string[]): Promise<void> {
|
|
16
|
+
const parsed = parseArgs(args);
|
|
17
|
+
|
|
18
|
+
if (parsed.help) {
|
|
19
|
+
printHelp(`
|
|
20
|
+
Usage: zzhub-pipeline cos-upload --file <path> [options]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--file Local image file path
|
|
24
|
+
--folder COS folder name, e.g. notes/note-id
|
|
25
|
+
--alt Markdown image alt text
|
|
26
|
+
--base-url Hub base URL for COS STS, defaults to wx.baseUrl
|
|
27
|
+
--pat COS PAT, defaults to cos.pat
|
|
28
|
+
--public-base-url Public CDN base URL
|
|
29
|
+
`.trim());
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
const localPath = requireArg(parsed, "file", "local image file path");
|
|
35
|
+
const folder = optionalArg(parsed, "folder") || "uploads";
|
|
36
|
+
const baseUrl = optionalArg(parsed, "base-url") || config.cos.baseUrl || config.wx.baseUrl;
|
|
37
|
+
const cosPat = optionalArg(parsed, "pat") || config.cos.pat;
|
|
38
|
+
const publicBaseUrl = optionalArg(parsed, "public-base-url") || config.cos.publicBaseUrl;
|
|
39
|
+
const alt = optionalArg(parsed, "alt") || "";
|
|
40
|
+
|
|
41
|
+
if (!cosPat) {
|
|
42
|
+
throw new Error("No COS PAT configured. Set cos.pat in config or pass --pat.");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = await uploader({
|
|
46
|
+
localPath,
|
|
47
|
+
folder,
|
|
48
|
+
baseUrl,
|
|
49
|
+
cosPat,
|
|
50
|
+
publicBaseUrl,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
printResult({
|
|
54
|
+
local_path: result.localPath,
|
|
55
|
+
key: result.key,
|
|
56
|
+
url: result.url,
|
|
57
|
+
markdown: ``,
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const cosUpload = createCosUploadCommand();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { parseArgs, optionalArg } from "../args";
|
|
3
|
+
import { printResult, renderDoctor } from "../output";
|
|
4
|
+
import {
|
|
5
|
+
configSummary,
|
|
6
|
+
getPipelineConfigPath,
|
|
7
|
+
getLegacyZCliConfigPath,
|
|
8
|
+
loadConfig,
|
|
9
|
+
resolveWorkspaceRoot,
|
|
10
|
+
resolveWorkspacePaths,
|
|
11
|
+
} from "../config";
|
|
12
|
+
import { listPublishProviders } from "../providers";
|
|
13
|
+
import { resolveBunBinary } from "../spawn";
|
|
14
|
+
import { runPluginDoctorChecks } from "../adapter-loader";
|
|
15
|
+
|
|
16
|
+
export async function doctor(args: string[]): Promise<void> {
|
|
17
|
+
const parsed = parseArgs(args);
|
|
18
|
+
|
|
19
|
+
if (parsed.help) {
|
|
20
|
+
console.log(`
|
|
21
|
+
Usage: zzhub-pipeline doctor [options]
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--workspace Workspace root to resolve default paths against
|
|
25
|
+
`.trim());
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const config = loadConfig();
|
|
30
|
+
const workspace = resolveWorkspaceRoot(optionalArg(parsed, "workspace"), config);
|
|
31
|
+
const paths = resolveWorkspacePaths(workspace, config);
|
|
32
|
+
|
|
33
|
+
let bunBinary: string | null = null;
|
|
34
|
+
let bunError: string | null = null;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
bunBinary = resolveBunBinary();
|
|
38
|
+
} catch (error) {
|
|
39
|
+
bunError = error instanceof Error ? error.message : String(error);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const pluginChecks = await runPluginDoctorChecks(config);
|
|
43
|
+
|
|
44
|
+
printResult(
|
|
45
|
+
{
|
|
46
|
+
...configSummary(config),
|
|
47
|
+
config_path_exists: existsSync(getPipelineConfigPath()),
|
|
48
|
+
legacy_config_path_exists: existsSync(getLegacyZCliConfigPath()),
|
|
49
|
+
workspace,
|
|
50
|
+
resolved_paths: paths,
|
|
51
|
+
resolved_paths_exist: {
|
|
52
|
+
postsRoot: existsSync(paths.postsRoot),
|
|
53
|
+
tempRoot: existsSync(paths.tempRoot),
|
|
54
|
+
blogRoot: existsSync(paths.blogRoot),
|
|
55
|
+
zotepadExportHtml: existsSync(paths.zotepadExportHtml),
|
|
56
|
+
},
|
|
57
|
+
bundled_imgx: true,
|
|
58
|
+
bundled_wechat_preview: true,
|
|
59
|
+
bun_binary: bunBinary,
|
|
60
|
+
bun_error: bunError,
|
|
61
|
+
publish_providers: listPublishProviders(),
|
|
62
|
+
plugin_checks: pluginChecks,
|
|
63
|
+
},
|
|
64
|
+
renderDoctor,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { parseArgs, optionalArg } from "../args";
|
|
2
|
+
import { printResult, renderTaskShape } from "../output";
|
|
3
|
+
import { findTask } from "../task-manager";
|
|
4
|
+
import { buildAgentTaskShape, parseTaskViewMode, renderTaskStatusMarkdown } from "../task-views";
|
|
5
|
+
|
|
6
|
+
export async function findRun(args: string[]): Promise<void> {
|
|
7
|
+
const parsed = parseArgs(args);
|
|
8
|
+
|
|
9
|
+
if (parsed.help) {
|
|
10
|
+
console.log(`
|
|
11
|
+
Usage: zzhub-pipeline find-run [options]
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--workspace Workspace root (optional)
|
|
15
|
+
--run-id Exact run id
|
|
16
|
+
--route Primary route filter
|
|
17
|
+
--account Account filter
|
|
18
|
+
--mode Mode filter
|
|
19
|
+
--phase Current phase filter
|
|
20
|
+
--title-contains Match title substring
|
|
21
|
+
--active Prefer only in-flight tasks
|
|
22
|
+
--full Include the full state payload
|
|
23
|
+
--view json | markdown | agent | agent-json (default: json)
|
|
24
|
+
`.trim());
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const includeFull = parsed.full === true || parsed.full === "true";
|
|
29
|
+
const view = parseTaskViewMode(optionalArg(parsed, "view"));
|
|
30
|
+
const task = await findTask(optionalArg(parsed, "workspace"), {
|
|
31
|
+
run_id: optionalArg(parsed, "run-id"),
|
|
32
|
+
route: optionalArg(parsed, "route"),
|
|
33
|
+
account: optionalArg(parsed, "account"),
|
|
34
|
+
mode: optionalArg(parsed, "mode"),
|
|
35
|
+
phase: optionalArg(parsed, "phase"),
|
|
36
|
+
title_contains: optionalArg(parsed, "title-contains"),
|
|
37
|
+
active_only: parsed.active === true || parsed.active === "true",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!task) {
|
|
41
|
+
throw new Error("No matching task found");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (view === "agent-json") {
|
|
45
|
+
console.log(JSON.stringify(buildAgentTaskShape(task), null, 2));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (view !== "json") {
|
|
50
|
+
console.log(renderTaskStatusMarkdown(task, view));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
printResult(includeFull ? task : task.summary, renderTaskShape);
|
|
55
|
+
}
|