ds-01 0.1.2 → 0.1.3

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
- > @repo/cli@0.0.1 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
2
+ > ds-01@0.1.2 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
3
3
  > tsc[?25h[?9001l[?1004l
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const add: Command;
3
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,63 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { execSync } from "child_process";
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
9
+ export const add = new Command()
10
+ .name("add")
11
+ .description("Add a component from DS01 to your project")
12
+ .argument("<component>", "The name of the component (e.g., premium-section)")
13
+ .action(async (componentName) => {
14
+ const cwd = process.cwd();
15
+ const configPath = path.join(cwd, "ds01.config.json");
16
+ 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.`);
19
+ process.exit(1);
20
+ }
21
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
22
+ const token = getToken();
23
+ const spinner = ora(`Fetching <${componentName} /> from registry...`).start();
24
+ try {
25
+ const response = await fetch(`${API_BASE_URL}/api/registry/${componentName}`, {
26
+ headers: { Authorization: `Bearer ${token || ""}` },
27
+ });
28
+ if (!response.ok) {
29
+ throw new Error(`Component not found or server error (${response.status})`);
30
+ }
31
+ const componentData = await response.json();
32
+ // Create a dedicated folder for the component inside the user's project
33
+ const targetDir = path.join(cwd, config.componentsPath, componentName);
34
+ if (!fs.existsSync(targetDir)) {
35
+ fs.mkdirSync(targetDir, { recursive: true });
36
+ }
37
+ // Write all the pure files!
38
+ for (const file of componentData.files) {
39
+ const filePath = path.join(targetDir, file.name);
40
+ fs.writeFileSync(filePath, file.content);
41
+ }
42
+ spinner.succeed(chalk.green(`Downloaded ${componentData.files.length} files for ${componentName}`));
43
+ // Auto-install dependencies like framer-motion
44
+ if (componentData.dependencies && componentData.dependencies.length > 0) {
45
+ const depsToInstall = componentData.dependencies.join(" ");
46
+ const installSpinner = ora(`Installing dependencies: ${chalk.cyan(depsToInstall)}...`).start();
47
+ try {
48
+ execSync(`npm install ${depsToInstall}`, { stdio: "ignore" });
49
+ installSpinner.succeed(chalk.green("Dependencies installed successfully."));
50
+ }
51
+ catch (error) {
52
+ installSpinner.fail(chalk.red("Failed to auto-install dependencies. Please install manually."));
53
+ }
54
+ }
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"`));
57
+ }
58
+ catch (error) {
59
+ spinner.fail(chalk.red(`Failed to add component.`));
60
+ console.error(chalk.red(error.message));
61
+ process.exit(1);
62
+ }
63
+ });
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const init: Command;
3
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,IAAI,SA4Fb,CAAC"}
@@ -0,0 +1,86 @@
1
+ import { Command } from "commander";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { intro, outro, spinner, text, isCancel, cancel } from "@clack/prompts";
5
+ import chalk from "chalk";
6
+ export const init = new Command()
7
+ .name("init")
8
+ .description("Initialize DS01 in your React/Next.js TypeScript project")
9
+ .action(async () => {
10
+ intro(chalk.bgCyan.black.bold(" DS01 Setup Wizard "));
11
+ const s = spinner();
12
+ s.start("Checking project environment...");
13
+ const cwd = process.cwd();
14
+ const packageJsonPath = path.join(cwd, "package.json");
15
+ const tsconfigPath = path.join(cwd, "tsconfig.json");
16
+ // 1. Check for package.json
17
+ if (!fs.existsSync(packageJsonPath)) {
18
+ s.stop("Environment check failed.");
19
+ cancel("No package.json found. Please run this command in the root of your project.");
20
+ process.exit(1);
21
+ }
22
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
23
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
24
+ // 2. Check strictly for React or Next.js
25
+ if (!deps.react && !deps.next) {
26
+ s.stop("Environment check failed.");
27
+ cancel("DS01 strictly supports React and Next.js environments. Please run this in a supported project.");
28
+ process.exit(1);
29
+ }
30
+ // 3. Check strictly for TypeScript
31
+ if (!fs.existsSync(tsconfigPath)) {
32
+ s.stop("Environment check failed.");
33
+ cancel("DS01 requires TypeScript. No tsconfig.json found in the project root.");
34
+ process.exit(1);
35
+ }
36
+ s.stop(chalk.green("✔ React and TypeScript detected!"));
37
+ // 4. Ask the user where their global CSS is located
38
+ const cssPath = await text({
39
+ message: "Where is your global CSS file?",
40
+ placeholder: "app/globals.css",
41
+ initialValue: "app/globals.css",
42
+ });
43
+ if (isCancel(cssPath)) {
44
+ cancel("Setup cancelled.");
45
+ process.exit(0);
46
+ }
47
+ // 5. Inject Dark Mode / Glassmorphic CSS variables
48
+ const fullCssPath = path.join(cwd, cssPath);
49
+ if (fs.existsSync(fullCssPath)) {
50
+ const cssContent = fs.readFileSync(fullCssPath, "utf-8");
51
+ if (!cssContent.includes("/* ds01-theme-start */")) {
52
+ const variables = `
53
+ /* ds01-theme-start */
54
+ @layer base {
55
+ :root {
56
+ --ds-background: #0a0a0a; /* Deep charcoal */
57
+ --ds-foreground: #ffffff;
58
+ --ds-glass-border: rgba(255, 255, 255, 0.08);
59
+ --ds-glass-blur: blur(16px);
60
+ }
61
+ }
62
+ /* ds01-theme-end */
63
+ `;
64
+ fs.appendFileSync(fullCssPath, variables);
65
+ console.log(chalk.green(`✔ Injected design tokens into ${cssPath}`));
66
+ }
67
+ else {
68
+ console.log(chalk.gray(`- Design tokens already exist in ${cssPath}`));
69
+ }
70
+ }
71
+ else {
72
+ console.log(chalk.yellow(`⚠ Could not find ${cssPath}. You may need to add the CSS variables manually.`));
73
+ }
74
+ // 6. Create the local configuration file so the CLI remembers this project
75
+ const configPath = path.join(cwd, "ds01.config.json");
76
+ const configData = {
77
+ $schema: "https://ds-01.vercel.app/schema.json",
78
+ style: "default",
79
+ typescript: true,
80
+ cssPath: cssPath,
81
+ componentsPath: "components/ui" // This is where we will inject components later
82
+ };
83
+ fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));
84
+ console.log(chalk.green(`✔ Created ds01.config.json`));
85
+ outro(chalk.green.bold("DS01 successfully initialized! Ready to add components."));
86
+ });
@@ -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;AAgBpC,eAAO,MAAM,KAAK,SAgGd,CAAC"}
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"}
@@ -3,13 +3,9 @@ import ora from "ora";
3
3
  import chalk from "chalk";
