@construct-space/cli 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +118 -94
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4507,22 +4507,6 @@ var dist_default6 = createPrompt((config, done) => {
4507
4507
  });
4508
4508
  // src/lib/utils.ts
4509
4509
  import { execSync } from "child_process";
4510
- import { platform } from "os";
4511
- function openBrowser(url) {
4512
- try {
4513
- switch (platform()) {
4514
- case "darwin":
4515
- execSync(`open "${url}"`);
4516
- break;
4517
- case "linux":
4518
- execSync(`xdg-open "${url}"`);
4519
- break;
4520
- case "win32":
4521
- execSync(`rundll32 url.dll,FileProtocolHandler "${url}"`);
4522
- break;
4523
- }
4524
- } catch {}
4525
- }
4526
4510
  function git(dir, ...args) {
4527
4511
  const quoted = args.map((a) => a.includes(" ") || a.includes(":") ? `"${a}"` : a);
4528
4512
  return execSync(`git ${quoted.join(" ")}`, { cwd: dir, encoding: "utf-8" }).trim();
@@ -9390,12 +9374,12 @@ import { join as join11 } from "path";
9390
9374
  // src/lib/appdir.ts
9391
9375
  import { join as join10 } from "path";
9392
9376
  import { homedir } from "os";
9393
- import { platform as platform2 } from "process";
9377
+ import { platform } from "process";
9394
9378
  function dataDir() {
9395
9379
  if (process.env.CONSTRUCT_DATA_DIR)
9396
9380
  return process.env.CONSTRUCT_DATA_DIR;
9397
9381
  const home = homedir();
9398
- switch (platform2) {
9382
+ switch (platform) {
9399
9383
  case "darwin":
9400
9384
  return join10(home, "Library", "Application Support", "Construct");
9401
9385
  case "win32": {
@@ -9411,6 +9395,9 @@ function dataDir() {
9411
9395
  function spacesDir() {
9412
9396
  return join10(dataDir(), "spaces");
9413
9397
  }
9398
+ function profilesDir() {
9399
+ return join10(dataDir(), "profiles");
9400
+ }
9414
9401
  function spaceDir(spaceId) {
9415
9402
  return join10(spacesDir(), spaceId);
9416
9403
  }
@@ -9440,14 +9427,43 @@ function install() {
9440
9427
  }
9441
9428
 
9442
9429
  // src/commands/publish.ts
9443
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync5, unlinkSync as unlinkSync2 } from "fs";
9430
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync6, unlinkSync as unlinkSync2 } from "fs";
9444
9431
  import { join as join14, basename as basename6 } from "path";
9445
9432
 
9446
9433
  // src/lib/auth.ts
9447
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync, existsSync as existsSync9 } from "fs";
9434
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync, existsSync as existsSync9, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
9448
9435
  import { join as join12, dirname as dirname4 } from "path";
9449
9436
  var CREDENTIALS_FILE = "credentials.json";
9450
- var DEFAULT_PORTAL = "https://developer.construct.space";
9437
+ var DEFAULT_PORTAL = "https://my.construct.space/api/developer";
9438
+ function listDesktopProfiles() {
9439
+ const dir = profilesDir();
9440
+ if (!existsSync9(dir))
9441
+ return [];
9442
+ const results = [];
9443
+ for (const entry of readdirSync4(dir)) {
9444
+ const full = join12(dir, entry);
9445
+ try {
9446
+ if (!statSync4(full).isDirectory())
9447
+ continue;
9448
+ const authPath = join12(full, "auth.json");
9449
+ if (!existsSync9(authPath))
9450
+ continue;
9451
+ const data = JSON.parse(readFileSync6(authPath, "utf-8"));
9452
+ if (!data.token)
9453
+ continue;
9454
+ if (data.authenticated !== undefined && !data.authenticated)
9455
+ continue;
9456
+ results.push({
9457
+ id: entry,
9458
+ token: data.token,
9459
+ user: data.user,
9460
+ updatedAt: data.updated_at,
9461
+ authenticated: true
9462
+ });
9463
+ } catch {}
9464
+ }
9465
+ return results;
9466
+ }
9451
9467
  function credentialsPath() {
9452
9468
  return join12(dataDir(), CREDENTIALS_FILE);
9453
9469
  }
@@ -9483,7 +9499,7 @@ function clear() {
9483
9499
  }
9484
9500
 
9485
9501
  // src/lib/pack.ts
9486
- import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync10 } from "fs";
9502
+ import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync10 } from "fs";
9487
9503
  import { join as join13 } from "path";
9488
9504
  import { tmpdir } from "os";
9489
9505
  import { execSync as execSync3 } from "child_process";
@@ -9525,8 +9541,8 @@ async function packSource(root) {
9525
9541
  if (existsSync10(join13(root, name)))
9526
9542
  entries.push(name);
9527
9543
  }
9528
- for (const entry of readdirSync4(root)) {
9529
- if (statSync4(join13(root, entry)).isDirectory())
9544
+ for (const entry of readdirSync5(root)) {
9545
+ if (statSync5(join13(root, entry)).isDirectory())
9530
9546
  continue;
9531
9547
  if (allowedRootFiles.includes(entry))
9532
9548
  continue;
@@ -9546,7 +9562,7 @@ async function packSource(root) {
9546
9562
  const excludes = "--exclude=node_modules --exclude=dist --exclude=.git --exclude=*.env --exclude=*.log --exclude=*.lock --exclude=*.lockb";
9547
9563
  const cmd = `tar czf "${tarballPath}" ${excludes} ${validEntries.join(" ")}`;
9548
9564
  execSync3(cmd, { cwd: root });
9549
- const size = statSync4(tarballPath).size;
9565
+ const size = statSync5(tarballPath).size;
9550
9566
  if (size > MAX_SIZE) {
9551
9567
  throw new Error(`Source exceeds maximum size of ${MAX_SIZE / 1024 / 1024}MB`);
9552
9568
  }
@@ -9560,7 +9576,7 @@ async function uploadSource(portalURL, token, tarballPath, m) {
9560
9576
  const fileData = readFileSync7(tarballPath);
9561
9577
  const blob = new Blob([fileData]);
9562
9578
  formData.append("source", blob, basename6(tarballPath));
9563
- const resp = await fetch(`${portalURL}/api/publish`, {
9579
+ const resp = await fetch(`${portalURL}/publish`, {
9564
9580
  method: "POST",
9565
9581
  headers: { Authorization: `Bearer ${token}` },
9566
9582
  body: formData
@@ -9696,7 +9712,7 @@ async function publish(options) {
9696
9712
  let tarballPath;
9697
9713
  try {
9698
9714
  tarballPath = await packSource(root);
9699
- const size = statSync5(tarballPath).size;
9715
+ const size = statSync6(tarballPath).size;
9700
9716
  spinner.succeed(`Source packed (${formatBytes(size)})`);
9701
9717
  } catch (err) {
9702
9718
  spinner.fail("Pack failed");
@@ -9875,9 +9891,8 @@ function clean(options) {
9875
9891
  }
9876
9892
 
9877
9893
  // src/commands/login.ts
9878
- import { createServer } from "http";
9894
+ var ACCOUNTS_SCOPE_URL = "https://my.construct.space/api/accounts/me/scope";
9879
9895
  async function login(options) {
9880
- const portalURL = options?.portal || DEFAULT_PORTAL;
9881
9896
  if (isAuthenticated()) {
9882
9897
  const creds = load2();
9883
9898
  const name = creds.user?.name || "unknown";
@@ -9885,72 +9900,81 @@ async function login(options) {
9885
9900
  console.log(source_default.dim(" Run 'construct logout' to sign out first."));
9886
9901
  return;
9887
9902
  }
9888
- const server = createServer();
9889
- await new Promise((resolve3) => server.listen(0, "127.0.0.1", resolve3));
9890
- const port = server.address().port;
9891
- server.close();
9892
- const callbackURL = `http://localhost:${port}/callback`;
9893
- const loginURL = `${portalURL}/api/auth/cli-login?callback=${encodeURIComponent(callbackURL)}`;
9894
- const tokenPromise = new Promise((resolve3, reject) => {
9895
- const timeout = setTimeout(() => {
9896
- srv.close();
9897
- reject(new Error("Login timed out. Try again."));
9898
- }, 5 * 60 * 1000);
9899
- const srv = createServer((req, res) => {
9900
- const url = new URL(req.url, `http://localhost:${port}`);
9901
- if (url.pathname !== "/callback") {
9902
- res.end();
9903
- return;
9904
- }
9905
- const error2 = url.searchParams.get("error");
9906
- const token = url.searchParams.get("token");
9907
- res.setHeader("Content-Type", "text/html");
9908
- if (error2) {
9909
- res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
9910
- <h2 style="color:#EF4444">Login Failed</h2><p>${error2}</p>
9911
- <p style="color:#6B7280">You can close this window.</p></body></html>`);
9912
- clearTimeout(timeout);
9913
- srv.close();
9914
- reject(new Error(error2));
9915
- return;
9916
- }
9917
- if (!token) {
9918
- res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
9919
- <h2 style="color:#EF4444">Login Failed</h2><p>No token received.</p>
9920
- <p style="color:#6B7280">You can close this window.</p></body></html>`);
9921
- clearTimeout(timeout);
9922
- srv.close();
9923
- reject(new Error("No token received"));
9924
- return;
9925
- }
9926
- res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
9927
- <h2 style="color:#10B981">Logged In!</h2>
9928
- <p>You can close this window and return to your terminal.</p></body></html>`);
9929
- clearTimeout(timeout);
9930
- srv.close();
9931
- resolve3(token);
9932
- });
9933
- srv.listen(port, "127.0.0.1");
9934
- });
9935
- console.log(source_default.blue("Opening browser to log in..."));
9936
- console.log(source_default.dim(" If the browser doesn't open, visit:"));
9937
- console.log(source_default.dim(` ${loginURL}`));
9903
+ if (options?.token) {
9904
+ await loginWithToken(options.token);
9905
+ return;
9906
+ }
9907
+ const profiles = listDesktopProfiles();
9908
+ if (profiles.length > 0) {
9909
+ await loginFromProfile(profiles);
9910
+ return;
9911
+ }
9912
+ console.log(source_default.yellow("No signed-in profiles found on this machine."));
9913
+ console.log();
9914
+ console.log(" Two options:");
9915
+ console.log(" 1. Sign in with the Construct desktop app and re-run this command, or");
9916
+ console.log(` 2. Paste a token from ${source_default.cyan("https://my.construct.space/settings/tokens")} below.`);
9938
9917
  console.log();
9939
- openBrowser(loginURL);
9940
- console.log(source_default.dim(" Waiting for authentication..."));
9918
+ const token = await dist_default5({
9919
+ message: "Token (or press Ctrl+C to cancel):",
9920
+ validate: (s) => s.trim().length > 0 || "Token required"
9921
+ });
9922
+ await loginWithToken(token.trim());
9923
+ }
9924
+ async function loginFromProfile(profiles) {
9925
+ let picked = profiles[0];
9926
+ if (profiles.length > 1) {
9927
+ picked = await dist_default6({
9928
+ message: "Choose a profile to use:",
9929
+ choices: profiles.map((p) => ({ name: formatLabel(p), value: p }))
9930
+ });
9931
+ }
9932
+ const user = {
9933
+ id: picked.user?.id || picked.id,
9934
+ name: picked.user?.name || picked.user?.username || picked.id,
9935
+ email: picked.user?.email || ""
9936
+ };
9937
+ store({ token: picked.token, portal: DEFAULT_PORTAL, user });
9938
+ console.log(source_default.green(`Logged in as ${user.name}`));
9939
+ if (user.email)
9940
+ console.log(source_default.dim(` ${user.email}`));
9941
+ console.log(source_default.dim(` profile: ${picked.id}`));
9942
+ }
9943
+ async function loginWithToken(token) {
9944
+ let resp;
9941
9945
  try {
9942
- const token = await tokenPromise;
9943
- const resp = await fetch(`${portalURL}/api/auth/cli-verify`, {
9946
+ resp = await fetch(ACCOUNTS_SCOPE_URL, {
9944
9947
  headers: { Authorization: `Bearer ${token}` }
9945
9948
  });
9946
- const { user } = await resp.json();
9947
- store({ token, portal: portalURL, user });
9948
- console.log();
9949
- console.log(source_default.green(`Logged in as ${user?.name || "there"}`));
9950
9949
  } catch (err) {
9951
- console.error(source_default.red(`Login failed: ${err.message}`));
9950
+ console.error(source_default.red(`Failed to reach accounts service: ${err.message}`));
9951
+ process.exit(1);
9952
+ }
9953
+ if (resp.status === 401) {
9954
+ console.error(source_default.red("Token rejected by accounts service (invalid or expired)."));
9952
9955
  process.exit(1);
9953
9956
  }
9957
+ if (!resp.ok) {
9958
+ console.error(source_default.red(`Token verification failed (HTTP ${resp.status}).`));
9959
+ process.exit(1);
9960
+ }
9961
+ const body = await resp.json();
9962
+ const u = body.user || {};
9963
+ const user = {
9964
+ id: u.id || u.uuid || "",
9965
+ name: u.name || u.username || "",
9966
+ email: u.email || ""
9967
+ };
9968
+ store({ token, portal: DEFAULT_PORTAL, user });
9969
+ console.log(source_default.green(`Logged in as ${user.name || "unknown"}`));
9970
+ if (user.email)
9971
+ console.log(source_default.dim(` ${user.email}`));
9972
+ }
9973
+ function formatLabel(p) {
9974
+ const name = p.user?.name || p.user?.username || p.id;
9975
+ const email = p.user?.email;
9976
+ const kind = p.id.startsWith("org:") ? " [org]" : "";
9977
+ return email ? `${name}${kind} ${source_default.dim(email)}` : `${name}${kind} ${source_default.dim(p.id)}`;
9954
9978
  }
9955
9979
  function logout() {
9956
9980
  clear();
@@ -10229,7 +10253,7 @@ function updateBarrel(modelsDir, modelName) {
10229
10253
  }
10230
10254
 
10231
10255
  // src/commands/graph/push.ts
10232
- import { existsSync as existsSync16, readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
10256
+ import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync12 } from "fs";
10233
10257
  import { join as join20, basename as basename7 } from "path";
10234
10258
  async function graphPush() {
10235
10259
  const root = process.cwd();
@@ -10243,7 +10267,7 @@ async function graphPush() {
10243
10267
  console.error(source_default.red("No src/models/ directory found. Run 'construct graph init' first."));
10244
10268
  process.exit(1);
10245
10269
  }
10246
- const modelFiles = readdirSync5(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10270
+ const modelFiles = readdirSync6(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10247
10271
  if (modelFiles.length === 0) {
10248
10272
  console.error(source_default.red("No model files found in src/models/"));
10249
10273
  console.log(source_default.dim(" Generate one: construct graph g User name:string email:string"));
@@ -10384,7 +10408,7 @@ function parseModelFile(content, fileName) {
10384
10408
  }
10385
10409
 
10386
10410
  // src/commands/graph/migrate.ts
10387
- import { existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
10411
+ import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync13 } from "fs";
10388
10412
  import { join as join21, basename as basename8 } from "path";
10389
10413
  async function graphMigrate(options) {
10390
10414
  const root = process.cwd();
@@ -10421,7 +10445,7 @@ async function graphMigrate(options) {
10421
10445
  spinner.fail("Could not fetch schema");
10422
10446
  process.exit(1);
10423
10447
  }
10424
- const modelFiles = readdirSync6(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10448
+ const modelFiles = readdirSync7(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10425
10449
  const localModels = [];
10426
10450
  for (const file of modelFiles) {
10427
10451
  const content = readFileSync13(join21(modelsDir, file), "utf-8");
@@ -10528,7 +10552,7 @@ function parseModelFields(content, fileName) {
10528
10552
  }
10529
10553
 
10530
10554
  // src/index.ts
10531
- var VERSION = "1.2.0";
10555
+ var VERSION = "1.3.1";
10532
10556
  var program2 = new Command;
10533
10557
  program2.name("construct").description("Construct CLI \u2014 scaffold, build, develop, and publish spaces").version(VERSION);
10534
10558
  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));
@@ -10539,7 +10563,7 @@ program2.command("publish").description("Publish a space to the Construct regist
10539
10563
  program2.command("validate").description("Validate space.manifest.json").action(() => validate3());
10540
10564
  program2.command("check").description("Run type-check (vue-tsc) and linter (eslint)").action(() => check());
10541
10565
  program2.command("clean").description("Remove build artifacts").option("--all", "Also remove node_modules and lockfiles").action((opts) => clean(opts));
10542
- program2.command("login").description("Authenticate with Construct").option("--portal <url>", "Portal URL").action(async (opts) => login(opts));
10566
+ program2.command("login").description("Authenticate with Construct").option("--portal <url>", "Portal URL").option("--token <token>", "Authenticate with a token directly (skip desktop profile picker)").action(async (opts) => login(opts));
10543
10567
  program2.command("logout").description("Sign out").action(() => logout());
10544
10568
  program2.command("update").description("Update the CLI to the latest version").action(() => update());
10545
10569
  var graph = program2.command("graph").description("Construct Graph \u2014 data models and GraphQL");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@construct-space/cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Construct CLI — scaffold, build, develop, and publish spaces",
5
5
  "type": "module",
6
6
  "bin": {