@easysociable/cli 0.1.9 → 0.1.11

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
@@ -53,6 +53,7 @@ unless you pass `--sync-workbench` (pulls from the public core checkout /
53
53
  | Command | Purpose |
54
54
  |---------|---------|
55
55
  | `easysociable engine init [--root] [--yes] [--serve]` | Clone public core into `Content Engine/` (no overwrite) |
56
+ | `easysociable engine update [--engine] [--yes]` | Sync core rules from the public template; preview first, apply with `--yes`. Never touches vault data |
56
57
  | `easysociable engine serve [--engine] [--no-open] [--sync-workbench]` | Rebuild + start on `:8765` |
57
58
  | `easysociable engine build [--engine] [--sync-workbench]` | Rebuild `workbench/index.html` |
58
59
  | `easysociable engine detect` | Find engines under search roots |
@@ -32645,7 +32645,8 @@ import {
32645
32645
  readdirSync,
32646
32646
  readFileSync as readFileSync2,
32647
32647
  rmSync,
32648
- statSync
32648
+ statSync,
32649
+ writeFileSync as writeFileSync2
32649
32650
  } from "fs";
32650
32651
  import net from "net";
32651
32652
  import { homedir as homedir2 } from "os";
@@ -32654,7 +32655,18 @@ var ENGINE_ROOT_NAME = "Content Engine";
32654
32655
  var WORKBENCH_PORT = 8765;
32655
32656
  var WORKBENCH_URL = `http://127.0.0.1:${WORKBENCH_PORT}/workbench/`;
32656
32657
  var CONTENT_ENGINE_REPO = "https://github.com/murphywuwu/content-engine.git";
32657
- var WORKBENCH_FILES = ["template.html", "build.py", "serve.py", "roots.py"];
32658
+ var WORKBENCH_FILES = [
32659
+ "template.html",
32660
+ "build.py",
32661
+ "serve.py",
32662
+ "roots.py",
32663
+ "README.md",
32664
+ "easysociable-mark.svg",
32665
+ "css/workbench.css",
32666
+ "js/i18n.js",
32667
+ "js/utils.js",
32668
+ "js/app.js"
32669
+ ];
32658
32670
  var REQUIRED_RELATIVE = [
32659
32671
  "CLAUDE.md",
32660
32672
  "engine.json",
@@ -32874,6 +32886,7 @@ function ensureWorkbench(engine, _moduleUrl, { update = false } = {}) {
32874
32886
  if (!existsSync3(from))
32875
32887
  throw new Error(`Missing ${name} in Content Engine core checkout`);
32876
32888
  const existed = existsSync3(dest);
32889
+ mkdirSync2(dirname3(dest), { recursive: true });
32877
32890
  copyFileSync(from, dest);
32878
32891
  if (existed)
32879
32892
  updated.push(name);
@@ -33002,6 +33015,133 @@ async function initEngine(rootOrEngine, moduleUrl, {
33002
33015
  source: contentEngineRepoUrl()
33003
33016
  };
33004
33017
  }
33018
+ var CORE_REFRESH_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "LAYOUT.md", "topics/scoring.md"]);
33019
+ var CORE_ADD_ONLY_FILES = /* @__PURE__ */ new Set([
33020
+ "media/_index.md",
33021
+ "media/README.md",
33022
+ "profiles/default/brand.md",
33023
+ "wiki/_index.md",
33024
+ "wiki/_unfiled.md",
33025
+ "wiki/README.md",
33026
+ "wiki/_template.md",
33027
+ "library/README.md",
33028
+ "library/swipe/_index.md",
33029
+ "library/atoms/_index.md",
33030
+ "library/claims/_index.md"
33031
+ ]);
33032
+ function classifyCoreFile(rel) {
33033
+ const parts = rel.split("/");
33034
+ if (parts[0] === ".git")
33035
+ return "skip";
33036
+ if (CORE_ADD_ONLY_FILES.has(rel))
33037
+ return "add";
33038
+ if (CORE_REFRESH_FILES.has(rel))
33039
+ return "refresh";
33040
+ if (parts[0] === "workbench")
33041
+ return "refresh";
33042
+ if (parts[0] === "platforms" && rel.endsWith(".md"))
33043
+ return "refresh";
33044
+ if (basename(rel) === "_template.md" || parts.includes("_template"))
33045
+ return "refresh";
33046
+ return "skip";
33047
+ }
33048
+ function walkCoreFiles(core, relDir = "") {
33049
+ const out = [];
33050
+ const absDir = join3(core, relDir);
33051
+ if (!isDir(absDir))
33052
+ return out;
33053
+ for (const name of readdirSync(absDir)) {
33054
+ if (name === ".git" || name === ".DS_Store")
33055
+ continue;
33056
+ const rel = relDir ? `${relDir}/${name}` : name;
33057
+ if (isDir(join3(core, rel)))
33058
+ out.push(...walkCoreFiles(core, rel));
33059
+ else
33060
+ out.push(rel);
33061
+ }
33062
+ return out;
33063
+ }
33064
+ function mergeEngineJson(engine, core) {
33065
+ const engPath = join3(engine, "engine.json");
33066
+ const corePath = join3(core, "engine.json");
33067
+ if (!existsSync3(engPath) || !existsSync3(corePath))
33068
+ return [];
33069
+ let eng;
33070
+ let cor;
33071
+ try {
33072
+ eng = JSON.parse(readFileSync2(engPath, "utf8"));
33073
+ cor = JSON.parse(readFileSync2(corePath, "utf8"));
33074
+ } catch {
33075
+ return [];
33076
+ }
33077
+ const engPaths = eng.paths ?? (eng.paths = {});
33078
+ const corPaths = cor.paths ?? {};
33079
+ const added = [];
33080
+ for (const [key, value] of Object.entries(corPaths)) {
33081
+ if (!(key in engPaths)) {
33082
+ engPaths[key] = value;
33083
+ added.push(`paths.${key}`);
33084
+ }
33085
+ }
33086
+ if (added.length)
33087
+ writeFileSync2(engPath, `${JSON.stringify(eng, null, 2)}
33088
+ `);
33089
+ return added;
33090
+ }
33091
+ function updateEngine(rootOrEngine, moduleUrl, { yes = false } = {}) {
33092
+ const engine = resolveEngine(rootOrEngine);
33093
+ if (!isEngine(engine)) {
33094
+ return {
33095
+ engine,
33096
+ action: "preview",
33097
+ valid: false,
33098
+ code: "NOT_AN_ENGINE",
33099
+ errors: [`${engine} is not a Content Engine. Run engine init first, or pass --engine <path>.`]
33100
+ };
33101
+ }
33102
+ const core = ensureCoreCheckout({ update: true });
33103
+ const refresh = [];
33104
+ const add = [];
33105
+ for (const rel of walkCoreFiles(core)) {
33106
+ const cls = classifyCoreFile(rel);
33107
+ if (cls === "refresh")
33108
+ refresh.push(rel);
33109
+ else if (cls === "add" && !existsSync3(join3(engine, rel)))
33110
+ add.push(rel);
33111
+ }
33112
+ if (!yes) {
33113
+ return {
33114
+ engine,
33115
+ action: "preview",
33116
+ source: contentEngineRepoUrl(),
33117
+ willRefresh: refresh,
33118
+ willAdd: add,
33119
+ next: "Re-run with --yes to apply. Vault data (profiles, needs, runs, media files) is never touched."
33120
+ };
33121
+ }
33122
+ const copy = (list) => {
33123
+ for (const rel of list) {
33124
+ const dest = join3(engine, rel);
33125
+ mkdirSync2(dirname3(dest), { recursive: true });
33126
+ copyFileSync(join3(core, rel), dest);
33127
+ }
33128
+ return list;
33129
+ };
33130
+ const refreshed = copy(refresh);
33131
+ const added = copy(add);
33132
+ const enginePaths = mergeEngineJson(engine, core);
33133
+ const workbenchBuild = buildWorkbench(engine);
33134
+ return {
33135
+ ...validateEngine(engine, moduleUrl),
33136
+ engine,
33137
+ action: "applied",
33138
+ source: contentEngineRepoUrl(),
33139
+ refreshed,
33140
+ added,
33141
+ enginePaths,
33142
+ workbenchBuild
33143
+ };
33144
+ }
33005
33145
  function detectEngines() {
33006
33146
  const extra = (process.env.EASYSOCIABLE_ENGINE_SEARCH_ROOTS || "").split(process.platform === "win32" ? ";" : ":").map((item) => item.trim()).filter(Boolean);
33007
33147
  const home = homedir2();
@@ -33048,6 +33188,21 @@ function validateRun(moduleUrl, args) {
33048
33188
  };
33049
33189
  }
33050
33190
 
33191
+ // src/studioIdentity.ts
33192
+ var FIXTURE_WORKSPACE_IDS = /* @__PURE__ */ new Set(["workspace_local"]);
33193
+ 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";
33194
+ async function assertHumanStudioIdentity(client, options = {}) {
33195
+ if (options.allowLocalFixture === true || process.env.EASYSOCIABLE_ALLOW_LOCAL_FIXTURE === "1")
33196
+ return;
33197
+ const envelope = await client.requestJson("GET", "/v1/commercial/me");
33198
+ const workspaceId = envelope.data?.principal?.workspaceId ?? envelope.principal?.workspaceId ?? null;
33199
+ if (typeof workspaceId === "string" && FIXTURE_WORKSPACE_IDS.has(workspaceId)) {
33200
+ throw new Error(
33201
+ `This CLI is using fixture workspace "${workspaceId}". ${IDENTITY_CHAIN_HINT}`
33202
+ );
33203
+ }
33204
+ }
33205
+
33051
33206
  // src/v1-cli.ts
33052
33207
  var CLI_MODULE_URL = import.meta.url;
33053
33208
  function readCliVersion() {
@@ -33310,6 +33465,15 @@ function buildV1Cli() {
33310
33465
  if (!built.ok)
33311
33466
  throw new Error(built.message);
33312
33467
  });
33468
+ jsonOption(engine.command("update").description("Sync core rules from the template (CLAUDE.md, workbench, platforms, templates) and add new scaffolds; never touches your vault data. Preview first; apply with --yes.").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot()).option("--yes", "Apply the update (default is a dry-run preview)")).action((...args) => {
33469
+ const command = args.at(-1);
33470
+ const opts = command.opts();
33471
+ const result = updateEngine(String(opts.engine), CLI_MODULE_URL, { yes: opts.yes === true });
33472
+ print(result, commandWantsJson(command));
33473
+ const code = result.code;
33474
+ if (code)
33475
+ throw new Error(result.errors?.[0] ?? code);
33476
+ });
33313
33477
  const auth = program.command("auth").description("Manage the shared EasySociable credential");
