@zaflun/lumio-cli 0.1.0 → 0.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.
Files changed (2) hide show
  1. package/dist/index.js +76 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  // src/program.ts
2
2
  import { Command } from "commander";
3
3
 
4
+ // src/commands/login.ts
5
+ import * as http from "http";
6
+ import * as crypto from "crypto";
7
+
4
8
  // src/auth/credential-store.ts
5
9
  import * as fs from "fs";
6
10
  import * as path from "path";
@@ -54,6 +58,11 @@ var CredentialStore = class {
54
58
  };
55
59
 
56
60
  // src/commands/login.ts
61
+ function generatePkceChallenge() {
62
+ const verifier = crypto.randomBytes(32).toString("base64url");
63
+ const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
64
+ return { verifier, challenge };
65
+ }
57
66
  function registerLogin(program2) {
58
67
  program2.command("login").description("Authenticate with Lumio via browser (PKCE) or token").option("--token <token>", "Authenticate with an API token (for CI/CD)").option("--api-url <url>", "Custom API URL", "https://api.lumio.vision").action(async (options) => {
59
68
  const store = new CredentialStore();
@@ -62,12 +71,69 @@ function registerLogin(program2) {
62
71
  console.log("Authenticated with token.");
63
72
  return;
64
73
  }
65
- console.log("Opening browser for authentication...");
66
- const { default: open } = await import("open");
67
- const pkceUrl = `${options.apiUrl}/v1/auth/cli/authorize`;
68
- await open(pkceUrl);
69
- console.log("Waiting for authentication callback...");
70
- console.log("If the browser did not open, visit:", pkceUrl);
74
+ const { verifier, challenge } = generatePkceChallenge();
75
+ const state = crypto.randomBytes(16).toString("hex");
76
+ const port = 9876;
77
+ const redirectUri = `http://localhost:${port}/callback`;
78
+ await new Promise((resolve9, reject) => {
79
+ const server = http.createServer(async (req, res) => {
80
+ if (!req.url?.startsWith("/callback")) {
81
+ res.writeHead(404);
82
+ res.end();
83
+ return;
84
+ }
85
+ const url = new URL(req.url, `http://localhost:${port}`);
86
+ const code = url.searchParams.get("code");
87
+ const returnedState = url.searchParams.get("state");
88
+ if (returnedState !== state || !code) {
89
+ res.writeHead(400, { "Content-Type": "text/html" });
90
+ res.end("<html><body><h2>Authentication failed.</h2><p>Invalid state or missing code. You can close this window.</p></body></html>");
91
+ server.close();
92
+ reject(new Error("Authentication failed: invalid state or missing code"));
93
+ return;
94
+ }
95
+ try {
96
+ const tokenRes = await fetch(`${options.apiUrl}/v1/auth/cli/token`, {
97
+ method: "POST",
98
+ headers: { "Content-Type": "application/json" },
99
+ body: JSON.stringify({
100
+ grant_type: "authorization_code",
101
+ code,
102
+ redirect_uri: redirectUri,
103
+ code_verifier: verifier
104
+ })
105
+ });
106
+ if (!tokenRes.ok) {
107
+ throw new Error(`Token exchange failed: ${tokenRes.status}`);
108
+ }
109
+ const tokenData = await tokenRes.json();
110
+ store.saveToken(tokenData.access_token, options.apiUrl);
111
+ res.writeHead(200, { "Content-Type": "text/html" });
112
+ res.end("<html><body><h2>Authenticated!</h2><p>You can close this window and return to the CLI.</p></body></html>");
113
+ } catch (e) {
114
+ res.writeHead(500, { "Content-Type": "text/html" });
115
+ res.end("<html><body><h2>Authentication failed.</h2><p>Token exchange error. You can close this window.</p></body></html>");
116
+ server.close();
117
+ reject(e);
118
+ return;
119
+ }
120
+ server.close();
121
+ resolve9();
122
+ });
123
+ server.listen(port, async () => {
124
+ const pkceUrl = `${options.apiUrl}/v1/auth/cli/authorize?code_challenge=${challenge}&code_challenge_method=S256&redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}`;
125
+ console.log("Opening browser for authentication...");
126
+ const { default: open } = await import("open");
127
+ await open(pkceUrl);
128
+ console.log("Waiting for authentication callback...");
129
+ console.log("If the browser did not open, visit:", pkceUrl);
130
+ });
131
+ setTimeout(() => {
132
+ server.close();
133
+ reject(new Error("Authentication timed out after 120 seconds"));
134
+ }, 12e4);
135
+ });
136
+ console.log("Authenticated successfully.");
71
137
  });
72
138
  }
73
139
 
@@ -253,7 +319,7 @@ function registerInit(program2) {
253
319
  const projectDir = path2.resolve(answers.name.toLowerCase().replace(/\s+/g, "-"));
254
320
  fs2.mkdirSync(projectDir, { recursive: true });
255
321
  const config = {
256
- $schema: "https://docs.lumio.vision/schemas/lumio.config.schema.json",
322
+ $schema: "https://lumio.vision/schemas/lumio.config.schema.json",
257
323
  extensionId: extension.id,
258
324
  name: answers.name,
259
325
  description: answers.description,
@@ -528,8 +594,10 @@ function registerDeploy(program2) {
528
594
  const sourceBuffer = fs4.readFileSync(sourceArchive);
529
595
  formData.append("source", new Blob([sourceBuffer]), "source.tar.gz");
530
596
  }
597
+ const manifest = JSON.parse(configJson);
598
+ manifest.version = version;
531
599
  formData.append("version", version);
532
- formData.append("manifest", configJson);
600
+ formData.append("manifest", JSON.stringify(manifest));
533
601
  formData.append("release_notes", releaseNotes);
534
602
  formData.append("is_breaking", String(!!options.breaking));
535
603
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaflun/lumio-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Lumio Extension CLI — Build, test, and deploy Lumio extensions",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",