4
4
  import open from "open";
5
5
  import machineIdPkg from "node-machine-id";
6
- import fs from "fs";
7
- import path from "path";
8
- import os from "os";
6
+ import { saveToken } from "../utils/config.js"; // <-- Imported our new wallet helper!
9
7
  // ⚠️ In production, replace with your actual deployed Vercel domain
10
8
  const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
11
- const CONFIG_DIR = path.join(os.homedir(), ".ds01");
12
- const AUTH_FILE = path.join(CONFIG_DIR, "auth.json");
13
9
  const { machineIdSync } = machineIdPkg;
14
10
  export const login = new Command()
15
11
  .name("login")
@@ -25,7 +21,7 @@ export const login = new Command()
25
21
  headers: {
26
22
  "Content-Type": "application/json",
27
23
  },
28
- body: JSON.stringify({ machineId: hardwareId }), // <-- Send the machine ID to the server
24
+ body: JSON.stringify({ machineId: hardwareId }),
29
25
  });
30
26
  if (!initResponse.ok) {
31
27
  throw new Error("Failed to reach DS01 authorization server.");
@@ -41,9 +37,8 @@ export const login = new Command()
41
37
  spinner.start("Waiting for web approval...");
42
38
  // 4. Poll the Token Endpoint
43
39
  let token = null;
44
- const pollInterval = (interval || 2) * 1000;
40
+ const pollInterval = (interval || 5) * 1000; // Updated to match our 5s secure interval
45
41
  while (!token) {
46
- // Wait before polling again
47
42
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
48
43
  const tokenResponse = await fetch(`${API_BASE_URL}/api/auth/device/token`, {
49
44
  method: "POST",
@@ -55,21 +50,14 @@ export const login = new Command()
55
50
  token = tokenData.accessToken;
56
51
  }
57
52
  else if (tokenData.error !== "authorization_pending") {
58
- // If error is anything OTHER than pending, fail out.
59
53
  spinner.fail(chalk.red("Authorization failed or expired."));
60
54
  console.error(chalk.red(`Reason: ${tokenData.error}`));
61
55
  process.exit(1);
62
56
  }
63
57
  }
64
- // 5. Save Token & Machine ID to ~/.ds01/auth.json
65
- if (!fs.existsSync(CONFIG_DIR)) {
66
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
67
- }
68
- fs.writeFileSync(AUTH_FILE, JSON.stringify({
69
- token,
70
- machineId: hardwareId,
71
- updatedAt: new Date().toISOString(),
72
- }, null, 2));
58
+ // 5. Save Token securely using our new config utility
59
+ // We pass the token and hardwareId into the wallet.
60
+ saveToken(token, hardwareId);
73
61
  spinner.succeed(chalk.green.bold("Terminal paired successfully!"));
74
62
  console.log(chalk.gray(`Session token bound to hardware signature and saved locally.`));
75
63
  }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const logout: Command;
3
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,MAAM,SAuBf,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { deleteToken, getToken } from "../utils/config.js";
5
+ export const logout = new Command()
6
+ .name("logout")
7
+ .description("Log out of your DS01 account and clear local credentials")
8
+ .action(async () => {
9
+ const token = getToken();
10
+ if (!token) {
11
+ console.log(chalk.yellow("You are already logged out."));
12
+ return;
13
+ }
14
+ const spinner = ora("Logging out...").start();
15
+ try {
16
+ // Throw away the digital wallet / token
17
+ deleteToken();
18
+ spinner.succeed(chalk.green("Successfully logged out."));
19
+ console.log(chalk.gray("Your local credentials have been cleared."));
20
+ }
21
+ catch (error) {
22
+ spinner.fail(chalk.red("Failed to log out cleanly."));
23
+ console.error(error);
24
+ }
25
+ });
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const whoami: Command;
3
+ //# sourceMappingURL=whoami.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,MAAM,SAef,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { getToken } from "../utils/config.js";
4
+ export const whoami = new Command()
5
+ .name("whoami")
6
+ .description("Check your current authentication status")
7
+ .action(async () => {
8
+ const token = getToken();
9
+ if (!token) {
10
+ console.log(chalk.red("✖ You are not logged in."));
11
+ console.log(`Run ${chalk.cyan("npx @arpit2023/ds01 login")} to authenticate.`);
12
+ process.exit(1);
13
+ }
14
+ // For now, we just verify the token is saved.
15
+ console.log(chalk.green("✔ You are securely logged in!"));
16
+ console.log(chalk.gray(`Token found ending in: ...${token.slice(-6)}`));
17
+ });
package/dist/index.js CHANGED
@@ -1,13 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
3
  import { login } from "./commands/login.js";
4
- // Note: Add .js extension if you have module resolution issues, or keep it without depending on your tsconfig
4
+ import { whoami } from "./commands/whoami.js";
5
+ import { logout } from "./commands/logout.js";
6
+ import { init } from "./commands/init.js"; // <-- Imported the new init command!
5
7
  const program = new Command();
6
8
  program
7
9
  .name("ds01")
8
10
  .description("DS01 Custom Component Registry CLI")
9
11
  .version("1.0.0");
10
- // Register the login command
12
+ // Register the commands
11
13
  program.addCommand(login);
14
+ program.addCommand(whoami);
15
+ program.addCommand(logout);
16
+ program.addCommand(init); // <-- Registered it here!
12
17
  // Parse the arguments from the terminal
13
18
  program.parse(process.argv);
@@ -0,0 +1,4 @@
1
+ export declare function saveToken(token: string, machineId: string): void;
2
+ export declare function getToken(): string | null;
3
+ export declare function deleteToken(): void;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAQA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAczD;AAED,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAWxC;AAED,wBAAgB,WAAW,SAI1B"}
@@ -0,0 +1,37 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+ // We use the same path you originally had for consistency
5
+ const configDir = path.join(os.homedir(), ".ds01");
6
+ const credentialsPath = path.join(configDir, "auth.json");
7
+ export function saveToken(token, machineId) {
8
+ if (!fs.existsSync(configDir)) {
9
+ fs.mkdirSync(configDir, { recursive: true });
10
+ }
11
+ const payload = {
12
+ token,
13
+ machineId,
14
+ updatedAt: new Date().toISOString(),
15
+ };
16
+ fs.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2), {
17
+ mode: 0o600,
18
+ });
19
+ }
20
+ export function getToken() {
21
+ if (fs.existsSync(credentialsPath)) {
22
+ try {
23
+ const data = fs.readFileSync(credentialsPath, "utf-8");
24
+ const parsed = JSON.parse(data);
25
+ return parsed.token || null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ export function deleteToken() {
34
+ if (fs.existsSync(credentialsPath)) {
35
+ fs.unlinkSync(credentialsPath);
36
+ }
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ds-01",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Make your site best with DS01",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,14 +13,15 @@
13
13
  "access": "public"
14
14
  },
15
15
  "dependencies": {
16
- "chalk": "^5",
16
+ "@clack/prompts": "^1.7.0",
17
+ "chalk": "^5.6.2",
17
18
  "commander": "^14",
18
19
  "node-machine-id": "^1",
19
20
  "open": "^10",
20
21
  "ora": "^8"
21
22
  },
22
23
  "devDependencies": {
23
- "@types/node": "^24",
24
+ "@types/node": "^24.13.3",
24
25
  "tsup": "^8.5.1",
25
26
  "typescript": "^5"
26
27
  }
@@ -0,0 +1,76 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { execSync } from "child_process";
7
+ import { getToken } from "../utils/config.js";
8
+
9
+ const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app"; // Point to localhost for testing
10
+
11
+ export const add = new Command()
12
+ .name("add")
13
+ .description("Add a component from DS01 to your project")
14
+ .argument("<component>", "The name of the component (e.g., premium-section)")
15
+ .action(async (componentName: string) => {
16
+ const cwd = process.cwd();
17
+ const configPath = path.join(cwd, "ds01.config.json");
18
+
19
+ 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.`);
22
+ process.exit(1);
23
+ }
24
+
25
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
26
+ const token = getToken();
27
+
28
+ const spinner = ora(`Fetching <${componentName} /> from registry...`).start();
29
+
30
+ try {
31
+ const response = await fetch(`${API_BASE_URL}/api/registry/${componentName}`, {
32
+ headers: { Authorization: `Bearer ${token || ""}` },
33
+ });
34
+
35
+ if (!response.ok) {
36
+ throw new Error(`Component not found or server error (${response.status})`);
37
+ }
38
+
39
+ const componentData = await response.json();
40
+
41
+ // Create a dedicated folder for the component inside the user's project
42
+ const targetDir = path.join(cwd, config.componentsPath, componentName);
43
+ if (!fs.existsSync(targetDir)) {
44
+ fs.mkdirSync(targetDir, { recursive: true });
45
+ }
46
+
47
+ // Write all the pure files!
48
+ for (const file of componentData.files) {
49
+ const filePath = path.join(targetDir, file.name);
50
+ fs.writeFileSync(filePath, file.content);
51
+ }
52
+
53
+ spinner.succeed(chalk.green(`Downloaded ${componentData.files.length} files for ${componentName}`));
54
+
55
+ // Auto-install dependencies like framer-motion
56
+ if (componentData.dependencies && componentData.dependencies.length > 0) {
57
+ const depsToInstall = componentData.dependencies.join(" ");
58
+ const installSpinner = ora(`Installing dependencies: ${chalk.cyan(depsToInstall)}...`).start();
59
+
60
+ try {
61
+ execSync(`npm install ${depsToInstall}`, { stdio: "ignore" });
62
+ installSpinner.succeed(chalk.green("Dependencies installed successfully."));
63
+ } catch (error) {
64
+ installSpinner.fail(chalk.red("Failed to auto-install dependencies. Please install manually."));
65
+ }
66
+ }
67
+
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
+
71
+ } catch (error: any) {
72
+ spinner.fail(chalk.red(`Failed to add component.`));
73
+ console.error(chalk.red(error.message));
74
+ process.exit(1);
75
+ }
76
+ });
@@ -0,0 +1,99 @@
1
+ import { Command } from "commander";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { intro, outro, spinner, text, isCancel, cancel } from "@clack/prompts";
5
+ import chalk from "chalk";
6
+
7
+ export const init = new Command()
8
+ .name("init")
9
+ .description("Initialize DS01 in your React/Next.js TypeScript project")
10
+ .action(async () => {
11
+ intro(chalk.bgCyan.black.bold(" DS01 Setup Wizard "));
12
+
13
+ const s = spinner();
14
+ s.start("Checking project environment...");
15
+
16
+ const cwd = process.cwd();
17
+ const packageJsonPath = path.join(cwd, "package.json");
18
+ const tsconfigPath = path.join(cwd, "tsconfig.json");
19
+
20
+ // 1. Check for package.json
21
+ if (!fs.existsSync(packageJsonPath)) {
22
+ s.stop("Environment check failed.");
23
+ cancel("No package.json found. Please run this command in the root of your project.");
24
+ process.exit(1);
25
+ }
26
+
27
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
28
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
29
+
30
+ // 2. Check strictly for React or Next.js
31
+ if (!deps.react && !deps.next) {
32
+ s.stop("Environment check failed.");
33
+ cancel("DS01 strictly supports React and Next.js environments. Please run this in a supported project.");
34
+ process.exit(1);
35
+ }
36
+
37
+ // 3. Check strictly for TypeScript
38
+ if (!fs.existsSync(tsconfigPath)) {
39
+ s.stop("Environment check failed.");
40
+ cancel("DS01 requires TypeScript. No tsconfig.json found in the project root.");
41
+ process.exit(1);
42
+ }
43
+
44
+ s.stop(chalk.green("✔ React and TypeScript detected!"));
45
+
46
+ // 4. Ask the user where their global CSS is located
47
+ const cssPath = await text({
48
+ message: "Where is your global CSS file?",
49
+ placeholder: "app/globals.css",
50
+ initialValue: "app/globals.css",
51
+ });
52
+
53
+ if (isCancel(cssPath)) {
54
+ cancel("Setup cancelled.");
55
+ process.exit(0);
56
+ }
57
+
58
+ // 5. Inject Dark Mode / Glassmorphic CSS variables
59
+ const fullCssPath = path.join(cwd, cssPath as string);
60
+ if (fs.existsSync(fullCssPath)) {
61
+ const cssContent = fs.readFileSync(fullCssPath, "utf-8");
62
+
63
+ if (!cssContent.includes("/* ds01-theme-start */")) {
64
+ const variables = `
65
+ /* ds01-theme-start */
66
+ @layer base {
67
+ :root {
68
+ --ds-background: #0a0a0a; /* Deep charcoal */
69
+ --ds-foreground: #ffffff;
70
+ --ds-glass-border: rgba(255, 255, 255, 0.08);
71
+ --ds-glass-blur: blur(16px);
72
+ }
73
+ }
74
+ /* ds01-theme-end */
75
+ `;
76
+ fs.appendFileSync(fullCssPath, variables);
77
+ console.log(chalk.green(`✔ Injected design tokens into ${cssPath}`));
78
+ } else {
79
+ console.log(chalk.gray(`- Design tokens already exist in ${cssPath}`));
80
+ }
81
+ } else {
82
+ console.log(chalk.yellow(`⚠ Could not find ${cssPath}. You may need to add the CSS variables manually.`));
83
+ }
84
+
85
+ // 6. Create the local configuration file so the CLI remembers this project
86
+ const configPath = path.join(cwd, "ds01.config.json");
87
+ const configData = {
88
+ $schema: "https://ds-01.vercel.app/schema.json",
89
+ style: "default",
90
+ typescript: true,
91
+ cssPath: cssPath,
92
+ componentsPath: "components/ui" // This is where we will inject components later
93
+ };
94
+
95
+ fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));
96
+ console.log(chalk.green(`✔ Created ds01.config.json`));
97
+
98
+ outro(chalk.green.bold("DS01 successfully initialized! Ready to add components."));
99
+ });
@@ -3,15 +3,10 @@ import ora from "ora";
3
3
  import chalk from "chalk";
4
4
  import open from "open";
5
5
  import machineIdPkg from "node-machine-id";
6
-
7
- import fs from "fs";
8
- import path from "path";
9
- import os from "os";
6
+ import { saveToken } from "../utils/config.js"; // <-- Imported our new wallet helper!
10
7
 
11
8
  // ⚠️ In production, replace with your actual deployed Vercel domain
12
9
  const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
13
- const CONFIG_DIR = path.join(os.homedir(), ".ds01");
14
- const AUTH_FILE = path.join(CONFIG_DIR, "auth.json");
15
10
  const { machineIdSync } = machineIdPkg;
16
11
 
17
12
  export const login = new Command()
@@ -30,7 +25,7 @@ export const login = new Command()
30
25
  headers: {
31
26
  "Content-Type": "application/json",
32
27
  },
33
- body: JSON.stringify({ machineId: hardwareId }), // <-- Send the machine ID to the server
28
+ body: JSON.stringify({ machineId: hardwareId }),
34
29
  });
35
30
 
36
31
  if (!initResponse.ok) {
@@ -54,10 +49,9 @@ export const login = new Command()
54
49
 
55
50
  // 4. Poll the Token Endpoint
56
51
  let token = null;
57
- const pollInterval = (interval || 2) * 1000;
52
+ const pollInterval = (interval || 5) * 1000; // Updated to match our 5s secure interval
58
53
 
59
54
  while (!token) {
60
- // Wait before polling again
61
55
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
62
56
 
63
57
  const tokenResponse = await fetch(
@@ -74,30 +68,15 @@ export const login = new Command()
74
68
  if (tokenResponse.ok && tokenData.accessToken) {
75
69
  token = tokenData.accessToken;
76
70
  } else if (tokenData.error !== "authorization_pending") {
77
- // If error is anything OTHER than pending, fail out.
78
71
  spinner.fail(chalk.red("Authorization failed or expired."));
79
72
  console.error(chalk.red(`Reason: ${tokenData.error}`));
80
73
  process.exit(1);
81
74
  }
82
75
  }
83
76
 
84
- // 5. Save Token & Machine ID to ~/.ds01/auth.json
85
- if (!fs.existsSync(CONFIG_DIR)) {
86
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
87
- }
88
-
89
- fs.writeFileSync(
90
- AUTH_FILE,
91
- JSON.stringify(
92
- {
93
- token,
94
- machineId: hardwareId,
95
- updatedAt: new Date().toISOString(),
96
- },
97
- null,
98
- 2,
99
- ),
100
- );
77
+ // 5. Save Token securely using our new config utility
78
+ // We pass the token and hardwareId into the wallet.
79
+ saveToken(token, hardwareId);
101
80
 
102
81
  spinner.succeed(chalk.green.bold("Terminal paired successfully!"));
103
82
  console.log(
@@ -110,4 +89,4 @@ export const login = new Command()
110
89
  console.error(chalk.red(error.message));
111
90
  process.exit(1);
112
91
  }
113
- });
92
+ });
@@ -0,0 +1,29 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { deleteToken, getToken } from "../utils/config.js";
5
+
6
+ export const logout = new Command()
7
+ .name("logout")
8
+ .description("Log out of your DS01 account and clear local credentials")
9
+ .action(async () => {
10
+ const token = getToken();
11
+
12
+ if (!token) {
13
+ console.log(chalk.yellow("You are already logged out."));
14
+ return;
15
+ }
16
+
17
+ const spinner = ora("Logging out...").start();
18
+
19
+ try {
20
+ // Throw away the digital wallet / token
21
+ deleteToken();
22
+
23
+ spinner.succeed(chalk.green("Successfully logged out."));
24
+ console.log(chalk.gray("Your local credentials have been cleared."));
25
+ } catch (error) {
26
+ spinner.fail(chalk.red("Failed to log out cleanly."));
27
+ console.error(error);
28
+ }
29
+ });
@@ -0,0 +1,20 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { getToken } from "../utils/config.js";
4
+
5
+ export const whoami = new Command()
6
+ .name("whoami")
7
+ .description("Check your current authentication status")
8
+ .action(async () => {
9
+ const token = getToken();
10
+
11
+ if (!token) {
12
+ console.log(chalk.red("✖ You are not logged in."));
13
+ console.log(`Run ${chalk.cyan("npx @arpit2023/ds01 login")} to authenticate.`);
14
+ process.exit(1);
15
+ }
16
+
17
+ // For now, we just verify the token is saved.
18
+ console.log(chalk.green("✔ You are securely logged in!"));
19
+ console.log(chalk.gray(`Token found ending in: ...${token.slice(-6)}`));
20
+ });
package/src/index.ts CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  import { Command } from "commander";
4
4
  import { login } from "./commands/login.js";
5
- // Note: Add .js extension if you have module resolution issues, or keep it without depending on your tsconfig
5
+ import { whoami } from "./commands/whoami.js";
6
+ import { logout } from "./commands/logout.js";
7
+ import { init } from "./commands/init.js"; // <-- Imported the new init command!
6
8
 
7
9
  const program = new Command();
8
10
 
@@ -11,8 +13,11 @@ program
11
13
  .description("DS01 Custom Component Registry CLI")
12
14
  .version("1.0.0");
13
15
 
14
- // Register the login command
16
+ // Register the commands
15
17
  program.addCommand(login);
18
+ program.addCommand(whoami);
19
+ program.addCommand(logout);
20
+ program.addCommand(init); // <-- Registered it here!
16
21
 
17
22
  // Parse the arguments from the terminal
18
- program.parse(process.argv);
23
+ program.parse(process.argv);
@@ -0,0 +1,42 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+
5
+ // We use the same path you originally had for consistency
6
+ const configDir = path.join(os.homedir(), ".ds01");
7
+ const credentialsPath = path.join(configDir, "auth.json");
8
+
9
+ export function saveToken(token: string, machineId: string) {
10
+ if (!fs.existsSync(configDir)) {
11
+ fs.mkdirSync(configDir, { recursive: true });
12
+ }
13
+
14
+ const payload = {
15
+ token,
16
+ machineId,
17
+ updatedAt: new Date().toISOString(),
18
+ };
19
+
20
+ fs.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2), {
21
+ mode: 0o600,
22
+ });
23
+ }
24
+
25
+ export function getToken(): string | null {
26
+ if (fs.existsSync(credentialsPath)) {
27
+ try {
28
+ const data = fs.readFileSync(credentialsPath, "utf-8");
29
+ const parsed = JSON.parse(data);
30
+ return parsed.token || null;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+
38
+ export function deleteToken() {
39
+ if (fs.existsSync(credentialsPath)) {
40
+ fs.unlinkSync(credentialsPath);
41
+ }
42
+ }