33314
33478
  jsonOption(auth.command("status").description("Show authentication status")).action(async (...args) => {
33315
33479
  const command = args.at(-1);
@@ -33367,7 +33531,10 @@ function buildV1Cli() {
33367
33531
  contentSlug: opts.contentSlug ? String(opts.contentSlug) : void 0,
33368
33532
  status: opts.status ? String(opts.status) : void 0
33369
33533
  })));
33370
- jsonOption(slideshows.command("create").requiredOption("--file <path>")).action(withClient(async (client, opts) => client.createSlideshowRun({ pack: JSON.parse(await readFile(String(opts.file), "utf8")) })));
33534
+ jsonOption(slideshows.command("create").requiredOption("--file <path>").option("--allow-local-fixture", "Allow workspace_local fixture keys (dev only)")).action(withClient(async (client, opts) => {
33535
+ await assertHumanStudioIdentity(client, { allowLocalFixture: opts.allowLocalFixture === true });
33536
+ return client.createSlideshowRun({ pack: JSON.parse(await readFile(String(opts.file), "utf8")) });
33537
+ }));
33371
33538
  jsonOption(slideshows.command("get").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.getSlideshowRun(String(opts.id))));
33372
33539
  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")))));
33373
33540
  jsonOption(slideshows.command("archive").requiredOption("--id <id>")).action(withClient(async (client, opts) => client.archiveSlideshowRun(String(opts.id))));