ds-01 1.0.4 → 1.0.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.5 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
2
+ > ds-01@1.0.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;AAkDpC,eAAO,MAAM,GAAG,SAqIZ,CAAC"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgEpC,eAAO,MAAM,GAAG,SAkRZ,CAAC"}
@@ -4,24 +4,24 @@ import pc from "picocolors";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import { execSync } from "child_process";
7
- import { getToken, getMachineId } from "../utils/config.js";
7
+ import { getToken } from "../utils/config.js";
8
+ import { signWithDeviceKey } from "../utils/secureKeyStore.js";
9
+ import machineIdPkg from "node-machine-id";
10
+ const { machineIdSync } = machineIdPkg;
8
11
  const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
9
12
  // Helper to verify Tailwind exists before doing anything
10
13
  function checkTailwindInstallation() {
11
14
  const targetDir = process.cwd();
12
15
  const pkgJsonPath = path.join(targetDir, "package.json");
13
- // 1. Ensure they are in a valid Node.js project
14
16
  if (!fs.existsSync(pkgJsonPath)) {
15
17
  cancel(pc.red("No package.json found. Please run this command inside a Node.js project."));
16
18
  process.exit(1);
17
19
  }
18
- // 2. Read package.json dependencies
19
20
  const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
20
21
  const allDeps = {
21
22
  ...pkgJson.dependencies,
22
23
  ...pkgJson.devDependencies,
23
24
  };
24
- // 3. Strict Tailwind Check
25
25
  if (!allDeps["tailwindcss"]) {
26
26
  cancel(pc.red("Tailwind CSS is missing from your project dependencies.\n") +
27
27
  pc.gray("DS01 components rely strictly on Tailwind CSS for styling.\n\n") +
@@ -37,10 +37,8 @@ export const add = new Command()
37
37
  .description("Add a component from DS01 to your project")
38
38
  .argument("<component>", "The name of the component (e.g., premium-section)")
39
39
  .action(async (componentName) => {
40
- console.log(); // Spacing for visual breathing room
41
- // 1. Premium Header
40
+ console.log();
42
41
  intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("Adding Component")}`);
43
- // 2. Run Pre-Flight Tailwind Check
44
42
  checkTailwindInstallation();
45
43
  const cwd = process.cwd();
46
44
  const configPath = path.join(cwd, "ds01.config.json");
@@ -49,9 +47,8 @@ export const add = new Command()
49
47
  process.exit(1);
50
48
  }
51
49
  const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
52
- // Read the GLOBAL credentials, not the local project config
53
50
  const token = getToken();
54
- const machineId = getMachineId();
51
+ const machineId = machineIdSync();
55
52
  if (!token || !machineId) {
56
53
  cancel(pc.red("You are not authenticated. Run 'npx ds-01 login' first."));
57
54
  process.exit(1);
@@ -59,11 +56,31 @@ export const add = new Command()
59
56
  const s = spinner();
60
57
  s.start(`Fetching ${pc.cyan(`<${componentName} />`)} from registry...`);
61
58
  try {
62
- // 3. Fetch component from your API
59
+ // --------------------------------------------------
60
+ // 1. Create request-specific message
61
+ // --------------------------------------------------
62
+ const timestamp = Date.now().toString();
63
+ const message = [
64
+ "GET",
65
+ `/api/registry/${componentName}`,
66
+ timestamp,
67
+ machineId,
68
+ ].join("\n");
69
+ // --------------------------------------------------
70
+ // 2. Sign message using the device private key
71
+ // --------------------------------------------------
72
+ const signature = await signWithDeviceKey(new TextEncoder().encode(message));
73
+ const signatureBase64 = Buffer.from(signature).toString("base64");
74
+ // --------------------------------------------------
75
+ // 3. Send token + machine ID + signature
76
+ // --------------------------------------------------
63
77
  const response = await fetch(`${API_BASE_URL}/api/registry/${componentName}`, {
78
+ method: "GET",
64
79
  headers: {
65
80
  Authorization: `Bearer ${token}`,
66
- "X-Machine-ID": machineId, // <-- TypeScript error fixed here
81
+ "X-Machine-ID": machineId,
82
+ "X-DS01-Timestamp": timestamp,
83
+ "X-DS01-Signature": signatureBase64,
67
84
  },
68
85
  });
69
86
  if (!response.ok) {
@@ -72,54 +89,63 @@ export const add = new Command()
72
89
  process.exit(1);
73
90
  }
74
91
  const componentData = await response.json();
75
- // 4. Create dedicated component folder
92
+ // --------------------------------------------------
93
+ // 4. Create component folder
94
+ // --------------------------------------------------
76
95
  const targetDir = path.join(cwd, config.componentsPath, componentName);
77
96
  if (!fs.existsSync(targetDir)) {
78
- fs.mkdirSync(targetDir, { recursive: true });
97
+ fs.mkdirSync(targetDir, {
98
+ recursive: true,
99
+ });
79
100
  }
80
- // 🛠️ EXTRACT NAMES FOR SMART REWRITING (e.g., ["PremiumHero", "Section", "AnimatedText"])
81
- // We strip the extension (.tsx, .ts) to get the raw component name for import matching
101
+ // --------------------------------------------------
102
+ // 5. Extract bundled file names
103
+ // --------------------------------------------------
82
104
  const bundledFileNames = componentData.files.map((file) => file.name.replace(/\.[^/.]+$/, ""));
83
- // 5. Inject the pure code files
105
+ // --------------------------------------------------
106
+ // 6. Write component files
107
+ // --------------------------------------------------
84
108
  for (const file of componentData.files) {
85
- // Because the server sends just the basename (e.g., PremiumHero.tsx),
86
- // path.join inherently flattens all files into the single targetDir folder.
87
109
  const filePath = path.join(targetDir, file.name);
88
110
  let content = file.content;
89
- // 🛠️ DYNAMIC IMPORT REWRITER
90
111
  bundledFileNames.forEach((fileName) => {
91
- // Finds any relative import pointing to a bundled file (e.g., "../Section", "../../Section")
92
- // and rewrites it strictly to a sibling import (e.g., "./Section")
93
112
  const regex = new RegExp(`from\\s+["']\\.[^"']*?\\/${fileName}["']`, "g");
94
113
  content = content.replace(regex, `from "./${fileName}"`);
95
114
  });
96
115
  fs.writeFileSync(filePath, content);
97
116
  }
98
117
  s.stop(pc.green(`Downloaded ${componentData.files.length} files into ${pc.white(`/${config.componentsPath}/${componentName}`)}`));
99
- // 6. Auto-install Missing Component Dependencies (e.g., framer-motion)
100
- if (componentData.dependencies && componentData.dependencies.length > 0) {
118
+ // --------------------------------------------------
119
+ // 7. Install dependencies
120
+ // --------------------------------------------------
121
+ if (componentData.dependencies &&
122
+ componentData.dependencies.length > 0) {
101
123
  const depsToInstall = componentData.dependencies.join(" ");
102
124
  s.start(`Installing missing dependencies: ${pc.cyan(depsToInstall)}...`);
103
125
  try {
104
- // Silently runs the npm install command in the background
105
- execSync(`npm install ${depsToInstall}`, { stdio: "ignore" });
126
+ execSync(`npm install ${depsToInstall}`, {
127
+ stdio: "ignore",
128
+ });
106
129
  s.stop(pc.green(`Dependencies installed successfully: ${pc.gray(depsToInstall)}`));
107
130
  }
108
- catch (error) {
131
+ catch {
109
132
  s.stop(pc.red("Failed to auto-install dependencies."));
110
133
  note(`Please run: ${pc.cyan(`npm install ${depsToInstall}`)} manually.`, "Manual Action Required");
111
134
  }
112
135
  }
113
- // 7. Clean Success Outro
114
- // Find the main file to import (prioritize the one that matches the component name or use the first one)
115
- const mainFile = bundledFileNames.find((name) => name.toLowerCase() === componentName.toLowerCase()) || bundledFileNames[0];
136
+ // --------------------------------------------------
137
+ // 8. Success
138
+ // --------------------------------------------------
139
+ const mainFile = bundledFileNames.find((name) => name.toLowerCase() ===
140
+ componentName.toLowerCase()) || bundledFileNames[0];
116
141
  outro(`${pc.white("✔")} ${pc.bold(`Component <${componentName} /> is ready!`)}\n` +
117
- pc.gray(`Import it: `) +
142
+ pc.gray("Import it: ") +
118
143
  pc.cyan(`import { ${mainFile} } from "@/${config.componentsPath}/${componentName}/${mainFile}"`));
119
144
  }
120
145
  catch (error) {
121
146
  s.stop(pc.red("An error occurred during injection."));
122
- cancel(pc.gray(error.message || "Unknown CLI Error"));
147
+ cancel(pc.gray(error?.message ||
148
+ "Unknown CLI Error"));
123
149
  process.exit(1);
124
150
  }
125
151
  });
@@ -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;AAUpC,eAAO,MAAM,KAAK,SA4Fd,CAAC"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,eAAO,MAAM,KAAK,SAuTd,CAAC"}
@@ -1,75 +1,155 @@
1
1
  import { Command } from "commander";
2
- import { intro, outro, spinner, note, cancel } from "@clack/prompts";
2
+ import { intro, outro, spinner, note, cancel, } from "@clack/prompts";
3
3
  import pc from "picocolors";
4
4
  import open from "open";
5
5
  import machineIdPkg from "node-machine-id";
6
6
  import { saveToken } from "../utils/config.js";
7
- const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
7
+ import { createDeviceKey, getDevicePublicKey, } from "../utils/secureKeyStore.js";
8
+ const API_BASE_URL = process.env.DS01_API_URL ||
9
+ "https://ds-01.vercel.app";
8
10
  const { machineIdSync } = machineIdPkg;
9
11
  export const login = new Command()
10
12
  .name("login")
11
13
  .description("Authenticate your terminal with DS01 via browser")
12
14
  .action(async () => {
13
- console.log(); // Spacing for breathing room
14
- // 1. Header banner
15
+ console.log();
15
16
  intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("v1.0.0 — Terminal Authentication")}`);
16
17
  const s = spinner();
17
18
  try {
18
- // 2. Hardware ID Generation & Device Code Request
19
- s.start("Generating hardware fingerprint & requesting pairing code...");
19
+ // --------------------------------------------------
20
+ // 1. Create / load the device's secure key
21
+ // --------------------------------------------------
22
+ s.start("Preparing secure device identity...");
23
+ await createDeviceKey();
24
+ // Only the PUBLIC key/certificate leaves
25
+ // the user's machine.
26
+ const publicKey = await getDevicePublicKey();
27
+ // Existing machine fingerprint.
20
28
  const hardwareId = machineIdSync();
29
+ // Detect the cryptographic key type.
30
+ const publicKeyType = process.platform === "win32"
31
+ ? "windows-rsa"
32
+ : "macos-ec";
33
+ // --------------------------------------------------
34
+ // 2. Start device authorization
35
+ // --------------------------------------------------
36
+ s.start("Requesting pairing code...");
21
37
  const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
22
38
  method: "POST",
23
- headers: { "Content-Type": "application/json" },
24
- body: JSON.stringify({ machineId: hardwareId }),
39
+ headers: {
40
+ "Content-Type": "application/json",
41
+ },
42
+ body: JSON.stringify({
43
+ machineId: hardwareId,
44
+ publicKey,
45
+ publicKeyType,
46
+ }),
25
47
  });
26
48
  if (!initResponse.ok) {
27
49
  s.stop(pc.red("Failed to reach DS01 authorization server."));
28
50
  cancel(`Server responded with HTTP ${initResponse.status}`);
29
51
  process.exit(1);
30
52
  }
31
- const { deviceCode, userCode, verificationUrl, interval } = await initResponse.json();
32
- s.stop(pc.green("Device authorization code generated."));
33
- // 3. Highlighted Box for User Action
53
+ const { deviceCode, userCode, verificationUrl, interval, } = await initResponse.json();
54
+ if (!deviceCode ||
55
+ !userCode ||
56
+ !verificationUrl) {
57
+ throw new Error("Authorization server returned an invalid device response.");
58
+ }
59
+ s.stop(pc.green("Secure device identity registered."));
60
+ // --------------------------------------------------
61
+ // 3. Ask user to approve in browser
62
+ // --------------------------------------------------
34
63
  note(`${pc.bold("Pairing Code:")} ${pc.cyan(pc.bold(` ${userCode} `))}\n` +
35
64
  `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`, "Action Required");
36
- // 4. Auto-launch Browser
37
65
  try {
38
66
  await open(verificationUrl);
39
67
  }
40
68
  catch {
41
- // Non-blocking fallback if browser launch fails on headless setups
69
+ // Browser launch failure
70
+ // is non-fatal.
42
71
  }
43
- // 5. Polling Loop
72
+ // --------------------------------------------------
73
+ // 4. Poll for authorization
74
+ // --------------------------------------------------
44
75
  s.start("Waiting for web authorization...");
45
- let token = null;
46
76
  const pollInterval = (interval || 5) * 1000;
47
- while (!token) {
77
+ let token = null;
78
+ while (true) {
48
79
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
49
80
  const tokenResponse = await fetch(`${API_BASE_URL}/api/auth/device/token`, {
50
81
  method: "POST",
51
- headers: { "Content-Type": "application/json" },
52
- body: JSON.stringify({ deviceCode, machineId: hardwareId }),
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ },
85
+ body: JSON.stringify({
86
+ deviceCode,
87
+ machineId: hardwareId,
88
+ }),
53
89
  });
54
- const tokenData = await tokenResponse.json();
55
- if (tokenResponse.ok && tokenData.accessToken) {
56
- token = tokenData.accessToken;
90
+ let tokenData;
91
+ try {
92
+ tokenData =
93
+ await tokenResponse.json();
57
94
  }
58
- else if (tokenData.error !== "authorization_pending") {
59
- s.stop(pc.red("Authorization failed or expired."));
60
- cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
95
+ catch {
96
+ s.stop(pc.red("Authorization server returned invalid JSON."));
97
+ cancel("Unable to read authorization response.");
61
98
  process.exit(1);
62
99
  }
100
+ // --------------------------------------------------
101
+ // SUCCESS
102
+ // --------------------------------------------------
103
+ if (tokenResponse.ok &&
104
+ typeof tokenData.accessToken ===
105
+ "string" &&
106
+ tokenData.accessToken.length > 0) {
107
+ token =
108
+ tokenData.accessToken;
109
+ // IMPORTANT:
110
+ // Stop the spinner immediately.
111
+ s.stop(pc.green("Hardware signature linked & token verified!"));
112
+ // IMPORTANT:
113
+ // Leave the polling loop immediately.
114
+ break;
115
+ }
116
+ // --------------------------------------------------
117
+ // STILL WAITING
118
+ // --------------------------------------------------
119
+ if (tokenData.error ===
120
+ "authorization_pending") {
121
+ continue;
122
+ }
123
+ // --------------------------------------------------
124
+ // FAILED / EXPIRED
125
+ // --------------------------------------------------
126
+ s.stop(pc.red("Authorization failed or expired."));
127
+ cancel(`Reason: ${tokenData.error ||
128
+ "Unknown authorization error"}`);
129
+ process.exit(1);
130
+ }
131
+ // --------------------------------------------------
132
+ // 5. Make absolutely sure token exists
133
+ // --------------------------------------------------
134
+ if (!token) {
135
+ throw new Error("Authorization completed without receiving an access token.");
63
136
  }
64
- // 6. Save token to local wallet
137
+ // --------------------------------------------------
138
+ // 6. Save CLI token + machine ID
139
+ // --------------------------------------------------
65
140
  saveToken(token, hardwareId);
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.")}`);
141
+ // --------------------------------------------------
142
+ // 7. Success
143
+ // --------------------------------------------------
144
+ outro(`${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
145
+ pc.gray("Secure device key and session token are ready."));
69
146
  }
70
147
  catch (error) {
148
+ // Only stop the spinner if it is
149
+ // still running.
71
150
  s.stop(pc.red("An error occurred during login."));
72
- cancel(pc.gray(error.message || "Unknown CLI Error"));
151
+ cancel(pc.gray(error?.message ||
152
+ "Unknown CLI Error"));
73
153
  process.exit(1);
74
154
  }
75
155
  });
@@ -1 +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"}
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,MAAM,SAiDf,CAAC"}
@@ -2,24 +2,44 @@ import { Command } from "commander";
2
2
  import chalk from "chalk";
3
3
  import ora from "ora";
4
4
  import { deleteToken, getToken } from "../utils/config.js";
5
+ import machineIdPkg from "node-machine-id";
6
+ const { machineIdSync } = machineIdPkg;
7
+ const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
5
8
  export const logout = new Command()
6
9
  .name("logout")
7
- .description("Log out of your DS01 account and clear local credentials")
10
+ .description("Log out of your DS01 account and revoke the CLI session")
8
11
  .action(async () => {
9
12
  const token = getToken();
10
- if (!token) {
13
+ const machineId = machineIdSync();
14
+ if (!token || !machineId) {
11
15
  console.log(chalk.yellow("You are already logged out."));
12
16
  return;
13
17
  }
14
- const spinner = ora("Logging out...").start();
18
+ const spinner = ora("Revoking CLI session...").start();
15
19
  try {
16
- // Throw away the digital wallet / token
20
+ const response = await fetch(`${API_BASE_URL}/api/auth/cli/logout`, {
21
+ method: "POST",
22
+ headers: {
23
+ Authorization: `Bearer ${token}`,
24
+ "X-Machine-ID": machineId,
25
+ },
26
+ });
27
+ // NEW: Do not silently delete local credentials if the server
28
+ // failed to revoke the session.
29
+ if (!response.ok) {
30
+ const data = await response.json().catch(() => null);
31
+ throw new Error(data?.error || `Logout failed (HTTP ${response.status})`);
32
+ }
33
+ // NEW: Delete local credentials only after the server confirms
34
+ // that the CLI session has been revoked.
35
+ console.log("✅ Server confirmed CLI session was revoked.");
17
36
  deleteToken();
37
+ console.log("✅ Local credentials deleted.");
18
38
  spinner.succeed(chalk.green("Successfully logged out."));
19
- console.log(chalk.gray("Your local credentials have been cleared."));
39
+ console.log(chalk.gray("Your CLI session has been revoked and local credentials have been cleared."));
20
40
  }
21
41
  catch (error) {
22
- spinner.fail(chalk.red("Failed to log out cleanly."));
23
- console.error(error);
42
+ spinner.fail(chalk.red("Failed to log out."));
43
+ console.error(chalk.red(error.message || "Unknown logout error."));
24
44
  }
25
45
  });
package/dist/index.js CHANGED
@@ -17,4 +17,6 @@ program.addCommand(logout);
17
17
  program.addCommand(init);
18
18
  program.addCommand(add); // <-- 2. Register the add command!
19
19
  // Parse the arguments from the terminal
20
+ // It is the point where Commander reads the user's terminal input and decides
21
+ // which registered command's .action() function should execute.
20
22
  program.parse(process.argv);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=test-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-key.d.ts","sourceRoot":"","sources":["../src/test-key.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import { createDeviceKey, getDevicePublicKey, signWithDeviceKey, } from "./utils/secureKeyStore.js";
2
+ await createDeviceKey();
3
+ const publicKey = await getDevicePublicKey();
4
+ console.log("Public key:", publicKey);
5
+ const message = new TextEncoder().encode("hello DS01");
6
+ const signature = await signWithDeviceKey(message);
7
+ console.log("Signature:", Buffer.from(signature).toString("base64"));
@@ -1,5 +1,4 @@
1
1
  export declare function saveToken(token: string, machineId: string): void;
2
2
  export declare function getToken(): string | null;
3
- export declare function getMachineId(): string | null;
4
3
  export declare function deleteToken(): void;
5
4
  //# sourceMappingURL=config.d.ts.map
@@ -1 +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,YAAY,IAAI,MAAM,GAAG,IAAI,CAW5C;AAED,wBAAgB,WAAW,SAI1B"}
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;AAID,wBAAgB,WAAW,SAI1B"}
@@ -30,19 +30,6 @@ export function getToken() {
30
30
  }
31
31
  return null;
32
32
  }
33
- export function getMachineId() {
34
- if (fs.existsSync(credentialsPath)) {
35
- try {
36
- const data = fs.readFileSync(credentialsPath, "utf-8");
37
- const parsed = JSON.parse(data);
38
- return parsed.machineId || null;
39
- }
40
- catch {
41
- return null;
42
- }
43
- }
44
- return null;
45
- }
46
33
  export function deleteToken() {
47
34
  if (fs.existsSync(credentialsPath)) {
48
35
  fs.unlinkSync(credentialsPath);
@@ -0,0 +1,3 @@
1
+ import { webcrypto } from "node:crypto";
2
+ export declare function generateDeviceKeyPair(): Promise<webcrypto.CryptoKeyPair>;
3
+ //# sourceMappingURL=deviceKey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviceKey.d.ts","sourceRoot":"","sources":["../../src/utils/deviceKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC,wBAAsB,qBAAqB,qCAc1C"}
@@ -0,0 +1,13 @@
1
+ import { webcrypto } from "node:crypto";
2
+ const { subtle } = webcrypto;
3
+ export async function generateDeviceKeyPair() {
4
+ // Generate an asymmetric key pair:
5
+ // - publicKey → can be shared with the server
6
+ // - privateKey → stays on this device
7
+ const keyPair = await subtle.generateKey({
8
+ name: "ECDSA",
9
+ namedCurve: "P-256",
10
+ }, false, // 🔐 Private key is NON-EXPORTABLE
11
+ ["sign", "verify"]);
12
+ return keyPair;
13
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * ---------------------------------------------------------
3
+ * PUBLIC API
4
+ * ---------------------------------------------------------
5
+ */
6
+ export declare function createDeviceKey(): Promise<void>;
7
+ export declare function getDevicePublicKey(): Promise<string>;
8
+ export declare function signWithDeviceKey(data: Uint8Array): Promise<Uint8Array>;
9
+ export declare function deleteDeviceKey(): Promise<void>;
10
+ //# sourceMappingURL=secureKeyStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secureKeyStore.d.ts","sourceRoot":"","sources":["../../src/utils/secureKeyStore.ts"],"names":[],"mappings":"AAwTA;;;;GAIG;AAEH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CASrD;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAQ1D;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAQrB;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CASrD"}