@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.
- package/dist/index.js +118 -94
- 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
|
|
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 (
|
|
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
|
|
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://
|
|
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
|
|
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
|
|
9529
|
-
if (
|
|
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 =
|
|
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}/
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
const
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
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
|
-
|
|
9940
|
-
|
|
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
|
-
|
|
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(`
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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.
|
|
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");
|