@easysociable/cli 0.1.10 → 0.1.12

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 CHANGED
@@ -83,9 +83,10 @@ easysociable mcp serve --transport stdio
83
83
  |---------|---------|
84
84
  | `easysociable install [--yes] [--skills list]` | Install Skills + MCP |
85
85
  | `easysociable update [--yes] [--skills list]` | Update Skills + MCP |
86
- | `easysociable templates …` | Page Template drafts / catalog |
86
+ | `easysociable templates …` | Page Template drafts / catalog (`list --platform <p> --all` for a full platform catalog) |
87
87
  | `easysociable brands …` | Workspace Brands |
88
88
  | `easysociable slideshows …` | Slideshow Runs |
89
+ | `easysociable slideshows recommend --file contracts.json --seed <slug>` | Per-page template shortlist from Page Contracts (recent families demoted, gaps reported) |
89
90
  | `easysociable renders …` | Single-page final render jobs |
90
91
  | `easysociable mcp serve --transport stdio` | Unified MCP server |
91
92
 
@@ -16297,11 +16297,13 @@ function isRetryableStatus(status) {
16297
16297
  function sleep(ms) {
16298
16298
  return new Promise((resolve3) => setTimeout(resolve3, ms));
16299
16299
  }
16300
- var TemplateGatewayError, TemplateGatewayClient;
16300
+ var DEFAULT_TEMPLATE_PAGE_SIZE, MAX_TEMPLATE_PAGE_SIZE, TemplateGatewayError, TemplateGatewayClient;
16301
16301
  var init_client = __esm({
16302
16302
  "../../packages/template-gateway-client/src/client.ts"() {
16303
16303
  "use strict";
16304
16304
  init_src();
16305
+ DEFAULT_TEMPLATE_PAGE_SIZE = 20;
16306
+ MAX_TEMPLATE_PAGE_SIZE = 50;
16305
16307
  TemplateGatewayError = class extends Error {
16306
16308
  code;
16307
16309
  retryable;
@@ -16347,6 +16349,13 @@ var init_client = __esm({
16347
16349
  return response.json;
16348
16350
  }
16349
16351
  async listTemplates(query) {
16352
+ return (await this.listTemplatesPage(query)).templates;
16353
+ }
16354
+ /**
16355
+ * Same as listTemplates plus `nextCursor` so Agents know the catalog is paged.
16356
+ * Derived client-side (full page ⇒ maybe more) to keep the strict v1 envelope.
16357
+ */
16358
+ async listTemplatesPage(query) {
16350
16359
  const parsed = parseListTemplatesQuery(query ?? {});
16351
16360
  const params = new URLSearchParams();
16352
16361
  const appendList = (key, values) => {
@@ -16375,7 +16384,23 @@ var init_client = __esm({
16375
16384
  const qs = params.toString();
16376
16385
  const response = await this.request("GET", qs ? `/v1/templates?${qs}` : "/v1/templates");
16377
16386
  this.throwIfError(response.status, response.json);
16378
- return parseTemplateSummaryList(response.json);
16387
+ const templates = parseTemplateSummaryList(response.json);
16388
+ const limit = parsed.limit ?? DEFAULT_TEMPLATE_PAGE_SIZE;
16389
+ const offset = Number.parseInt(parsed.cursor ?? "0", 10) || 0;
16390
+ return { templates, nextCursor: templates.length >= limit ? String(offset + limit) : null };
16391
+ }
16392
+ /** Drain every page for a filtered query (platform catalogs are tens of templates). */
16393
+ async listAllTemplates(query) {
16394
+ const all = [];
16395
+ let cursor;
16396
+ for (let page = 0; page < 40; page += 1) {
16397
+ const result = await this.listTemplatesPage({ ...query, limit: MAX_TEMPLATE_PAGE_SIZE, cursor });
16398
+ all.push(...result.templates);
16399
+ if (!result.nextCursor)
16400
+ return all;
16401
+ cursor = result.nextCursor;
16402
+ }
16403
+ return all;
16379
16404
  }
16380
16405
  async getTemplate(templateId) {
16381
16406
  const response = await this.request("GET", `/v1/templates/${encodeURIComponent(templateId)}`);
@@ -16543,6 +16568,12 @@ var init_client = __esm({
16543
16568
  this.throwIfError(response.status, response.json);
16544
16569
  return parseSlideshowRun(response.json);
16545
16570
  }
16571
+ /** Per-page shortlist (hard filter → fit score → recent-family penalty → explore slot). Agent still picks. */
16572
+ async recommendSlideshowTemplates(input) {
16573
+ const response = await this.request("POST", "/v1/slideshow/template-recommendations", input);
16574
+ this.throwIfError(response.status, response.json);
16575
+ return response.json.data;
16576
+ }
16546
16577
  async listSlideshowRuns(query = {}) {
16547
16578
  const input = listSlideshowRunsQuerySchema.parse(query);
16548
16579
  const params = new URLSearchParams();
@@ -31720,7 +31751,7 @@ var init_src4 = __esm({
31720
31751
  {
31721
31752
  name: "list_templates",
31722
31753
  title: "List templates",
31723
- description: "List public template catalog summaries from Gateway (filterable; no AST)",
31754
+ description: "List public template catalog summaries from Gateway (filterable; no AST). Paged: follow nextCursor, or pass all=true with platforms to get the whole platform catalog before choosing. For Slideshow page choice prefer recommend_slideshow_templates.",
31724
31755
  effect: "read-only",
31725
31756
  inputSchema: {
31726
31757
  type: "object",
@@ -31734,11 +31765,13 @@ var init_src4 = __esm({
31734
31765
  requiresImage: { type: "boolean" },
31735
31766
  locale: { type: "string" },
31736
31767
  q: { type: "string" },
31737
- limit: { type: "number" }
31768
+ limit: { type: "number" },
31769
+ cursor: { type: "string" },
31770
+ all: { type: "boolean" }
31738
31771
  },
31739
31772
  additionalProperties: false
31740
31773
  },
31741
- outputSchema: { type: "object", properties: { templates: { type: "array" } } }
31774
+ outputSchema: { type: "object", properties: { templates: { type: "array" }, nextCursor: { type: ["string", "null"] } } }
31742
31775
  },
31743
31776
  {
31744
31777
  name: "get_template",
@@ -32057,7 +32090,7 @@ var init_src4 = __esm({
32057
32090
  };
32058
32091
  try {
32059
32092
  if (toolName === "list_templates") {
32060
- const templates = await this.client.listTemplates({
32093
+ const query = {
32061
32094
  platforms: args.platforms,
32062
32095
  roles: args.roles,
32063
32096
  contentFamily: args.contentFamily,
@@ -32066,10 +32099,16 @@ var init_src4 = __esm({
32066
32099
  canvasPreset: args.canvasPreset,
32067
32100
  requiresImage: typeof args.requiresImage === "boolean" ? args.requiresImage : void 0,
32068
32101
  locale: args.locale,
32069
- q: typeof args.q === "string" ? args.q : void 0,
32070
- limit: typeof args.limit === "number" ? args.limit : void 0
32102
+ q: typeof args.q === "string" ? args.q : void 0
32103
+ };
32104
+ if (args.all === true)
32105
+ return { ok: true, data: { templates: await this.client.listAllTemplates(query), nextCursor: null }, meta: meta4 };
32106
+ const page = await this.client.listTemplatesPage({
32107
+ ...query,
32108
+ limit: typeof args.limit === "number" ? args.limit : void 0,
32109
+ cursor: typeof args.cursor === "string" ? args.cursor : void 0
32071
32110
  });
32072
- return { ok: true, data: { templates }, meta: meta4 };
32111
+ return { ok: true, data: page, meta: meta4 };
32073
32112
  }
32074
32113
  if (toolName === "get_template") {
32075
32114
  const template = await this.client.getTemplate(String(args.templateId));
@@ -32277,6 +32316,7 @@ var init_src5 = __esm({
32277
32316
  objectSchema = { type: "object", properties: {}, additionalProperties: true };
32278
32317
  slideshowToolRegistry = [
32279
32318
  { name: "list_runs", title: "List slideshow runs", description: "List owner-scoped Slideshow Runs with optional filters.", effect: "read-only", inputSchema: { ...objectSchema, properties: { q: { type: "string" }, platform: { type: "string" }, profileId: { type: "string" }, brandProfileId: { type: "string" }, contentSlug: { type: "string" }, status: { type: "string" } } }, outputSchema: objectSchema },
32319
+ { name: "recommend_templates", title: "Recommend slideshow page templates", description: "Before create_run: send the Page Contracts (role, density, slotSummary per page) and get a shortlist per page with reasons. Recently used Layout Families are demoted and one unused family is always kept. Pick one templateId per page yourself; gaps list pages needing more templates.", effect: "read-only", inputSchema: { ...objectSchema, properties: { platform: { type: "string" }, canvasPreset: { type: "string" }, pages: { type: "array", items: { type: "object", properties: { role: { type: "string" }, density: { type: "string" }, slotSummary: { type: "object" }, requiresImage: { type: "boolean" } }, required: ["role"] } }, k: { type: "integer", minimum: 2, maximum: 5 }, seed: { type: "string" } }, required: ["platform", "pages"] }, outputSchema: objectSchema },
32280
32320
  { name: "create_run", title: "Create slideshow run", description: "Create an ADR-0026 Slideshow Run from a ContentPack.", effect: "local-write-job", inputSchema: { ...objectSchema, properties: { pack: { type: "object" }, brandId: { type: "string" }, brandProfileId: { type: "string" }, contentSlug: { type: "string" } }, required: ["pack"] }, outputSchema: objectSchema },
32281
32321
  { name: "get_run", title: "Get slideshow run", description: "Get an owner-scoped Slideshow Run.", effect: "read-only", inputSchema: { ...objectSchema, properties: { runId: { type: "string" } }, required: ["runId"] }, outputSchema: objectSchema },
32282
32322
  { name: "update_run", title: "Update slideshow run", description: "Edit page-instance slots/layout/style/decorations or presentation preference with optimistic revision.", effect: "local-write-job", inputSchema: { ...objectSchema, properties: { runId: { type: "string" }, pack: { type: "object" }, brandId: { type: "string" }, brandProfileId: { type: "string" }, contentSlug: { type: "string" }, baseRevision: { type: "integer", minimum: 1 }, pageIndex: { type: "integer", minimum: 0 }, slots: { type: "object" }, layout: { type: "object" }, style: { type: "object" }, decorations: { type: "array" } }, required: ["runId"] }, outputSchema: objectSchema },
@@ -32305,6 +32345,8 @@ var init_src5 = __esm({
32305
32345
  contentSlug: args.contentSlug,
32306
32346
  status: args.status
32307
32347
  }), meta: m };
32348
+ case "recommend_templates":
32349
+ return { ok: true, data: await this.client.recommendSlideshowTemplates({ platform: String(args.platform), pages: args.pages, canvasPreset: args.canvasPreset, k: args.k, seed: args.seed }), meta: m };
32308
32350
  case "create_run":
32309
32351
  return { ok: true, data: await this.client.createSlideshowRun({ pack: args.pack, brandId: args.brandId, brandProfileId: args.brandProfileId, contentSlug: args.contentSlug }), meta: m };
32310
32352
  case "get_run":
@@ -32376,6 +32418,7 @@ var init_src6 = __esm({
32376
32418
  };
32377
32419
  slideshowNames = {
32378
32420
  list_runs: "list_slideshow_runs",
32421
+ recommend_templates: "recommend_slideshow_templates",
32379
32422
  create_run: "create_slideshow_run",
32380
32423
  get_run: "get_slideshow_run",
32381
32424
  update_run: "update_slideshow_run",
@@ -32655,7 +32698,18 @@ var ENGINE_ROOT_NAME = "Content Engine";
32655
32698
  var WORKBENCH_PORT = 8765;
32656
32699
  var WORKBENCH_URL = `http://127.0.0.1:${WORKBENCH_PORT}/workbench/`;
32657
32700
  var CONTENT_ENGINE_REPO = "https://github.com/murphywuwu/content-engine.git";
32658
- var WORKBENCH_FILES = ["template.html", "build.py", "serve.py", "roots.py"];
32701
+ var WORKBENCH_FILES = [
32702
+ "template.html",
32703
+ "build.py",
32704
+ "serve.py",
32705
+ "roots.py",
32706
+ "README.md",
32707
+ "easysociable-mark.svg",
32708
+ "css/workbench.css",
32709
+ "js/i18n.js",
32710
+ "js/utils.js",
32711
+ "js/app.js"
32712
+ ];
32659
32713
  var REQUIRED_RELATIVE = [
32660
32714
  "CLAUDE.md",
32661
32715
  "engine.json",
@@ -32875,6 +32929,7 @@ function ensureWorkbench(engine, _moduleUrl, { update = false } = {}) {
32875
32929
  if (!existsSync3(from))
32876
32930
  throw new Error(`Missing ${name} in Content Engine core checkout`);
32877
32931
  const existed = existsSync3(dest);
32932
+ mkdirSync2(dirname3(dest), { recursive: true });
32878
32933
  copyFileSync(from, dest);
32879
32934
  if (existed)
32880
32935
  updated.push(name);
@@ -33004,7 +33059,19 @@ async function initEngine(rootOrEngine, moduleUrl, {
33004
33059
  };
33005
33060
  }
33006
33061
  var CORE_REFRESH_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "LAYOUT.md", "topics/scoring.md"]);
33007
- var CORE_ADD_ONLY_FILES = /* @__PURE__ */ new Set(["media/_index.md", "media/README.md", "profiles/default/brand.md"]);
33062
+ var CORE_ADD_ONLY_FILES = /* @__PURE__ */ new Set([
33063
+ "media/_index.md",
33064
+ "media/README.md",
33065
+ "profiles/default/brand.md",
33066
+ "wiki/_index.md",
33067
+ "wiki/_unfiled.md",
33068
+ "wiki/README.md",
33069
+ "wiki/_template.md",
33070
+ "library/README.md",
33071
+ "library/swipe/_index.md",
33072
+ "library/atoms/_index.md",
33073
+ "library/claims/_index.md"
33074
+ ]);
33008
33075
  function classifyCoreFile(rel) {
33009
33076
  const parts = rel.split("/");
33010
33077
  if (parts[0] === ".git")
@@ -33164,6 +33231,21 @@ function validateRun(moduleUrl, args) {
33164
33231
  };
33165
33232
  }
33166
33233
 
33234
+ // src/studioIdentity.ts
33235
+ var FIXTURE_WORKSPACE_IDS = /* @__PURE__ */ new Set(["workspace_local"]);
33236
+ var IDENTITY_CHAIN_HINT = "Studio login \u2192 Dashboard create API key \u2192 easysociable auth login --api-key <key> \u2192 create Run \u2192 open studioUrl as the same account. See docs/operations/studio-identity-chain.md";
33237
+ async function assertHumanStudioIdentity(client, options = {}) {
33238
+ if (options.allowLocalFixture === true || process.env.EASYSOCIABLE_ALLOW_LOCAL_FIXTURE === "1")
33239
+ return;
33240
+ const envelope = await client.requestJson("GET", "/v1/commercial/me");
33241
+ const workspaceId = envelope.data?.principal?.workspaceId ?? envelope.principal?.workspaceId ?? null;
33242
+ if (typeof workspaceId === "string" && FIXTURE_WORKSPACE_IDS.has(workspaceId)) {
33243
+ throw new Error(
33244
+ `This CLI is using fixture workspace "${workspaceId}". ${IDENTITY_CHAIN_HINT}`
33245
+ );
33246
+ }
33247
+ }
33248
+
33167
33249
  // src/v1-cli.ts
33168
33250
  var CLI_MODULE_URL = import.meta.url;
33169
33251
  function readCliVersion() {
@@ -33464,7 +33546,20 @@ function buildV1Cli() {
33464
33546
  jsonOption(images.command("search").requiredOption("--q <query>")).action(withClient(async (client, opts) => client.requestJson("GET", `/v1/images?q=${encodeURIComponent(String(opts.q))}`)));
33465
33547
  jsonOption(images.command("attest").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.requestJson("POST", `/v1/images/${opts.id}/attest`, { accepted: true })));
33466
33548
  const templates = program.command("templates").description("Create and manage Page Template drafts");
33467
- jsonOption(templates.command("list")).action(withClient(async (client) => client.listTemplates()));
33549
+ jsonOption(templates.command("list").option("--platform <platform>", "tiktok | instagram | linkedin | threads | xiaohongshu").option("--canvas <preset>", "Canvas preset e.g. 9:16, 4:5").option("--role <role>", "Page role filter (weak tag)").option("--limit <n>", "Page size (max 50)").option("--cursor <cursor>", "Offset cursor from a previous page").option("--all", "Return every matching template (drains pages)")).action(withClient(async (client, opts) => {
33550
+ const query = {
33551
+ platforms: opts.platform ? [String(opts.platform)] : void 0,
33552
+ canvasPreset: opts.canvas ? String(opts.canvas) : void 0,
33553
+ roles: opts.role ? [String(opts.role)] : void 0
33554
+ };
33555
+ if (opts.all === true)
33556
+ return client.listAllTemplates(query);
33557
+ const page = await client.listTemplatesPage({ ...query, limit: opts.limit ? Number(opts.limit) : void 0, cursor: opts.cursor ? String(opts.cursor) : void 0 });
33558
+ if (page.nextCursor)
33559
+ process.stderr.write(`More templates: --cursor ${page.nextCursor} or --all
33560
+ `);
33561
+ return page.templates;
33562
+ }));
33468
33563
  jsonOption(templates.command("get").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.getTemplate(String(opts.id))));
33469
33564
  jsonOption(templates.command("create-draft").requiredOption("--file <path>")).action(withClient(async (client, opts) => client.createDraft(JSON.parse(await readFile(String(opts.file), "utf8")))));
33470
33565
  jsonOption(templates.command("update-draft").requiredOption("--id <id>").requiredOption("--file <path>")).action(withClient(async (client, opts) => client.updateDraft(String(opts.id), JSON.parse(await readFile(String(opts.file), "utf8")))));
@@ -33492,7 +33587,14 @@ function buildV1Cli() {
33492
33587
  contentSlug: opts.contentSlug ? String(opts.contentSlug) : void 0,
33493
33588
  status: opts.status ? String(opts.status) : void 0
33494
33589
  })));
33495
- jsonOption(slideshows.command("create").requiredOption("--file <path>")).action(withClient(async (client, opts) => client.createSlideshowRun({ pack: JSON.parse(await readFile(String(opts.file), "utf8")) })));
33590
+ jsonOption(slideshows.command("recommend").description("Shortlist Page Templates per page from Page Contracts JSON ({ platform, pages: [{ role, density?, slotSummary? }], seed? })").requiredOption("--file <path>").option("--seed <seed>", "Tie-break seed, e.g. the content slug")).action(withClient(async (client, opts) => {
33591
+ const input = JSON.parse(await readFile(String(opts.file), "utf8"));
33592
+ return client.recommendSlideshowTemplates(opts.seed ? { ...input, seed: String(opts.seed) } : input);
33593
+ }));
33594
+ jsonOption(slideshows.command("create").requiredOption("--file <path>").option("--allow-local-fixture", "Allow workspace_local fixture keys (dev only)")).action(withClient(async (client, opts) => {
33595
+ await assertHumanStudioIdentity(client, { allowLocalFixture: opts.allowLocalFixture === true });
33596
+ return client.createSlideshowRun({ pack: JSON.parse(await readFile(String(opts.file), "utf8")) });
33597
+ }));
33496
33598
  jsonOption(slideshows.command("get").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.getSlideshowRun(String(opts.id))));
33497
33599
  jsonOption(slideshows.command("update").requiredOption("--id <id>").requiredOption("--file <path>")).action(withClient(async (client, opts) => client.updateSlideshowRun(String(opts.id), JSON.parse(await readFile(String(opts.file), "utf8")))));
33498
33600
  jsonOption(slideshows.command("archive").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.archiveSlideshowRun(String(opts.id))));
@@ -33556,5 +33658,5 @@ async function runV1Cli(argv = process.argv) {
33556
33658
 
33557
33659
  // src/bin.ts
33558
33660
  var exitCode = await runV1Cli();
33559
- process.exit(exitCode);
33661
+ process.stdout.write("", () => process.exit(exitCode));
33560
33662
  //# sourceMappingURL=easysociable.js.map