@construct-space/cli 1.1.11 → 1.2.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/index.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __require = import.meta.require;
@@ -2807,6 +2789,7 @@ Object.defineProperties(createChalk.prototype, styles2);
2807
2789
  var chalk = createChalk();
2808
2790
  var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
2809
2791
  var source_default = chalk;
2792
+
2810
2793
  // node_modules/@inquirer/core/dist/lib/key.js
2811
2794
  var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
2812
2795
  var isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n";
@@ -2951,7 +2934,7 @@ var effectScheduler = {
2951
2934
  // node_modules/@inquirer/core/dist/lib/use-state.js
2952
2935
  function useState(defaultValue) {
2953
2936
  return withPointer((pointer) => {
2954
- const setState = AsyncResource2.bind(function setState2(newValue) {
2937
+ const setState = AsyncResource2.bind(function setState(newValue) {
2955
2938
  if (pointer.get() !== newValue) {
2956
2939
  pointer.set(newValue);
2957
2940
  handleChange();
@@ -4727,7 +4710,7 @@ async function scaffold(nameArg, options) {
4727
4710
  }
4728
4711
 
4729
4712
  // src/commands/build.ts
4730
- import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync2, renameSync, statSync as statSync2 } from "fs";
4713
+ import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync3, renameSync, statSync as statSync3 } from "fs";
4731
4714
  import { join as join6 } from "path";
4732
4715
  import { createHash } from "crypto";
4733
4716
 
@@ -7380,8 +7363,8 @@ function validate2(m) {
7380
7363
  errors2.push("author: must be an object with a name");
7381
7364
  if (!m.icon)
7382
7365
  errors2.push("icon: must be a string");
7383
- if (!["company", "project", "both"].includes(m.scope))
7384
- errors2.push('scope: must be "company", "project", or "both"');
7366
+ if (!["app", "project", "org", "any"].includes(m.scope))
7367
+ errors2.push('scope: must be "app", "project", "org", or "any"');
7385
7368
  if (!m.pages?.length)
7386
7369
  errors2.push("pages: must be a non-empty array");
7387
7370
  if (!m.navigation?.label)
@@ -7408,30 +7391,84 @@ function exists(dir) {
7408
7391
  }
7409
7392
 
7410
7393
  // src/lib/entry.ts
7411
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
7412
- import { join as join4, basename, extname } from "path";
7394
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4, readdirSync, statSync } from "fs";
7395
+ import { join as join4, basename, extname, relative } from "path";
7413
7396
  function capitalize(s) {
7414
7397
  if (!s)
7415
7398
  return s;
7416
7399
  return s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
7417
7400
  }
7418
- function resolvePages(m, prefix) {
7401
+ function scanPagesDir(pagesDir) {
7402
+ const results = [];
7403
+ function walk(dir, routeSegments) {
7404
+ if (!existsSync4(dir))
7405
+ return;
7406
+ const entries = readdirSync(dir).sort();
7407
+ for (const entry of entries) {
7408
+ const fullPath = join4(dir, entry);
7409
+ const stat = statSync(fullPath);
7410
+ if (stat.isDirectory()) {
7411
+ const segment = entry.replace(/^\[(.+)\]$/, ":$1");
7412
+ walk(fullPath, [...routeSegments, segment]);
7413
+ } else if (stat.isFile() && entry.endsWith(".vue")) {
7414
+ const nameWithoutExt = entry.replace(/\.vue$/, "");
7415
+ const relFile = relative(join4(pagesDir, ".."), fullPath).replace(/\\/g, "/");
7416
+ let routePath;
7417
+ if (nameWithoutExt === "index") {
7418
+ routePath = routeSegments.join("/");
7419
+ } else {
7420
+ const segment = nameWithoutExt.replace(/^\[(.+)\]$/, ":$1");
7421
+ routePath = [...routeSegments, segment].join("/");
7422
+ }
7423
+ results.push({ filePath: relFile, routePath });
7424
+ }
7425
+ }
7426
+ }
7427
+ walk(pagesDir, []);
7428
+ return results;
7429
+ }
7430
+ function varNameFromFile(filePath) {
7431
+ let cleaned = filePath.replace(/^pages\//, "").replace(/\.vue$/, "");
7432
+ cleaned = cleaned.replace(/\[([^\]]+)\]/g, "$1");
7433
+ const segments = cleaned.split(/[\/-]/).filter(Boolean);
7434
+ const name = segments.map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
7435
+ return (name || "Index") + "Page";
7436
+ }
7437
+ function resolvePages(m, root, prefix) {
7438
+ const pagesDir = join4(root, "src", "pages");
7439
+ const fsPages = scanPagesDir(pagesDir);
7440
+ const fsMap = new Map;
7441
+ for (const fp of fsPages) {
7442
+ fsMap.set(fp.routePath, fp);
7443
+ }
7419
7444
  return m.pages.map((p) => {
7420
- let component = p.component;
7421
- if (!component) {
7422
- component = p.path === "" ? "pages/index.vue" : `pages/${p.path}.vue`;
7445
+ if (p.component) {
7446
+ const base = basename(p.component);
7447
+ const nameWithoutExt = base.replace(extname(base), "").replace(/[\[\]]/g, "");
7448
+ const dir = p.component.replace(/^pages\//, "").replace(/\/[^/]+$/, "");
7449
+ let varName;
7450
+ if (dir && dir !== p.component.replace(/^pages\//, "")) {
7451
+ varName = capitalize(dir.split("/").map((s) => s.replace(/[\[\]]/g, "")).join("-")) + capitalize(nameWithoutExt) + "Page";
7452
+ } else {
7453
+ varName = capitalize(nameWithoutExt) + "Page";
7454
+ }
7455
+ return { varName, importPath: prefix + p.component, path: p.path };
7456
+ }
7457
+ const fsPage = fsMap.get(p.path);
7458
+ if (fsPage) {
7459
+ const varName = varNameFromFile(fsPage.filePath);
7460
+ return { varName, importPath: prefix + fsPage.filePath, path: p.path };
7423
7461
  }
7424
- let varName = "IndexPage";
7425
- if (p.path) {
7426
- let cleanPath = p.path.replace(/:/g, "");
7427
- if (p.component) {
7428
- let base = basename(p.component);
7429
- base = base.replace(extname(base), "").replace(/[\[\]]/g, "");
7430
- cleanPath = base;
7462
+ const legacyComponent = p.path === "" ? "pages/index.vue" : `pages/${p.path}.vue`;
7463
+ const legacyFullPath = join4(root, "src", legacyComponent);
7464
+ if (existsSync4(legacyFullPath)) {
7465
+ let varName = "IndexPage";
7466
+ if (p.path) {
7467
+ varName = capitalize(p.path.replace(/[\/:]/g, "-").replace(/-+/g, "-")) + "Page";
7431
7468
  }
7432
- varName = capitalize(cleanPath) + "Page";
7469
+ return { varName, importPath: prefix + legacyComponent, path: p.path };
7433
7470
  }
7434
- return { varName, importPath: prefix + component, path: p.path };
7471
+ throw new Error(`[entry] Could not resolve component for page "${p.path}". ` + `Checked: manifest component field, filesystem scan of src/pages/, ` + `and legacy fallback at ${legacyComponent}. ` + `Ensure a .vue file exists for this route.`);
7435
7472
  });
7436
7473
  }
7437
7474
  function resolveWidgets(m, prefix) {
@@ -7450,7 +7487,7 @@ function resolveWidgets(m, prefix) {
7450
7487
  }
7451
7488
  function generate(root, m) {
7452
7489
  const pagePrefix = existsSync4(join4(root, "src", "pages")) ? "./" : "../";
7453
- const pages = resolvePages(m, pagePrefix);
7490
+ const pages = resolvePages(m, root, pagePrefix);
7454
7491
  const widgets = resolveWidgets(m, "../");
7455
7492
  const actionsPath = join4(root, "src", "actions.ts");
7456
7493
  const hasActions = existsSync4(actionsPath);
@@ -7506,7 +7543,7 @@ function writeEntry(root, m) {
7506
7543
  }
7507
7544
 
7508
7545
  // src/lib/agent.ts
7509
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync, existsSync as existsSync5 } from "fs";
7546
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2, existsSync as existsSync5 } from "fs";
7510
7547
  import { join as join5, extname as extname2, basename as basename2 } from "path";
7511
7548
  var AGENT_KEY = "construct-agent-obfuscate-v1";
7512
7549
  function encode(content) {
@@ -7522,7 +7559,7 @@ function readMdFiles(dir) {
7522
7559
  const result = {};
7523
7560
  if (!existsSync5(dir))
7524
7561
  return result;
7525
- for (const f of readdirSync(dir)) {
7562
+ for (const f of readdirSync2(dir)) {
7526
7563
  if (extname2(f) !== ".md")
7527
7564
  continue;
7528
7565
  result[basename2(f, ".md")] = readFileSync3(join5(dir, f), "utf-8");
@@ -7533,7 +7570,7 @@ function readJsonFiles(dir) {
7533
7570
  const result = {};
7534
7571
  if (!existsSync5(dir))
7535
7572
  return result;
7536
- for (const f of readdirSync(dir)) {
7573
+ for (const f of readdirSync2(dir)) {
7537
7574
  if (extname2(f) !== ".json")
7538
7575
  continue;
7539
7576
  result[basename2(f, ".json")] = readFileSync3(join5(dir, f), "utf-8");
@@ -7623,7 +7660,7 @@ async function build(options) {
7623
7660
  }
7624
7661
  runHook(m.hooks, "postBuild", root);
7625
7662
  const agentDir = join6(root, "agent");
7626
- if (existsSync6(agentDir) && statSync2(agentDir).isDirectory()) {
7663
+ if (existsSync6(agentDir) && statSync3(agentDir).isDirectory()) {
7627
7664
  const distDir2 = join6(root, "dist");
7628
7665
  bundleAgentDir(agentDir, distDir2);
7629
7666
  bundleAgentDir(agentDir, root);
@@ -7632,7 +7669,7 @@ async function build(options) {
7632
7669
  const expectedBundle = `space-${m.id}.iife.js`;
7633
7670
  let bundlePath = join6(distDir, expectedBundle);
7634
7671
  if (!existsSync6(bundlePath)) {
7635
- const matches = readdirSync2(distDir).filter((f) => f.startsWith("space-") && f.endsWith(".iife.js"));
7672
+ const matches = readdirSync3(distDir).filter((f) => f.startsWith("space-") && f.endsWith(".iife.js"));
7636
7673
  if (matches.length === 1) {
7637
7674
  renameSync(join6(distDir, matches[0]), bundlePath);
7638
7675
  const oldCSS = join6(distDir, matches[0].replace(".iife.js", ".css"));
@@ -7666,16 +7703,16 @@ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
7666
7703
  import { join as join9 } from "path";
7667
7704
  import { createHash as createHash2 } from "crypto";
7668
7705
 
7669
- // node_modules/chokidar/esm/index.js
7670
- import { stat as statcb } from "fs";
7671
- import { stat as stat3, readdir as readdir2 } from "fs/promises";
7706
+ // node_modules/chokidar/index.js
7672
7707
  import { EventEmitter } from "events";
7673
- import * as sysPath2 from "path";
7708
+ import { stat as statcb, Stats } from "fs";
7709
+ import { readdir as readdir2, stat as stat3 } from "fs/promises";
7710
+ import * as sp2 from "path";
7674
7711
 
7675
- // node_modules/readdirp/esm/index.js
7676
- import { stat, lstat, readdir, realpath } from "fs/promises";
7712
+ // node_modules/readdirp/index.js
7713
+ import { lstat, readdir, realpath, stat } from "fs/promises";
7714
+ import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "path";
7677
7715
  import { Readable } from "stream";
7678
- import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "path";
7679
7716
  var EntryTypes = {
7680
7717
  FILE_TYPE: "files",
7681
7718
  DIR_TYPE: "directories",
@@ -7731,6 +7768,20 @@ var normalizeFilter = (filter) => {
7731
7768
  };
7732
7769
 
7733
7770
  class ReaddirpStream extends Readable {
7771
+ parents;
7772
+ reading;
7773
+ parent;
7774
+ _stat;
7775
+ _maxDepth;
7776
+ _wantsDir;
7777
+ _wantsFile;
7778
+ _wantsEverything;
7779
+ _root;
7780
+ _isDirent;
7781
+ _statsProp;
7782
+ _rdOptions;
7783
+ _fileFilter;
7784
+ _directoryFilter;
7734
7785
  constructor(options = {}) {
7735
7786
  super({
7736
7787
  objectMode: true,
@@ -7747,7 +7798,7 @@ class ReaddirpStream extends Readable {
7747
7798
  } else {
7748
7799
  this._stat = statMethod;
7749
7800
  }
7750
- this._maxDepth = opts.depth ?? defaultOptions.depth;
7801
+ this._maxDepth = opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
7751
7802
  this._wantsDir = type ? DIR_TYPES.has(type) : false;
7752
7803
  this._wantsFile = type ? FILE_TYPES.has(type) : false;
7753
7804
  this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
@@ -7892,11 +7943,11 @@ function readdirp(root, options = {}) {
7892
7943
  return new ReaddirpStream(options);
7893
7944
  }
7894
7945
 
7895
- // node_modules/chokidar/esm/handler.js
7896
- import { watchFile, unwatchFile, watch as fs_watch } from "fs";
7897
- import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
7898
- import * as sysPath from "path";
7946
+ // node_modules/chokidar/handler.js
7947
+ import { watch as fs_watch, unwatchFile, watchFile } from "fs";
7948
+ import { realpath as fsrealpath, lstat as lstat2, open, stat as stat2 } from "fs/promises";
7899
7949
  import { type as osType } from "os";
7950
+ import * as sp from "path";
7900
7951
  var STR_DATA = "data";
7901
7952
  var STR_END = "end";
7902
7953
  var STR_CLOSE = "close";
@@ -8188,7 +8239,7 @@ var binaryExtensions = new Set([
8188
8239
  "zip",
8189
8240
  "zipx"
8190
8241
  ]);
8191
- var isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
8242
+ var isBinaryPath = (filePath) => binaryExtensions.has(sp.extname(filePath).slice(1).toLowerCase());
8192
8243
  var foreach = (val, fn) => {
8193
8244
  if (val instanceof Set) {
8194
8245
  val.forEach(fn);
@@ -8226,7 +8277,7 @@ function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
8226
8277
  listener(path);
8227
8278
  emitRaw(rawEvent, evPath, { watchedPath: path });
8228
8279
  if (evPath && path !== evPath) {
8229
- fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));
8280
+ fsWatchBroadcast(sp.resolve(path, evPath), KEY_LISTENERS, sp.join(path, evPath));
8230
8281
  }
8231
8282
  };
8232
8283
  try {
@@ -8341,17 +8392,19 @@ var setFsWatchFileListener = (path, fullPath, options, handlers) => {
8341
8392
  };
8342
8393
 
8343
8394
  class NodeFsHandler {
8395
+ fsw;
8396
+ _boundHandleError;
8344
8397
  constructor(fsW) {
8345
8398
  this.fsw = fsW;
8346
8399
  this._boundHandleError = (error2) => fsW._handleError(error2);
8347
8400
  }
8348
8401
  _watchWithNodeFs(path, listener) {
8349
8402
  const opts = this.fsw.options;
8350
- const directory = sysPath.dirname(path);
8351
- const basename4 = sysPath.basename(path);
8403
+ const directory = sp.dirname(path);
8404
+ const basename4 = sp.basename(path);
8352
8405
  const parent = this.fsw._getWatchedDir(directory);
8353
8406
  parent.add(basename4);
8354
- const absolutePath = sysPath.resolve(path);
8407
+ const absolutePath = sp.resolve(path);
8355
8408
  const options = {
8356
8409
  persistent: opts.persistent
8357
8410
  };
@@ -8378,8 +8431,8 @@ class NodeFsHandler {
8378
8431
  if (this.fsw.closed) {
8379
8432
  return;
8380
8433
  }
8381
- const dirname3 = sysPath.dirname(file);
8382
- const basename4 = sysPath.basename(file);
8434
+ const dirname3 = sp.dirname(file);
8435
+ const basename4 = sp.basename(file);
8383
8436
  const parent = this.fsw._getWatchedDir(dirname3);
8384
8437
  let prevStats = stats;
8385
8438
  if (parent.has(basename4))
@@ -8462,8 +8515,9 @@ class NodeFsHandler {
8462
8515
  this.fsw._symlinkPaths.set(full, true);
8463
8516
  }
8464
8517
  _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
8465
- directory = sysPath.join(directory, "");
8466
- throttler = this.fsw._throttle("readdir", directory, 1000);
8518
+ directory = sp.join(directory, "");
8519
+ const throttleKey = target ? `${directory}:${target}` : directory;
8520
+ throttler = this.fsw._throttle("readdir", throttleKey, 1000);
8467
8521
  if (!throttler)
8468
8522
  return;
8469
8523
  const previous = this.fsw._getWatchedDir(wh.path);
@@ -8480,7 +8534,7 @@ class NodeFsHandler {
8480
8534
  return;
8481
8535
  }
8482
8536
  const item = entry.path;
8483
- let path = sysPath.join(directory, item);
8537
+ let path = sp.join(directory, item);
8484
8538
  current.add(item);
8485
8539
  if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
8486
8540
  return;
@@ -8491,7 +8545,7 @@ class NodeFsHandler {
8491
8545
  }
8492
8546
  if (item === target || !target && !previous.has(item)) {
8493
8547
  this.fsw._incrReadyCount();
8494
- path = sysPath.join(dir, sysPath.relative(dir, path));
8548
+ path = sp.join(dir, sp.relative(dir, path));
8495
8549
  this._addToNodeFs(path, initialAdd, wh, depth + 1);
8496
8550
  }
8497
8551
  }).on(EV.ERROR, this._boundHandleError);
@@ -8517,12 +8571,12 @@ class NodeFsHandler {
8517
8571
  });
8518
8572
  }
8519
8573
  async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath2) {
8520
- const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
8521
- const tracked = parentDir.has(sysPath.basename(dir));
8574
+ const parentDir = this.fsw._getWatchedDir(sp.dirname(dir));
8575
+ const tracked = parentDir.has(sp.basename(dir));
8522
8576
  if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
8523
8577
  this.fsw._emit(EV.ADD_DIR, dir, stats);
8524
8578
  }
8525
- parentDir.add(sysPath.basename(dir));
8579
+ parentDir.add(sp.basename(dir));
8526
8580
  this.fsw._getWatchedDir(dir);
8527
8581
  let throttler;
8528
8582
  let closer;
@@ -8563,7 +8617,7 @@ class NodeFsHandler {
8563
8617
  const follow = this.fsw.options.followSymlinks;
8564
8618
  let closer;
8565
8619
  if (stats.isDirectory()) {
8566
- const absPath = sysPath.resolve(path);
8620
+ const absPath = sp.resolve(path);
8567
8621
  const targetPath = follow ? await fsrealpath(path) : path;
8568
8622
  if (this.fsw.closed)
8569
8623
  return;
@@ -8577,14 +8631,14 @@ class NodeFsHandler {
8577
8631
  const targetPath = follow ? await fsrealpath(path) : path;
8578
8632
  if (this.fsw.closed)
8579
8633
  return;
8580
- const parent = sysPath.dirname(wh.watchPath);
8634
+ const parent = sp.dirname(wh.watchPath);
8581
8635
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
8582
8636
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
8583
8637
  closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
8584
8638
  if (this.fsw.closed)
8585
8639
  return;
8586
8640
  if (targetPath !== undefined) {
8587
- this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
8641
+ this.fsw._symlinkPaths.set(sp.resolve(path), targetPath);
8588
8642
  }
8589
8643
  } else {
8590
8644
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
@@ -8602,7 +8656,7 @@ class NodeFsHandler {
8602
8656
  }
8603
8657
  }
8604
8658
 
8605
- // node_modules/chokidar/esm/index.js
8659
+ // node_modules/chokidar/index.js
8606
8660
  /*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
8607
8661
  var SLASH = "/";
8608
8662
  var SLASH_SLASH = "//";
@@ -8610,7 +8664,7 @@ var ONE_DOT = ".";
8610
8664
  var TWO_DOTS = "..";
8611
8665
  var STRING_TYPE = "string";
8612
8666
  var BACK_SLASH_RE = /\\/g;
8613
- var DOUBLE_SLASH_RE = /\/\//;
8667
+ var DOUBLE_SLASH_RE = /\/\//g;
8614
8668
  var DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
8615
8669
  var REPLACER_RE = /^\.[/\\]/;
8616
8670
  function arrify(item) {
@@ -8629,11 +8683,11 @@ function createPattern(matcher) {
8629
8683
  if (matcher.path === string)
8630
8684
  return true;
8631
8685
  if (matcher.recursive) {
8632
- const relative3 = sysPath2.relative(matcher.path, string);
8633
- if (!relative3) {
8686
+ const relative4 = sp2.relative(matcher.path, string);
8687
+ if (!relative4) {
8634
8688
  return false;
8635
8689
  }
8636
- return !relative3.startsWith("..") && !sysPath2.isAbsolute(relative3);
8690
+ return !relative4.startsWith("..") && !sp2.isAbsolute(relative4);
8637
8691
  }
8638
8692
  return false;
8639
8693
  };
@@ -8643,14 +8697,12 @@ function createPattern(matcher) {
8643
8697
  function normalizePath(path) {
8644
8698
  if (typeof path !== "string")
8645
8699
  throw new Error("string expected");
8646
- path = sysPath2.normalize(path);
8700
+ path = sp2.normalize(path);
8647
8701
  path = path.replace(/\\/g, "/");
8648
8702
  let prepend = false;
8649
8703
  if (path.startsWith("//"))
8650
8704
  prepend = true;
8651
- const DOUBLE_SLASH_RE2 = /\/\//;
8652
- while (path.match(DOUBLE_SLASH_RE2))
8653
- path = path.replace(DOUBLE_SLASH_RE2, "/");
8705
+ path = path.replace(DOUBLE_SLASH_RE, "/");
8654
8706
  if (prepend)
8655
8707
  path = "/" + path;
8656
8708
  return path;
@@ -8691,31 +8743,32 @@ var toUnix = (string) => {
8691
8743
  if (str.startsWith(SLASH_SLASH)) {
8692
8744
  prepend = true;
8693
8745
  }
8694
- while (str.match(DOUBLE_SLASH_RE)) {
8695
- str = str.replace(DOUBLE_SLASH_RE, SLASH);
8696
- }
8746
+ str = str.replace(DOUBLE_SLASH_RE, SLASH);
8697
8747
  if (prepend) {
8698
8748
  str = SLASH + str;
8699
8749
  }
8700
8750
  return str;
8701
8751
  };
8702
- var normalizePathToUnix = (path) => toUnix(sysPath2.normalize(toUnix(path)));
8752
+ var normalizePathToUnix = (path) => toUnix(sp2.normalize(toUnix(path)));
8703
8753
  var normalizeIgnored = (cwd = "") => (path) => {
8704
8754
  if (typeof path === "string") {
8705
- return normalizePathToUnix(sysPath2.isAbsolute(path) ? path : sysPath2.join(cwd, path));
8755
+ return normalizePathToUnix(sp2.isAbsolute(path) ? path : sp2.join(cwd, path));
8706
8756
  } else {
8707
8757
  return path;
8708
8758
  }
8709
8759
  };
8710
8760
  var getAbsolutePath = (path, cwd) => {
8711
- if (sysPath2.isAbsolute(path)) {
8761
+ if (sp2.isAbsolute(path)) {
8712
8762
  return path;
8713
8763
  }
8714
- return sysPath2.join(cwd, path);
8764
+ return sp2.join(cwd, path);
8715
8765
  };
8716
8766
  var EMPTY_SET = Object.freeze(new Set);
8717
8767
 
8718
8768
  class DirEntry {
8769
+ path;
8770
+ _removeWatcher;
8771
+ items;
8719
8772
  constructor(dir, removeWatcher) {
8720
8773
  this.path = dir;
8721
8774
  this._removeWatcher = removeWatcher;
@@ -8740,7 +8793,7 @@ class DirEntry {
8740
8793
  await readdir2(dir);
8741
8794
  } catch (err) {
8742
8795
  if (this._removeWatcher) {
8743
- this._removeWatcher(sysPath2.dirname(dir), sysPath2.basename(dir));
8796
+ this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
8744
8797
  }
8745
8798
  }
8746
8799
  }
@@ -8768,12 +8821,19 @@ var STAT_METHOD_F = "stat";
8768
8821
  var STAT_METHOD_L = "lstat";
8769
8822
 
8770
8823
  class WatchHelper {
8824
+ fsw;
8825
+ path;
8826
+ watchPath;
8827
+ fullWatchPath;
8828
+ dirParts;
8829
+ followSymlinks;
8830
+ statMethod;
8771
8831
  constructor(path, follow, fsw) {
8772
8832
  this.fsw = fsw;
8773
8833
  const watchPath = path;
8774
8834
  this.path = path = path.replace(REPLACER_RE, "");
8775
8835
  this.watchPath = watchPath;
8776
- this.fullWatchPath = sysPath2.resolve(watchPath);
8836
+ this.fullWatchPath = sp2.resolve(watchPath);
8777
8837
  this.dirParts = [];
8778
8838
  this.dirParts.forEach((parts) => {
8779
8839
  if (parts.length > 1)
@@ -8783,7 +8843,7 @@ class WatchHelper {
8783
8843
  this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
8784
8844
  }
8785
8845
  entryPath(entry) {
8786
- return sysPath2.join(this.watchPath, sysPath2.relative(this.watchPath, entry.fullPath));
8846
+ return sp2.join(this.watchPath, sp2.relative(this.watchPath, entry.fullPath));
8787
8847
  }
8788
8848
  filterPath(entry) {
8789
8849
  const { stats } = entry;
@@ -8798,6 +8858,24 @@ class WatchHelper {
8798
8858
  }
8799
8859
 
8800
8860
  class FSWatcher extends EventEmitter {
8861
+ closed;
8862
+ options;
8863
+ _closers;
8864
+ _ignoredPaths;
8865
+ _throttled;
8866
+ _streams;
8867
+ _symlinkPaths;
8868
+ _watched;
8869
+ _pendingWrites;
8870
+ _pendingUnlinks;
8871
+ _readyCount;
8872
+ _emitReady;
8873
+ _closePromise;
8874
+ _userIgnored;
8875
+ _readyEmitted;
8876
+ _emitRaw;
8877
+ _boundRemove;
8878
+ _nodeFsHandler;
8801
8879
  constructor(_opts = {}) {
8802
8880
  super();
8803
8881
  this.closed = false;
@@ -8906,7 +8984,7 @@ class FSWatcher extends EventEmitter {
8906
8984
  return;
8907
8985
  results.forEach((item) => {
8908
8986
  if (item)
8909
- this.add(sysPath2.dirname(item), sysPath2.basename(_origAdd || item));
8987
+ this.add(sp2.dirname(item), sp2.basename(_origAdd || item));
8910
8988
  });
8911
8989
  });
8912
8990
  return this;
@@ -8917,10 +8995,10 @@ class FSWatcher extends EventEmitter {
8917
8995
  const paths = unifyPaths(paths_);
8918
8996
  const { cwd } = this.options;
8919
8997
  paths.forEach((path) => {
8920
- if (!sysPath2.isAbsolute(path) && !this._closers.has(path)) {
8998
+ if (!sp2.isAbsolute(path) && !this._closers.has(path)) {
8921
8999
  if (cwd)
8922
- path = sysPath2.join(cwd, path);
8923
- path = sysPath2.resolve(path);
9000
+ path = sp2.join(cwd, path);
9001
+ path = sp2.resolve(path);
8924
9002
  }
8925
9003
  this._closePath(path);
8926
9004
  this._addIgnoredPath(path);
@@ -8964,7 +9042,7 @@ class FSWatcher extends EventEmitter {
8964
9042
  getWatched() {
8965
9043
  const watchList = {};
8966
9044
  this._watched.forEach((entry, dir) => {
8967
- const key = this.options.cwd ? sysPath2.relative(this.options.cwd, dir) : dir;
9045
+ const key = this.options.cwd ? sp2.relative(this.options.cwd, dir) : dir;
8968
9046
  const index = key || ONE_DOT;
8969
9047
  watchList[index] = entry.getChildren().sort();
8970
9048
  });
@@ -8980,9 +9058,9 @@ class FSWatcher extends EventEmitter {
8980
9058
  return;
8981
9059
  const opts = this.options;
8982
9060
  if (isWindows)
8983
- path = sysPath2.normalize(path);
9061
+ path = sp2.normalize(path);
8984
9062
  if (opts.cwd)
8985
- path = sysPath2.relative(opts.cwd, path);
9063
+ path = sp2.relative(opts.cwd, path);
8986
9064
  const args = [path];
8987
9065
  if (stats != null)
8988
9066
  args.push(stats);
@@ -9033,7 +9111,7 @@ class FSWatcher extends EventEmitter {
9033
9111
  return this;
9034
9112
  }
9035
9113
  if (opts.alwaysStat && stats === undefined && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
9036
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path) : path;
9114
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path) : path;
9037
9115
  let stats2;
9038
9116
  try {
9039
9117
  stats2 = await stat3(fullPath);
@@ -9089,8 +9167,8 @@ class FSWatcher extends EventEmitter {
9089
9167
  const pollInterval = awf.pollInterval;
9090
9168
  let timeoutHandler;
9091
9169
  let fullPath = path;
9092
- if (this.options.cwd && !sysPath2.isAbsolute(path)) {
9093
- fullPath = sysPath2.join(this.options.cwd, path);
9170
+ if (this.options.cwd && !sp2.isAbsolute(path)) {
9171
+ fullPath = sp2.join(this.options.cwd, path);
9094
9172
  }
9095
9173
  const now = new Date;
9096
9174
  const writes = this._pendingWrites;
@@ -9147,7 +9225,7 @@ class FSWatcher extends EventEmitter {
9147
9225
  return new WatchHelper(path, this.options.followSymlinks, this);
9148
9226
  }
9149
9227
  _getWatchedDir(directory) {
9150
- const dir = sysPath2.resolve(directory);
9228
+ const dir = sp2.resolve(directory);
9151
9229
  if (!this._watched.has(dir))
9152
9230
  this._watched.set(dir, new DirEntry(dir, this._boundRemove));
9153
9231
  return this._watched.get(dir);
@@ -9158,8 +9236,8 @@ class FSWatcher extends EventEmitter {
9158
9236
  return Boolean(Number(stats.mode) & 256);
9159
9237
  }
9160
9238
  _remove(directory, item, isDirectory) {
9161
- const path = sysPath2.join(directory, item);
9162
- const fullPath = sysPath2.resolve(path);
9239
+ const path = sp2.join(directory, item);
9240
+ const fullPath = sp2.resolve(path);
9163
9241
  isDirectory = isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
9164
9242
  if (!this._throttle("remove", path, 100))
9165
9243
  return;
@@ -9177,7 +9255,7 @@ class FSWatcher extends EventEmitter {
9177
9255
  }
9178
9256
  let relPath = path;
9179
9257
  if (this.options.cwd)
9180
- relPath = sysPath2.relative(this.options.cwd, path);
9258
+ relPath = sp2.relative(this.options.cwd, path);
9181
9259
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
9182
9260
  const event = this._pendingWrites.get(relPath).cancelWait();
9183
9261
  if (event === EVENTS.ADD)
@@ -9192,8 +9270,8 @@ class FSWatcher extends EventEmitter {
9192
9270
  }
9193
9271
  _closePath(path) {
9194
9272
  this._closeFile(path);
9195
- const dir = sysPath2.dirname(path);
9196
- this._getWatchedDir(dir).remove(sysPath2.basename(path));
9273
+ const dir = sp2.dirname(path);
9274
+ this._getWatchedDir(dir).remove(sp2.basename(path));
9197
9275
  }
9198
9276
  _closeFile(path) {
9199
9277
  const closers = this._closers.get(path);
@@ -9362,51 +9440,17 @@ function install() {
9362
9440
  }
9363
9441
 
9364
9442
  // src/commands/publish.ts
9365
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync4, unlinkSync as unlinkSync2 } from "fs";
9443
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync5, unlinkSync as unlinkSync2 } from "fs";
9366
9444
  import { join as join14, basename as basename6 } from "path";
9367
9445
 
9368
9446
  // src/lib/auth.ts
9369
9447
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync, existsSync as existsSync9 } from "fs";
9370
9448
  import { join as join12, dirname as dirname4 } from "path";
9371
9449
  var CREDENTIALS_FILE = "credentials.json";
9372
- var APP_PROFILES_FILE = "profiles.json";
9373
9450
  var DEFAULT_PORTAL = "https://developer.construct.space";
9374
9451
  function credentialsPath() {
9375
9452
  return join12(dataDir(), CREDENTIALS_FILE);
9376
9453
  }
9377
- function appProfilesPath() {
9378
- return join12(dataDir(), APP_PROFILES_FILE);
9379
- }
9380
- function appProfileAuthPath(profileId) {
9381
- return join12(dataDir(), "profiles", profileId, "auth.json");
9382
- }
9383
- function loadFromApp() {
9384
- try {
9385
- const profilesPath = appProfilesPath();
9386
- if (!existsSync9(profilesPath))
9387
- return null;
9388
- const profiles = JSON.parse(readFileSync6(profilesPath, "utf-8"));
9389
- if (!profiles?.active_profile)
9390
- return null;
9391
- const authPath = appProfileAuthPath(profiles.active_profile);
9392
- if (!existsSync9(authPath))
9393
- return null;
9394
- const auth = JSON.parse(readFileSync6(authPath, "utf-8"));
9395
- if (!auth?.authenticated || !auth?.token)
9396
- return null;
9397
- return {
9398
- token: auth.token,
9399
- portal: DEFAULT_PORTAL,
9400
- user: {
9401
- id: auth.user.id,
9402
- name: auth.user.name || [auth.user.first_name, auth.user.last_name].filter(Boolean).join(" ") || auth.user.email,
9403
- email: auth.user.email
9404
- }
9405
- };
9406
- } catch {
9407
- return null;
9408
- }
9409
- }
9410
9454
  function store(creds) {
9411
9455
  const path = credentialsPath();
9412
9456
  mkdirSync4(dirname4(path), { recursive: true });
@@ -9414,12 +9458,9 @@ function store(creds) {
9414
9458
  `, { mode: 384 });
9415
9459
  }
9416
9460
  function load2() {
9417
- const fromApp = loadFromApp();
9418
- if (fromApp)
9419
- return fromApp;
9420
9461
  const path = credentialsPath();
9421
9462
  if (!existsSync9(path)) {
9422
- throw new Error("not logged in \u2014 run 'construct login' first (or sign in to the Construct app)");
9463
+ throw new Error("not logged in \u2014 run 'construct login' first");
9423
9464
  }
9424
9465
  const data = JSON.parse(readFileSync6(path, "utf-8"));
9425
9466
  if (!data.token) {
@@ -9442,7 +9483,7 @@ function clear() {
9442
9483
  }
9443
9484
 
9444
9485
  // src/lib/pack.ts
9445
- import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync10 } from "fs";
9486
+ import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync10 } from "fs";
9446
9487
  import { join as join13 } from "path";
9447
9488
  import { tmpdir } from "os";
9448
9489
  import { execSync as execSync3 } from "child_process";
@@ -9484,8 +9525,8 @@ async function packSource(root) {
9484
9525
  if (existsSync10(join13(root, name)))
9485
9526
  entries.push(name);
9486
9527
  }
9487
- for (const entry of readdirSync3(root)) {
9488
- if (statSync3(join13(root, entry)).isDirectory())
9528
+ for (const entry of readdirSync4(root)) {
9529
+ if (statSync4(join13(root, entry)).isDirectory())
9489
9530
  continue;
9490
9531
  if (allowedRootFiles.includes(entry))
9491
9532
  continue;
@@ -9505,7 +9546,7 @@ async function packSource(root) {
9505
9546
  const excludes = "--exclude=node_modules --exclude=dist --exclude=.git --exclude=*.env --exclude=*.log --exclude=*.lock --exclude=*.lockb";
9506
9547
  const cmd = `tar czf "${tarballPath}" ${excludes} ${validEntries.join(" ")}`;
9507
9548
  execSync3(cmd, { cwd: root });
9508
- const size = statSync3(tarballPath).size;
9549
+ const size = statSync4(tarballPath).size;
9509
9550
  if (size > MAX_SIZE) {
9510
9551
  throw new Error(`Source exceeds maximum size of ${MAX_SIZE / 1024 / 1024}MB`);
9511
9552
  }
@@ -9528,6 +9569,16 @@ async function uploadSource(portalURL, token, tarballPath, m) {
9528
9569
  if (resp.status === 401) {
9529
9570
  throw new Error("authentication failed \u2014 run 'construct login' to re-authenticate");
9530
9571
  }
9572
+ if (resp.status === 403) {
9573
+ let msg = result.error || "You are not the owner of this space";
9574
+ if (result.owner_user_id) {
9575
+ msg += `
9576
+ Current owner: ${result.owner_user_id}`;
9577
+ }
9578
+ msg += `
9579
+ Fork to a new space_id to publish your own version.`;
9580
+ throw new Error(msg);
9581
+ }
9531
9582
  if (resp.status >= 400) {
9532
9583
  const msg = result.error || result.errors?.join("; ") || `server returned ${resp.status}`;
9533
9584
  throw new Error(msg);
@@ -9562,24 +9613,6 @@ async function publish(options) {
9562
9613
  console.log(source_default.dim(" Run 'construct login' to authenticate."));
9563
9614
  process.exit(1);
9564
9615
  }
9565
- const publishers = creds.publishers || [];
9566
- if (publishers.length === 0) {
9567
- console.log(source_default.yellow(" No publisher linked to this session. The upload will likely be rejected."));
9568
- console.log(source_default.dim(" Run 'construct login' after enrolling at developer.construct.space."));
9569
- } else {
9570
- const primary = publishers[0];
9571
- const kindLabel = primary.kind === "org" ? source_default.cyan("org") : primary.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
9572
- console.log(source_default.dim(` Publishing as ${primary.name} (${kindLabel})`));
9573
- if (publishers.length > 1 && !yes) {
9574
- const proceed = await dist_default4({
9575
- message: `You have ${publishers.length} publisher identities. Publish under ${primary.name}?`
9576
- });
9577
- if (!proceed) {
9578
- console.log("Cancelled.");
9579
- return;
9580
- }
9581
- }
9582
- }
9583
9616
  const status = gitSafe(root, "status", "--porcelain");
9584
9617
  if (status) {
9585
9618
  console.log(source_default.yellow("You have uncommitted changes."));
@@ -9663,7 +9696,7 @@ async function publish(options) {
9663
9696
  let tarballPath;
9664
9697
  try {
9665
9698
  tarballPath = await packSource(root);
9666
- const size = statSync4(tarballPath).size;
9699
+ const size = statSync5(tarballPath).size;
9667
9700
  spinner.succeed(`Source packed (${formatBytes(size)})`);
9668
9701
  } catch (err) {
9669
9702
  spinner.fail("Pack failed");
@@ -9845,12 +9878,6 @@ function clean(options) {
9845
9878
  import { createServer } from "http";
9846
9879
  async function login(options) {
9847
9880
  const portalURL = options?.portal || DEFAULT_PORTAL;
9848
- const fromApp = loadFromApp();
9849
- if (fromApp) {
9850
- console.log(source_default.green(`Using Construct app profile: ${fromApp.user?.name || fromApp.user?.email}`));
9851
- console.log(source_default.dim(" To use a different identity, sign out of the app or run `construct logout` after login."));
9852
- return;
9853
- }
9854
9881
  if (isAuthenticated()) {
9855
9882
  const creds = load2();
9856
9883
  const name = creds.user?.name || "unknown";
@@ -9916,101 +9943,18 @@ async function login(options) {
9916
9943
  const resp = await fetch(`${portalURL}/api/auth/cli-verify`, {
9917
9944
  headers: { Authorization: `Bearer ${token}` }
9918
9945
  });
9919
- const { user, publishers } = await resp.json();
9920
- store({ token, portal: portalURL, user, publishers });
9946
+ const { user } = await resp.json();
9947
+ store({ token, portal: portalURL, user });
9921
9948
  console.log();
9922
9949
  console.log(source_default.green(`Logged in as ${user?.name || "there"}`));
9923
- const list = publishers || [];
9924
- if (list.length === 0) {
9925
- console.log(source_default.yellow(" No publisher yet. Run developer enrollment to publish spaces."));
9926
- } else {
9927
- for (const p of list) {
9928
- const label = p.kind === "org" ? source_default.cyan("org") : p.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
9929
- console.log(source_default.dim(` Publisher: ${p.name} (${label})`));
9930
- }
9931
- }
9932
9950
  } catch (err) {
9933
9951
  console.error(source_default.red(`Login failed: ${err.message}`));
9934
9952
  process.exit(1);
9935
9953
  }
9936
9954
  }
9937
9955
  function logout() {
9938
- const wasCli = isAuthenticated();
9939
9956
  clear();
9940
- if (wasCli) {
9941
- console.log(source_default.green("Logged out of CLI credentials."));
9942
- } else {
9943
- console.log(source_default.dim("No CLI credentials to clear."));
9944
- }
9945
- const appStill = loadFromApp();
9946
- if (appStill) {
9947
- console.log();
9948
- console.log(source_default.yellow("Note: the Construct app is still signed in as ") + source_default.white(appStill.user?.name || appStill.user?.email || ""));
9949
- console.log(source_default.dim(" The CLI will continue to use the app profile. Sign out of the app to fully disconnect."));
9950
- }
9951
- }
9952
-
9953
- // src/commands/whoami.ts
9954
- async function whoami() {
9955
- const fromApp = loadFromApp();
9956
- let creds = null;
9957
- let source = "none";
9958
- if (fromApp) {
9959
- creds = fromApp;
9960
- source = "app";
9961
- } else {
9962
- try {
9963
- creds = load2();
9964
- source = "cli";
9965
- } catch {
9966
- source = "none";
9967
- }
9968
- }
9969
- if (source === "none" || !creds) {
9970
- console.log(source_default.yellow("Not signed in."));
9971
- console.log(source_default.dim(" Run ") + source_default.white("construct login") + source_default.dim(" or sign in to the Construct app."));
9972
- return;
9973
- }
9974
- const user = creds.user;
9975
- const sourceLabel = source === "app" ? source_default.cyan("Construct app profile") : source_default.blue("CLI login");
9976
- console.log();
9977
- console.log(source_default.bold(user?.name || "Signed in"));
9978
- console.log(source_default.dim(" " + (user?.email || "")));
9979
- console.log(source_default.dim(" id: ") + source_default.dim(user?.id || "\u2014"));
9980
- console.log(source_default.dim(" source: ") + sourceLabel);
9981
- console.log(source_default.dim(" portal: ") + (creds.portal || DEFAULT_PORTAL));
9982
- try {
9983
- const resp = await fetch(`${creds.portal || DEFAULT_PORTAL}/api/auth/cli-verify`, {
9984
- headers: { Authorization: `Bearer ${creds.token}` }
9985
- });
9986
- if (!resp.ok) {
9987
- console.log(source_default.dim(" publishers: ") + source_default.red(`error (${resp.status})`));
9988
- return;
9989
- }
9990
- const body = await resp.json();
9991
- const list = body.publishers || [];
9992
- if (list.length === 0) {
9993
- console.log(source_default.dim(" publishers: ") + source_default.yellow("none yet \u2014 enroll at developer.construct.space"));
9994
- return;
9995
- }
9996
- console.log();
9997
- console.log(source_default.bold("Publishers"));
9998
- for (const p of list) {
9999
- const kindLabel = p.kind === "org" ? source_default.cyan("org") : p.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
10000
- const verified = p.verified ? source_default.green(" \u2713") : "";
10001
- console.log(` ${source_default.white(p.name)} (${kindLabel})${verified}`);
10002
- if (p.orgId)
10003
- console.log(source_default.dim(` org: ${p.orgId}`));
10004
- }
10005
- } catch (err) {
10006
- console.log(source_default.dim(" publishers: ") + source_default.red("could not reach portal"));
10007
- }
10008
- console.log();
10009
- if (source === "app") {
10010
- console.log(source_default.dim("To switch identities, change the active profile in the Construct app."));
10011
- } else {
10012
- console.log(source_default.dim("To switch identities, run 'construct logout' then 'construct login'."));
10013
- }
9957
+ console.log(source_default.green("Logged out."));
10014
9958
  }
10015
9959
 
10016
9960
  // src/commands/update.ts
@@ -10285,7 +10229,7 @@ function updateBarrel(modelsDir, modelName) {
10285
10229
  }
10286
10230
 
10287
10231
  // src/commands/graph/push.ts
10288
- import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync12 } from "fs";
10232
+ import { existsSync as existsSync16, readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
10289
10233
  import { join as join20, basename as basename7 } from "path";
10290
10234
  async function graphPush() {
10291
10235
  const root = process.cwd();
@@ -10299,7 +10243,7 @@ async function graphPush() {
10299
10243
  console.error(source_default.red("No src/models/ directory found. Run 'construct graph init' first."));
10300
10244
  process.exit(1);
10301
10245
  }
10302
- const modelFiles = readdirSync4(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10246
+ const modelFiles = readdirSync5(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10303
10247
  if (modelFiles.length === 0) {
10304
10248
  console.error(source_default.red("No model files found in src/models/"));
10305
10249
  console.log(source_default.dim(" Generate one: construct graph g User name:string email:string"));
@@ -10324,15 +10268,17 @@ async function graphPush() {
10324
10268
  console.error(source_default.red(err.message));
10325
10269
  process.exit(1);
10326
10270
  }
10327
- const graphURL = process.env.GRAPH_URL || "https://graph.construct.space";
10271
+ const graphURL = process.env.GRAPH_URL || "https://my.construct.space/api/graph";
10328
10272
  const spinner = ora("Registering models...").start();
10329
10273
  try {
10274
+ const userID = creds.user?.id || "";
10330
10275
  const resp = await fetch(`${graphURL}/api/schemas/register`, {
10331
10276
  method: "POST",
10332
10277
  headers: {
10333
10278
  "Content-Type": "application/json",
10334
10279
  Authorization: `Bearer ${creds.token}`,
10335
- "X-Space-ID": m.id
10280
+ "X-Space-ID": m.id,
10281
+ "X-Auth-User-ID": userID
10336
10282
  },
10337
10283
  body: JSON.stringify({
10338
10284
  space_id: m.id,
@@ -10342,6 +10288,20 @@ async function graphPush() {
10342
10288
  manifest: { version: 1, models }
10343
10289
  })
10344
10290
  });
10291
+ if (resp.status === 403) {
10292
+ spinner.fail("Ownership check failed");
10293
+ try {
10294
+ const errBody = await resp.json();
10295
+ console.error(source_default.red(` ${errBody.error || "You are not the owner of this space"}`));
10296
+ if (errBody.owner_user_id) {
10297
+ console.error(source_default.dim(` Current owner: ${errBody.owner_user_id}`));
10298
+ }
10299
+ console.error(source_default.dim(" Fork to a new space_id to publish your own version."));
10300
+ } catch {
10301
+ console.error(source_default.red(` 403: Forbidden \u2014 ownership check failed`));
10302
+ }
10303
+ process.exit(1);
10304
+ }
10345
10305
  if (!resp.ok) {
10346
10306
  const body = await resp.text();
10347
10307
  spinner.fail("Registration failed");
@@ -10424,7 +10384,7 @@ function parseModelFile(content, fileName) {
10424
10384
  }
10425
10385
 
10426
10386
  // src/commands/graph/migrate.ts
10427
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync13 } from "fs";
10387
+ import { existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
10428
10388
  import { join as join21, basename as basename8 } from "path";
10429
10389
  async function graphMigrate(options) {
10430
10390
  const root = process.cwd();
@@ -10445,7 +10405,7 @@ async function graphMigrate(options) {
10445
10405
  console.error(source_default.red(err.message));
10446
10406
  process.exit(1);
10447
10407
  }
10448
- const graphURL = process.env.GRAPH_URL || "https://graph.construct.space";
10408
+ const graphURL = process.env.GRAPH_URL || "https://my.construct.space/api/graph";
10449
10409
  const spinner = ora("Fetching current schema...").start();
10450
10410
  let serverModels = [];
10451
10411
  try {
@@ -10461,7 +10421,7 @@ async function graphMigrate(options) {
10461
10421
  spinner.fail("Could not fetch schema");
10462
10422
  process.exit(1);
10463
10423
  }
10464
- const modelFiles = readdirSync5(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10424
+ const modelFiles = readdirSync6(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10465
10425
  const localModels = [];
10466
10426
  for (const file of modelFiles) {
10467
10427
  const content = readFileSync13(join21(modelsDir, file), "utf-8");
@@ -10568,7 +10528,7 @@ function parseModelFields(content, fileName) {
10568
10528
  }
10569
10529
 
10570
10530
  // src/index.ts
10571
- var VERSION = "1.1.11";
10531
+ var VERSION = "1.2.0";
10572
10532
  var program2 = new Command;
10573
10533
  program2.name("construct").description("Construct CLI \u2014 scaffold, build, develop, and publish spaces").version(VERSION);
10574
10534
  program2.command("scaffold [name]").alias("new").alias("create").description("Create a new Construct space project").option("--with-tests", "Include E2E testing boilerplate").option("--full", "Full preset: multiple pages, extra skills, widget templates").action(async (name, opts) => scaffold(name, opts));
@@ -10581,7 +10541,6 @@ program2.command("check").description("Run type-check (vue-tsc) and linter (esli
10581
10541
  program2.command("clean").description("Remove build artifacts").option("--all", "Also remove node_modules and lockfiles").action((opts) => clean(opts));
10582
10542
  program2.command("login").description("Authenticate with Construct").option("--portal <url>", "Portal URL").action(async (opts) => login(opts));
10583
10543
  program2.command("logout").description("Sign out").action(() => logout());
10584
- program2.command("whoami").alias("status").description("Show the active profile, its source (app or CLI), and linked publishers").action(() => whoami());
10585
10544
  program2.command("update").description("Update the CLI to the latest version").action(() => update());
10586
10545
  var graph = program2.command("graph").description("Construct Graph \u2014 data models and GraphQL");
10587
10546
  graph.command("init").description("Initialize Graph in a space project").action(() => graphInit());
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -34,7 +34,7 @@
34
34
  "keywords": ["{{.ID}}"],
35
35
  "agent": "agent/config.md",
36
36
  "skills": ["agent/skills/default.md"],
37
- "actions": {},
37
+ "actions": "src/actions.ts",
38
38
  "widgets": [
39
39
  {
40
40
  "id": "summary",
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 2×1 compact view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 4×1 wide view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@construct-space/cli",
3
- "version": "1.1.11",
3
+ "version": "1.2.0",
4
4
  "description": "Construct CLI — scaffold, build, develop, and publish spaces",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -34,7 +34,7 @@
34
34
  "keywords": ["{{.ID}}"],
35
35
  "agent": "agent/config.md",
36
36
  "skills": ["agent/skills/default.md"],
37
- "actions": {},
37
+ "actions": "src/actions.ts",
38
38
  "widgets": [
39
39
  {
40
40
  "id": "summary",
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 2×1 compact view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 4×1 wide view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>