ds-01 0.1.4 → 0.1.6

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.
@@ -1,3 +1,3 @@
1
1
  [?9001h[?1004h]0;C:\WINDOWS\system32\cmd.exe[?25h[?25l
2
- > ds-01@0.1.3 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
2
+ > ds-01@0.1.5 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
3
3
  > tsc[?25h[?9001l[?1004l
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,GAAG,SAiEZ,CAAC"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkDpC,eAAO,MAAM,GAAG,SAwGZ,CAAC"}
@@ -1,63 +1,104 @@
1
1
  import { Command } from "commander";
2
- import chalk from "chalk";
3
- import ora from "ora";
2
+ import { intro, outro, spinner, cancel, note } from "@clack/prompts";
3
+ import pc from "picocolors";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import { execSync } from "child_process";
7
7
  import { getToken } from "../utils/config.js";
8
- const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app"; // Point to localhost for testing
8
+ const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
9
+ // Helper to verify Tailwind exists before doing anything
10
+ function checkTailwindInstallation() {
11
+ const targetDir = process.cwd();
12
+ const pkgJsonPath = path.join(targetDir, "package.json");
13
+ // 1. Ensure they are in a valid Node.js project
14
+ if (!fs.existsSync(pkgJsonPath)) {
15
+ cancel(pc.red("No package.json found. Please run this command inside a Node.js project."));
16
+ process.exit(1);
17
+ }
18
+ // 2. Read package.json dependencies
19
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
20
+ const allDeps = {
21
+ ...pkgJson.dependencies,
22
+ ...pkgJson.devDependencies,
23
+ };
24
+ // 3. Strict Tailwind Check
25
+ if (!allDeps["tailwindcss"]) {
26
+ cancel(pc.red("Tailwind CSS is missing from your project dependencies.\n") +
27
+ pc.gray("DS01 components rely strictly on Tailwind CSS for styling.\n\n") +
28
+ pc.white("Kindly install it and configure your project, then retry:\n") +
29
+ pc.cyan(" npm install tailwindcss @tailwindcss/postcss postcss\n\n") +
30
+ pc.gray("Official Setup Guide: ") +
31
+ pc.underline("https://tailwindcss.com/docs/installation"));
32
+ process.exit(1);
33
+ }
34
+ }
9
35
  export const add = new Command()
10
36
  .name("add")
11
37
  .description("Add a component from DS01 to your project")
12
38
  .argument("<component>", "The name of the component (e.g., premium-section)")
13
39
  .action(async (componentName) => {
40
+ console.log(); // Spacing for visual breathing room
41
+ // 1. Premium Header
42
+ intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("Adding Component")}`);
43
+ // 2. Run Pre-Flight Tailwind Check
44
+ checkTailwindInstallation();
14
45
  const cwd = process.cwd();
15
46
  const configPath = path.join(cwd, "ds01.config.json");
16
47
  if (!fs.existsSync(configPath)) {
17
- console.log(chalk.red("ds01.config.json not found."));
18
- console.log(`Run ${chalk.cyan("npx @arpit2023/ds01 init")} first.`);
48
+ cancel(pc.red("ds01.config.json not found. Run 'npx ds-01 init' first."));
19
49
  process.exit(1);
20
50
  }
21
51
  const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
22
52
  const token = getToken();
23
- const spinner = ora(`Fetching <${componentName} /> from registry...`).start();
53
+ const s = spinner();
54
+ s.start(`Fetching ${pc.cyan(`<${componentName} />`)} from registry...`);
24
55
  try {
56
+ // 3. Fetch component from your API
25
57
  const response = await fetch(`${API_BASE_URL}/api/registry/${componentName}`, {
26
- headers: { Authorization: `Bearer ${token || ""}` },
58
+ headers: {
59
+ Authorization: `Bearer ${config.token}`,
60
+ "X-Machine-ID": config.machineId, // <-- ADD THIS LINE
61
+ },
27
62
  });
28
63
  if (!response.ok) {
29
- throw new Error(`Component not found or server error (${response.status})`);
64
+ s.stop(pc.red("Component fetch failed."));
65
+ cancel(`Component not found or server error (HTTP ${response.status})`);
66
+ process.exit(1);
30
67
  }
31
68
  const componentData = await response.json();
32
- // Create a dedicated folder for the component inside the user's project
69
+ // 4. Create dedicated component folder
33
70
  const targetDir = path.join(cwd, config.componentsPath, componentName);
34
71
  if (!fs.existsSync(targetDir)) {
35
72
  fs.mkdirSync(targetDir, { recursive: true });
36
73
  }
37
- // Write all the pure files!
74
+ // 5. Inject the pure code files
38
75
  for (const file of componentData.files) {
39
76
  const filePath = path.join(targetDir, file.name);
40
77
  fs.writeFileSync(filePath, file.content);
41
78
  }
42
- spinner.succeed(chalk.green(`Downloaded ${componentData.files.length} files for ${componentName}`));
43
- // Auto-install dependencies like framer-motion
79
+ s.stop(pc.green(`Downloaded ${componentData.files.length} files into ${pc.white(`/${config.componentsPath}/${componentName}`)}`));
80
+ // 6. Auto-install Missing Component Dependencies (e.g., framer-motion)
44
81
  if (componentData.dependencies && componentData.dependencies.length > 0) {
45
82
  const depsToInstall = componentData.dependencies.join(" ");
46
- const installSpinner = ora(`Installing dependencies: ${chalk.cyan(depsToInstall)}...`).start();
83
+ s.start(`Installing missing dependencies: ${pc.cyan(depsToInstall)}...`);
47
84
  try {
85
+ // Silently runs the npm install command in the background
48
86
  execSync(`npm install ${depsToInstall}`, { stdio: "ignore" });
49
- installSpinner.succeed(chalk.green("Dependencies installed successfully."));
87
+ s.stop(pc.green(`Dependencies installed successfully: ${pc.gray(depsToInstall)}`));
50
88
  }
51
89
  catch (error) {
52
- installSpinner.fail(chalk.red("Failed to auto-install dependencies. Please install manually."));
90
+ s.stop(pc.red("Failed to auto-install dependencies."));
91
+ note(`Please run: ${pc.cyan(`npm install ${depsToInstall}`)} manually.`, "Manual Action Required");
53
92
  }
54
93
  }
55
- console.log("\n" + chalk.green.bold(`✔ Component <${componentName} /> is ready!`));
56
- console.log(chalk.gray(`Import it: import { Section } from "@/${config.componentsPath}/${componentName}/Section"`));
94
+ // 7. Clean Success Outro
95
+ outro(`${pc.white("✔")} ${pc.bold(`Component <${componentName} /> is ready!`)}\n` +
96
+ pc.gray(`Import it: `) +
97
+ pc.cyan(`import { Section } from "@/${config.componentsPath}/${componentName}/Section"`));
57
98
  }
58
99
  catch (error) {
59
- spinner.fail(chalk.red(`Failed to add component.`));
60
- console.error(chalk.red(error.message));
100
+ s.stop(pc.red("An error occurred during injection."));
101
+ cancel(pc.gray(error.message || "Unknown CLI Error"));
61
102
  process.exit(1);
62
103
  }
63
104
  });
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,eAAO,MAAM,KAAK,SAgFd,CAAC"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,KAAK,SA4Fd,CAAC"}
@@ -1,43 +1,49 @@
1
1
  import { Command } from "commander";
2
- import ora from "ora";
3
- import chalk from "chalk";
2
+ import { intro, outro, spinner, note, cancel } from "@clack/prompts";
3
+ import pc from "picocolors";
4
4
  import open from "open";
5
5
  import machineIdPkg from "node-machine-id";
6
- import { saveToken } from "../utils/config.js"; // <-- Imported our new wallet helper!
7
- // ⚠️ In production, replace with your actual deployed Vercel domain
6
+ import { saveToken } from "../utils/config.js";
8
7
  const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
9
8
  const { machineIdSync } = machineIdPkg;
10
9
  export const login = new Command()
11
10
  .name("login")
12
11
  .description("Authenticate your terminal with DS01 via browser")
13
12
  .action(async () => {
14
- const spinner = ora("Initializing secure login flow...").start();
13
+ console.log(); // Spacing for breathing room
14
+ // 1. Header banner
15
+ intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("v1.0.0 — Terminal Authentication")}`);
16
+ const s = spinner();
15
17
  try {
16
- // 1. Generate Hardware Fingerprint Hash (MAC/CPU bound)
18
+ // 2. Hardware ID Generation & Device Code Request
19
+ s.start("Generating hardware fingerprint & requesting pairing code...");
17
20
  const hardwareId = machineIdSync();
18
- // 2. Request a new Device Code from the Next.js API
19
21
  const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
20
22
  method: "POST",
21
- headers: {
22
- "Content-Type": "application/json",
23
- },
23
+ headers: { "Content-Type": "application/json" },
24
24
  body: JSON.stringify({ machineId: hardwareId }),
25
25
  });
26
26
  if (!initResponse.ok) {
27
- throw new Error("Failed to reach DS01 authorization server.");
27
+ s.stop(pc.red("Failed to reach DS01 authorization server."));
28
+ cancel(`Server responded with HTTP ${initResponse.status}`);
29
+ process.exit(1);
28
30
  }
29
31
  const { deviceCode, userCode, verificationUrl, interval } = await initResponse.json();
30
- spinner.stop();
31
- console.log("\n" + chalk.cyan.bold("=== DS01 Secure Terminal Login ==="));
32
- console.log(`\nYour device pairing code is: ${chalk.bgCyan.black.bold(` ${userCode} `)}\n`);
33
- console.log(`If your browser does not open automatically, visit:`);
34
- console.log(chalk.underline.blue(verificationUrl) + "\n");
35
- // 3. Automatically open the user's default browser
36
- await open(verificationUrl);
37
- spinner.start("Waiting for web approval...");
38
- // 4. Poll the Token Endpoint
32
+ s.stop(pc.green("Device authorization code generated."));
33
+ // 3. Highlighted Box for User Action
34
+ note(`${pc.bold("Pairing Code:")} ${pc.cyan(pc.bold(` ${userCode} `))}\n` +
35
+ `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`, "Action Required");
36
+ // 4. Auto-launch Browser
37
+ try {
38
+ await open(verificationUrl);
39
+ }
40
+ catch {
41
+ // Non-blocking fallback if browser launch fails on headless setups
42
+ }
43
+ // 5. Polling Loop
44
+ s.start("Waiting for web authorization...");
39
45
  let token = null;
40
- const pollInterval = (interval || 5) * 1000; // Updated to match our 5s secure interval
46
+ const pollInterval = (interval || 5) * 1000;
41
47
  while (!token) {
42
48
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
43
49
  const tokenResponse = await fetch(`${API_BASE_URL}/api/auth/device/token`, {
@@ -50,20 +56,20 @@ export const login = new Command()
50
56
  token = tokenData.accessToken;
51
57
  }
52
58
  else if (tokenData.error !== "authorization_pending") {
53
- spinner.fail(chalk.red("Authorization failed or expired."));
54
- console.error(chalk.red(`Reason: ${tokenData.error}`));
59
+ s.stop(pc.red("Authorization failed or expired."));
60
+ cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
55
61
  process.exit(1);
56
62
  }
57
63
  }
58
- // 5. Save Token securely using our new config utility
59
- // We pass the token and hardwareId into the wallet.
64
+ // 6. Save token to local wallet
60
65
  saveToken(token, hardwareId);
61
- spinner.succeed(chalk.green.bold("Terminal paired successfully!"));
62
- console.log(chalk.gray(`Session token bound to hardware signature and saved locally.`));
66
+ s.stop(pc.green("Hardware signature linked & token verified!"));
67
+ // 7. Clean Success Outro
68
+ outro(`${pc.white("✔")} ${pc.bold("Terminal synced successfully")} ${pc.gray("Session token saved locally.")}`);
63
69
  }
64
70
  catch (error) {
65
- spinner.fail(chalk.red("An error occurred during login."));
66
- console.error(chalk.red(error.message));
71
+ s.stop(pc.red("An error occurred during login."));
72
+ cancel(pc.gray(error.message || "Unknown CLI Error"));
67
73
  process.exit(1);
68
74
  }
69
75
  });
package/dist/index.js CHANGED
@@ -3,16 +3,18 @@ import { Command } from "commander";
3
3
  import { login } from "./commands/login.js";
4
4
  import { whoami } from "./commands/whoami.js";
5
5
  import { logout } from "./commands/logout.js";
6
- import { init } from "./commands/init.js"; // <-- Imported the new init command!
6
+ import { init } from "./commands/init.js";
7
+ import { add } from "./commands/add.js"; // <-- 1. Import the add command!
7
8
  const program = new Command();
8
9
  program
9
- .name("ds01")
10
+ .name("ds-01")
10
11
  .description("DS01 Custom Component Registry CLI")
11
- .version("1.0.0");
12
+ .version("0.1.5");
12
13
  // Register the commands
13
14
  program.addCommand(login);
14
15
  program.addCommand(whoami);
15
16
  program.addCommand(logout);
16
- program.addCommand(init); // <-- Registered it here!
17
+ program.addCommand(init);
18
+ program.addCommand(add); // <-- 2. Register the add command!
17
19
  // Parse the arguments from the terminal
18
20
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ds-01",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Make your site best with DS01",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,76 +1,155 @@
1
1
  import { Command } from "commander";
2
- import chalk from "chalk";
3
- import ora from "ora";
2
+ import { intro, outro, spinner, cancel, note } from "@clack/prompts";
3
+ import pc from "picocolors";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import { execSync } from "child_process";
7
7
  import { getToken } from "../utils/config.js";
8
8
 
9
- const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app"; // Point to localhost for testing
9
+ const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
10
+
11
+ // Helper to verify Tailwind exists before doing anything
12
+ function checkTailwindInstallation() {
13
+ const targetDir = process.cwd();
14
+ const pkgJsonPath = path.join(targetDir, "package.json");
15
+
16
+ // 1. Ensure they are in a valid Node.js project
17
+ if (!fs.existsSync(pkgJsonPath)) {
18
+ cancel(
19
+ pc.red(
20
+ "No package.json found. Please run this command inside a Node.js project.",
21
+ ),
22
+ );
23
+ process.exit(1);
24
+ }
25
+
26
+ // 2. Read package.json dependencies
27
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
28
+ const allDeps = {
29
+ ...pkgJson.dependencies,
30
+ ...pkgJson.devDependencies,
31
+ };
32
+
33
+ // 3. Strict Tailwind Check
34
+ if (!allDeps["tailwindcss"]) {
35
+ cancel(
36
+ pc.red("Tailwind CSS is missing from your project dependencies.\n") +
37
+ pc.gray(
38
+ "DS01 components rely strictly on Tailwind CSS for styling.\n\n",
39
+ ) +
40
+ pc.white(
41
+ "Kindly install it and configure your project, then retry:\n",
42
+ ) +
43
+ pc.cyan(" npm install tailwindcss @tailwindcss/postcss postcss\n\n") +
44
+ pc.gray("Official Setup Guide: ") +
45
+ pc.underline("https://tailwindcss.com/docs/installation"),
46
+ );
47
+ process.exit(1);
48
+ }
49
+ }
10
50
 
11
51
  export const add = new Command()
12
52
  .name("add")
13
53
  .description("Add a component from DS01 to your project")
14
54
  .argument("<component>", "The name of the component (e.g., premium-section)")
15
55
  .action(async (componentName: string) => {
56
+ console.log(); // Spacing for visual breathing room
57
+
58
+ // 1. Premium Header
59
+ intro(
60
+ `${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("Adding Component")}`,
61
+ );
62
+
63
+ // 2. Run Pre-Flight Tailwind Check
64
+ checkTailwindInstallation();
65
+
16
66
  const cwd = process.cwd();
17
67
  const configPath = path.join(cwd, "ds01.config.json");
18
68
 
19
69
  if (!fs.existsSync(configPath)) {
20
- console.log(chalk.red("ds01.config.json not found."));
21
- console.log(`Run ${chalk.cyan("npx @arpit2023/ds01 init")} first.`);
70
+ cancel(pc.red("ds01.config.json not found. Run 'npx ds-01 init' first."));
22
71
  process.exit(1);
23
72
  }
24
73
 
25
74
  const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
26
75
  const token = getToken();
27
76
 
28
- const spinner = ora(`Fetching <${componentName} /> from registry...`).start();
77
+ const s = spinner();
78
+ s.start(`Fetching ${pc.cyan(`<${componentName} />`)} from registry...`);
29
79
 
30
80
  try {
31
- const response = await fetch(`${API_BASE_URL}/api/registry/${componentName}`, {
32
- headers: { Authorization: `Bearer ${token || ""}` },
33
- });
81
+ // 3. Fetch component from your API
82
+ const response = await fetch(
83
+ `${API_BASE_URL}/api/registry/${componentName}`,
84
+ {
85
+ headers: {
86
+ Authorization: `Bearer ${config.token}`,
87
+ "X-Machine-ID": config.machineId, // <-- ADD THIS LINE
88
+ },
89
+ },
90
+ );
34
91
 
35
92
  if (!response.ok) {
36
- throw new Error(`Component not found or server error (${response.status})`);
93
+ s.stop(pc.red("Component fetch failed."));
94
+ cancel(`Component not found or server error (HTTP ${response.status})`);
95
+ process.exit(1);
37
96
  }
38
97
 
39
98
  const componentData = await response.json();
40
99
 
41
- // Create a dedicated folder for the component inside the user's project
100
+ // 4. Create dedicated component folder
42
101
  const targetDir = path.join(cwd, config.componentsPath, componentName);
43
102
  if (!fs.existsSync(targetDir)) {
44
103
  fs.mkdirSync(targetDir, { recursive: true });
45
104
  }
46
105
 
47
- // Write all the pure files!
106
+ // 5. Inject the pure code files
48
107
  for (const file of componentData.files) {
49
108
  const filePath = path.join(targetDir, file.name);
50
109
  fs.writeFileSync(filePath, file.content);
51
110
  }
52
-
53
- spinner.succeed(chalk.green(`Downloaded ${componentData.files.length} files for ${componentName}`));
54
111
 
55
- // Auto-install dependencies like framer-motion
112
+ s.stop(
113
+ pc.green(
114
+ `Downloaded ${componentData.files.length} files into ${pc.white(`/${config.componentsPath}/${componentName}`)}`,
115
+ ),
116
+ );
117
+
118
+ // 6. Auto-install Missing Component Dependencies (e.g., framer-motion)
56
119
  if (componentData.dependencies && componentData.dependencies.length > 0) {
57
120
  const depsToInstall = componentData.dependencies.join(" ");
58
- const installSpinner = ora(`Installing dependencies: ${chalk.cyan(depsToInstall)}...`).start();
59
-
121
+ s.start(
122
+ `Installing missing dependencies: ${pc.cyan(depsToInstall)}...`,
123
+ );
124
+
60
125
  try {
126
+ // Silently runs the npm install command in the background
61
127
  execSync(`npm install ${depsToInstall}`, { stdio: "ignore" });
62
- installSpinner.succeed(chalk.green("Dependencies installed successfully."));
128
+ s.stop(
129
+ pc.green(
130
+ `Dependencies installed successfully: ${pc.gray(depsToInstall)}`,
131
+ ),
132
+ );
63
133
  } catch (error) {
64
- installSpinner.fail(chalk.red("Failed to auto-install dependencies. Please install manually."));
134
+ s.stop(pc.red("Failed to auto-install dependencies."));
135
+ note(
136
+ `Please run: ${pc.cyan(`npm install ${depsToInstall}`)} manually.`,
137
+ "Manual Action Required",
138
+ );
65
139
  }
66
140
  }
67
141
 
68
- console.log("\n" + chalk.green.bold(`✔ Component <${componentName} /> is ready!`));
69
- console.log(chalk.gray(`Import it: import { Section } from "@/${config.componentsPath}/${componentName}/Section"`));
70
-
142
+ // 7. Clean Success Outro
143
+ outro(
144
+ `${pc.white("✔")} ${pc.bold(`Component <${componentName} /> is ready!`)}\n` +
145
+ pc.gray(`Import it: `) +
146
+ pc.cyan(
147
+ `import { Section } from "@/${config.componentsPath}/${componentName}/Section"`,
148
+ ),
149
+ );
71
150
  } catch (error: any) {
72
- spinner.fail(chalk.red(`Failed to add component.`));
73
- console.error(chalk.red(error.message));
151
+ s.stop(pc.red("An error occurred during injection."));
152
+ cancel(pc.gray(error.message || "Unknown CLI Error"));
74
153
  process.exit(1);
75
154
  }
76
- });
155
+ });
@@ -1,11 +1,10 @@
1
1
  import { Command } from "commander";
