@wix/himalaya-cli 0.1351.0 → 0.1352.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -7706,7 +7706,6 @@ function resolveTranslation(value, catalog3, sourceCatalog) {
7706
7706
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
7707
7707
  var init_bindings = __esm({
7708
7708
  "../../core/ts/src/bindings.ts"() {
7709
- "use strict";
7710
7709
  BINDING_SIGILS = ["service", "flag", "item", "t"];
7711
7710
  BINDING_SIGIL_RE = new RegExp(
7712
7711
  `^\\$(?:${BINDING_SIGILS.filter((s2) => s2 !== "t").join("|")})\\b|^\\$t:`
@@ -32826,8 +32825,15 @@ var init_app_identity = __esm({
32826
32825
  });
32827
32826
 
32828
32827
  // ../../core/ts/src/registry-app-normalize.ts
32828
+ function isPlayerRequirements(v2) {
32829
+ if (!v2 || typeof v2 !== "object") return false;
32830
+ const r2 = v2;
32831
+ return PLAYER_REQUIREMENT_KINDS.every((kind) => Array.isArray(r2[kind]) && r2[kind].every((id) => typeof id === "string"));
32832
+ }
32833
+ var PLAYER_REQUIREMENT_KINDS;
32829
32834
  var init_registry_app_normalize = __esm({
32830
32835
  "../../core/ts/src/registry-app-normalize.ts"() {
32836
+ PLAYER_REQUIREMENT_KINDS = ["capabilities", "nativeComponents", "authProviders"];
32831
32837
  }
32832
32838
  });
32833
32839
 
@@ -37418,7 +37424,7 @@ function newRicosRequestId() {
37418
37424
  async function runRicosImageUpload(o2) {
37419
37425
  const { session, uploadService: svc, requestId: requestId2, file: file2, deps } = o2;
37420
37426
  const t2 = deps.timers ?? { setTimeout: (fn2, ms2) => setTimeout(fn2, ms2), clearTimeout: (h2) => clearTimeout(h2) };
37421
- const fail4 = (reason) => {
37427
+ const fail5 = (reason) => {
37422
37428
  session.failMedia(requestId2);
37423
37429
  return { ok: false, reason };
37424
37430
  };
@@ -37444,32 +37450,32 @@ async function runRicosImageUpload(o2) {
37444
37450
  });
37445
37451
  deps.dispatch({ actionId: `tier5.${svc}.genUpload`, args: { requestId: requestId2, mimeType: file2.mimeType, fileName: file2.fileName, ...file2.sizeBytes !== void 0 ? { sizeBytes: file2.sizeBytes } : {} } });
37446
37452
  });
37447
- if (!entry) return fail4("timeout");
37448
- if (entry.status !== "ready" || !entry.uploadUrl || !/^https:\/\//i.test(entry.uploadUrl)) return fail4("gen_upload");
37453
+ if (!entry) return fail5("timeout");
37454
+ if (entry.status !== "ready" || !entry.uploadUrl || !/^https:\/\//i.test(entry.uploadUrl)) return fail5("gen_upload");
37449
37455
  let body;
37450
37456
  try {
37451
37457
  body = typeof file2.body === "function" ? await file2.body() : file2.body;
37452
37458
  } catch {
37453
- return fail4("read");
37459
+ return fail5("read");
37454
37460
  }
37455
37461
  const sep21 = entry.uploadUrl.includes("?") ? "&" : "?";
37456
37462
  let res;
37457
37463
  try {
37458
37464
  res = await deps.fetch(`${entry.uploadUrl}${sep21}filename=${encodeURIComponent(file2.fileName)}`, { method: "PUT", headers: { "Content-Type": file2.mimeType }, body });
37459
37465
  } catch {
37460
- return fail4("put");
37466
+ return fail5("put");
37461
37467
  }
37462
- if (!res.ok) return fail4("put");
37468
+ if (!res.ok) return fail5("put");
37463
37469
  let json6;
37464
37470
  try {
37465
37471
  json6 = await res.json();
37466
37472
  } catch {
37467
- return fail4("bad_descriptor");
37473
+ return fail5("bad_descriptor");
37468
37474
  }
37469
37475
  const root = json6 && typeof json6 === "object" ? json6 : {};
37470
37476
  const d2 = root.file && typeof root.file === "object" ? root.file : root;
37471
37477
  const id = typeof d2.id === "string" ? d2.id : "";
37472
- if (!id) return fail4("bad_descriptor");
37478
+ if (!id) return fail5("bad_descriptor");
37473
37479
  const ok2 = session.resolveMedia(requestId2, {
37474
37480
  id,
37475
37481
  url: typeof d2.url === "string" ? d2.url : "",
@@ -134518,7 +134524,7 @@ async function crawlScreen(opts) {
134518
134524
  async function crawlScreenJourney(opts) {
134519
134525
  return withSwallowedRejections(async () => {
134520
134526
  const findings = [];
134521
- const fail4 = (message, step) => {
134527
+ const fail5 = (message, step) => {
134522
134528
  findings.push({
134523
134529
  screen: opts.screenId,
134524
134530
  trigger: step ? { tap: step.component ?? null, key: "journey", action: step.action } : "boot",
@@ -134529,7 +134535,7 @@ async function crawlScreenJourney(opts) {
134529
134535
  });
134530
134536
  };
134531
134537
  if (!opts.bundleJs) {
134532
- fail4(`journey "${opts.journey.name}" targets a screen without a worker bundle`);
134538
+ fail5(`journey "${opts.journey.name}" targets a screen without a worker bundle`);
134533
134539
  return { screen: opts.screenId, journey: opts.journey.name, stepsExercised: 0, findings };
134534
134540
  }
134535
134541
  const { call: wixCall } = layeredTransport({ rules: opts.rules, cmsSeed: opts.cmsSeed, canned: opts.canned });
@@ -134554,23 +134560,23 @@ async function crawlScreenJourney(opts) {
134554
134560
  for (const step of opts.journey.steps) {
134555
134561
  const declared = sites.find((site2) => site2.actionId === step.action && (step.component === void 0 || site2.componentId === step.component));
134556
134562
  if (!declared) {
134557
- fail4(`journey "${opts.journey.name}" cannot find ${step.component ? `${step.component} \u2192 ` : ""}${step.action} in this screen`, step);
134563
+ fail5(`journey "${opts.journey.name}" cannot find ${step.component ? `${step.component} \u2192 ` : ""}${step.action} in this screen`, step);
134558
134564
  continue;
134559
134565
  }
134560
134566
  const state = snapshot(worker).state;
134561
134567
  const site = effectiveSiteFor(declared, state);
134562
134568
  if (!site) {
134563
- fail4(`journey "${opts.journey.name}" cannot dispatch ${step.action}: its list has no rendered row`, step);
134569
+ fail5(`journey "${opts.journey.name}" cannot dispatch ${step.action}: its list has no rendered row`, step);
134564
134570
  continue;
134565
134571
  }
134566
134572
  const parsed = parseWorkerAction(site.actionId);
134567
134573
  if (!parsed) {
134568
- fail4(`journey "${opts.journey.name}" step ${step.action} is not a worker action; cross-screen navigation belongs in the regular edge pass`, step);
134574
+ fail5(`journey "${opts.journey.name}" step ${step.action} is not a worker action; cross-screen navigation belongs in the regular edge pass`, step);
134569
134575
  continue;
134570
134576
  }
134571
134577
  const handle = worker.handles[parsed.service];
134572
134578
  if (!handle || !handle.actionNames().includes(parsed.action)) {
134573
- fail4(`journey "${opts.journey.name}" names unavailable worker action ${step.action}`, step);
134579
+ fail5(`journey "${opts.journey.name}" names unavailable worker action ${step.action}`, step);
134574
134580
  continue;
134575
134581
  }
134576
134582
  const mark = { wix: worker.calls.length, host: worker.hostCalls.length };
@@ -134579,18 +134585,18 @@ async function crawlScreenJourney(opts) {
134579
134585
  for (let i2 = 0; i2 < 8; i2++) await yieldMacrotask();
134580
134586
  stepsExercised += 1;
134581
134587
  } catch (error) {
134582
- fail4(`journey "${opts.journey.name}" step ${step.action} threw: ${errText(error)}`, step);
134588
+ fail5(`journey "${opts.journey.name}" step ${step.action} threw: ${errText(error)}`, step);
134583
134589
  }
134584
134590
  const stepEffect = effectSince(worker, mark, site.actionId, site.componentId);
134585
134591
  if (stepEffect) effects.push(stepEffect);
134586
134592
  }
134587
134593
  const settled = snapshot(worker);
134588
134594
  if (worker.errors.length) {
134589
- for (const error of worker.errors) fail4(`journey "${opts.journey.name}" worker error: ${error.service}: ${error.message}`);
134595
+ for (const error of worker.errors) fail5(`journey "${opts.journey.name}" worker error: ${error.service}: ${error.message}`);
134590
134596
  }
134591
134597
  const values = collectObservations(opts.descriptor, settled.state).join("\n");
134592
134598
  for (const expected of opts.journey.expectText) {
134593
- if (!values.includes(expected)) fail4(`journey "${opts.journey.name}" never rendered its expected visible result: ${JSON.stringify(expected)}`);
134599
+ if (!values.includes(expected)) fail5(`journey "${opts.journey.name}" never rendered its expected visible result: ${JSON.stringify(expected)}`);
134594
134600
  }
134595
134601
  return { screen: opts.screenId, journey: opts.journey.name, stepsExercised, findings, ...effects.length ? { effects } : {} };
134596
134602
  });
@@ -136054,12 +136060,14 @@ __export(build_exports2, {
136054
136060
  bakeCatalogs: () => bakeCatalogs,
136055
136061
  bakeNeedsLiveOrigin: () => bakeNeedsLiveOrigin,
136056
136062
  bakeWidgets: () => bakeWidgets,
136063
+ buildFromLoaded: () => buildFromLoaded,
136057
136064
  buildRelease: () => buildRelease,
136058
136065
  buildReleaseFromContent: () => buildReleaseFromContent,
136059
136066
  bundleNameFromUrl: () => bundleNameFromUrl,
136060
136067
  collectComponentTypes: () => collectComponentTypes,
136061
136068
  contentReleaseId: () => contentReleaseId,
136062
136069
  countingWixCall: () => countingWixCall,
136070
+ declaresHostProvidedIdentity: () => declaresHostProvidedIdentity,
136063
136071
  gitSha: () => gitSha,
136064
136072
  isMonorepoCheckout: () => isMonorepoCheckout,
136065
136073
  l10nLintOptions: () => l10nLintOptions,
@@ -137218,6 +137226,11 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
137218
137226
  nativeComponents: [...requiredNativeComponents].sort(),
137219
137227
  authProviders: [config.auth.providerId].filter(Boolean).sort()
137220
137228
  },
137229
+ // #4091 — the app takes its Wix identity from a HOST (Spaces Lite) instead of signing in
137230
+ // itself (#4088 `hostProvided` on its auth provider). A host that lends its identity only
137231
+ // opens an app that accepts it. Written only when true, so every other app's release — and
137232
+ // its releaseId — is byte-identical to one built before the field existed.
137233
+ ...declaresHostProvidedIdentity(config.auth) ? { hostProvidedIdentity: true } : {},
137221
137234
  iconHash,
137222
137235
  ...brandFonts.length ? { brandFonts } : {},
137223
137236
  handlers,
@@ -137344,6 +137357,10 @@ function listBuildableApps() {
137344
137357
  const appsDir = join47(REPO_ROOT7, "apps");
137345
137358
  return readdirSync25(appsDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && !d2.name.startsWith("_")).map((d2) => d2.name);
137346
137359
  }
137360
+ function declaresHostProvidedIdentity(auth) {
137361
+ const config = auth?.config;
137362
+ return typeof config === "object" && config !== null && config.hostProvided === true;
137363
+ }
137347
137364
  var BAKE_OFFLINE_BASE, KNOWN_ICON_NAMES, KNOWN_ICONS, REPO_ROOT7, TIER5_DIR, requireFromHere, SOURCEMAP_ON_DEMAND_PATH, BAKED_SERVER_BASE_URL;
137348
137365
  var init_build3 = __esm({
137349
137366
  "../release-build/build.ts"() {
@@ -152375,30 +152392,30 @@ function logicalCollection(spec, app) {
152375
152392
  }
152376
152393
  function assertFieldValue(type, key2, value) {
152377
152394
  if (value === null) return;
152378
- const fail4 = () => {
152395
+ const fail5 = () => {
152379
152396
  throw new Error(`field "${key2}" must be ${type}`);
152380
152397
  };
152381
152398
  if (type === "NUMBER") {
152382
- if (typeof value !== "number" || !Number.isFinite(value)) fail4();
152399
+ if (typeof value !== "number" || !Number.isFinite(value)) fail5();
152383
152400
  return;
152384
152401
  }
152385
152402
  if (type === "BOOLEAN") {
152386
- if (typeof value !== "boolean") fail4();
152403
+ if (typeof value !== "boolean") fail5();
152387
152404
  return;
152388
152405
  }
152389
152406
  if (type === "ARRAY_STRING") {
152390
- if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) fail4();
152407
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) fail5();
152391
152408
  return;
152392
152409
  }
152393
152410
  if (type === "ADDRESS" || type === "RICH_CONTENT") {
152394
- if (!value || typeof value !== "object" || Array.isArray(value)) fail4();
152411
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail5();
152395
152412
  return;
152396
152413
  }
152397
152414
  if (type === "MEDIA_GALLERY") {
152398
- if (!Array.isArray(value)) fail4();
152415
+ if (!Array.isArray(value)) fail5();
152399
152416
  return;
152400
152417
  }
152401
- if (typeof value !== "string") fail4();
152418
+ if (typeof value !== "string") fail5();
152402
152419
  }
152403
152420
  function createFunctionDataCapability(options) {
152404
152421
  const declaredKv = new Map((options.data?.kv ?? []).map((spec) => [spec.name, spec]));
@@ -155819,6 +155836,9 @@ ${releaseId}`;
155819
155836
  ...m2.publishedAt ? { publishedAt: m2.publishedAt } : {},
155820
155837
  ...publicationKind ? { publicationKind } : {},
155821
155838
  ...m2.playerRequirements ? { playerRequirements: m2.playerRequirements } : {},
155839
+ // #4091 — whether the app accepts its host's identity; Spaces opens an owner-picked
155840
+ // member app only when it does. Absent = it signs in itself (or predates the field).
155841
+ ...m2.hostProvidedIdentity === true ? { hostProvidedIdentity: true } : {},
155822
155842
  // RFC 0051 §3.6 — the release's surface envelope, for tooling that has no
155823
155843
  // source tree (hosted admin surface picker, gallery). Absent on releases
155824
155844
  // built before the envelope rode the manifest.
@@ -155939,11 +155959,11 @@ ${releaseId}`;
155939
155959
  if (pin) {
155940
155960
  const m3 = app ? await pinnedManifest(app, pin) : null;
155941
155961
  if (!m3) return json4(404, { error: "release_not_found", app, releaseId: pin });
155942
- return json4(200, { releaseId: m3.releaseId, app: m3.app, pinned: true, ...m3.minBinaryVersion ? { minBinaryVersion: m3.minBinaryVersion } : {}, ...m3.playerRequirements ? { playerRequirements: m3.playerRequirements } : {}, ...m3.platforms ? { platforms: m3.platforms } : {} });
155962
+ return json4(200, { releaseId: m3.releaseId, app: m3.app, pinned: true, ...m3.minBinaryVersion ? { minBinaryVersion: m3.minBinaryVersion } : {}, ...m3.playerRequirements ? { playerRequirements: m3.playerRequirements } : {}, ...m3.hostProvidedIdentity === true ? { hostProvidedIdentity: true } : {}, ...m3.platforms ? { platforms: m3.platforms } : {} });
155943
155963
  }
155944
155964
  const m2 = app ? await manifest(app) : null;
155945
155965
  if (!m2) return json4(404, { error: "no_release", app });
155946
- return json4(200, { releaseId: m2.releaseId, app: m2.app, ...m2.minBinaryVersion ? { minBinaryVersion: m2.minBinaryVersion } : {}, ...m2.playerRequirements ? { playerRequirements: m2.playerRequirements } : {}, ...m2.platforms ? { platforms: m2.platforms } : {} });
155966
+ return json4(200, { releaseId: m2.releaseId, app: m2.app, ...m2.minBinaryVersion ? { minBinaryVersion: m2.minBinaryVersion } : {}, ...m2.playerRequirements ? { playerRequirements: m2.playerRequirements } : {}, ...m2.hostProvidedIdentity === true ? { hostProvidedIdentity: true } : {}, ...m2.platforms ? { platforms: m2.platforms } : {} });
155947
155967
  }
155948
155968
  const bundleMatch = /^\/v1\/bundles\/([a-f0-9]{64})\.bundle\.js$/.exec(path2);
155949
155969
  if (bundleMatch) {
@@ -160575,19 +160595,19 @@ async function runWixMobileRelease(pos, flags, io2, deps) {
160575
160595
  const invokedAs = pos[0];
160576
160596
  const app = invokedAs === "testflight" ? pos[1] : pos[2];
160577
160597
  const print = (value) => io2.log(JSON.stringify(value, null, 2));
160578
- const fail4 = (error, extra = {}) => {
160598
+ const fail5 = (error, extra = {}) => {
160579
160599
  io2.error(JSON.stringify({ error, provider: "wix-mobile-release", ...extra }));
160580
160600
  return 2;
160581
160601
  };
160582
- if (invokedAs !== "testflight" && !(invokedAs === "store" && pos[1] === "upload")) return fail4("Wix Mobile Release only supports `himi testflight <app>` or `himi store upload <app>`");
160583
- if (app !== "himi-player") return fail4("Wix Mobile Release is reserved for the Wix-owned Himi Player binary", { app });
160584
- if (str5(flags.artifact)) return fail4("--artifact is not accepted for Himi Player; pass --release so deployed serve can validate the Job Service IPA and call MRE with its service identity");
160585
- if (on2(flags["no-wait"])) return fail4("--no-wait is not supported for Himi Player TestFlight releases; wait for MRE processing to complete");
160602
+ if (invokedAs !== "testflight" && !(invokedAs === "store" && pos[1] === "upload")) return fail5("Wix Mobile Release only supports `himi testflight <app>` or `himi store upload <app>`");
160603
+ if (app !== "himi-player") return fail5("Wix Mobile Release is reserved for the Wix-owned Himi Player binary", { app });
160604
+ if (str5(flags.artifact)) return fail5("--artifact is not accepted for Himi Player; pass --release so deployed serve can validate the Job Service IPA and call MRE with its service identity");
160605
+ if (on2(flags["no-wait"])) return fail5("--no-wait is not supported for Himi Player TestFlight releases; wait for MRE processing to complete");
160586
160606
  const release2 = str5(flags.release);
160587
160607
  const version4 = str5(flags.version);
160588
- if (!release2) return fail4("--release is required for a Himi Player TestFlight release");
160589
- if (!version4 || !/^\d+(?:\.\d+){0,2}$/.test(version4)) return fail4("--version needs the Player marketing version (for example 1.0.0)");
160590
- if (str5(flags.track) && str5(flags.track) !== "testflight" && str5(flags.track) !== "internal") return fail4("Wix Mobile Release only supports TestFlight upload; public App Store release remains a separate human step");
160608
+ if (!release2) return fail5("--release is required for a Himi Player TestFlight release");
160609
+ if (!version4 || !/^\d+(?:\.\d+){0,2}$/.test(version4)) return fail5("--version needs the Player marketing version (for example 1.0.0)");
160610
+ if (str5(flags.track) && str5(flags.track) !== "testflight" && str5(flags.track) !== "internal") return fail5("Wix Mobile Release only supports TestFlight upload; public App Store release remains a separate human step");
160591
160611
  const plan = {
160592
160612
  ok: true,
160593
160613
  dryRun: on2(flags["dry-run"]),
@@ -160602,9 +160622,9 @@ async function runWixMobileRelease(pos, flags, io2, deps) {
160602
160622
  print(plan);
160603
160623
  return 0;
160604
160624
  }
160605
- if (!deps.releasePlayerJob) return fail4("this Himi CLI cannot create a service-side Himi Player TestFlight job; target deployed serve");
160625
+ if (!deps.releasePlayerJob) return fail5("this Himi CLI cannot create a service-side Himi Player TestFlight job; target deployed serve");
160606
160626
  if (!on2(flags.yes)) {
160607
- if (io2.isTTY !== true) return fail4("refusing to dispatch a Wix TestFlight release without --yes in a non-interactive shell");
160627
+ if (io2.isTTY !== true) return fail5("refusing to dispatch a Wix TestFlight release without --yes in a non-interactive shell");
160608
160628
  if (!await deps.confirm(` Dispatch Himi Player ${version4} to Wix TestFlight?`)) {
160609
160629
  io2.log(" aborted \u2014 no release was dispatched.");
160610
160630
  return 1;
@@ -160613,11 +160633,11 @@ async function runWixMobileRelease(pos, flags, io2, deps) {
160613
160633
  try {
160614
160634
  const released = await deps.releasePlayerJob(release2, version4);
160615
160635
  const status = released.status.toUpperCase();
160616
- if (mreStatusKind(status) !== "completed") return fail4(`Himi Player TestFlight job ${status || "FAILED"}`, { jobId: released.jobId, ...released.requestId ? { requestId: released.requestId } : {}, ...released.error ? { error: released.error } : {} });
160636
+ if (mreStatusKind(status) !== "completed") return fail5(`Himi Player TestFlight job ${status || "FAILED"}`, { jobId: released.jobId, ...released.requestId ? { requestId: released.requestId } : {}, ...released.error ? { error: released.error } : {} });
160617
160637
  print({ ok: true, provider: "wix-mobile-release", app, jobId: released.jobId, ...released.requestId ? { requestId: released.requestId } : {}, status, ...released.url ? { url: released.url } : {} });
160618
160638
  return 0;
160619
160639
  } catch (error) {
160620
- return fail4(`could not complete the Himi Player TestFlight Job Service release: ${error.message}`);
160640
+ return fail5(`could not complete the Himi Player TestFlight Job Service release: ${error.message}`);
160621
160641
  }
160622
160642
  }
160623
160643
  var str5, on2;
@@ -160673,23 +160693,23 @@ async function runStore(pos, flags, io2, deps) {
160673
160693
  const app = invokedAs === "testflight" ? pos[1] : pos[2];
160674
160694
  const json6 = on3(flags.json);
160675
160695
  const print = (o2) => io2.log(JSON.stringify(o2, null, 2));
160676
- const fail4 = (error, extra = {}) => {
160696
+ const fail5 = (error, extra = {}) => {
160677
160697
  io2.error(JSON.stringify({ error, ...extra }));
160678
160698
  return 2;
160679
160699
  };
160680
160700
  if (!sub || !["preflight", "upload", "status"].includes(sub)) {
160681
- return fail4(`unknown store subcommand ${sub ? `"${sub}"` : ""}`.trim(), {
160701
+ return fail5(`unknown store subcommand ${sub ? `"${sub}"` : ""}`.trim(), {
160682
160702
  usage: "himi store preflight|upload|status <app> \xB7 himi testflight <app>"
160683
160703
  });
160684
160704
  }
160685
- if (!app) return fail4(`store ${sub} needs an <app>`);
160705
+ if (!app) return fail5(`store ${sub} needs an <app>`);
160686
160706
  if (flags.provider !== void 0) {
160687
- return fail4(`unknown release provider "${String(flags.provider)}"`, {
160707
+ return fail5(`unknown release provider "${String(flags.provider)}"`, {
160688
160708
  known: ["wix-mobile-release"]
160689
160709
  });
160690
160710
  }
160691
160711
  if (!deps.hasRepoCheckout()) {
160692
- return fail4(
160712
+ return fail5(
160693
160713
  "himi store needs a Himalaya monorepo checkout \u2014 it builds and signs apps/<app>'s native target, which does not exist off-repo. Content changes (screens, Tier-5 bundles, tokens, app-config) ship over the air with `himi push` and need no binary.",
160694
160714
  { app }
160695
160715
  );
@@ -160698,14 +160718,14 @@ async function runStore(pos, flags, io2, deps) {
160698
160718
  try {
160699
160719
  cfg = deps.loadStoreConfig(app);
160700
160720
  } catch (e2) {
160701
- return fail4(e2.message);
160721
+ return fail5(e2.message);
160702
160722
  }
160703
160723
  if (sub === "status") {
160704
160724
  let creds2;
160705
160725
  try {
160706
160726
  creds2 = deps.resolveCredentials({ app, cfg, profileFlag: str6(flags.profile) });
160707
160727
  } catch (e2) {
160708
- return fail4(e2.message);
160728
+ return fail5(e2.message);
160709
160729
  }
160710
160730
  try {
160711
160731
  const s2 = await deps.ascStatus(app, credsToEnv(creds2));
@@ -160718,14 +160738,14 @@ async function runStore(pos, flags, io2, deps) {
160718
160738
  }
160719
160739
  return 0;
160720
160740
  } catch (e2) {
160721
- return fail4(`could not read App Store Connect status: ${e2.message}`);
160741
+ return fail5(`could not read App Store Connect status: ${e2.message}`);
160722
160742
  }
160723
160743
  }
160724
160744
  let findings;
160725
160745
  try {
160726
160746
  findings = await deps.preflightApp(app, { profileFlag: str6(flags.profile) });
160727
160747
  } catch (e2) {
160728
- return fail4(e2.message);
160748
+ return fail5(e2.message);
160729
160749
  }
160730
160750
  const { errors, warnings } = partitionFindings(findings);
160731
160751
  if (sub === "preflight") {
@@ -160742,12 +160762,12 @@ async function runStore(pos, flags, io2, deps) {
160742
160762
  const configTrack = cfg.review?.defaultTrackIos;
160743
160763
  const track = explicitTrack ?? (invokedAs === "testflight" ? "testflight" : configTrack ?? "testflight");
160744
160764
  if (track === "appstore") {
160745
- return fail4(
160765
+ return fail5(
160746
160766
  "himi store does not submit for public App Store review \u2014 that stays a human step. Use TestFlight here, then /submit-ios-app when you mean to go public." + (explicitTrack ? "" : ` (inherited from review.defaultTrackIos; pass --track testflight to upload.)`)
160747
160767
  );
160748
160768
  }
160749
160769
  if (!TRACKS.has(track)) {
160750
- return fail4(`unknown --track "${track}"`, { known: [...TRACKS] });
160770
+ return fail5(`unknown --track "${track}"`, { known: [...TRACKS] });
160751
160771
  }
160752
160772
  const bundleId = cfg.ios?.bundleId ?? "(unset)";
160753
160773
  let creds;
@@ -160756,10 +160776,10 @@ async function runStore(pos, flags, io2, deps) {
160756
160776
  creds = deps.resolveCredentials({ app, cfg, profileFlag: str6(flags.profile) });
160757
160777
  version4 = deps.readVersion(app);
160758
160778
  } catch (e2) {
160759
- return fail4(e2.message);
160779
+ return fail5(e2.message);
160760
160780
  }
160761
160781
  if (!Number.isInteger(version4.build)) {
160762
- return fail4(
160782
+ return fail5(
160763
160783
  `${app} has a non-integer CFBundleVersion (${JSON.stringify(version4.build)}) \u2014 this command bumps and matches build numbers arithmetically. Use an integer CFBundleVersion.`,
160764
160784
  { app, marketing: version4.marketing }
160765
160785
  );
@@ -160774,7 +160794,7 @@ async function runStore(pos, flags, io2, deps) {
160774
160794
  for (const f2 of findings) io2.log(` ${f2.level === "error" ? "\u2717" : "!"} ${f2.message}`);
160775
160795
  }
160776
160796
  if (errors.length) {
160777
- return fail4(`${app} is not ready to upload \u2014 ${errors.length} blocking preflight issue(s).`, {
160797
+ return fail5(`${app} is not ready to upload \u2014 ${errors.length} blocking preflight issue(s).`, {
160778
160798
  findings: errors,
160779
160799
  hint: "himi store preflight " + app
160780
160800
  });
@@ -160805,7 +160825,7 @@ async function runStore(pos, flags, io2, deps) {
160805
160825
  }
160806
160826
  if (!on3(flags.yes)) {
160807
160827
  if (io2.isTTY !== true) {
160808
- return fail4(
160828
+ return fail5(
160809
160829
  "refusing to upload without confirmation in a non-interactive shell. Pass --yes if you really mean to publish this build, or --dry-run to see the plan.",
160810
160830
  { app, track, team: creds.team }
160811
160831
  );
@@ -160820,7 +160840,7 @@ async function runStore(pos, flags, io2, deps) {
160820
160840
  try {
160821
160841
  uploaded = reuseExisting ? version4 : deps.bumpBuild(app);
160822
160842
  } catch (e2) {
160823
- return fail4(`could not bump the build number: ${e2.message}`);
160843
+ return fail5(`could not bump the build number: ${e2.message}`);
160824
160844
  }
160825
160845
  const rollback = () => {
160826
160846
  if (reuseExisting) return;
@@ -160836,11 +160856,11 @@ async function runStore(pos, flags, io2, deps) {
160836
160856
  r2 = await deps.exec(step, env);
160837
160857
  } catch (e2) {
160838
160858
  rollback();
160839
- return fail4(`${step} failed: ${e2.message}`, { app, step, build: uploaded.build, buildNumberRestored: !reuseExisting });
160859
+ return fail5(`${step} failed: ${e2.message}`, { app, step, build: uploaded.build, buildNumberRestored: !reuseExisting });
160840
160860
  }
160841
160861
  if (r2.code !== 0) {
160842
160862
  if (step !== "upload") rollback();
160843
- return fail4(`${step} failed (exit ${r2.code})`, {
160863
+ return fail5(`${step} failed (exit ${r2.code})`, {
160844
160864
  app,
160845
160865
  step,
160846
160866
  build: uploaded.build,
@@ -160864,7 +160884,7 @@ async function runStore(pos, flags, io2, deps) {
160864
160884
  try {
160865
160885
  s2 = await deps.ascStatus(app, env);
160866
160886
  } catch (e2) {
160867
- return fail4(`uploaded build ${uploaded.build}, but could not read processing status: ${e2.message}`, {
160887
+ return fail5(`uploaded build ${uploaded.build}, but could not read processing status: ${e2.message}`, {
160868
160888
  app,
160869
160889
  build: uploaded.build,
160870
160890
  uploaded: true
@@ -160880,7 +160900,7 @@ async function runStore(pos, flags, io2, deps) {
160880
160900
  return 0;
160881
160901
  }
160882
160902
  if (mine && (mine.processingState === "FAILED" || mine.processingState === "INVALID")) {
160883
- return fail4(`build ${uploaded.build} is ${mine.processingState} in App Store Connect`, {
160903
+ return fail5(`build ${uploaded.build} is ${mine.processingState} in App Store Connect`, {
160884
160904
  app,
160885
160905
  build: uploaded.build,
160886
160906
  processingState: mine.processingState,
@@ -160888,7 +160908,7 @@ async function runStore(pos, flags, io2, deps) {
160888
160908
  });
160889
160909
  }
160890
160910
  if (waited >= deadline) {
160891
- return fail4(
160911
+ return fail5(
160892
160912
  `build ${uploaded.build} never appeared in App Store Connect after ${Math.round(deadline / 6e4)} minutes. A binary rejected during processing leaves no record and the reason is emailed, not exposed by the API \u2014 check the Apple ID inbox for team ${creds.team}.`,
160893
160913
  { app, build: uploaded.build, uploaded: true, processed: false }
160894
160914
  );
@@ -165460,7 +165480,7 @@ async function ownerPass(appId2, ports, sample) {
165460
165480
  const gaps = [];
165461
165481
  const r2 = { identity: {}, entities: [], functions: [], agents: [], connectors: [], secrets: [], routes: [], sourceFiles: [], gaps };
165462
165482
  const client2 = api(ports, appId2);
165463
- const fail4 = (chapter, e2) => {
165483
+ const fail5 = (chapter, e2) => {
165464
165484
  gaps.push({ chapter, reason: e2.message });
165465
165485
  };
165466
165486
  try {
@@ -165476,7 +165496,7 @@ async function ownerPass(appId2, ports, sample) {
165476
165496
  source: "rest"
165477
165497
  };
165478
165498
  } catch (e2) {
165479
- fail4("auth", e2);
165499
+ fail5("auth", e2);
165480
165500
  }
165481
165501
  let files = /* @__PURE__ */ new Map();
165482
165502
  if (!ports.downloadSource) {
@@ -165493,7 +165513,7 @@ async function ownerPass(appId2, ports, sample) {
165493
165513
  files = new Map(kept.map((e2) => [e2.path, e2.bytes.toString("utf8")]));
165494
165514
  r2.source = { root: "source", files: kept.length, denied: skipped.map((p2) => p2.split("\\").join("/")), secretFlagged: flagged };
165495
165515
  } catch (e2) {
165496
- fail4("source", e2);
165516
+ fail5("source", e2);
165497
165517
  } finally {
165498
165518
  dl2?.cleanup();
165499
165519
  }
@@ -165527,13 +165547,13 @@ async function ownerPass(appId2, ports, sample) {
165527
165547
  if (typeof f2.name !== "string" || r2.functions.some((x2) => x2.name === f2.name)) continue;
165528
165548
  r2.functions.push({ name: f2.name, kind: "unknown", invoke: `base44.functions.invoke(${JSON.stringify(f2.name)}, payload)`, ...typeof f2.entry === "string" ? { sourcePath: f2.entry } : {}, source: "rest" });
165529
165549
  }
165530
- } else if (!r2.functions.length) fail4("functions", fnList.reason);
165550
+ } else if (!r2.functions.length) fail5("functions", fnList.reason);
165531
165551
  if (agentList.status === "fulfilled") {
165532
165552
  for (const cfg of agentList.value.items ?? []) {
165533
165553
  const a2 = agentFromConfig(cfg, "rest");
165534
165554
  if (a2 && !r2.agents.some((x2) => x2.name === a2.name)) r2.agents.push(a2);
165535
165555
  }
165536
- } else if (!r2.agents.length) fail4("agents", agentList.reason);
165556
+ } else if (!r2.agents.length) fail5("agents", agentList.reason);
165537
165557
  if (connList.status === "fulfilled") {
165538
165558
  for (const c2 of connList.value.integrations ?? []) {
165539
165559
  const type = typeof c2.integration_type === "string" ? c2.integration_type : void 0;
@@ -165546,9 +165566,9 @@ async function ownerPass(appId2, ports, sample) {
165546
165566
  if (!existing.scopes.length) existing.scopes = scopes;
165547
165567
  } else r2.connectors.push({ type, scopes, ...status ? { status } : {}, source: "rest" });
165548
165568
  }
165549
- } else if (!r2.connectors.length) fail4("connectors", connList.reason);
165569
+ } else if (!r2.connectors.length) fail5("connectors", connList.reason);
165550
165570
  if (secretMap.status === "fulfilled") r2.secrets = Object.keys(secretMap.value ?? {}).sort();
165551
- else fail4("secrets", secretMap.reason);
165571
+ else fail5("secrets", secretMap.reason);
165552
165572
  if (sample > 0 && samples.status === "fulfilled") {
165553
165573
  const refused2 = [];
165554
165574
  samples.value.forEach((settled, i2) => {
@@ -169707,6 +169727,436 @@ var init_cli = __esm({
169707
169727
  }
169708
169728
  });
169709
169729
 
169730
+ // ../../apps/himi-spaces-lite/spaces-host-manifest.ts
169731
+ var SPACES_HOST_MANIFEST;
169732
+ var init_spaces_host_manifest = __esm({
169733
+ "../../apps/himi-spaces-lite/spaces-host-manifest.ts"() {
169734
+ SPACES_HOST_MANIFEST = {
169735
+ "version": "1.3.0",
169736
+ "capabilities": [
169737
+ "animation",
169738
+ "app_info",
169739
+ "base44",
169740
+ "browser_auth",
169741
+ "calendar",
169742
+ "clipboard",
169743
+ "file_picker",
169744
+ "files",
169745
+ "godot",
169746
+ "haptics",
169747
+ "himi_apps",
169748
+ "iap",
169749
+ "keyboard",
169750
+ "live_activity",
169751
+ "local_database",
169752
+ "media_playback",
169753
+ "motion",
169754
+ "on_device_ai",
169755
+ "open_url",
169756
+ "pdf",
169757
+ "presigned_upload",
169758
+ "push",
169759
+ "release_control",
169760
+ "secure_storage",
169761
+ "share",
169762
+ "speaker_diarization",
169763
+ "stream",
169764
+ "theme",
169765
+ "ui",
169766
+ "wix_api",
169767
+ "wix_cashier",
169768
+ "wix_platform"
169769
+ ],
169770
+ "unlinked": {
169771
+ "ios": [
169772
+ "animation",
169773
+ "base44",
169774
+ "browser_auth",
169775
+ "file_picker",
169776
+ "files",
169777
+ "godot",
169778
+ "himi_apps",
169779
+ "keyboard",
169780
+ "on_device_ai",
169781
+ "pdf",
169782
+ "presigned_upload",
169783
+ "speaker_diarization",
169784
+ "ui",
169785
+ "wix-native",
169786
+ "wix_cashier"
169787
+ ],
169788
+ "macos": [
169789
+ "animation",
169790
+ "base44",
169791
+ "browser_auth",
169792
+ "calendar",
169793
+ "file_picker",
169794
+ "files",
169795
+ "godot",
169796
+ "himi_apps",
169797
+ "iap",
169798
+ "keyboard",
169799
+ "live_activity",
169800
+ "motion",
169801
+ "on_device_ai",
169802
+ "pdf",
169803
+ "presigned_upload",
169804
+ "push",
169805
+ "speaker_diarization",
169806
+ "ui",
169807
+ "wix-native",
169808
+ "wix_cashier"
169809
+ ],
169810
+ "android": [
169811
+ "animation",
169812
+ "base44",
169813
+ "browser_auth",
169814
+ "file_picker",
169815
+ "files",
169816
+ "godot",
169817
+ "himi_apps",
169818
+ "keyboard",
169819
+ "on_device_ai",
169820
+ "pdf",
169821
+ "presigned_upload",
169822
+ "speaker_diarization",
169823
+ "ui",
169824
+ "wix-native",
169825
+ "wix_cashier"
169826
+ ],
169827
+ "web": []
169828
+ },
169829
+ "nativeComponents": [
169830
+ "himi_spaces_lite.HimiSpacesLiteCard"
169831
+ ],
169832
+ "authProviders": [
169833
+ "wix-consumer",
169834
+ "wix-native"
169835
+ ],
169836
+ "nativeActions": [
169837
+ "himi_spaces_lite.greet"
169838
+ ]
169839
+ };
169840
+ }
169841
+ });
169842
+
169843
+ // ../../core/ts/src/host-compatibility.ts
169844
+ function compareVersion(a2, b2) {
169845
+ const parts = (v2) => v2.split("-")[0].split(".").map((p2) => /^\d+$/.test(p2) ? Number(p2) : 0);
169846
+ const av = parts(a2);
169847
+ const bv = parts(b2);
169848
+ for (let i2 = 0; i2 < Math.max(av.length, bv.length); i2 += 1) {
169849
+ if ((av[i2] ?? 0) !== (bv[i2] ?? 0)) return (av[i2] ?? 0) - (bv[i2] ?? 0);
169850
+ }
169851
+ return 0;
169852
+ }
169853
+ function claimsFor(host, shell) {
169854
+ const gaps = shell && host.unlinked[shell] ? host.unlinked[shell] : [...host.unlinked.ios ?? [], ...host.unlinked.android ?? []];
169855
+ const served = (ids) => ids.filter((id) => !gaps.includes(id));
169856
+ return { capabilities: served(host.capabilities), nativeComponents: served(host.nativeComponents), authProviders: served(host.authProviders) };
169857
+ }
169858
+ function missingClaims(requirements, offered) {
169859
+ const out2 = {};
169860
+ for (const kind of HOST_CLAIM_KINDS) {
169861
+ const has = new Set(offered[kind]);
169862
+ out2[kind] = [...new Set(requirements[kind].filter((id) => !has.has(id)))];
169863
+ }
169864
+ return out2;
169865
+ }
169866
+ function missingReasons(missing, hostName = "the host") {
169867
+ const reasons = [];
169868
+ if (missing.capabilities.length) reasons.push(`Missing capabilities: ${missing.capabilities.join(", ")}`);
169869
+ if (missing.nativeComponents.length) reasons.push(`Native components not compiled into ${hostName}: ${missing.nativeComponents.join(", ")}`);
169870
+ if (missing.authProviders.length) reasons.push(`Unsupported auth providers: ${missing.authProviders.join(", ")}`);
169871
+ return reasons;
169872
+ }
169873
+ function checkHostCompatibility(requirements, host, options = {}) {
169874
+ const hostName = options.hostName ?? "the host";
169875
+ const reasons = missingReasons(missingClaims(requirements, claimsFor(host, options.shell)), hostName);
169876
+ const running = options.installedVersion || host.version;
169877
+ if (requirements.minBinaryVersion && compareVersion(running, requirements.minBinaryVersion) < 0) {
169878
+ reasons.push(`Requires ${hostName} ${requirements.minBinaryVersion} or newer (installed ${running})`);
169879
+ }
169880
+ if (options.requiresHostIdentity && !requirements.hostProvidedIdentity) {
169881
+ reasons.push(`Does not accept ${hostName}'s sign-in: the app must take its identity from the host (hostProvided)`);
169882
+ }
169883
+ return reasons.length ? { ok: false, reason: reasons.join("; "), reasons } : { ok: true };
169884
+ }
169885
+ var HOST_CLAIM_KINDS;
169886
+ var init_host_compatibility = __esm({
169887
+ "../../core/ts/src/host-compatibility.ts"() {
169888
+ init_registry_app_normalize();
169889
+ HOST_CLAIM_KINDS = PLAYER_REQUIREMENT_KINDS;
169890
+ }
169891
+ });
169892
+
169893
+ // ../../core/dev-server/tier5-bundles-src/_shared/branded-lite-hosted.ts
169894
+ var VERTICAL_ROOT_SCREENS, VERTICALS;
169895
+ var init_branded_lite_hosted = __esm({
169896
+ "../../core/dev-server/tier5-bundles-src/_shared/branded-lite-hosted.ts"() {
169897
+ VERTICAL_ROOT_SCREENS = {
169898
+ store: ["himi_shop"],
169899
+ bookings: ["himi_services"],
169900
+ restaurants: ["himi_menu", "himi_reserve"],
169901
+ programs: ["himi_programs"],
169902
+ plans: ["himi_plans"],
169903
+ feed: ["himi_feed"],
169904
+ members: ["himi_members"],
169905
+ forum: ["himi_forum"],
169906
+ coupons: ["himi_coupons"],
169907
+ notifications: ["himi_notifications"],
169908
+ loyalty: ["himi_loyalty"],
169909
+ reviews: ["himi_reviews"],
169910
+ portfolio: ["himi_portfolio"],
169911
+ reservations: ["himi_reservations"],
169912
+ invoices: ["himi_invoices"],
169913
+ badges: ["himi_badges"],
169914
+ notifsettings: ["himi_notif_settings"],
169915
+ events: ["himi_events"],
169916
+ blog: ["himi_blog"],
169917
+ groups: ["himi_groups"],
169918
+ cms: ["himi_cms"],
169919
+ donations: ["himi_donations"],
169920
+ giftCards: ["himi_gift_cards"],
169921
+ videos: ["himi_videos"],
169922
+ podcasts: ["himi_podcasts"],
169923
+ forms: ["himi_forms"],
169924
+ galleries: ["himi_galleries"],
169925
+ inbox: ["himi_inbox"],
169926
+ faq: ["himi_faq"]
169927
+ };
169928
+ VERTICALS = new Set(Object.keys(VERTICAL_ROOT_SCREENS));
169929
+ }
169930
+ });
169931
+
169932
+ // ../../apps/himi-branded-lite/hosted-mode.ts
169933
+ var init_hosted_mode = __esm({
169934
+ "../../apps/himi-branded-lite/hosted-mode.ts"() {
169935
+ init_branded_lite_hosted();
169936
+ }
169937
+ });
169938
+
169939
+ // ../../apps/himi-branded-lite/site-provisioning.ts
169940
+ var init_site_provisioning = __esm({
169941
+ "../../apps/himi-branded-lite/site-provisioning.ts"() {
169942
+ }
169943
+ });
169944
+
169945
+ // ../../apps/himi-spaces-lite/site-open.ts
169946
+ var HOSTED_MEMBER_APP;
169947
+ var init_site_open = __esm({
169948
+ "../../apps/himi-spaces-lite/site-open.ts"() {
169949
+ init_hosted_mode();
169950
+ init_site_provisioning();
169951
+ HOSTED_MEMBER_APP = "himi-branded-lite";
169952
+ }
169953
+ });
169954
+
169955
+ // ../../apps/himi-spaces-lite/site-app.ts
169956
+ function siteAppPickFrom(value) {
169957
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
169958
+ const appId2 = value.appId;
169959
+ if (appId2 === null) return null;
169960
+ if (typeof appId2 !== "string") return void 0;
169961
+ const id = appId2.trim();
169962
+ return APP_ID3.test(id) ? id : void 0;
169963
+ }
169964
+ function siteAppRequirementsFrom(appId2, entry) {
169965
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return { ok: false, reason: `No release of ${appId2} to judge` };
169966
+ const e2 = entry;
169967
+ const names = e2.id !== void 0 ? e2.id : e2.app;
169968
+ if (names !== appId2) return { ok: false, reason: `No release of ${appId2} to judge` };
169969
+ if (!isPlayerRequirements(e2.playerRequirements)) {
169970
+ return { ok: false, reason: e2.playerRequirements === void 0 ? `${appId2}'s release does not declare what it requires (republish it)` : `${appId2}'s release declares malformed requirements` };
169971
+ }
169972
+ const minBinaryVersion = typeof e2.minBinaryVersion === "string" && e2.minBinaryVersion ? e2.minBinaryVersion : void 0;
169973
+ return {
169974
+ ok: true,
169975
+ requirements: {
169976
+ capabilities: [...e2.playerRequirements.capabilities],
169977
+ nativeComponents: [...e2.playerRequirements.nativeComponents],
169978
+ authProviders: [...e2.playerRequirements.authProviders],
169979
+ ...minBinaryVersion ? { minBinaryVersion } : {},
169980
+ // Only an explicit `true` counts: an app that signs in by itself would ask a member to sign
169981
+ // in again inside Spaces, with an identity Spaces does not lend (#4088).
169982
+ ...e2.hostProvidedIdentity === true ? { hostProvidedIdentity: true } : {}
169983
+ }
169984
+ };
169985
+ }
169986
+ function messageOf2(err) {
169987
+ const message = err?.message;
169988
+ return typeof message === "string" && message ? message : String(err);
169989
+ }
169990
+ function decideSiteApp(input) {
169991
+ const fallback = (pickedAppId, reason) => ({ appId: DEFAULT_SITE_APP, source: "fallback", pickedAppId, reason });
169992
+ if (input.pick === void 0) return { appId: DEFAULT_SITE_APP, source: "default" };
169993
+ if (!input.pick.ok) return fallback(null, `Could not read the site's member app: ${messageOf2(input.pick.error)}`);
169994
+ const picked = siteAppPickFrom(input.pick.value);
169995
+ if (picked === void 0) return fallback(null, "The site's member app setting is unreadable");
169996
+ if (picked === null || picked === DEFAULT_SITE_APP) return { appId: DEFAULT_SITE_APP, source: "default" };
169997
+ if (input.release === void 0) return fallback(picked, `No release of ${picked} to judge`);
169998
+ if (!input.release.ok) return fallback(picked, `Could not read ${picked}'s release: ${messageOf2(input.release.error)}`);
169999
+ const req = siteAppRequirementsFrom(picked, input.release.value);
170000
+ if (!req.ok) return fallback(picked, req.reason);
170001
+ const verdict = checkHostCompatibility(req.requirements, input.host, {
170002
+ hostName: "Spaces",
170003
+ requiresHostIdentity: true,
170004
+ ...input.shell ? { shell: input.shell } : {},
170005
+ ...input.installedVersion ? { installedVersion: input.installedVersion } : {}
170006
+ });
170007
+ if (!verdict.ok) return fallback(picked, verdict.reason);
170008
+ if (!(input.hostable ?? NATIVELY_HOSTABLE_APPS).includes(picked)) {
170009
+ return fallback(picked, `This Spaces build cannot host a custom member app yet (${picked} is compatible)`);
170010
+ }
170011
+ return { appId: picked, source: "picked" };
170012
+ }
170013
+ var DEFAULT_SITE_APP, NATIVELY_HOSTABLE_APPS, APP_ID3;
170014
+ var init_site_app = __esm({
170015
+ "../../apps/himi-spaces-lite/site-app.ts"() {
170016
+ init_host_compatibility();
170017
+ init_registry_app_normalize();
170018
+ init_site_open();
170019
+ DEFAULT_SITE_APP = HOSTED_MEMBER_APP;
170020
+ NATIVELY_HOSTABLE_APPS = [DEFAULT_SITE_APP];
170021
+ APP_ID3 = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
170022
+ }
170023
+ });
170024
+
170025
+ // src/site-member-app.ts
170026
+ var site_member_app_exports = {};
170027
+ __export(site_member_app_exports, {
170028
+ SITE_MEMBER_APP_CODES: () => SITE_MEMBER_APP_CODES,
170029
+ SITE_MEMBER_APP_USAGE: () => SITE_MEMBER_APP_USAGE,
170030
+ SPACES_DEFAULT_APP: () => SPACES_DEFAULT_APP,
170031
+ memberAppVerdict: () => memberAppVerdict,
170032
+ runSiteMemberApp: () => runSiteMemberApp
170033
+ });
170034
+ function codeFor(status, serverError) {
170035
+ if (serverError === "forbidden_site" || serverError === "forbidden_app" || serverError === "app_bound_to_other_site") return serverError;
170036
+ if (status === 401) return "unauthorized";
170037
+ return "request_failed";
170038
+ }
170039
+ function memberAppVerdict(appId2, release2, shell) {
170040
+ if (!appId2 || appId2 === SPACES_DEFAULT_APP) return { opens: SPACES_DEFAULT_APP, compatible: null };
170041
+ const input = {
170042
+ pick: { ok: true, value: { appId: appId2 } },
170043
+ ...release2 !== void 0 ? { release: { ok: true, value: release2 } } : {},
170044
+ host: SPACES_HOST_MANIFEST,
170045
+ ...shell ? { shell } : {}
170046
+ };
170047
+ const fits = decideSiteApp({ ...input, hostable: [SPACES_DEFAULT_APP, appId2] });
170048
+ const opens = decideSiteApp(input);
170049
+ return {
170050
+ opens: opens.appId,
170051
+ compatible: fits.source === "picked",
170052
+ ...opens.source === "fallback" ? { reason: opens.reason } : {}
170053
+ };
170054
+ }
170055
+ async function runSiteMemberApp(input, deps = {}) {
170056
+ const op = input.op;
170057
+ if (op !== "set" && op !== "clear" && op !== "show") {
170058
+ return fail4("invalid_usage", op ? `unknown site member-app op "${op}"` : "site member-app needs an op: set | clear | show", { usage: SITE_MEMBER_APP_USAGE });
170059
+ }
170060
+ const site = input.site;
170061
+ if (!site || !SITE_RE2.test(site)) return fail4("invalid_usage", "a metaSiteId (UUID) is required", { usage: SITE_MEMBER_APP_USAGE });
170062
+ if (op === "set" && !input.app) return fail4("invalid_usage", "set needs --app <id>", { usage: SITE_MEMBER_APP_USAGE });
170063
+ if (op === "set" && !isValidAppId(input.app)) return fail4("invalid_usage", `--app "${input.app}" is not a valid app id`, { usage: SITE_MEMBER_APP_USAGE });
170064
+ if (input.shell !== void 0 && input.shell !== "ios" && input.shell !== "android") {
170065
+ return fail4("invalid_usage", `--platform must be ios or android (got "${input.shell}")`, { usage: SITE_MEMBER_APP_USAGE });
170066
+ }
170067
+ let target;
170068
+ try {
170069
+ target = (deps.resolve ?? resolveTarget)({ target: input.target, defaultTarget: input.defaultTarget, controlUrl: input.controlUrl, controlToken: input.controlToken });
170070
+ } catch (err) {
170071
+ return fail4("invalid_usage", err.message);
170072
+ }
170073
+ const insecure = credentialHostRefusal(target.control);
170074
+ if (insecure) return fail4("insecure_target", insecure);
170075
+ if (!target.controlToken) return fail4("control_token_required", "no control token \u2014 run `himi login` (or pass --control-token)");
170076
+ let wixToken;
170077
+ try {
170078
+ wixToken = await (deps.wixToken ?? accountToken)();
170079
+ } catch {
170080
+ return fail4("wix_token_unavailable", "could not mint the account's Wix token (`wix token`) \u2014 the publishing service needs it to verify you own the site");
170081
+ }
170082
+ const fetchImpl = deps.fetchImpl ?? fetch;
170083
+ const control = target.control.replace(/\/+$/, "");
170084
+ const url = `${control}/v1/admin/site/member-app${op === "set" ? "" : `?site=${encodeURIComponent(site)}`}`;
170085
+ let status;
170086
+ let body;
170087
+ try {
170088
+ const response = await fetchImpl(url, {
170089
+ method: op === "set" ? "PUT" : op === "clear" ? "DELETE" : "GET",
170090
+ redirect: "error",
170091
+ signal: AbortSignal.timeout(3e4),
170092
+ headers: {
170093
+ "content-type": "application/json",
170094
+ "x-control-token": target.controlToken,
170095
+ Authorization: wixToken,
170096
+ ...target.previewRoute ? { "x-wix-route-serve": target.previewRoute } : {}
170097
+ },
170098
+ ...op === "set" ? { body: JSON.stringify({ site, app: input.app }) } : {}
170099
+ });
170100
+ status = response.status;
170101
+ body = await response.json().catch(() => ({}));
170102
+ } catch {
170103
+ return fail4("control_unreachable", `could not reach the publishing service at ${control}`);
170104
+ }
170105
+ if (status < 200 || status >= 300) {
170106
+ return fail4(
170107
+ codeFor(status, body.error),
170108
+ typeof body.error === "string" ? body.error : `HTTP ${status}`,
170109
+ { status, ...typeof body.error === "string" ? { serverError: body.error } : {} }
170110
+ );
170111
+ }
170112
+ if (op !== "show") return { ok: true, ...body };
170113
+ const pick2 = body.memberAppInSpaces;
170114
+ const appId2 = typeof pick2?.appId === "string" ? pick2.appId : null;
170115
+ let release2;
170116
+ if (appId2 && appId2 !== SPACES_DEFAULT_APP && !body.invalid) {
170117
+ try {
170118
+ const response = await fetchImpl(`${target.serve.replace(/\/+$/, "")}/v1/release/current?app=${encodeURIComponent(appId2)}`, {
170119
+ redirect: "error",
170120
+ signal: AbortSignal.timeout(3e4),
170121
+ // The same deployment the pick was read from (a preview route), never production's release.
170122
+ ...target.previewRoute ? { headers: { "x-wix-route-serve": target.previewRoute } } : {}
170123
+ });
170124
+ release2 = response.ok ? await response.json() : void 0;
170125
+ } catch {
170126
+ release2 = void 0;
170127
+ }
170128
+ }
170129
+ const verdict = body.invalid ? { opens: SPACES_DEFAULT_APP, compatible: false, reason: `The pick is no longer valid (${String(body.invalid)}): the site resolves to Branded Lite` } : memberAppVerdict(appId2, release2, input.shell);
170130
+ return { ok: true, ...body, ...verdict, host: { name: "Spaces", version: SPACES_HOST_MANIFEST.version, ...input.shell ? { shell: input.shell } : {} } };
170131
+ }
170132
+ var SPACES_DEFAULT_APP, SITE_MEMBER_APP_USAGE, SITE_MEMBER_APP_CODES, SITE_RE2, fail4;
170133
+ var init_site_member_app = __esm({
170134
+ "src/site-member-app.ts"() {
170135
+ init_config();
170136
+ init_credential_hosts();
170137
+ init_contract();
170138
+ init_spaces_host_manifest();
170139
+ init_site_app();
170140
+ init_wix_owner();
170141
+ SPACES_DEFAULT_APP = DEFAULT_SITE_APP;
170142
+ SITE_MEMBER_APP_USAGE = "himi site member-app set <site> --app <id> | clear <site> | show <site> [--platform ios|android] [--target <name>] [--control-url <url>] [--control-token <t>] (<site> may be given as --site <msid>)";
170143
+ SITE_MEMBER_APP_CODES = [
170144
+ "invalid_usage",
170145
+ "insecure_target",
170146
+ "control_token_required",
170147
+ "wix_token_unavailable",
170148
+ "forbidden_site",
170149
+ "forbidden_app",
170150
+ "app_bound_to_other_site",
170151
+ "unauthorized",
170152
+ "control_unreachable",
170153
+ "request_failed"
170154
+ ];
170155
+ SITE_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
170156
+ fail4 = (code, error, extra = {}) => ({ ok: false, code, error, ...extra });
170157
+ }
170158
+ });
170159
+
169710
170160
  // src/site-analyze/visitor-auth.ts
169711
170161
  async function mintVisitorToken(fetchImpl, clientId) {
169712
170162
  const res = await fetchImpl(`${WIX_API3}/oauth2/token`, {
@@ -176466,7 +176916,7 @@ var COMMANDS2 = [
176466
176916
  { name: "connector", summary: "personal access tokens for the hosted MCP connector (Wix Native App Builder), for an AI tool that cannot sign in with OAuth (e.g. Base44's builder): `token create` opens a Wix sign-in page and prints a long-lived token once, to paste into the tool as `Authorization: Bearer <token>`; `token list` / `token revoke <id>` manage them with the `himi login --browser` token. The token can only build and publish DRAFTS.", required: "token create [--name <n>] [--days <1-90>] [--no-open] | token list | token revoke <id> [--target <t>]" },
176467
176917
  { name: "notify", summary: "Hosted --all-members requires --yes for a live send; --dry-run checks ownership/readiness and counts approved recipients without sending. `setup` optionally configures Branded/Ping from local customer key files; `status --refresh` reads live backend readiness. Setup may be deferred while building. These use serve-control and require owner plus Wix account authentication. `send`, `tokens`, `doctor`: send a REAL push notification to registered devices (RFC 0036 WS5) \u2014 APNs .p8 over HTTP/2 for iOS, FCM v1 for Android. `send` targets one device (--token / --device <registry id>) or fans out to the whole app (--all), reporting a per-device result; a token the provider says is uninstalled (APNs 410 Unregistered / FCM UNREGISTERED) is auto-disabled, while a merely wrong one (BadDeviceToken \u2014 usually a sandbox/production mismatch) is recorded and left enabled. `tokens` lists the registry. `doctor` reports which credentials resolve and which sender each platform picks. Talks to the admin-server via --admin-url/HIMALAYA_ADMIN_URL, authenticated with --admin-session/HIMALAYA_ADMIN_SESSION. `send --provider ping` is the hosted alternative: the serve plane sends to one site member (--member-email) or all approved members (--all-members) through Ping as Himalaya, with owner login instead of provider keys, and reports acceptance, never delivery. NOT `himi push`, which publishes a release.", required: "setup|status [--content <dir>] [--target <t>] [--refresh] [--yes] [--no-push-setup] | send --provider ping [--content <dir>] [--app <a>] [--site <msid>] (--member-email <email> | --all-members (--dry-run | --yes)) --title <s> [--body <s>] --link <url> [--request-id <uuid>] [--dry-run] [--json] | send|tokens|doctor [--app <a>] [--token <t> | --device <id> | --all] [--title <s>] [--body <s>] [--link <url>] [--platform ios|android] [--badge <n>] [--channel <id>] [--silent] [--dry-run] [--json]" },
176468
176918
  { name: "signing", summary: "READ-ONLY App Store Connect inventory for customer BYO TestFlight readiness. `inspect` lists whether the bundle id, distribution certificate, IOS_APP_STORE profile, and ASC app record exist for --bundle-id / --team-id using a local ASC .p8 (flags or ASC_KEY_* env). Never uploads or mutates Apple resources. Optional --cert-file/--profile-file classify on-disk material.", required: "inspect --bundle-id <id> --team-id <id> --asc-key-file <path> --asc-key-id <id> --asc-issuer-id <id> [--cert-file <p12> --cert-password <pw> --profile-file <mobileprovision>] [--json]" },
176469
- { name: "site", summary: "PROVISION a Wix project, then analyze or extend it. `create` is the one door that makes a site instead of adopting one: POST /funnel/projects/v1/create (a WIX site, a HEADLESS backend, a BRANDED_APP, or a VIBE site), and for headless it also mints the OAuth app whose id IS the `WIX_CLIENT_ID` \u2014 with --redirect-uri already registered, because an unregistered redirect makes native signInDirect fail at redirect-session. `--verticals` REQUIRES `--template` and reports only what Wix confirmed: measured, installing onto a project created from the BLANK template returns 200 while the vertical's API answers 428 forever, because there is no site for it to attach to (the install asks for CREATE_SITE). A real template ships its verticals installed and populated, and installing further ones onto it then works. It MUTATES your Wix account and needs --yes; without it you get the payload and nothing is sent. `list` is the read-only site inventory (with each site's premium flag, which is what explains an installed-but-428 vertical). Auth is the account-scoped `wix token` (no --site), falling back to WIX_API_KEY + WIX_ACCOUNT_ID. `client` is the same OAuth step for a site that ALREADY exists \u2014 the one himi adopts rather than makes \u2014 so a clientId no longer means a manual trip to the dashboard: `client ensure` reconciles rather than mints, because a site can already carry a client with NO registered redirect and shipping that one fails signInDirect at runtime; it reuses a client that carries --redirect-uri, PATCHes one that matches --name but lacks it (unioning, so another app's redirect survives), and creates only when there is nothing to reuse (--new forces a fresh one). --name IS the idempotency key. `client` auth is SITE-scoped (`wix token --site <msid>`) because minting is owner-of-that-site, and --yes gates every mutation: a site that already has a usable client resolves WITHOUT it, but --write-provisioning needs --yes even then because it rewrites a manifest on disk (the dry run's planned.provisioning names the file). That query endpoint IGNORES paging (measured: query.paging, cursorPaging and a top-level paging all return the full set at offset 0), so when pagingMetadata reports more clients than came back, `ensure` REFUSES EVERY outcome \u2014 create, patch AND reuse. Reuse is refused too because pickClient breaks ties on the OLDEST createdDate, so an omitted older same-named MOBILE client with the same redirect would win a later complete read and the resolved clientId would MOVE between runs \u2014 and it gets written into provisioning.json; convergence is what makes a stray duplicate untidy rather than harmful. --name cannot narrow the query (no filter is sent; the name is applied to the rows after they arrive), so the recovery is the dashboard; --new skips the query entirely. A `patched` result is verified against what Wix STORED \u2014 the redirect list is read off the PATCH response, and re-read via GET when the response omits it, because falling back to the list himi just sent would make the guard pass vacuously on exactly the silent no-op it exists to catch. `client list` is the read-only inventory of a site's clients: act on its `shippable` column rather than `hasRedirect`, because shippable ALSO requires a MOBILE applicationType and `ensure` refuses a WEB client \u2014 a WEB one carrying your redirect looks right and is not a candidate \u2014 and it sets truncated:true rather than presenting a partial inventory as the whole one. `analyze --read-only` returns API-only generation context as JSON (identity, access probes, CMS permissions and selected redacted Velo source); it needs no browser and never uploads or applies anything. A URL is optional there: `--url` lets it read the published page's Velo code-app ids (credential-free, pinned transport) so the backend source is found on sites where the owner code-app probe 404s. `analyze` reads a site into a business-book folder (REST + Playwright, GET-only); `--url` alone is the visitor-only pass over the published pages, and `--client-id` adds the vertical probes and the app binding `--apply` requires; `site function` list/get/put/publish/invokes that site's Velo HTTP functions and is not `himi functions` (serve-plane jobs). `--apply` bakes into a brandable shell (a site shell needs no `--client-id`: it is visitor-only and binds no client); `--shell` takes the same names `himi dist --shell` does and defaults to branded-lite, and a shell that ships no prepare:customer is refused by name rather than half-built. `--shell brandy` (the site shell, RFC 0062) also derives the site's page map from its published pages and header menu \u2014 Home first, up to four tabs for the pages a visitor returns to, the rest in the drawer \u2014 into the overlay's `site` block, so it can be edited before `--apply`; only a site shell ever receives that block. It never overwrites any shell's committed apps/<shell>/provisioning.json.", required: "create <name> [--type wix|headless|branded-app|vibe] [--template <guid>] [--verticals stores,bookings,\u2026] [--folder <guid>] [--redirect-uri <uri>] [--no-oauth-app] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | list | client ensure|list --site <msid> [--name <name>] [--redirect-uri <uri>] [--new] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | analyze --read-only [--site <msid>] [--client-id <id>] [--url <https>] [--json] | analyze --url <https> [--client-id <id>] [--site <msid>] [--out <dir>] [--redirect-uri <uri>] [--apply] [--shell " + SHELL_ENUM + "] | function list|get|put|publish|invoke [--site <msid>] [--url <https>] [--method GET|POST|PUT|DELETE] [--source <file>] [--body <json>] [--publish] [--invoke]" },
176919
+ { name: "site", summary: "PROVISION a Wix project, then analyze or extend it. `create` is the one door that makes a site instead of adopting one: POST /funnel/projects/v1/create (a WIX site, a HEADLESS backend, a BRANDED_APP, or a VIBE site), and for headless it also mints the OAuth app whose id IS the `WIX_CLIENT_ID` \u2014 with --redirect-uri already registered, because an unregistered redirect makes native signInDirect fail at redirect-session. `--verticals` REQUIRES `--template` and reports only what Wix confirmed: measured, installing onto a project created from the BLANK template returns 200 while the vertical's API answers 428 forever, because there is no site for it to attach to (the install asks for CREATE_SITE). A real template ships its verticals installed and populated, and installing further ones onto it then works. It MUTATES your Wix account and needs --yes; without it you get the payload and nothing is sent. `list` is the read-only site inventory (with each site's premium flag, which is what explains an installed-but-428 vertical). Auth is the account-scoped `wix token` (no --site), falling back to WIX_API_KEY + WIX_ACCOUNT_ID. `client` is the same OAuth step for a site that ALREADY exists \u2014 the one himi adopts rather than makes \u2014 so a clientId no longer means a manual trip to the dashboard: `client ensure` reconciles rather than mints, because a site can already carry a client with NO registered redirect and shipping that one fails signInDirect at runtime; it reuses a client that carries --redirect-uri, PATCHes one that matches --name but lacks it (unioning, so another app's redirect survives), and creates only when there is nothing to reuse (--new forces a fresh one). --name IS the idempotency key. `client` auth is SITE-scoped (`wix token --site <msid>`) because minting is owner-of-that-site, and --yes gates every mutation: a site that already has a usable client resolves WITHOUT it, but --write-provisioning needs --yes even then because it rewrites a manifest on disk (the dry run's planned.provisioning names the file). That query endpoint IGNORES paging (measured: query.paging, cursorPaging and a top-level paging all return the full set at offset 0), so when pagingMetadata reports more clients than came back, `ensure` REFUSES EVERY outcome \u2014 create, patch AND reuse. Reuse is refused too because pickClient breaks ties on the OLDEST createdDate, so an omitted older same-named MOBILE client with the same redirect would win a later complete read and the resolved clientId would MOVE between runs \u2014 and it gets written into provisioning.json; convergence is what makes a stray duplicate untidy rather than harmful. --name cannot narrow the query (no filter is sent; the name is applied to the rows after they arrive), so the recovery is the dashboard; --new skips the query entirely. A `patched` result is verified against what Wix STORED \u2014 the redirect list is read off the PATCH response, and re-read via GET when the response omits it, because falling back to the list himi just sent would make the guard pass vacuously on exactly the silent no-op it exists to catch. `client list` is the read-only inventory of a site's clients: act on its `shippable` column rather than `hasRedirect`, because shippable ALSO requires a MOBILE applicationType and `ensure` refuses a WEB client \u2014 a WEB one carrying your redirect looks right and is not a candidate \u2014 and it sets truncated:true rather than presenting a partial inventory as the whole one. `analyze --read-only` returns API-only generation context as JSON (identity, access probes, CMS permissions and selected redacted Velo source); it needs no browser and never uploads or applies anything. A URL is optional there: `--url` lets it read the published page's Velo code-app ids (credential-free, pinned transport) so the backend source is found on sites where the owner code-app probe 404s. `analyze` reads a site into a business-book folder (REST + Playwright, GET-only); `--url` alone is the visitor-only pass over the published pages, and `--client-id` adds the vertical probes and the app binding `--apply` requires; `site function` list/get/put/publish/invokes that site's Velo HTTP functions and is not `himi functions` (serve-plane jobs). `--apply` bakes into a brandable shell (a site shell needs no `--client-id`: it is visitor-only and binds no client); `--shell` takes the same names `himi dist --shell` does and defaults to branded-lite, and a shell that ships no prepare:customer is refused by name rather than half-built. `--shell brandy` (the site shell, RFC 0062) also derives the site's page map from its published pages and header menu \u2014 Home first, up to four tabs for the pages a visitor returns to, the rest in the drawer \u2014 into the overlay's `site` block, so it can be edited before `--apply`; only a site shell ever receives that block. It never overwrites any shell's committed apps/<shell>/provisioning.json. `member-app set|clear|show <msid>` is the site's member app in Spaces (#4091): the himi app the site's members open in Spaces Lite instead of Branded Lite. `set --app <id>` takes the site's own app or one your account owns (an owned app not yet tied to the site is linked first; another account's app is refused, 403); `clear` returns the site to Branded Lite; `show` prints the pick plus the COMPUTED verdict \u2014 the picked release's requirements judged against the Spaces host manifest (checkHostCompatibility, `--platform` to judge one phone platform) \u2014 and `reason` when it falls back to Branded Lite. Auth is your `himi login` control token plus the account `wix token`, which the plane uses to verify you own the site.", required: "create <name> [--type wix|headless|branded-app|vibe] [--template <guid>] [--verticals stores,bookings,\u2026] [--folder <guid>] [--redirect-uri <uri>] [--no-oauth-app] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | list | client ensure|list --site <msid> [--name <name>] [--redirect-uri <uri>] [--new] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | analyze --read-only [--site <msid>] [--client-id <id>] [--url <https>] [--json] | analyze --url <https> [--client-id <id>] [--site <msid>] [--out <dir>] [--redirect-uri <uri>] [--apply] [--shell " + SHELL_ENUM + "] | function list|get|put|publish|invoke [--site <msid>] [--url <https>] [--method GET|POST|PUT|DELETE] [--source <file>] [--body <json>] [--publish] [--invoke] | member-app set|clear|show <msid> [--app <id>] [--platform ios|android]" },
176470
176920
  { name: "base44", summary: "read a BASE44 app into an app book (book.json + BOOK.md) for the agent building a Himi app from it \u2014 the Base44 counterpart of `site analyze`, READ-ONLY. `analyze --url <published app>` is the VISITOR pass: the app id, name, favicon and colours from the published SPA, its router paths, and screenshots + look tokens from the Playwright crawl. `analyze --app-id <id>` adds the OWNER pass and needs a session: the project source under <out>/source/ (the read half of `base44 eject` \u2014 GET api/apps/<id>/eject \u2014 through the same deny-list and secret scan `himi push` uses, widened by the book's token shapes; `eject` itself is never run because it CREATES a copy of the app), every entity's JSON schema, required fields and RLS rules with up to --sample redacted rows (default 5, 0 = off), backend functions, agents, connectors (names and scopes only, never tokens), secret NAMES (never values), auth visibility + providers, and the route \u2192 component \u2192 entities map from the source router. Every chapter it could not read is listed in `gaps` rather than failing the run. `login` runs Base44's device sign-in and stores the session in ~/.himi/base44/session.json (never the repo; --from-base44-cli imports `npx base44 login`'s session instead); BASE44_ACCESS_TOKEN in the environment wins, for CI. `whoami` prints who is signed in (never the token); `logout` removes the stored session. `auth-check --url <tenant> --ios-app-id <PREFIX.bundle.id>` and/or `--android-package <package> --android-sha256 <fingerprint>` checks public native-login associations and prints missing entries; it never reads an owner token. --json prints the book itself. himi docs base44-app-context", required: "analyze (--url <https://\u2026> | --app-id <id>) [--out .himi/base44-book] [--sample <n>] [--json] | login [--from-base44-cli] | whoami | logout | auth-check --url <https://tenant> [--ios-app-id <PREFIX.bundle.id>] [--android-package <package> --android-sha256 <fingerprint>]" },
176471
176921
  { name: "doctor", summary: 'off-VPN preflight in one shot: probe the PUBLIC data plane (www.wixapis.com \u2014 reachable off-VPN), the INTERNAL serve/control plane for the selected target (VPN-gated), and report auth state (dpx session on disk + stored control token). --dpx additionally runs a live, EADDRINUSE-safe `dpx whoami`. Machine-readable with --json (an unreachable internal plane carries a stable code:"needs_vpn" so an agent branches on the code, not the prose); human-readable otherwise. Never loads the authoring runtime \u2014 works off-repo.', required: "[--target <t>] [--dpx] [--json] [--data-url <url>] [--timeout <ms>]" },
176472
176922
  { name: "upgrade", summary: "install the newest published CLI (`npm i -g @wix/himalaya-cli@latest`) and report what moved. This is the explicit door: himi also updates itself when the running build is more than a week old \u2014 unattended it just does it and re-runs your command, at a terminal it asks first \u2014 and `HIMI_NO_UPDATE_CHECK=1` turns that off, leaving this command as the way to catch up. Reports `unchanged` rather than claiming success when npm exits 0 without moving the version, which is what a registry mirror serving its own pinned copy looks like.", required: "[--json]" },
@@ -177179,13 +177629,13 @@ function previewTelemetryUrl(serveBase, app, release2) {
177179
177629
  function diagnoseBody(body) {
177180
177630
  const findings = body.findings;
177181
177631
  if (!Array.isArray(findings)) return body;
177182
- const messageOf2 = (entry) => {
177632
+ const messageOf3 = (entry) => {
177183
177633
  const message = entry && typeof entry === "object" ? entry.message : void 0;
177184
177634
  return typeof message === "string" ? message : void 0;
177185
177635
  };
177186
- const shown2 = new Set(findings.map(messageOf2).filter((message) => message !== void 0));
177636
+ const shown2 = new Set(findings.map(messageOf3).filter((message) => message !== void 0));
177187
177637
  const folded = (value) => Array.isArray(value) && value.length > 0 && value.every((entry) => {
177188
- const message = messageOf2(entry);
177638
+ const message = messageOf3(entry);
177189
177639
  return message !== void 0 && shown2.has(message);
177190
177640
  });
177191
177641
  const out2 = {};
@@ -184339,6 +184789,23 @@ Code: ${userCode}`);
184339
184789
  }
184340
184790
  case "site": {
184341
184791
  const sub = pos[1];
184792
+ if (sub === "member-app") {
184793
+ const { runSiteMemberApp: runSiteMemberApp2 } = await Promise.resolve().then(() => (init_site_member_app(), site_member_app_exports));
184794
+ const r2 = await runSiteMemberApp2({
184795
+ op: pos[2],
184796
+ site: pos[3] ?? str9(flags.site),
184797
+ app: str9(flags.app),
184798
+ shell: str9(flags.platform),
184799
+ target: str9(flags.target),
184800
+ // Off-repo, `himi login` stores its token under `wix`; resolve where it did.
184801
+ defaultTarget: contextualDefaultTarget,
184802
+ controlUrl: str9(flags["control-url"]),
184803
+ controlToken: str9(flags["control-token"])
184804
+ });
184805
+ if (r2.ok) print(r2);
184806
+ else io2.error(JSON.stringify(r2));
184807
+ return r2.ok ? 0 : r2.code === "invalid_usage" ? 2 : 1;
184808
+ }
184342
184809
  if (sub === "function") {
184343
184810
  const { runSiteFunctionCli: runSiteFunctionCli2, SITE_FUNCTION_USAGE: SITE_FUNCTION_USAGE2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
184344
184811
  try {
@@ -6,7 +6,7 @@ repurposed when human wording changes. New codes may be added; consumers must
6
6
  handle an unknown code as a failure, without parsing prose.
7
7
 
8
8
  **Rollout:** this contract is enforced for **`himi push`**, including `--only`,
9
- first, and for **`himi store iap connect`**. Other subcommands still have legacy failures without codes and are a
9
+ first, for **`himi store iap connect`**, and for **`himi site member-app`**. Other subcommands still have legacy failures without codes and are a
10
10
  follow-up migration. This is an additive change: existing `error`, `errors`,
11
11
  `message`, `hint`, status, body, and gate-specific fields stay as they were.
12
12
  Some legacy envelopes have no singular `error` or `hint`; consumers must not
@@ -106,6 +106,31 @@ failure codes. A forced push still follows the existing override rules.
106
106
  | `config_not_applied` | `--content` was given, but the binding is not in its `config.ts` (no config file, a dynamic or symlinked config, an edit `himi validate` rolled back, or no validator); the `config` report or `note` says what to add by hand. |
107
107
  | `connect_failed` | An otherwise unclassified error stopped connect (for example an unreadable manifest, or a failed `--out` write). The original message is preserved. |
108
108
 
109
+ ## `himi site member-app` codes
110
+
111
+ `himi site member-app set|clear|show <site>` prints one envelope. A refusal from the publishing service
112
+ keeps the service's own `error` in `serverError` and its HTTP `status`; the CLI `code` is one of:
113
+
114
+ | Code | Meaning |
115
+ |---|---|
116
+ | `invalid_usage` | Missing or malformed op, site id, `--app` or `--platform`. Nothing was sent. |
117
+ | `insecure_target` | The target's control URL is not an https `wix.com` host (or loopback), so the Wix credential was neither minted nor sent. Nothing was sent. |
118
+ | `control_token_required` | No control token for the target — run `himi login`. Nothing was sent. |
119
+ | `wix_token_unavailable` | The account Wix token (`wix token`) could not be minted; the publishing service needs it to verify site ownership. Nothing was sent. |
120
+ | `forbidden_site` | The publishing service could not verify that your account owns the site (403). |
121
+ | `forbidden_app` | The app is not yours, or does not exist (403). Nothing was stored. |
122
+ | `app_bound_to_other_site` | The app is another site's own app (403). |
123
+ | `unauthorized` | The control token was refused or revoked (401). |
124
+ | `control_unreachable` | The publishing service could not be reached. |
125
+ | `request_failed` | Any other refusal; `status` and `serverError` say which. |
126
+
127
+ `show` succeeds with `opens` (what members get: the picked app or `himi-branded-lite`), `compatible`
128
+ (whether the picked release fits the Spaces host manifest, judged with `checkHostCompatibility`), and —
129
+ whenever members get Branded Lite instead of the pick — `reason`. `opens` is Spaces' own decision, so a
130
+ compatible pick that this Spaces build cannot host yet reports `compatible: true` with `opens:
131
+ "himi-branded-lite"` and that reason. An incompatible pick is not a failure: it is `ok: true` with
132
+ `compatible: false`.
133
+
109
134
  ## Follow-ups
110
135
 
111
136
  - Migrate failure envelopes in subcommands other than `push`, keeping this contract.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1351.0",
3
+ "version": "0.1352.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "7a0aebeceac5d04d67aaff28b7a887d5cdd1d32013f8f26f81a04b81"
74
+ "falconPackageHash": "3d6e10a135511ba6f09d842a8dd9aee9013e5b36a3f4d79fe5a3f266"
75
75
  }