@easysociable/cli 0.1.2 → 0.1.3

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.
Files changed (120) hide show
  1. package/README.md +29 -21
  2. package/dist/easysociable.js +481 -52
  3. package/dist/easysociable.js.map +1 -1
  4. package/package.json +3 -2
  5. package/skills/ONBOARDING.md +20 -16
  6. package/skills/easysociable-content/SKILL.md +29 -104
  7. package/skills/easysociable-slideshow/SKILL.md +4 -3
  8. package/skills/easysociable-template/SKILL.md +26 -24
  9. package/skills/easysociable-template/references/CATALOG_METADATA.md +2 -2
  10. package/skills/easysociable-template/references/DESIGN_V2_1.md +53 -2
  11. package/skills/easysociable-template/references/GATEWAY.md +7 -2
  12. package/skills/easysociable-template/references/PLATFORM_SPECS.md +11 -3
  13. package/skills/easysociable-template/references/THEME_TOKENS.md +1 -1
  14. package/skills/easysociable-template/references/fixtures/image-reference.design-plan.json +74 -0
  15. package/skills/easysociable-template/references/fixtures/image-reference.visual-qa.json +28 -0
  16. package/skills/easysociable-template/scripts/validate-design-v2.mjs +97 -0
  17. package/skills/easysociable-template/workflows/ASSET_PLAN.md +2 -1
  18. package/skills/easysociable-template/workflows/AUTHORING_FROM_BRIEF.md +4 -4
  19. package/skills/easysociable-template/workflows/AUTHORING_FROM_IMAGE.md +66 -16
  20. package/skills/easysociable-template/workflows/AUTHORING_STUDIO.md +28 -15
  21. package/skills/easysociable-template/workflows/DESIGN_BRIEF.md +4 -3
  22. package/skills/easysociable-template/workflows/MULTI_PLATFORM_FAMILY.md +81 -0
  23. package/skills/easysociable-template/workflows/PUBLISH_FROM_DRAFT.md +1 -1
  24. package/skills/easysociable-template/workflows/VISUAL_QA.md +10 -0
  25. package/skills/easysociable-content/INSTALL.md +0 -60
  26. package/skills/easysociable-content/platforms/claude.md +0 -4
  27. package/skills/easysociable-content/platforms/codex.md +0 -4
  28. package/skills/easysociable-content/platforms/hermes.md +0 -4
  29. package/skills/easysociable-content/platforms/openclaw.md +0 -4
  30. package/skills/easysociable-content/references/CLI.md +0 -40
  31. package/skills/easysociable-content/references/CONTRACT_SYNC.md +0 -42
  32. package/skills/easysociable-content/references/MCP.md +0 -15
  33. package/skills/easysociable-content/references/PAGE_PACK.md +0 -129
  34. package/skills/easysociable-content/references/PLATFORM_GUIDE.md +0 -36
  35. package/skills/easysociable-content/references/RUN_LAYOUT.md +0 -73
  36. package/skills/easysociable-content/references/TROUBLESHOOTING.md +0 -13
  37. package/skills/easysociable-content/references/VAULT.md +0 -40
  38. package/skills/easysociable-content/references/VOICE.md +0 -16
  39. package/skills/easysociable-content/scripts/package-skill.mjs +0 -49
  40. package/skills/easysociable-content/scripts/setup.mjs +0 -200
  41. package/skills/easysociable-content/scripts/setup.test.mjs +0 -82
  42. package/skills/easysociable-content/workflows/ANALYSIS_AND_COVERAGE.md +0 -17
  43. package/skills/easysociable-content/workflows/CONTENT_EXPORT.md +0 -24
  44. package/skills/easysociable-content/workflows/GETTING_STARTED.md +0 -40
  45. package/skills/easysociable-content/workflows/MCP_FIRST.md +0 -5
  46. package/skills/easysociable-content/workflows/OUT_OF_SCOPE.md +0 -18
  47. package/skills/easysociable-content/workflows/PACK_AUTHORING.md +0 -42
  48. package/skills/easysociable-content/workflows/SWIPE_CAPTURE.md +0 -18
  49. package/skills/easysociable-content/workflows/TOPIC_DRAFT.md +0 -29
  50. package/skills/easysociable-content/workflows/WORKING_DRAFT.md +0 -19
  51. /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/draft.md +0 -0
  52. /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/idea.md +0 -0
  53. /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/pack-tiktok.json +0 -0
  54. /package/{skills/easysociable-content → content-engine}/scripts/contracts.mjs +0 -0
  55. /package/{skills/easysociable-content → content-engine}/scripts/export-contracts.mjs +0 -0
  56. /package/{skills/easysociable-content → content-engine}/scripts/fixture.test.mjs +0 -0
  57. /package/{skills/easysociable-content → content-engine}/scripts/starter.mjs +0 -0
  58. /package/{skills/easysociable-content → content-engine}/scripts/validate-export.mjs +0 -0
  59. /package/{skills/easysociable-content → content-engine}/scripts/validate-pack.mjs +0 -0
  60. /package/{skills/easysociable-content → content-engine}/scripts/validate-run.mjs +0 -0
  61. /package/{skills/easysociable-content → content-engine}/scripts/validate.test.mjs +0 -0
  62. /package/{skills/easysociable-content → content-engine}/scripts/workbench.mjs +0 -0
  63. /package/{skills/easysociable-content → content-engine}/starter/CLAUDE.md +0 -0
  64. /package/{skills/easysociable-content → content-engine}/starter/accounts/README.md +0 -0
  65. /package/{skills/easysociable-content → content-engine}/starter/accounts/_index.md +0 -0
  66. /package/{skills/easysociable-content → content-engine}/starter/accounts/overrides/README.md +0 -0
  67. /package/{skills/easysociable-content → content-engine}/starter/discovery/README.md +0 -0
  68. /package/{skills/easysociable-content → content-engine}/starter/discovery/_index.md +0 -0
  69. /package/{skills/easysociable-content → content-engine}/starter/discovery/_template.md +0 -0
  70. /package/{skills/easysociable-content → content-engine}/starter/engine.json +0 -0
  71. /package/{skills/easysociable-content → content-engine}/starter/exports/README.md +0 -0
  72. /package/{skills/easysociable-content → content-engine}/starter/inbox/README.md +0 -0
  73. /package/{skills/easysociable-content → content-engine}/starter/inbox/_index.md +0 -0
  74. /package/{skills/easysociable-content → content-engine}/starter/inbox/entries/README.md +0 -0
  75. /package/{skills/easysociable-content → content-engine}/starter/platforms/README.md +0 -0
  76. /package/{skills/easysociable-content → content-engine}/starter/platforms/ig.md +0 -0
  77. /package/{skills/easysociable-content → content-engine}/starter/platforms/instagram.md +0 -0
  78. /package/{skills/easysociable-content → content-engine}/starter/platforms/linkedin.md +0 -0
  79. /package/{skills/easysociable-content → content-engine}/starter/platforms/threads.md +0 -0
  80. /package/{skills/easysociable-content → content-engine}/starter/platforms/tiktok.md +0 -0
  81. /package/{skills/easysociable-content → content-engine}/starter/platforms/x.md +0 -0
  82. /package/{skills/easysociable-content → content-engine}/starter/platforms/xiaohongshu.md +0 -0
  83. /package/{skills/easysociable-content → content-engine}/starter/profiles/_index.md +0 -0
  84. /package/{skills/easysociable-content → content-engine}/starter/profiles/default/README.md +0 -0
  85. /package/{skills/easysociable-content → content-engine}/starter/profiles/default/audience.md +0 -0
  86. /package/{skills/easysociable-content → content-engine}/starter/profiles/default/pillars.md +0 -0
  87. /package/{skills/easysociable-content → content-engine}/starter/profiles/default/voice.md +0 -0
  88. /package/{skills/easysociable-content → content-engine}/starter/published/_index.md +0 -0
  89. /package/{skills/easysociable-content → content-engine}/starter/raw/README.md +0 -0
  90. /package/{skills/easysociable-content → content-engine}/starter/raw/_index.md +0 -0
  91. /package/{skills/easysociable-content → content-engine}/starter/runs/README.md +0 -0
  92. /package/{skills/easysociable-content → content-engine}/starter/runs/_index.md +0 -0
  93. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/brief.md +0 -0
  94. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/draft.md +0 -0
  95. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/editor.md +0 -0
  96. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/feedback.md +0 -0
  97. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/idea.md +0 -0
  98. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/pack.md +0 -0
  99. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/packet.md +0 -0
  100. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/rubric.md +0 -0
  101. /package/{skills/easysociable-content → content-engine}/starter/swipe/_index.md +0 -0
  102. /package/{skills/easysociable-content → content-engine}/starter/swipe/_template.md +0 -0
  103. /package/{skills/easysociable-content → content-engine}/starter/topics/README.md +0 -0
  104. /package/{skills/easysociable-content → content-engine}/starter/topics/WATCHLIST.md +0 -0
  105. /package/{skills/easysociable-content → content-engine}/starter/topics/_index.md +0 -0
  106. /package/{skills/easysociable-content → content-engine}/starter/topics/_template-item.md +0 -0
  107. /package/{skills/easysociable-content → content-engine}/starter/topics/items/README.md +0 -0
  108. /package/{skills/easysociable-content → content-engine}/starter/topics/scoring.md +0 -0
  109. /package/{skills/easysociable-content → content-engine}/starter/topics/watchlists/README.md +0 -0
  110. /package/{skills/easysociable-content → content-engine}/starter/topics/watchlists/_shared.md +0 -0
  111. /package/{skills/easysociable-content → content-engine}/starter/topics/watchlists/default.md +0 -0
  112. /package/{skills/easysociable-content → content-engine}/starter/wiki/README.md +0 -0
  113. /package/{skills/easysociable-content → content-engine}/starter/wiki/atoms/_index.md +0 -0
  114. /package/{skills/easysociable-content → content-engine}/starter/wiki/atoms/_template.md +0 -0
  115. /package/{skills/easysociable-content → content-engine}/starter/wiki/claims/_index.md +0 -0
  116. /package/{skills/easysociable-content → content-engine}/starter/wiki/claims/_template.md +0 -0
  117. /package/{skills/easysociable-content → content-engine}/starter/workbench/README.md +0 -0
  118. /package/{skills/easysociable-content → content-engine}/workbench/build.py +0 -0
  119. /package/{skills/easysociable-content → content-engine}/workbench/serve.py +0 -0
  120. /package/{skills/easysociable-content → content-engine}/workbench/template.html +0 -0