2
- import ora from "ora";
3
- import chalk from "chalk";
2
+ import { intro, outro, spinner, note, cancel } from "@clack/prompts";
3
+ import pc from "picocolors";
4
4
  import open from "open";
5
5
  import machineIdPkg from "node-machine-id";
6
- import { saveToken } from "../utils/config.js"; // <-- Imported our new wallet helper!
6
+ import { saveToken } from "../utils/config.js";
7
7
 
8
- // ⚠️ In production, replace with your actual deployed Vercel domain
9
8
  const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
10
9
  const { machineIdSync } = machineIdPkg;
11
10
 
@@ -13,43 +12,56 @@ export const login = new Command()
13
12
  .name("login")
14
13
  .description("Authenticate your terminal with DS01 via browser")
15
14
  .action(async () => {
16
- const spinner = ora("Initializing secure login flow...").start();
15
+ console.log(); // Spacing for breathing room
16
+
17
+ // 1. Header banner
18
+ intro(
19
+ `${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("v1.0.0 — Terminal Authentication")}`
20
+ );
21
+
22
+ const s = spinner();
17
23
 
18
24
  try {
19
- // 1. Generate Hardware Fingerprint Hash (MAC/CPU bound)
25
+ // 2. Hardware ID Generation & Device Code Request
26
+ s.start("Generating hardware fingerprint & requesting pairing code...");
20
27
  const hardwareId = machineIdSync();
21
28
 
22
- // 2. Request a new Device Code from the Next.js API
23
29
  const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
24
30
  method: "POST",
25
- headers: {
26
- "Content-Type": "application/json",
27
- },
28
- body: JSON.stringify({ machineId: hardwareId }),
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify({ machineId: hardwareId }),
29
33
  });
30
34
 
31
35
  if (!initResponse.ok) {
32
- throw new Error("Failed to reach DS01 authorization server.");
36
+ s.stop(pc.red("Failed to reach DS01 authorization server."));
37
+ cancel(`Server responded with HTTP ${initResponse.status}`);
38
+ process.exit(1);
33
39
  }
34
40
 
35
41
  const { deviceCode, userCode, verificationUrl, interval } =
36
42
  await initResponse.json();
37
43
 
38
- spinner.stop();
39
- console.log("\n" + chalk.cyan.bold("=== DS01 Secure Terminal Login ==="));
40
- console.log(
41
- `\nYour device pairing code is: ${chalk.bgCyan.black.bold(` ${userCode} `)}\n`,
44
+ s.stop(pc.green("Device authorization code generated."));
45
+
46
+ // 3. Highlighted Box for User Action
47
+ note(
48
+ `${pc.bold("Pairing Code:")} ${pc.cyan(pc.bold(` ${userCode} `))}\n` +
49
+ `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`,
50
+ "Action Required"
42
51
  );
43
- console.log(`If your browser does not open automatically, visit:`);
44
- console.log(chalk.underline.blue(verificationUrl) + "\n");
45
52
 
46
- // 3. Automatically open the user's default browser
47
- await open(verificationUrl);
48
- spinner.start("Waiting for web approval...");
53
+ // 4. Auto-launch Browser
54
+ try {
55
+ await open(verificationUrl);
56
+ } catch {
57
+ // Non-blocking fallback if browser launch fails on headless setups
58
+ }
59
+
60
+ // 5. Polling Loop
61
+ s.start("Waiting for web authorization...");
49
62
 
50
- // 4. Poll the Token Endpoint
51
- let token = null;
52
- const pollInterval = (interval || 5) * 1000; // Updated to match our 5s secure interval
63
+ let token: string | null = null;
64
+ const pollInterval = (interval || 5) * 1000;
53
65
 
54
66
  while (!token) {
55
67
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
@@ -60,7 +72,7 @@ export const login = new Command()
60
72
  method: "POST",
61
73
  headers: { "Content-Type": "application/json" },
62
74
  body: JSON.stringify({ deviceCode, machineId: hardwareId }),
63
- },
75
+ }
64
76
  );
