@easysociable/cli 0.1.28 → 0.1.30
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/package.json
CHANGED
|
@@ -10,7 +10,7 @@ export const PAGE_ROLES = [
|
|
|
10
10
|
|
|
11
11
|
export const PACK_PLATFORMS = ['tiktok', 'xiaohongshu', 'instagram', 'linkedin', 'threads']
|
|
12
12
|
|
|
13
|
-
export const SLIDESHOW_PLATFORMS =
|
|
13
|
+
export const SLIDESHOW_PLATFORMS = PACK_PLATFORMS
|
|
14
14
|
|
|
15
15
|
export const PLATFORM_CAPS = {
|
|
16
16
|
tiktok: { hardMin: 4, hardMax: 35 },
|
|
@@ -22,8 +22,8 @@ export const PLATFORM_CAPS = {
|
|
|
22
22
|
|
|
23
23
|
const FORBIDDEN_PACK_KEYS = ['deck_template_id', 'deckTemplateId']
|
|
24
24
|
|
|
25
|
-
const PACK_KEYS = new Set(['schemaVersion', 'platform', 'title', 'pages'])
|
|
26
|
-
const PAGE_KEYS = new Set(['layoutFamilyId', 'pageTemplateId', 'role', 'slots'])
|
|
25
|
+
const PACK_KEYS = new Set(['schemaVersion', 'platform', 'title', 'contentSlug', 'pages'])
|
|
26
|
+
const PAGE_KEYS = new Set(['layoutFamilyId', 'pageTemplateId', 'layout', 'role', 'slots'])
|
|
27
27
|
export const RUN_FILE_STATUSES = [
|
|
28
28
|
'RUN_OPEN', 'BRIEF_WAIT', 'DRAFTED', 'EDIT_WAIT', 'SCORED',
|
|
29
29
|
'READY_TO_POST', 'PUBLISHED', 'HOLD', 'KILLED', 'SUPERSEDED',
|
|
@@ -98,8 +98,14 @@ export function validateContentPack(pack, options = {}) {
|
|
|
98
98
|
pushUnknownKeys(errors, page, PAGE_KEYS, label)
|
|
99
99
|
if (!PAGE_ROLES.includes(page.role)) errors.push(`${label}.role: invalid page role`)
|
|
100
100
|
const layoutRef = page.layoutFamilyId ?? page.pageTemplateId
|
|
101
|
-
if (
|
|
102
|
-
|
|
101
|
+
if (page.layout !== undefined) {
|
|
102
|
+
// Agent-generated page: its own Studio source on the pack platform's canvas.
|
|
103
|
+
const { studioJson, canvas } = isRecord(page.layout) ? page.layout : {}
|
|
104
|
+
if (typeof studioJson !== 'string' || !studioJson || !isRecord(canvas) || !Number.isInteger(canvas.width) || !Number.isInteger(canvas.height))
|
|
105
|
+
errors.push(`${label}.layout: needs studioJson and canvas { width, height }`)
|
|
106
|
+
}
|
|
107
|
+
else if (typeof layoutRef !== 'string' || layoutRef.length === 0) {
|
|
108
|
+
errors.push(`${label}.layoutFamilyId: required (or pageTemplateId, or a generated layout) and must be a non-empty string`)
|
|
103
109
|
}
|
|
104
110
|
if (!isRecord(page.slots)) {
|
|
105
111
|
errors.push(`${label}.slots: must be an object`)
|
|
@@ -137,47 +143,59 @@ export function ideaField(text, name) {
|
|
|
137
143
|
return match ? match[1].trim() : ''
|
|
138
144
|
}
|
|
139
145
|
|
|
146
|
+
/**
|
|
147
|
+
* A Run folder in either layout:
|
|
148
|
+
* - current (Formula-led): content.md with run_id/platform/status frontmatter; the pack is pack.json,
|
|
149
|
+
* pack-<platform>.json, or the JSON block under content.md "## Pack".
|
|
150
|
+
* - legacy (idea → draft → pack): idea.md + draft.md + pack-<platform>.json.
|
|
151
|
+
*/
|
|
140
152
|
export function validateRunLayout(runDir, options = {}) {
|
|
141
153
|
const { requirePack = false } = options
|
|
142
154
|
const errors = []
|
|
143
155
|
const warnings = []
|
|
144
|
-
const files = { ideaMd: false, draftMd: false, packMd: false, packs: [] }
|
|
145
|
-
const ideaPath = join(runDir, 'idea.md')
|
|
156
|
+
const files = { contentMd: false, ideaMd: false, draftMd: false, packMd: false, packs: [] }
|
|
146
157
|
|
|
147
158
|
if (!existsSync(runDir) || !statSync(runDir).isDirectory()) {
|
|
148
159
|
errors.push('run: directory required')
|
|
149
160
|
return { valid: false, errors, warnings, files, frontmatter: {} }
|
|
150
161
|
}
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
files.contentMd = existsSync(join(runDir, 'content.md'))
|
|
163
|
+
files.ideaMd = existsSync(join(runDir, 'idea.md'))
|
|
164
|
+
const main = files.contentMd ? 'content.md' : files.ideaMd ? 'idea.md' : ''
|
|
165
|
+
if (!main) {
|
|
166
|
+
errors.push('content.md: required (or idea.md for a legacy run)')
|
|
153
167
|
return { valid: false, errors, warnings, files, frontmatter: {} }
|
|
154
168
|
}
|
|
155
169
|
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
const platform = (frontmatter.platform || ideaField(ideaText, 'primary_platform') || ideaField(ideaText, 'platforms')).replace(/\s.*/, '')
|
|
170
|
+
const mainText = readFileSync(join(runDir, main), 'utf8')
|
|
171
|
+
const frontmatter = parseFrontmatter(mainText)
|
|
172
|
+
const platform = (frontmatter.platform || ideaField(mainText, 'primary_platform') || ideaField(mainText, 'platforms')).replace(/\s.*/, '')
|
|
160
173
|
frontmatter.platform = platform
|
|
161
174
|
frontmatter.slug = frontmatter.slug || basename(runDir)
|
|
162
|
-
if (!frontmatter.run_id) errors.push(
|
|
163
|
-
if (!frontmatter.status) errors.push(
|
|
175
|
+
if (!frontmatter.run_id) errors.push(`${main} frontmatter: missing run_id`)
|
|
176
|
+
if (!frontmatter.status) errors.push(`${main} frontmatter: missing status`)
|
|
164
177
|
if (frontmatter.status && !RUN_STATUSES.includes(frontmatter.status)) {
|
|
165
|
-
errors.push(
|
|
178
|
+
errors.push(`${main} status: must be one of the engine run statuses`)
|
|
166
179
|
}
|
|
167
|
-
if (!platform) warnings.push(
|
|
180
|
+
if (!platform) warnings.push(`${main}: platform recommended`)
|
|
168
181
|
|
|
169
|
-
|
|
182
|
+
if (!files.contentMd) {
|
|
183
|
+
files.draftMd = existsSync(join(runDir, 'draft.md'))
|
|
184
|
+
if (!files.draftMd) warnings.push('draft.md: recommended')
|
|
185
|
+
}
|
|
170
186
|
files.packMd = existsSync(join(runDir, 'pack.md')) || readdirSync(runDir).some((name) => /^pack-[a-z0-9_-]+\.md$/i.test(name))
|
|
171
|
-
if (!files.draftMd) warnings.push('draft.md: recommended')
|
|
172
187
|
|
|
173
|
-
files.packs = readdirSync(runDir).filter((name) => /^pack-[a-z0-9_-]
|
|
188
|
+
files.packs = readdirSync(runDir).filter((name) => /^pack(-[a-z0-9_-]+)?\.json$/i.test(name))
|
|
189
|
+
// content.md may carry the pack inline under "## Pack" instead of a file.
|
|
190
|
+
const inline = files.contentMd ? mainText.match(/^## Pack\b[\s\S]*?```json\s*\n([\s\S]*?)\n```/m) : null
|
|
191
|
+
if (inline) files.inlinePack = inline[1]
|
|
174
192
|
const slideshowRun = SLIDESHOW_PLATFORMS.includes(platform) || platform === 'ig'
|
|
175
193
|
const expectedPack = platform === 'ig' ? 'pack-instagram.json' : (platform ? `pack-${platform}.json` : 'pack-<platform>.json')
|
|
176
|
-
if (requirePack && files.packs.length === 0) {
|
|
177
|
-
errors.push(`${expectedPack}: required for slideshow handoff`)
|
|
194
|
+
if (requirePack && files.packs.length === 0 && !inline) {
|
|
195
|
+
errors.push(`${expectedPack} (or pack.json, or a json block under content.md ## Pack): required for slideshow handoff`)
|
|
178
196
|
}
|
|
179
|
-
if (!slideshowRun && files.packs.length > 0) {
|
|
180
|
-
errors.push('pack
|
|
197
|
+
if (!slideshowRun && (files.packs.length > 0 || inline)) {
|
|
198
|
+
errors.push('pack: only slideshow platforms may contain a Content Pack')
|
|
181
199
|
}
|
|
182
200
|
|
|
183
201
|
return { valid: errors.length === 0, errors, warnings, files, frontmatter }
|
|
@@ -39,10 +39,13 @@ try {
|
|
|
39
39
|
const runDir = resolve(options.run)
|
|
40
40
|
const layout = validateRunLayout(runDir, { requirePack: options.requirePack })
|
|
41
41
|
const packs = []
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const sources = layout.files.packs.map(name => ({ name, read: () => readFileSync(join(runDir, name), 'utf8') }))
|
|
43
|
+
if (layout.files.inlinePack) sources.push({ name: 'content.md ## Pack', read: () => layout.files.inlinePack })
|
|
44
|
+
for (const { name, read } of sources) {
|
|
45
|
+
// pack-<platform>.json names its platform; pack.json and the inline block follow the Run's.
|
|
46
|
+
const platform = /^pack-/.test(name) ? name.replace(/^pack-/, '').replace(/\.json$/i, '') : layout.frontmatter.platform || undefined
|
|
44
47
|
try {
|
|
45
|
-
const pack = JSON.parse(
|
|
48
|
+
const pack = JSON.parse(read())
|
|
46
49
|
packs.push({ file: name, pages: Array.isArray(pack.pages) ? pack.pages.length : 0, ...validateContentPack(pack, {
|
|
47
50
|
requirePageTemplateId: options.requirePageTemplateId,
|
|
48
51
|
platform,
|