@@ -15357,7 +15357,7 @@ function parseBrand(payload) {
15357
15357
  function parseBrandList(payload) {
15358
15358
  return parseDataEnvelope(payload, external_exports.array(brandSchema));
15359
15359
  }
15360
- var FORBIDDEN_PUBLIC_KEYS, templatePlatformSchema, canvasPresetSchema, pageRoleSchema, contentFamilySchema, contentArchetypeSchema, densityLevelSchema, visualStyleSchema, imageRoleSchema, slotSummarySchema, selectionMetadataSchema, selectionMetadataDraftSchema, listTemplatesQuerySchema, templateSummarySchema, publicTemplateSlotTypeSchema, publicTemplateSlotSchema, templateDetailSchema, templateSelectionSchema, presentationIntentSchema, renderSlotValueSchema, renderJobRequestSchema, legacyRenderJobRequestSchema, renderJobStatusSchema, renderCaptureHintSchema, renderJobViewSchema, renderPreviewMaterializationSchema, renderSelectionSummarySchema, renderManifestSchema, templateAuthoringCanvasSchema, templateAuthoringProvenanceSchema, templateImageModeSchema, templateImageNodeSchema, authoringImageNodeSchema, authoringIconNodeSchema, templateAuthoringSourceSchema, themeContrastEntrySchema, themePaletteSchema, themeTokensSchema, themeSummarySchema, themeDetailSchema, gradientTextSafeZoneSchema, gradientStopSchema, gradientSummarySchema, gradientDetailSchema, listGradientsQuerySchema, templateSurfaceKindSchema, templateSurfacePreviewSchema, templateImageProviderSchema, templateImageRightsStatusSchema, templateImageProvenanceSchema, importTemplateImageFileSchema, importTemplateImageInputSchema, importTemplateImageBodySchema, createRenderAssetInputSchema, renderAssetReferenceSchema, iconStyleSchema, templateIconLicenseSchema, searchTemplateIconsRequestSchema, templateIconCandidateSchema, getTemplateIconRequestSchema, templateIconProvenanceSchema, templateIconAssetReferenceSchema, templateIconErrorCodeSchema, templateImageReferenceSchema, themeCreateInputSchema, draftThemeInputSchema, createTemplateDraftInputSchema, updateTemplateDraftInputSchema, templateAuthoringDraftSchema, DEFAULT_AUTHORING_STUDIO_BASE_URL, templatePrincipalErrorSchema, slideshowPlatformCaps, slideshowPlatformSchema, slideshowRunStatusSchema, publicSlotValueSchema, contentPackPageSchema, contentPackSchema, slideshowRunPageSchema, brandProfilePreferenceSchema, brandProfileSchema, createBrandProfileInputSchema, updateBrandProfileInputSchema, brandTypographySchema, brandColorsSchema, brandSchema, createBrandInputSchema, updateBrandInputSchema, slideshowRunSummarySchema, slideshowRunSchema, createSlideshowRunInputSchema, updateSlideshowRunInputSchema, renderSlideshowRunInputSchema, listSlideshowRunsQuerySchema;
15360
+ var FORBIDDEN_PUBLIC_KEYS, templatePlatformSchema, canvasPresetSchema, pageRoleSchema, contentFamilySchema, contentArchetypeSchema, densityLevelSchema, visualStyleSchema, imageRoleSchema, slotSummarySchema, selectionMetadataSchema, selectionMetadataDraftSchema, listTemplatesQuerySchema, templateSummarySchema, publicTemplateSlotTypeSchema, publicTemplateSlotSchema, templateDetailSchema, templateSelectionSchema, presentationIntentSchema, renderSlotValueSchema, renderJobRequestSchema, legacyRenderJobRequestSchema, renderJobStatusSchema, renderCaptureHintSchema, renderJobViewSchema, renderPreviewMaterializationSchema, renderSelectionSummarySchema, renderManifestSchema, templateAuthoringCanvasSchema, templateAuthoringProvenanceSchema, templateImageModeSchema, templateImageNodeSchema, authoringImageNodeSchema, authoringIconNodeSchema, templateAuthoringSourceSchema, themeContrastEntrySchema, themePaletteSchema, themeTokensSchema, themeSummarySchema, themeDetailSchema, gradientTextSafeZoneSchema, gradientStopSchema, gradientSummarySchema, gradientDetailSchema, listGradientsQuerySchema, templateSurfaceKindSchema, templateSurfacePreviewSchema, templateImageProviderSchema, templateImageRightsStatusSchema, templateImageProvenanceSchema, importTemplateImageFileSchema, importTemplateImageInputSchema, importTemplateImageBodySchema, createRenderAssetInputSchema, renderAssetReferenceSchema, iconStyleSchema, templateIconLicenseSchema, searchTemplateIconsRequestSchema, templateIconCandidateSchema, getTemplateIconRequestSchema, templateIconProvenanceSchema, templateIconAssetReferenceSchema, templateIconErrorCodeSchema, templateImageReferenceSchema, themeCreateInputSchema, draftThemeInputSchema, createTemplateDraftInputSchema, updateTemplateDraftInputSchema, authoringDraftVariantSchema, templateAuthoringDraftSchema, DEFAULT_AUTHORING_STUDIO_BASE_URL, templatePrincipalErrorSchema, slideshowPlatformCaps, slideshowPlatformSchema, slideshowRunStatusSchema, publicSlotValueSchema, contentPackPageSchema, contentPackSchema, slideshowRunPageSchema, brandProfilePreferenceSchema, brandProfileSchema, createBrandProfileInputSchema, updateBrandProfileInputSchema, brandTypographySchema, brandColorsSchema, brandSchema, createBrandInputSchema, updateBrandInputSchema, slideshowRunSummarySchema, slideshowRunSchema, createSlideshowRunInputSchema, updateSlideshowRunInputSchema, renderSlideshowRunInputSchema, listSlideshowRunsQuerySchema;
15361
15361
  var init_src = __esm({
15362
15362
  "../../packages/template-public-contracts/src/index.ts"() {
15363
15363
  "use strict";
@@ -15487,8 +15487,8 @@ var init_src = __esm({
15487
15487
  notFor: external_exports.array(external_exports.string()).min(1).max(3).optional(),
15488
15488
  selectionHints: external_exports.string().optional(),
15489
15489
  visualStyle: visualStyleSchema.optional(),
15490
- defaultThemeId: external_exports.string().optional(),
15491
- compatibleThemeIds: external_exports.array(external_exports.string()).optional(),
15490
+ // ADR-0033: published selection metadata must not bind a Theme.
15491
+ // Runtime appearance comes from Brand themeSnapshot at fill/render.
15492
15492
  locale: external_exports.enum(["zh-CN", "en", "bilingual"]).optional(),
15493
15493
  exampleFilledPreviewUrl: external_exports.string().url().optional(),
15494
15494
  // P2
@@ -15630,7 +15630,11 @@ var init_src = __esm({
15630
15630
  }).strict();
15631
15631
  renderSelectionSummarySchema = external_exports.object({
15632
15632
  templateId: external_exports.string().min(1),
15633
- themeId: external_exports.string().min(1),
15633
+ /**
15634
+ * Optional audit id for the tokens used (Brand snapshot / preview fallback).
15635
+ * Not a Template→Theme publish binding (ADR-0033).
15636
+ */
15637
+ themeId: external_exports.string().min(1).optional(),
15634
15638
  role: external_exports.string().min(1),
15635
15639
  canvas: external_exports.object({
15636
15640
  width: external_exports.number().int().positive(),
@@ -15999,23 +16003,18 @@ var init_src = __esm({
15999
16003
  tokens: themeTokensSchema
16000
16004
  }).strict();
16001
16005
  draftThemeInputSchema = external_exports.object({
16006
+ /** @deprecated TemplateTheme retired — ignored; use create.tokens or tokens. */
16002
16007
  themeId: external_exports.string().min(1).optional(),
16003
- /** With themeId: fork a new Theme when tokens differ (never mutate shared themes). */
16008
+ /** Preferred: write these onto LayoutFamily.preview_tokens. */
16004
16009
  tokens: themeTokensSchema.optional(),
16005
16010
  create: themeCreateInputSchema.optional()
16006
16011
  }).strict().refine(
16007
16012
  (value) => {
16008
- const hasId = Boolean(value.themeId);
16009
16013
  const hasCreate = Boolean(value.create);
16010
- if (hasId && hasCreate)
16011
- return false;
16012
- if (!hasId && !hasCreate)
16013
- return false;
16014
- if (value.tokens && !hasId)
16015
- return false;
16016
- return true;
16014
+ const hasTokens = Boolean(value.tokens);
16015
+ return hasCreate || hasTokens;
16017
16016
  },
16018
- { message: "theme must provide themeId (optionally with tokens) or create" }
16017
+ { message: "theme must provide create.tokens or tokens for Family.preview_tokens" }
16019
16018
  );
16020
16019
  createTemplateDraftInputSchema = external_exports.object({
16021
16020
  name: external_exports.string().min(1),
@@ -16035,12 +16034,28 @@ var init_src = __esm({
16035
16034
  theme: draftThemeInputSchema.optional(),
16036
16035
  selectionMetadata: selectionMetadataDraftSchema.optional()
16037
16036
  }).strict();
16037
+ authoringDraftVariantSchema = external_exports.object({
16038
+ pageId: external_exports.string(),
16039
+ familyRole: external_exports.enum(["master", "variant"]),
16040
+ canvasPreset: external_exports.string().nullable().optional(),
16041
+ canvas: external_exports.object({
16042
+ width: external_exports.number(),
16043
+ height: external_exports.number()
16044
+ }),
16045
+ platforms: external_exports.array(external_exports.string())
16046
+ }).strict();
16038
16047
  templateAuthoringDraftSchema = external_exports.object({
16039
16048
  id: external_exports.string(),
16040
16049
  /** Equals draft id — drafts are folded into TemplatePage. */
16041
16050
  templateId: external_exports.string().optional(),
16042
16051
  /** Soft theme preview reference; tokens are not stored on the draft. */
16043
16052
  themeId: external_exports.string().optional(),
16053
+ /** Alias of themeId after Layout Family / Theme unbind. */
16054
+ previewThemeId: external_exports.string().nullable().optional(),
16055
+ familyId: external_exports.string().optional(),
16056
+ familyRole: external_exports.enum(["master", "variant"]).optional(),
16057
+ variants: external_exports.array(authoringDraftVariantSchema).optional(),
16058
+ intendedPlatforms: external_exports.array(external_exports.string()).optional(),
16044
16059
  name: external_exports.string(),
16045
16060
  status: external_exports.enum(["draft", "validated", "published", "archived", "trashed"]),
16046
16061
  revision: external_exports.number().int().positive().optional(),
@@ -16273,7 +16288,7 @@ function isRetryableStatus(status) {
16273
16288
  return status === 429 || status === 502 || status === 503 || status === 504;
16274
16289
  }
16275
16290
  function sleep(ms) {
16276
- return new Promise((resolve) => setTimeout(resolve, ms));
16291
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
16277
16292
  }
16278
16293
  var TemplateGatewayError, TemplateGatewayClient;
16279
16294
  var init_client = __esm({
@@ -16449,6 +16464,25 @@ var init_client = __esm({
16449
16464
  this.options.authoringStudioBaseUrl
16450
16465
  );
16451
16466
  }
16467
+ async getTemplateFamily(familyId) {
16468
+ const response = await this.request("GET", `/v1/template-families/${encodeURIComponent(familyId)}`);
16469
+ this.throwIfError(response.status, response.json);
16470
+ return response.json.data ?? response.json;
16471
+ }
16472
+ async deriveTemplateFamily(familyId, body = {}) {
16473
+ const response = await this.request(
16474
+ "POST",
16475
+ `/v1/template-families/${encodeURIComponent(familyId)}/derive`,
16476
+ body
16477
+ );
16478
+ this.throwIfError(response.status, response.json);
16479
+ return response.json.data ?? response.json;
16480
+ }
16481
+ async migrateLegacyTemplateFamilies() {
16482
+ const response = await this.request("POST", "/v1/template-families/migrate-legacy", {});
16483
+ this.throwIfError(response.status, response.json);
16484
+ return response.json.data;
16485
+ }
16452
16486
  async updateDraft(draftId, body) {
16453
16487
  const input = parseUpdateTemplateDraftInput(body);
16454
16488
  const response = await this.request("PUT", `/v1/template-drafts/${encodeURIComponent(draftId)}`, input);
@@ -23418,7 +23452,7 @@ var init_protocol = __esm({
23418
23452
  return;
23419
23453
  }
23420
23454
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
23421
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
23455
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
23422
23456
  options?.signal?.throwIfAborted();
23423
23457
  }
23424
23458
  } catch (error52) {
@@ -23435,7 +23469,7 @@ var init_protocol = __esm({
23435
23469
  */
23436
23470
  request(request, resultSchema, options) {
23437
23471
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
23438
- return new Promise((resolve, reject) => {
23472
+ return new Promise((resolve2, reject) => {
23439
23473
  const earlyReject = (error52) => {
23440
23474
  reject(error52);
23441
23475
  };
@@ -23513,7 +23547,7 @@ var init_protocol = __esm({
23513
23547
  if (!parseResult.success) {
23514
23548
  reject(parseResult.error);
23515
23549
  } else {
23516
- resolve(parseResult.data);
23550
+ resolve2(parseResult.data);
23517
23551
  }
23518
23552
  } catch (error52) {
23519
23553
  reject(error52);
@@ -23774,12 +23808,12 @@ var init_protocol = __esm({
23774
23808
  }
23775
23809
  } catch {
23776
23810
  }
23777
- return new Promise((resolve, reject) => {
23811
+ return new Promise((resolve2, reject) => {
23778
23812
  if (signal.aborted) {
23779
23813
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
23780
23814
  return;
23781
23815
  }
23782
- const timeoutId = setTimeout(resolve, interval);
23816
+ const timeoutId = setTimeout(resolve2, interval);
23783
23817
  signal.addEventListener("abort", () => {
23784
23818
  clearTimeout(timeoutId);
23785
23819
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -26806,7 +26840,7 @@ var require_compile = __commonJS({
26806
26840
  const schOrFunc = root.refs[ref];
26807
26841
  if (schOrFunc)
26808
26842
  return schOrFunc;
26809
- let _sch = resolve.call(this, root, ref);
26843
+ let _sch = resolve2.call(this, root, ref);
26810
26844
  if (_sch === void 0) {
26811
26845
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
26812
26846
  const { schemaId } = this.opts;
@@ -26833,7 +26867,7 @@ var require_compile = __commonJS({
26833
26867
  function sameSchemaEnv(s1, s2) {
26834
26868
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
26835
26869
  }
26836
- function resolve(root, ref) {
26870
+ function resolve2(root, ref) {
26837
26871
  let sch;
26838
26872
  while (typeof (sch = this.refs[ref]) == "string")
26839
26873
  ref = sch;
@@ -27408,7 +27442,7 @@ var require_fast_uri = __commonJS({
27408
27442
  }
27409
27443
  return uri;
27410
27444
  }
27411
- function resolve(baseURI, relativeURI, options) {
27445
+ function resolve2(baseURI, relativeURI, options) {
27412
27446
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
27413
27447
  const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
27414
27448
  schemelessOptions.skipEscape = true;
@@ -27635,7 +27669,7 @@ var require_fast_uri = __commonJS({
27635
27669
  var fastUri = {
27636
27670
  SCHEMES,
27637
27671
  normalize,
27638
- resolve,
27672
+ resolve: resolve2,
27639
27673
  resolveComponent,
27640
27674
  equal,
27641
27675
  serialize,
@@ -31534,12 +31568,12 @@ var init_stdio2 = __esm({
31534
31568
  this.onclose?.();
31535
31569
  }
31536
31570
  send(message) {
31537
- return new Promise((resolve) => {
31571
+ return new Promise((resolve2) => {
31538
31572
  const json2 = serializeMessage(message);
31539
31573
  if (this._stdout.write(json2)) {
31540
- resolve();
31574
+ resolve2();
31541
31575
  } else {
31542
- this._stdout.once("drain", resolve);
31576
+ this._stdout.once("drain", resolve2);
31543
31577
  }
31544
31578
  });
31545
31579
  }
@@ -31610,8 +31644,8 @@ async function serveStdioMcp(options) {
31610
31644
  await waitForStdioServerClose(server, process.stdin);
31611
31645
  }
31612
31646
  async function waitForStdioServerClose(server, stdin = process.stdin) {
31613
- await new Promise((resolve) => {
31614
- server.onclose = () => resolve();
31647
+ await new Promise((resolve2) => {
31648
+ server.onclose = () => resolve2();
31615
31649
  if (stdin.readableEnded || stdin.destroyed) {
31616
31650
  void server.close().catch(() => void 0);
31617
31651
  return;
@@ -32453,10 +32487,10 @@ var init_v1_mcp = __esm({
32453
32487
  // src/v1-cli.ts
32454
32488
  init_src2();
32455
32489
  import { cp, mkdtemp, readFile, rename as rename2, rm, writeFile, mkdir, chmod, copyFile } from "fs/promises";
32456
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
32490
+ import { existsSync as existsSync4, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
32457
32491
  import { createHash } from "crypto";
32458
- import { homedir as homedir2, tmpdir } from "os";
32459
- import { join as join3 } from "path";
32492
+ import { homedir as homedir3, tmpdir } from "os";
32493
+ import { join as join4 } from "path";
32460
32494
  import { Command } from "commander";
32461
32495
 
32462
32496
  // src/runtime/mcp-hosts.ts
@@ -32581,24 +32615,330 @@ function resolveDefaultSkillsSource(moduleUrl) {
32581
32615
  return bundled;
32582
32616
  }
32583
32617
 
32618
+ // src/engine.ts
32619
+ import { spawn, spawnSync } from "child_process";
32620
+ import {
32621
+ copyFileSync,
32622
+ existsSync as existsSync3,
32623
+ mkdirSync as mkdirSync2,
32624
+ readdirSync,
32625
+ readFileSync as readFileSync2,
32626
+ statSync,
32627
+ writeFileSync as writeFileSync2
32628
+ } from "fs";
32629
+ import net from "net";
32630
+ import { homedir as homedir2 } from "os";
32631
+ import { basename, dirname as dirname3, join as join3, resolve } from "path";
32632
+ var ENGINE_ROOT_NAME = "Content Engine";
32633
+ var WORKBENCH_PORT = 8765;
32634
+ var WORKBENCH_URL = `http://127.0.0.1:${WORKBENCH_PORT}/workbench/`;
32635
+ var WORKBENCH_FILES = ["template.html", "build.py", "serve.py"];
32636
+ function contentEnginePackageRoot(moduleUrl) {
32637
+ const pkg = cliPackageRoot(moduleUrl);
32638
+ const bundled = join3(pkg, "content-engine");
32639
+ if (existsSync3(join3(bundled, "starter")))
32640
+ return bundled;
32641
+ const sibling = join3(pkg, "content-engine");
32642
+ if (existsSync3(join3(sibling, "starter")))
32643
+ return sibling;
32644
+ return bundled;
32645
+ }
32646
+ function starterDir(moduleUrl) {
32647
+ return join3(contentEnginePackageRoot(moduleUrl), "starter");
32648
+ }
32649
+ function workbenchSrc(moduleUrl) {
32650
+ return join3(contentEnginePackageRoot(moduleUrl), "workbench");
32651
+ }
32652
+ function scriptsDir(moduleUrl) {
32653
+ return join3(contentEnginePackageRoot(moduleUrl), "scripts");
32654
+ }
32655
+ function isDir(path) {
32656
+ return existsSync3(path) && statSync(path).isDirectory();
32657
+ }
32658
+ function isEngine(path) {
32659
+ return isDir(path) && existsSync3(join3(path, "CLAUDE.md")) && isDir(join3(path, "runs"));
32660
+ }
32661
+ function resolveEngine(rootOrEngine) {
32662
+ const absolute = resolve(rootOrEngine);
32663
+ if (isEngine(absolute) || basename(absolute) === ENGINE_ROOT_NAME)
32664
+ return absolute;
32665
+ return join3(absolute, ENGINE_ROOT_NAME);
32666
+ }
32667
+ function walkFiles(dir) {
32668
+ const out = [];
32669
+ if (!existsSync3(dir))
32670
+ return out;
32671
+ for (const name of readdirSync(dir)) {
32672
+ if (name === ".DS_Store")
32673
+ continue;
32674
+ const path = join3(dir, name);
32675
+ if (statSync(path).isDirectory())
32676
+ out.push(...walkFiles(path));
32677
+ else
32678
+ out.push(path);
32679
+ }
32680
+ return out;
32681
+ }
32682
+ function engineJson() {
32683
+ return `${JSON.stringify({
32684
+ schemaVersion: "content-engine.v1",
32685
+ locale: "en",
32686
+ root: ENGINE_ROOT_NAME,
32687
+ paths: {
32688
+ runs: "runs",
32689
+ exports: "exports",
32690
+ topics: "topics",
32691
+ discovery: "discovery",
32692
+ swipe: "swipe",
32693
+ profiles: "profiles",
32694
+ inbox: "inbox",
32695
+ accounts: "accounts",
32696
+ raw: "raw",
32697
+ published: "published",
32698
+ wiki: "wiki",
32699
+ platforms: "platforms",
32700
+ workbench: "workbench"
32701
+ }
32702
+ }, null, 2)}
32703
+ `;
32704
+ }
32705
+ function starterFiles(moduleUrl) {
32706
+ const root = starterDir(moduleUrl);
32707
+ const files = {};
32708
+ for (const path of walkFiles(root)) {
32709
+ const relativePath = path.slice(root.length + 1).split("\\").join("/");
32710
+ files[relativePath] = readFileSync2(path, "utf8");
32711
+ }
32712
+ files["engine.json"] = engineJson();
32713
+ return files;
32714
+ }
32715
+ function inventory(engine, moduleUrl) {
32716
+ const files = starterFiles(moduleUrl);
32717
+ const missing = [];
32718
+ const existing = [];
32719
+ for (const relative of Object.keys(files)) {
32720
+ const path = join3(engine, relative);
32721
+ (existsSync3(path) ? existing : missing).push(relative);
32722
+ }
32723
+ return {
32724
+ engine,
32725
+ engineExists: isDir(engine),
32726
+ rootName: ENGINE_ROOT_NAME,
32727
+ missing,
32728
+ existing,
32729
+ starterComplete: missing.length === 0
32730
+ };
32731
+ }
32732
+ function validateEngine(rootOrEngine, moduleUrl, { strict = false } = {}) {
32733
+ const engine = resolveEngine(rootOrEngine);
32734
+ const parent = resolve(engine, "..");
32735
+ const errors = [];
32736
+ if (!existsSync3(parent) || !statSync(parent).isDirectory()) {
32737
+ errors.push("Parent directory does not exist");
32738
+ return { ...inventory(engine, moduleUrl), valid: false, errors, created: [] };
32739
+ }
32740
+ const result = inventory(engine, moduleUrl);
32741
+ if (existsSync3(engine) && !isDir(engine))
32742
+ errors.push("Content Engine exists but is not a directory");
32743
+ if (strict) {
32744
+ if (!result.engineExists)
32745
+ errors.push("Content Engine/ folder does not exist");
32746
+ else if (result.missing.length > 0)
32747
+ errors.push(`Starter incomplete: ${result.missing.length} file(s) missing (use engine init --yes to add only missing files)`);
32748
+ }
32749
+ return { ...result, valid: errors.length === 0, errors, created: [] };
32750
+ }
32751
+ function python3Bin() {
32752
+ const result = spawnSync("python3", ["--version"], { encoding: "utf8" });
32753
+ return result.status === 0 ? "python3" : "";
32754
+ }
32755
+ function ensureWorkbench(engine, moduleUrl) {
32756
+ const destDir = join3(engine, "workbench");
32757
+ mkdirSync2(destDir, { recursive: true });
32758
+ const copied = [];
32759
+ const skipped = [];
32760
+ const src = workbenchSrc(moduleUrl);
32761
+ for (const name of WORKBENCH_FILES) {
32762
+ const dest = join3(destDir, name);
32763
+ if (existsSync3(dest)) {
32764
+ skipped.push(name);
32765
+ continue;
32766
+ }
32767
+ copyFileSync(join3(src, name), dest);
32768
+ copied.push(name);
32769
+ }
32770
+ return { destDir, copied, skipped };
32771
+ }
32772
+ function buildWorkbench(engine) {
32773
+ const python = python3Bin();
32774
+ if (!python)
32775
+ return { ok: false, code: "PYTHON3_REQUIRED", message: "python3 is required for the workbench. Install Python 3 and retry." };
32776
+ const script = join3(engine, "workbench", "build.py");
32777
+ if (!existsSync3(script))
32778
+ return { ok: false, code: "WORKBENCH_MISSING", message: "workbench/build.py is missing. Re-run easysociable engine init --yes." };
32779
+ const result = spawnSync(python, [script], { encoding: "utf8", cwd: engine });
32780
+ if (result.status !== 0)
32781
+ return { ok: false, code: "WORKBENCH_BUILD_FAILED", message: result.stderr.trim() || result.stdout.trim() || "workbench build failed" };
32782
+ return { ok: true, message: result.stdout.trim() };
32783
+ }
32784
+ function portInUse(port = WORKBENCH_PORT) {
32785
+ return new Promise((resolvePort) => {
32786
+ const socket = net.connect({ port, host: "127.0.0.1" }, () => {
32787
+ socket.end();
32788
+ resolvePort(true);
32789
+ });
32790
+ socket.on("error", () => resolvePort(false));
32791
+ });
32792
+ }
32793
+ function openUrl(url2) {
32794
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
32795
+ const args = process.platform === "win32" ? ["/c", "start", url2] : [url2];
32796
+ spawn(command, args, { stdio: "ignore", detached: true }).unref();
32797
+ }
32798
+ async function startWorkbench(engine, moduleUrl, { noOpen = false } = {}) {
32799
+ const python = python3Bin();
32800
+ if (!python)
32801
+ return { ok: false, code: "PYTHON3_REQUIRED", message: "python3 is required for the workbench. Install Python 3 and retry." };
32802
+ ensureWorkbench(engine, moduleUrl);
32803
+ const built = buildWorkbench(engine);
32804
+ if (!built.ok)
32805
+ return built;
32806
+ const url2 = WORKBENCH_URL;
32807
+ if (await portInUse()) {
32808
+ if (!noOpen && process.env.EASYSOCIABLE_NO_OPEN !== "true")
32809
+ openUrl(url2);
32810
+ return { ok: true, alreadyRunning: true, url: url2, build: built.message };
32811
+ }
32812
+ const args = [join3(engine, "workbench", "serve.py")];
32813
+ if (noOpen)
32814
+ args.push("--no-open");
32815
+ const child = spawn(python, args, { cwd: engine, detached: true, stdio: "ignore" });
32816
+ child.unref();
32817
+ return { ok: true, alreadyRunning: false, url: url2, pid: child.pid, build: built.message };
32818
+ }
32819
+ async function initEngine(rootOrEngine, moduleUrl, {
32820
+ yes = false,
32821
+ noOpen = false,
32822
+ serve = false
32823
+ } = {}) {
32824
+ const engine = resolveEngine(rootOrEngine);
32825
+ if (!yes) {
32826
+ const preview = validateEngine(engine, moduleUrl);
32827
+ return {
32828
+ ...preview,
32829
+ action: "preview",
32830
+ next: "Review missing files and re-run with --yes"
32831
+ };
32832
+ }
32833
+ const parent = resolve(engine, "..");
32834
+ if (!existsSync3(parent) || !statSync(parent).isDirectory()) {
32835
+ return {
32836
+ engine,
32837
+ action: "applied",
32838
+ valid: false,
32839
+ code: "PARENT_MISSING",
32840
+ errors: ["Parent directory does not exist"],
32841
+ created: []
32842
+ };
32843
+ }
32844
+ mkdirSync2(engine, { recursive: true });
32845
+ const report = inventory(engine, moduleUrl);
32846
+ const created = [];
32847
+ const files = starterFiles(moduleUrl);
32848
+ for (const relative of report.missing) {
32849
+ const path = join3(engine, relative);
32850
+ mkdirSync2(dirname3(path), { recursive: true });
32851
+ writeFileSync2(path, files[relative], { encoding: "utf8", flag: "wx" });
32852
+ created.push(relative);
32853
+ }
32854
+ const workbench = ensureWorkbench(engine, moduleUrl);
32855
+ const python = python3Bin();
32856
+ if (!python) {
32857
+ return {
32858
+ ...validateEngine(engine, moduleUrl),
32859
+ created,
32860
+ workbench,
32861
+ action: "applied",
32862
+ valid: false,
32863
+ code: "PYTHON3_REQUIRED",
32864
+ errors: ["python3 is required for the workbench. Install Python 3 and retry. Starter files were written; workbench was not started."]
32865
+ };
32866
+ }
32867
+ const built = buildWorkbench(engine);
32868
+ const started = serve ? await startWorkbench(engine, moduleUrl, { noOpen }) : { skipped: true, build: built };
32869
+ return {
32870
+ ...validateEngine(engine, moduleUrl),
32871
+ created,
32872
+ workbench,
32873
+ workbenchBuild: built,
32874
+ workbenchStart: started,
32875
+ action: "applied"
32876
+ };
32877
+ }
32878
+ function detectEngines() {
32879
+ const extra = (process.env.EASYSOCIABLE_ENGINE_SEARCH_ROOTS || "").split(process.platform === "win32" ? ";" : ":").map((item) => item.trim()).filter(Boolean);
32880
+ const home = homedir2();
32881
+ const roots = [...extra, join3(home, "Documents"), home];
32882
+ const found = [];
32883
+ for (const root of roots) {
32884
+ if (!isDir(root))
32885
+ continue;
32886
+ const direct = join3(root, ENGINE_ROOT_NAME);
32887
+ if (isEngine(direct) || isDir(direct))
32888
+ found.push(direct);
32889
+ try {
32890
+ for (const name of readdirSync(root)) {
32891
+ const child = join3(root, name, ENGINE_ROOT_NAME);
32892
+ if (isDir(child))
32893
+ found.push(child);
32894
+ }
32895
+ } catch {
32896
+ continue;
32897
+ }
32898
+ }
32899
+ return [...new Set(found)];
32900
+ }
32901
+ function validateRun(moduleUrl, args) {
32902
+ const script = join3(scriptsDir(moduleUrl), "validate-run.mjs");
32903
+ if (!existsSync3(script))
32904
+ throw new Error(`Missing validate-run script at ${script}`);
32905
+ const argv = ["--run", args.run];
32906
+ if (args.requirePack)
32907
+ argv.push("--require-pack");
32908
+ if (args.requireExport)
32909
+ argv.push("--require-export");
32910
+ if (args.exportDir)
32911
+ argv.push("--export", args.exportDir);
32912
+ if (args.requirePageTemplateId)
32913
+ argv.push("--require-page-template-id");
32914
+ if (args.json !== false)
32915
+ argv.push("--json");
32916
+ const result = spawnSync(process.execPath, [script, ...argv], { encoding: "utf8" });
32917
+ return {
32918
+ status: result.status ?? 1,
32919
+ stdout: result.stdout,
32920
+ stderr: result.stderr
32921
+ };
32922
+ }
32923
+
32584
32924
  // src/v1-cli.ts
32585
32925
  var CLI_MODULE_URL = import.meta.url;
32586
32926
  function readCliVersion() {
32587
32927
  try {
32588
- const pkg = JSON.parse(readFileSync2(join3(cliPackageRoot(CLI_MODULE_URL), "package.json"), "utf8"));
32928
+ const pkg = JSON.parse(readFileSync3(join4(cliPackageRoot(CLI_MODULE_URL), "package.json"), "utf8"));
32589
32929
  return typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
32590
32930
  } catch {
32591
32931
  return "0.0.0";
32592
32932
  }
32593
32933
  }
32594
- var credentialPath = join3(homedir2(), ".config", "easysociable", "credentials.json");
32934
+ var credentialPath = join4(homedir3(), ".config", "easysociable", "credentials.json");
32595
32935
  function resolveInstallSkillIds(options) {
32596
32936
  return parseInstallSkills(typeof options.skills === "string" ? options.skills : void 0);
32597
32937
  }
32598
32938
  function readStoredCredentials() {
32599
32939
  try {
32600
- if (!existsSync3(credentialPath)) return null;
32601
- const parsed = JSON.parse(readFileSync2(credentialPath, "utf8"));
32940
+ if (!existsSync4(credentialPath)) return null;
32941
+ const parsed = JSON.parse(readFileSync3(credentialPath, "utf8"));
32602
32942
  if (typeof parsed.apiKey !== "string" || typeof parsed.baseUrl !== "string") return null;
32603
32943
  return { apiKey: parsed.apiKey, baseUrl: parsed.baseUrl };
32604
32944
  } catch {
@@ -32606,7 +32946,7 @@ function readStoredCredentials() {
32606
32946
  }
32607
32947
  }
32608
32948
  async function writeStoredCredentials(credentials) {
32609
- await mkdir(join3(homedir2(), ".config", "easysociable"), { recursive: true, mode: 448 });
32949
+ await mkdir(join4(homedir3(), ".config", "easysociable"), { recursive: true, mode: 448 });
32610
32950
  await writeFile(credentialPath, `${JSON.stringify(credentials, null, 2)}
32611
32951
  `, { mode: 384 });
32612
32952
  await chmod(credentialPath, 384);
@@ -32622,6 +32962,13 @@ function print(value, json2) {
32622
32962
  else process.stdout.write(`${JSON.stringify(value, null, 2)}
32623
32963
  `);
32624
32964
  }
32965
+ function commandWantsJson(command) {
32966
+ const local = command.opts();
32967
+ if (local.json === true)
32968
+ return true;
32969
+ const withGlobals = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {};
32970
+ return withGlobals.json === true;
32971
+ }
32625
32972
  function clientFromEnv() {
32626
32973
  const stored = readStoredCredentials();
32627
32974
  const allowEnv = process.env.EASYSOCIABLE_ALLOW_ENV_CREDENTIALS === "true";
@@ -32638,8 +32985,16 @@ function clientFromEnv() {
32638
32985
  function jsonOption(command) {
32639
32986
  return command.option("--json", "Emit a JSON envelope");
32640
32987
  }
32988
+ function defaultEngineRoot() {
32989
+ try {
32990
+ if (existsSync4("/workspace") && statSync2("/workspace").isDirectory())
32991
+ return "/workspace";
32992
+ } catch {
32993
+ }
32994
+ return process.cwd();
32995
+ }
32641
32996
  function installHome(options) {
32642
- return typeof options.home === "string" && options.home ? String(options.home) : homedir2();
32997
+ return typeof options.home === "string" && options.home ? String(options.home) : homedir3();
32643
32998
  }
32644
32999
  function installSource(options) {
32645
33000
  if (typeof options.source === "string" && options.source)
@@ -32650,17 +33005,17 @@ function installSource(options) {
32650
33005
  }
32651
33006
  function detectedHosts(home) {
32652
33007
  const candidates = [
32653
- { host: "codex", dir: join3(home, ".codex") },
32654
- { host: "claude", dir: join3(home, ".claude") }
33008
+ { host: "codex", dir: join4(home, ".codex") },
33009
+ { host: "claude", dir: join4(home, ".claude") }
32655
33010
  ];
32656
- return candidates.filter((item) => existsSync3(item.dir)).map((item) => ({ host: item.host, configPath: join3(item.dir, "mcp.json"), skillsPath: join3(item.dir, "skills") }));
33011
+ return candidates.filter((item) => existsSync4(item.dir)).map((item) => ({ host: item.host, configPath: join4(item.dir, "mcp.json"), skillsPath: join4(item.dir, "skills") }));
32657
33012
  }
32658
33013
  async function planInstallation(options) {
32659
33014
  const home = installHome(options);
32660
33015
  const source = installSource(options);
32661
33016
  const hosts = detectedHosts(home);
32662
33017
  const skillNames = resolveInstallSkillIds(options);
32663
- const missingSkills = skillNames.filter((name) => !existsSync3(join3(source, name)));
33018
+ const missingSkills = skillNames.filter((name) => !existsSync4(join4(source, name)));
32664
33019
  return {
32665
33020
  source,
32666
33021
  home,
@@ -32669,7 +33024,7 @@ async function planInstallation(options) {
32669
33024
  missingSkills,
32670
33025
  changes: hosts.flatMap((host) => [
32671
33026
  `merge ${host.configPath} with one easysociable MCP registration`,
32672
- ...skillNames.map((name) => `install ${join3(host.skillsPath, name)}`)
33027
+ ...skillNames.map((name) => `install ${join4(host.skillsPath, name)}`)
32673
33028
  ])
32674
33029
  };
32675
33030
  }
@@ -32685,7 +33040,7 @@ async function applyInstallation(options) {
32685
33040
  const backupSuffix = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32686
33041
  const backups = [];
32687
33042
  for (const host of detectedHosts(String(plan.home))) {
32688
- if (existsSync3(host.configPath)) {
33043
+ if (existsSync4(host.configPath)) {
32689
33044
  const backupPath = `${host.configPath}.bak.${backupSuffix}`;
32690
33045
  await copyFile(host.configPath, backupPath);
32691
33046
  backups.push(backupPath);
@@ -32693,12 +33048,12 @@ async function applyInstallation(options) {
32693
33048
  await installHostConfig({ host: host.host, dataDir: "", configPath: host.configPath });
32694
33049
  await mkdir(host.skillsPath, { recursive: true, mode: 448 });
32695
33050
  for (const skill of resolveInstallSkillIds(options))
32696
- await cp(join3(String(plan.source), skill), join3(host.skillsPath, skill), { recursive: true, force: false, errorOnExist: false });
33051
+ await cp(join4(String(plan.source), skill), join4(host.skillsPath, skill), { recursive: true, force: false, errorOnExist: false });
32697
33052
  }
32698
33053
  return { ...plan, applied: true, backups };
32699
33054
  }
32700
33055
  async function readJsonArgument(value) {
32701
- if (existsSync3(value))
33056
+ if (existsSync4(value))
32702
33057
  return JSON.parse(await readFile(value, "utf8"));
32703
33058
  return JSON.parse(value);
32704
33059
  }
@@ -32736,7 +33091,7 @@ async function exportManifest(client, jobId, output) {
32736
33091
  const job = await client.getRenderJob(jobId);
32737
33092
  if (job.status !== "completed" || !job.manifest)
32738
33093
  throw new Error(`Render job ${jobId} is not completed or has no manifest`);
32739
- const staging = await mkdtemp(join3(tmpdir(), "easysociable-export-"));
33094
+ const staging = await mkdtemp(join4(tmpdir(), "easysociable-export-"));
32740
33095
  try {
32741
33096
  for (const [index, artifact] of job.manifest.artifacts.entries()) {
32742
33097
  const response = await fetch(artifact.url);
@@ -32744,9 +33099,9 @@ async function exportManifest(client, jobId, output) {
32744
33099
  const bytes = Buffer.from(await response.arrayBuffer());
32745
33100
  const checksum = createHash("sha256").update(bytes).digest("hex");
32746
33101
  if (checksum !== artifact.sha256) throw new Error(`Artifact checksum mismatch for page ${index + 1}`);
32747
- await writeFile(join3(staging, `${String(index + 1).padStart(2, "0")}.png`), bytes);
33102
+ await writeFile(join4(staging, `${String(index + 1).padStart(2, "0")}.png`), bytes);
32748
33103
  }
32749
- await writeFile(join3(staging, "manifest.json"), `${JSON.stringify(job.manifest, null, 2)}
33104
+ await writeFile(join4(staging, "manifest.json"), `${JSON.stringify(job.manifest, null, 2)}
32750
33105
  `);
32751
33106
  await rm(output, { recursive: true, force: true });
32752
33107
  await rename2(staging, output);
@@ -32758,7 +33113,7 @@ async function exportManifest(client, jobId, output) {
32758
33113
  }
32759
33114
  async function exportSlideshowRun(client, runId, output) {
32760
33115
  const manifest = await client.getSlideshowRunManifest(runId);
32761
- const staging = await mkdtemp(join3(tmpdir(), "easysociable-slideshow-export-"));
33116
+ const staging = await mkdtemp(join4(tmpdir(), "easysociable-slideshow-export-"));
32762
33117
  try {
32763
33118
  const pages = [];
32764
33119
  for (const artifact of manifest.artifacts) {
@@ -32768,7 +33123,7 @@ async function exportSlideshowRun(client, runId, output) {
32768
33123
  throw new Error(`Artifact checksum mismatch for page ${artifact.pageIndex + 1}`);
32769
33124
  const role = artifact.role || `page${artifact.pageIndex}`;
32770
33125
  const fileName = `${String(artifact.pageIndex + 1).padStart(2, "0")}-${role}.png`;
32771
- await writeFile(join3(staging, fileName), bytes);
33126
+ await writeFile(join4(staging, fileName), bytes);
32772
33127
  pages.push({
32773
33128
  pageIndex: artifact.pageIndex,
32774
33129
  role,
@@ -32785,7 +33140,7 @@ async function exportSlideshowRun(client, runId, output) {
32785
33140
  deckJobId: manifest.deckJobId,
32786
33141
  pages
32787
33142
  };
32788
- await writeFile(join3(staging, "manifest.json"), `${JSON.stringify(localManifest, null, 2)}
33143
+ await writeFile(join4(staging, "manifest.json"), `${JSON.stringify(localManifest, null, 2)}
32789
33144
  `);
32790
33145
  await rm(output, { recursive: true, force: true });
32791
33146
  await rename2(staging, output);
@@ -32821,6 +33176,73 @@ function buildV1Cli() {
32821
33176
  const result = opts.yes === true ? await applyInstallation(opts) : { ...plan, applied: false, next: "Review changes and re-run with --yes" };
32822
33177
  print(result, Boolean(opts.json));
32823
33178
  });
33179
+ const engine = program.command("engine").description("Create and operate a local Content Engine + workbench");
33180
+ jsonOption(engine.command("detect").description("Find Content Engine folders under search roots")).action((...args) => {
33181
+ const command = args.at(-1);
33182
+ print({ action: "detect", engines: detectEngines() }, commandWantsJson(command));
33183
+ });
33184
+ jsonOption(engine.command("init").description("Scaffold Content Engine starter files (never overwrites existing)").option("--root <path>", "Parent directory or Content Engine path", defaultEngineRoot()).option("--yes", "Write missing starter files").option("--serve", "Also start the workbench server").option("--no-open", "Do not open a browser when starting the workbench")).action(async (...args) => {
33185
+ const command = args.at(-1);
33186
+ const opts = command.opts();
33187
+ const result = await initEngine(String(opts.root), CLI_MODULE_URL, {
33188
+ yes: opts.yes === true,
33189
+ noOpen: opts.noOpen === true,
33190
+ serve: opts.serve === true
33191
+ });
33192
+ print(result, commandWantsJson(command));
33193
+ if (opts.yes === true && (result.valid === false || result.code)) {
33194
+ const errors = result.errors;
33195
+ throw new Error(errors?.[0] ?? result.code ?? "engine init failed");
33196
+ }
33197
+ });
33198
+ jsonOption(engine.command("validate").description("Validate engine starter completeness or a run folder").option("--root <path>", "Parent directory or Content Engine path").option("--run <path>", "Run directory to validate").option("--require-pack", "Require pack-*.json on the run").option("--require-export", "Require export directory").option("--export <path>", "Export directory for --require-export")).action((...args) => {
33199
+ const command = args.at(-1);
33200
+ const opts = command.opts();
33201
+ if (opts.run) {
33202
+ const result = validateRun(CLI_MODULE_URL, {
33203
+ run: String(opts.run),
33204
+ requirePack: opts.requirePack === true,
33205
+ requireExport: opts.requireExport === true,
33206
+ exportDir: opts.export ? String(opts.export) : void 0,
33207
+ json: true
33208
+ });
33209
+ if (result.stdout)
33210
+ process.stdout.write(result.stdout.endsWith("\n") ? result.stdout : `${result.stdout}
33211
+ `);
33212
+ if (result.status !== 0)
33213
+ throw new Error(result.stderr.trim() || "engine validate --run failed");
33214
+ return;
33215
+ }
33216
+ if (!opts.root)
33217
+ throw new Error("Pass --root <path> or --run <path>.");
33218
+ const report = validateEngine(String(opts.root), CLI_MODULE_URL, { strict: true });
33219
+ print({ ...report, action: "validated" }, commandWantsJson(command));
33220
+ if (!report.valid)
33221
+ throw new Error(report.errors[0] ?? "engine validate failed");
33222
+ });
33223
+ jsonOption(engine.command("serve").description("Build and start the workbench on :8765").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot()).option("--no-open", "Do not open a browser")).action(async (...args) => {
33224
+ const command = args.at(-1);
33225
+ const opts = command.opts();
33226
+ const enginePath = resolveEngine(String(opts.engine));
33227
+ ensureWorkbench(enginePath, CLI_MODULE_URL);
33228
+ const built = buildWorkbench(enginePath);
33229
+ if (!built.ok)
33230
+ throw new Error(built.message);
33231
+ const started = await startWorkbench(enginePath, CLI_MODULE_URL, { noOpen: opts.noOpen === true });
33232
+ print({ action: "serve", engine: enginePath, ...started }, commandWantsJson(command));
33233
+ if (!started.ok)
33234
+ throw new Error("message" in started ? String(started.message) : "engine serve failed");
33235
+ });
33236
+ jsonOption(engine.command("build").description("Rebuild workbench/index.html from markdown indexes").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot())).action((...args) => {
33237
+ const command = args.at(-1);
33238
+ const opts = command.opts();
33239
+ const enginePath = resolveEngine(String(opts.engine));
33240
+ ensureWorkbench(enginePath, CLI_MODULE_URL);
33241
+ const built = buildWorkbench(enginePath);
33242
+ print({ action: "build", engine: enginePath, ...built }, commandWantsJson(command));
33243
+ if (!built.ok)
33244
+ throw new Error(built.message);
33245
+ });
32824
33246
  const auth = program.command("auth").description("Manage the shared EasySociable credential");
32825
33247
  jsonOption(auth.command("status").description("Show authentication status")).action(async (...args) => {
32826
33248
  const command = args.at(-1);
@@ -32860,6 +33282,13 @@ function buildV1Cli() {
32860
33282
  jsonOption(templates.command("archive").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.archiveDraft(String(opts.id))));
32861
33283
  jsonOption(templates.command("restore").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.restoreDraft(String(opts.id))));
32862
33284
  jsonOption(templates.command("delete").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.deleteDraft(String(opts.id))));
33285
+ const families = templates.command("families").description("Layout Family (multi-platform template identity)");
33286
+ jsonOption(families.command("get").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.getTemplateFamily(String(opts.id))));
33287
+ jsonOption(families.command("derive").requiredOption("--id <id>").option("--presets <list>", "Comma-separated presets e.g. 4:5,9:16")).action(withClient(async (client, opts) => {
33288
+ const presets = typeof opts.presets === "string" && opts.presets.trim() ? String(opts.presets).split(",").map((item) => item.trim()).filter(Boolean) : void 0;
33289
+ return client.deriveTemplateFamily(String(opts.id), { presets });
33290
+ }));
33291
+ jsonOption(families.command("migrate-legacy").description("Wrap pages missing familyId into single-variant families")).action(withClient(async (client) => client.migrateLegacyTemplateFamilies()));
32863
33292
  const icons = program.command("icons").description("Search the internal safe icon catalog");
32864
33293
  jsonOption(icons.command("search").requiredOption("--query <query>")).action(withClient(async (client, opts) => client.searchIcons({ query: String(opts.query) })));
32865
33294
  jsonOption(icons.command("get").requiredOption("--id <id>").requiredOption("--idempotency-key <key>")).action(withClient(async (client, opts) => client.getIcon({ iconId: String(opts.id), idempotencyKey: String(opts.idempotencyKey) })));