65
77
 
66
78
  const tokenData = await tokenResponse.json();
@@ -68,25 +80,24 @@ export const login = new Command()
68
80
  if (tokenResponse.ok && tokenData.accessToken) {
69
81
  token = tokenData.accessToken;
70
82
  } else if (tokenData.error !== "authorization_pending") {
71
- spinner.fail(chalk.red("Authorization failed or expired."));
72
- console.error(chalk.red(`Reason: ${tokenData.error}`));
83
+ s.stop(pc.red("Authorization failed or expired."));
84
+ cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
73
85
  process.exit(1);
74
86
  }
75
87
  }
76
88
 
77
- // 5. Save Token securely using our new config utility
78
- // We pass the token and hardwareId into the wallet.
89
+ // 6. Save token to local wallet
79
90
  saveToken(token, hardwareId);
80
91
 
81
- spinner.succeed(chalk.green.bold("Terminal paired successfully!"));
82
- console.log(
83
- chalk.gray(
84
- `Session token bound to hardware signature and saved locally.`,
85
- ),
92
+ s.stop(pc.green("Hardware signature linked & token verified!"));
93
+
94
+ // 7. Clean Success Outro
95
+ outro(
96
+ `${pc.white("✔")} ${pc.bold("Terminal synced successfully")} ${pc.gray("Session token saved locally.")}`
86
97
  );
87
98
  } catch (error: any) {
88
- spinner.fail(chalk.red("An error occurred during login."));
89
- console.error(chalk.red(error.message));
99
+ s.stop(pc.red("An error occurred during login."));
100
+ cancel(pc.gray(error.message || "Unknown CLI Error"));
90
101
  process.exit(1);
91
102
  }
92
103
  });
package/src/index.ts CHANGED
@@ -4,20 +4,22 @@ import { Command } from "commander";
4
4
  import { login } from "./commands/login.js";
5
5
  import { whoami } from "./commands/whoami.js";
6
6
  import { logout } from "./commands/logout.js";
7
- import { init } from "./commands/init.js"; // <-- Imported the new init command!
7
+ import { init } from "./commands/init.js";
8
+ import { add } from "./commands/add.js"; // <-- 1. Import the add command!
8
9
 
9
10
  const program = new Command();
10
11
 
11
12
  program
12
- .name("ds01")
13
+ .name("ds-01")
13
14
  .description("DS01 Custom Component Registry CLI")
14
- .version("1.0.0");
15
+ .version("0.1.5");
15
16
 
16
17
  // Register the commands
17
18
  program.addCommand(login);
18
19
  program.addCommand(whoami);
19
20
  program.addCommand(logout);
20
- program.addCommand(init); // <-- Registered it here!
21
+ program.addCommand(init);
22
+ program.addCommand(add); // <-- 2. Register the add command!
21
23
 
22
24
  // Parse the arguments from the terminal
23
25
  program.parse(process.argv);